The JS-facing entry point to the Java agent orchestrator, exposed as services.agentOrchestratorManager. agents-lib's _prompt function calls this instead of ai-lib's promptManager loop when the feature flag is on. The flag is checked on the JS side rather than here, so this class stays free of request-scoped setting lookups and remains straightforward to test. The tool layer is untouched: tools are still enumerated and executed by ai-lib, reached back through JsPromptFunctionsBridge, so the call path is JS to Java to JS, which is deliberate for this milestone - it keeps the change small enough that a parity comparison against the legacy loop is meaningful. Conversation storage stays in JS: this class neither reads nor writes ai-lib's ConversationContext, the caller instead passes the prior turns in as history and gets this turn's new items back out, then persists them itself, keeping a single owner for the on-disk transcript format. Identity is inherited, never passed: this runs inside the async job's operation, already scoped to the agent owner.

Group: Managers


Methods

newRun(String modelName)

Returns: RunBuilder

Begins configuring an agent turn. Everything except the model is optional, so callers only state what differs from the defaults, and later milestones (consent resume, an explicit run id) can add settings without breaking the call sites that do not use them.

ParameterDescription
modelNamethe LLM model to use, as named in the agent's config

pauseRunForConsent(Long runId, String chatItemId, String toolId, String specialist, String channel, String replyParams, int expirySecs)

Returns: String

Completes the pause of a run that broke out awaiting consent, now that the caller has saved the turn and so knows which chat item carries the request. Splitting this from execute() is what keeps the row honest: the pause is only recorded once it can name the thing it is waiting on, so there is never a row claiming to await consent that no resume could satisfy. Until this is called the run stays claimed, which is what stops a second dispatch picking it up.

ParameterDescription
runIdthe run returned by {@code execute()} as {@code runId}
chatItemIdthe chat item the consent block was saved onto
toolIdthe model's call id for the gated call
specialistthe specialist that raised it, or null when the run asked for the tool itself
channelthe channel identifier the request goes out over, so an unanswered one can be announced back over it
replyParamsthat channel's routing blob as JSON, carried opaquely
expirySecshow long the owner has to answer before the sweep expires it; must be positive

findRunAwaitingConsent(String correlationToken, Long adminOrgId)

Returns: Map<String,Object>

Finds the run a channel reply is answering, by the token that went out with the request. Returns the pointers a caller needs to resolve the approved call for itself - conversationId, chatItemId, toolId - not the call itself. Whether the approval is still good is decided by the chat item's own consent block, which carries the already-executed flag, so that check keeps happening in exactly one place rather than being duplicated against this row. Scoped to the tenant as well as the token, so a guessed or replayed value cannot cross tenant boundaries.

ParameterDescription
correlationTokenthe token that was embedded in the outbound consent request
adminOrgIdthe tenant the reply arrived for
To get full access to the Kademi Hub existing customers can login here, or new customers can register here.