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.
| Parameter | Description |
|---|---|
modelName | the 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.
| Parameter | Description |
|---|---|
runId | the run returned by {@code execute()} as {@code runId} |
chatItemId | the chat item the consent block was saved onto |
toolId | the model's call id for the gated call |
specialist | the specialist that raised it, or null when the run asked for the tool itself |
channel | the channel identifier the request goes out over, so an unanswered one can be announced back over it |
replyParams | that channel's routing blob as JSON, carried opaquely |
expirySecs | how 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.
| Parameter | Description |
|---|---|
correlationToken | the token that was embedded in the outbound consent request |
adminOrgId | the tenant the reply arrived for |