@librechat/agents 3.4.4 → 3.4.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/common/enum.cjs +2 -0
- package/dist/cjs/common/enum.cjs.map +1 -1
- package/dist/cjs/graphs/Graph.cjs +268 -14
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/hitl/askUserQuestions.cjs +66 -0
- package/dist/cjs/hitl/askUserQuestions.cjs.map +1 -0
- package/dist/cjs/hitl/askUserQuestionsInterrupt.cjs +46 -0
- package/dist/cjs/hitl/askUserQuestionsInterrupt.cjs.map +1 -0
- package/dist/cjs/hitl/index.cjs +2 -0
- package/dist/cjs/langfuseTraceShaping.cjs +9 -6
- package/dist/cjs/langfuseTraceShaping.cjs.map +1 -1
- package/dist/cjs/main.cjs +6 -0
- package/dist/cjs/run.cjs +147 -14
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/cjs/session/AgentSession.cjs +93 -31
- package/dist/cjs/session/AgentSession.cjs.map +1 -1
- package/dist/cjs/session/handlers.cjs +10 -0
- package/dist/cjs/session/handlers.cjs.map +1 -1
- package/dist/cjs/stream.cjs +17 -3
- package/dist/cjs/stream.cjs.map +1 -1
- package/dist/cjs/summarization/node.cjs +1 -0
- package/dist/cjs/summarization/node.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +4 -2
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/handlers.cjs +1 -0
- package/dist/cjs/tools/handlers.cjs.map +1 -1
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs +28 -0
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs.map +1 -1
- package/dist/cjs/types/hitl.cjs +13 -0
- package/dist/cjs/types/hitl.cjs.map +1 -0
- package/dist/cjs/utils/handlers.cjs +3 -0
- package/dist/cjs/utils/handlers.cjs.map +1 -1
- package/dist/esm/common/enum.mjs +2 -0
- package/dist/esm/common/enum.mjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +268 -14
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/hitl/askUserQuestions.mjs +66 -0
- package/dist/esm/hitl/askUserQuestions.mjs.map +1 -0
- package/dist/esm/hitl/askUserQuestionsInterrupt.mjs +43 -0
- package/dist/esm/hitl/askUserQuestionsInterrupt.mjs.map +1 -0
- package/dist/esm/hitl/index.mjs +2 -0
- package/dist/esm/langfuseTraceShaping.mjs +9 -6
- package/dist/esm/langfuseTraceShaping.mjs.map +1 -1
- package/dist/esm/main.mjs +3 -1
- package/dist/esm/run.mjs +147 -14
- package/dist/esm/run.mjs.map +1 -1
- package/dist/esm/session/AgentSession.mjs +93 -31
- package/dist/esm/session/AgentSession.mjs.map +1 -1
- package/dist/esm/session/handlers.mjs +10 -0
- package/dist/esm/session/handlers.mjs.map +1 -1
- package/dist/esm/stream.mjs +17 -3
- package/dist/esm/stream.mjs.map +1 -1
- package/dist/esm/summarization/node.mjs +1 -0
- package/dist/esm/summarization/node.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +4 -2
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/handlers.mjs +1 -0
- package/dist/esm/tools/handlers.mjs.map +1 -1
- package/dist/esm/tools/subagent/SubagentExecutor.mjs +28 -0
- package/dist/esm/tools/subagent/SubagentExecutor.mjs.map +1 -1
- package/dist/esm/types/hitl.mjs +13 -0
- package/dist/esm/types/hitl.mjs.map +1 -0
- package/dist/esm/utils/handlers.mjs +3 -0
- package/dist/esm/utils/handlers.mjs.map +1 -1
- package/dist/types/common/enum.d.ts +2 -0
- package/dist/types/graphs/Graph.d.ts +87 -0
- package/dist/types/hitl/askUserQuestions.d.ts +24 -0
- package/dist/types/hitl/askUserQuestionsInterrupt.d.ts +11 -0
- package/dist/types/hitl/index.d.ts +2 -0
- package/dist/types/langfuseTraceShaping.d.ts +4 -3
- package/dist/types/run.d.ts +9 -1
- package/dist/types/session/types.d.ts +1 -1
- package/dist/types/summarization/node.d.ts +6 -0
- package/dist/types/types/graph.d.ts +3 -3
- package/dist/types/types/hitl.d.ts +31 -2
- package/dist/types/types/stream.d.ts +57 -0
- package/dist/types/utils/handlers.d.ts +1 -0
- package/package.json +2 -1
- package/src/aggregator.test.ts +127 -0
- package/src/common/enum.ts +2 -0
- package/src/graphs/Graph.ts +365 -7
- package/src/graphs/__tests__/Graph.closeRunStep.test.ts +385 -0
- package/src/hitl/askUserQuestions.ts +126 -0
- package/src/hitl/askUserQuestionsInterrupt.ts +115 -0
- package/src/hitl/index.ts +6 -0
- package/src/langfuseTraceShaping.ts +14 -7
- package/src/run.ts +219 -41
- package/src/session/AgentSession.ts +105 -13
- package/src/session/handlers.ts +14 -2
- package/src/session/types.ts +1 -0
- package/src/specs/activity-label-observability.live.test.ts +262 -0
- package/src/specs/activity-label-observability.test.ts +167 -0
- package/src/specs/ask-user-questions.live.test.ts +185 -0
- package/src/specs/ask-user-questions.test.ts +293 -0
- package/src/specs/langfuse-trace-shaping.test.ts +32 -0
- package/src/specs/run-step-timestamps.test.ts +414 -0
- package/src/stream.ts +29 -6
- package/src/summarization/node.ts +11 -0
- package/src/tools/ToolNode.ts +2 -0
- package/src/tools/__tests__/SubagentExecutor.test.ts +52 -10
- package/src/tools/__tests__/handlers.test.ts +2 -0
- package/src/tools/handlers.ts +1 -0
- package/src/tools/subagent/SubagentExecutor.ts +46 -4
- package/src/types/graph.ts +9 -6
- package/src/types/hitl.ts +36 -2
- package/src/types/stream.ts +65 -12
- package/src/utils/handlers.ts +13 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
//#region src/types/hitl.ts
|
|
2
|
+
/**
|
|
3
|
+
* Type guard narrowing an arbitrary value to an
|
|
4
|
+
* `AskUserQuestionInterruptPayload`. Same `unknown`-tolerant contract
|
|
5
|
+
* as `isToolApprovalInterrupt`.
|
|
6
|
+
*/
|
|
7
|
+
function isAskUserQuestionInterrupt(payload) {
|
|
8
|
+
return typeof payload === "object" && payload !== null && payload.type === "ask_user_question";
|
|
9
|
+
}
|
|
10
|
+
//#endregion
|
|
11
|
+
exports.isAskUserQuestionInterrupt = isAskUserQuestionInterrupt;
|
|
12
|
+
|
|
13
|
+
//# sourceMappingURL=hitl.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hitl.cjs","names":[],"sources":["../../../src/types/hitl.ts"],"sourcesContent":["/**\n * First-class human-in-the-loop (HITL) types for `@librechat/agents`.\n * Surfaces the interrupt payload that `ToolNode` raises when a `PreToolUse`\n * hook returns `decision: 'ask'` and HITL is enabled on the run, plus the\n * resume-decision shape the host returns to continue or reject the tool.\n *\n * Mirrors the LangChain HITL middleware shape (action_requests /\n * review_configs) so hosts and clients can share rendering/UI semantics\n * across the langchain ecosystem.\n */\n\n/** Per-tool approval request emitted inside an interrupt payload. */\nexport interface ToolApprovalRequest {\n /** Stable id of the tool call (matches LangGraph `ToolCall.id`). */\n tool_call_id: string;\n /** Tool name being invoked. */\n name: string;\n /**\n * Arguments the tool is about to be invoked with — already resolved by\n * any `{{tool<i>turn<n>}}` references and any `updatedInput` returned\n * by the firing PreToolUse hook.\n */\n arguments: Record<string, unknown>;\n /**\n * Optional reason the hook supplied for asking (e.g., \"destructive\n * filesystem write\"). Hosts can render this verbatim.\n */\n description?: string;\n}\n\n/** Allowed host-side decisions for a `tool_approval` interrupt. */\nexport type ToolApprovalDecisionType =\n | 'approve'\n | 'reject'\n | 'edit'\n | 'respond';\n\n/** Per-action review configuration paired with each action_request. */\nexport interface ToolApprovalReviewConfig {\n /** Tool name (matches the `name` field on the corresponding action_request). */\n action_name: string;\n /**\n * Stable id of the tool call this review_config applies to (matches\n * the `tool_call_id` of the corresponding action_request). Lets a UI\n * map review_configs → action_requests directly when a batch\n * contains the same tool called more than once — by-position\n * mapping breaks down with duplicates.\n */\n tool_call_id: string;\n /** Decisions the host UI is allowed to surface for this action. */\n allowed_decisions: ToolApprovalDecisionType[];\n}\n\n/**\n * Resume value the host returns through `Run.resume(decisions)` after a\n * `tool_approval` interrupt. One entry per action_request, in the same\n * order. Hosts may also return a record keyed by `tool_call_id`; the SDK\n * handles either shape.\n *\n * Variants:\n * - `approve`: run the tool with its original (or hook-rewritten) args.\n * - `reject`: skip the tool, emit a blocked error `ToolMessage` with\n * `reason` surfaced to the model.\n * - `edit`: replace the tool's args with `updatedInput` (re-resolves\n * any `{{tool<i>turn<n>}}` placeholders) and run the tool.\n * - `respond`: skip the tool entirely and emit `responseText` as a\n * successful `ToolMessage`. Mirrors LangChain HITL middleware's\n * `respond` semantic — the human supplies the result the model sees,\n * bypassing tool execution. Useful when the user wants to short-circuit\n * a tool call with a hand-written answer (e.g., \"don't actually run\n * the search, just tell the model 'no relevant results'\").\n *\n * Note on hook semantics: `respond` does NOT fire the per-tool\n * `PostToolUse` hook (no real tool execution happened, so the\n * \"post-tool\" semantic doesn't apply). It DOES appear in the\n * `PostToolBatch` entry array with `status: 'success'` and the\n * user-supplied text as `toolOutput`, so batch-level audit /\n * convention hooks see the full set of outcomes.\n */\nexport type ToolApprovalDecision =\n | { type: 'approve' }\n | { type: 'reject'; reason?: string }\n | { type: 'edit'; updatedInput: Record<string, unknown> }\n | { type: 'respond'; responseText: string };\n\n/** Map form of resume decisions, keyed by tool call id. */\nexport type ToolApprovalDecisionMap = Record<string, ToolApprovalDecision>;\n\n/**\n * Categories of human-in-the-loop interrupts the SDK can raise. Hosts\n * narrow on `HumanInterruptPayload.type` to determine which payload\n * shape they're handling and which resume value to send back through\n * `Run.resume()`.\n *\n * Exported as a discrete type so downstream consumers (notably\n * LibreChat's wire types in `librechat-data-provider`) can mirror\n * the discriminator alongside their own host-side `PendingAction`\n * record without re-declaring the union themselves. Internal SDK\n * code narrows directly on the literal strings via the type guards\n * below; this type alias is primarily an integration-layer contract.\n */\nexport type HumanInterruptType = 'tool_approval' | 'ask_user_question';\n\n/** Identifies an interrupt that originated inside a checkpointed subagent. */\nexport interface SubagentInterruptScope {\n /** Child execution run id used by subagent update and usage events. */\n run_id: string;\n /** Child agent id that owns the interrupted tool call. */\n agent_id: string;\n /** Configured subagent type selected by the parent tool call. */\n subagent_type: string;\n /** Parent `subagent` tool call that launched this child. */\n parent_tool_call_id?: string;\n}\n\n/**\n * Structured payload the SDK passes to `interrupt()` when one or more\n * pending tool calls require host approval. All `ask`-decision tool calls\n * from a single ToolNode batch are bundled into one interrupt so the host\n * can render and resolve them together.\n *\n * Resume value: `ToolApprovalDecision[]` (in `action_requests` order) or\n * `ToolApprovalDecisionMap` (keyed by `tool_call_id`).\n */\nexport interface ToolApprovalInterruptPayload {\n type: 'tool_approval';\n action_requests: ToolApprovalRequest[];\n review_configs: ToolApprovalReviewConfig[];\n /** Hook-registry session whose policy raised this interrupt. */\n hook_session_id?: string;\n /** Present when the approval request was bridged from a child graph. */\n subagent?: SubagentInterruptScope;\n}\n\n/**\n * Pre-defined option the user can pick when answering an\n * `ask_user_question` interrupt. The selected option's `value` becomes\n * the resume value's `answer` field.\n */\nexport interface AskUserQuestionOption {\n /** Human-readable label rendered in the host UI. */\n label: string;\n /** Value returned via `AskUserQuestionResolution.answer` if picked. */\n value: string;\n}\n\n/** Question request emitted inside an `ask_user_question` interrupt. */\nexport interface AskUserQuestionRequest {\n /** The question to ask the human. */\n question: string;\n /** Optional context / description rendered alongside the question. */\n description?: string;\n /**\n * Optional pre-defined response options. When present, hosts can render\n * a picker; the user may still type a free-form answer when the host\n * UI allows it. Omit to require a free-form answer.\n */\n options?: AskUserQuestionOption[];\n /**\n * When `true`, the host UI may let the user pick several options; the\n * resulting `AskUserQuestionResolution.answer` is the selected option\n * values joined by `\", \"`. When omitted or `false`, hosts render a\n * single-select picker. Only meaningful alongside `options`.\n */\n multiSelect?: boolean;\n}\n\n/** One independently answerable question in a batched question request. */\nexport interface AskUserQuestionBatchItem extends AskUserQuestionRequest {\n /** Batch-unique identifier (`[A-Za-z][A-Za-z0-9_-]{0,63}`). */\n id: string;\n /** Optional short heading rendered above the question. */\n header?: string;\n}\n\n/** Input shape for one tool call that asks one to four questions together. */\nexport interface AskUserQuestionsRequest {\n questions: AskUserQuestionBatchItem[];\n}\n\n/**\n * Structured payload the SDK passes to `interrupt()` when an agent (or\n * a custom node) needs to ask the user a clarifying question. Mirrors\n * Claude Code's `AskUserQuestion` semantic. Resume value is\n * `AskUserQuestionResolution` for a single question, or\n * `AskUserQuestionsResolution` when `questions` is present.\n */\nexport interface AskUserQuestionInterruptPayload {\n type: 'ask_user_question';\n /**\n * Single-question request, or the first question as a compatibility\n * fallback when `questions` contains a batch. This lets existing hosts show\n * a useful preview during a staged rollout, but they must support `questions`\n * and `AskUserQuestionsResolution` before enabling a batched tool schema.\n */\n question: AskUserQuestionRequest;\n /** One to four questions collected by one `ask_user_question` tool call. */\n questions?: AskUserQuestionsRequest['questions'];\n /**\n * The `tool_call_id` of the ask-tool call that raised this interrupt,\n * when the tool body supplied it (see `askUserQuestion`'s `options`).\n * Lets hosts attribute the question — and later the answer — to the\n * exact tool-call content part instead of guessing by position, which\n * mislabels cards when a model emits several ask calls in one turn.\n */\n tool_call_id?: string;\n}\n\n/** Batch-specialized ask payload for hosts that render several questions. */\nexport interface AskUserQuestionsInterruptPayload\n extends AskUserQuestionInterruptPayload {\n questions: AskUserQuestionsRequest['questions'];\n}\n\n/**\n * Discriminated union of every interrupt payload the SDK raises. New\n * variants can be added without breaking existing handlers as long as\n * those handlers check `payload.type` before reading variant-specific\n * fields. Use the `isToolApprovalInterrupt` / `isAskUserQuestionInterrupt`\n * type guards for ergonomic narrowing.\n */\nexport type HumanInterruptPayload =\n | ToolApprovalInterruptPayload\n | AskUserQuestionInterruptPayload;\n\n/** Resume value the host returns for an `ask_user_question` interrupt. */\nexport interface AskUserQuestionResolution {\n /**\n * The human's answer. Free-form text, or — when `options` were\n * provided — one of the option `value`s (or, when the request set\n * `multiSelect`, several option `value`s joined by `\", \"`). Hosts may\n * also send any structured object their custom UI defines; see the\n * host docs for what your downstream consumer expects.\n */\n answer: string;\n}\n\n/** Resume value for a batched `ask_user_question` interrupt. */\nexport interface AskUserQuestionsResolution {\n /** Human answers keyed by each `AskUserQuestionBatchItem.id`. */\n answers: Record<string, string>;\n}\n\n/**\n * Type guard narrowing an arbitrary value to a `ToolApprovalInterruptPayload`.\n * Accepts `unknown` (not just `HumanInterruptPayload`) because hosts can\n * raise custom interrupt payloads from custom nodes — `getInterrupt()`\n * surfaces them as-is, and downstream code must validate the shape at\n * runtime before reading variant-specific fields.\n */\nexport function isToolApprovalInterrupt(\n payload: unknown\n): payload is ToolApprovalInterruptPayload {\n return (\n typeof payload === 'object' &&\n payload !== null &&\n (payload as { type?: unknown }).type === 'tool_approval'\n );\n}\n\n/**\n * Type guard narrowing an arbitrary value to an\n * `AskUserQuestionInterruptPayload`. Same `unknown`-tolerant contract\n * as `isToolApprovalInterrupt`.\n */\nexport function isAskUserQuestionInterrupt(\n payload: unknown\n): payload is AskUserQuestionInterruptPayload {\n return (\n typeof payload === 'object' &&\n payload !== null &&\n (payload as { type?: unknown }).type === 'ask_user_question'\n );\n}\n\n/**\n * Run-level configuration controlling HITL semantics. **HITL is OFF by\n * default** for now — the SDK ships the interrupt machinery, but the\n * default stays opt-in until host UIs (notably LibreChat) ship the\n * approval-rendering affordances needed to surface interrupts to end\n * users. Without that UI, an interrupt with no resolver looks like a\n * hung tool-call card. Hosts opt in explicitly with\n * `{ enabled: true }`. The intent is to flip this default to ON in a\n * future minor once the consumer ecosystem is ready to render\n * interrupts end-to-end.\n *\n * When enabled (`{ enabled: true }`):\n *\n * - `PreToolUse` hooks returning `decision: 'ask'` raise a real\n * LangGraph `interrupt()` instead of being treated as a synchronous\n * deny.\n * - `Run.create` installs a `MemorySaver` checkpointer fallback on the\n * run's compile options if the host did not provide one, since\n * LangGraph requires a checkpointer to suspend and resume.\n *\n * When disabled (the default — omitted, or `{ enabled: false }`):\n * `ask` decisions are fail-closed (blocked with an error\n * `ToolMessage`) and no checkpointer is implicitly attached. This\n * matches the pre-HITL behavior so existing hosts upgrading the SDK\n * see no change until they're ready to wire the resume UI.\n *\n * ## Scope: every tool the ToolNode runs\n *\n * The interrupt path is wired into both `dispatchToolEvents` (the\n * event-driven path) and `runDirectToolWithLifecycleHooks` (the\n * direct path used by `directToolNames` entries — graph-managed\n * handoff/subagent tools and every in-process `graphTool` instance).\n * `PreToolUse` hooks fire for every tool the ToolNode invokes, and\n * HITL approval gates every tool whose hook returns `'ask'` —\n * regardless of whether the tool is dispatched as an event or\n * invoked in-process. This convergence happened in two follow-up\n * commits to the original HITL surface (see `Graph.ts` —\n * `hookRegistry`/`humanInTheLoop` are passed in both\n * event-driven and legacy branches; and `ToolNode.runDirectToolWithLifecycleHooks`\n * — direct-path tools build their own single-tool `tool_approval`\n * payload and raise `interrupt()` the same way the event path does).\n *\n * Practical implications:\n * - Every host gets the full HITL surface across every tool the\n * model calls — event-dispatched, direct, mixed.\n * - `createToolPolicyHook` and `createWorkspacePolicyHook` apply\n * uniformly. A hook can be registered without knowing or caring\n * which path the tool will take.\n * - Direct tools that the host opted into via `directToolNames` no\n * longer bypass policy. If you need a tool to skip the hook\n * surface entirely, omit it from any registered matcher.\n *\n * ## Resume re-execution: every tool in the interrupted batch\n *\n * LangGraph rolls back to the start of the interrupted node on\n * resume. That means **every tool in the same batch as the one that\n * interrupted re-runs from the top on the resume pass**, not just\n * the interrupting tool, and not just the direct half (this used to\n * be framed as a direct-tool-specific concern; it is not — it\n * applies to event-dispatched siblings too). Practical contract:\n *\n * - The body of the interrupting tool itself runs **once** total\n * (the first pass interrupted *before* the body, the resume pass\n * ran the body after the host's decision was applied).\n * - The body of any sibling tool that already executed in the\n * same batch before the interrupting tool runs **twice** — once\n * on the first pass, once on the resume pass.\n * - `PreToolUse` hooks fire **once per pass per tool**. A hook\n * that always returns `'ask'` will loop forever on resume; real\n * hooks should be deterministic w.r.t. inputs and use the\n * `'ask' → host approves → resume → hook returns 'allow'`\n * pattern, where the second-pass `allow` reflects the host\n * having recorded the approval (e.g., a session-scoped approved-\n * paths set keyed by `runId`).\n *\n * Consequence: any tool with side effects MUST be idempotent if\n * there's any chance another tool in the same batch could trigger\n * an interrupt. This applies equally to direct tools (handoffs,\n * subagents) and to event tools.\n *\n * ### Guarding non-idempotent siblings via `interruptingToolNames`\n *\n * The \"must be idempotent\" rule above is unavoidable in the general\n * case, but the SDK can protect siblings against the one interrupt\n * shape it can predict: a tool whose *body* raises `interrupt()`\n * mid-execution — the `ask_user_question` shape, where the tool\n * suspends the run to collect a human answer. Declare such tools in\n * `RunConfig.interruptingToolNames`\n * ({@link ToolNodeOptions.interruptingToolNames}) and the ToolNode\n * schedules them, within each batch, **ahead of** their\n * non-interrupting direct siblings. When one interrupts, the batch\n * unwinds before any declared-safe sibling has run, so the sibling\n * executes exactly once (on resume) instead of twice. Empirically:\n *\n * - A **direct** sibling sharing the interrupter's in-process\n * `Promise.all` is the only shape that double-executes; declaring\n * the interrupter closes it.\n * - An **event-dispatched** sibling is already safe without any\n * config: the ToolNode awaits the whole direct group (where the\n * body interrupt unwinds) before it dispatches event tools, so a\n * dispatched sibling never runs on the first pass.\n *\n * This is a *scheduling* guard, not full resume idempotency: it only\n * covers tools that interrupt from their own body and only protects\n * siblings scheduled after them. It does not retroactively make a\n * `PreToolUse` `'ask'` gate on tool B stop tool A (already executed)\n * from re-running — unless B is itself declared interrupting, so it\n * runs first. Tools with side effects should still be written\n * idempotent as defense in depth.\n *\n * The guard only REORDERS the direct group — declaring a name does not\n * force it onto the direct path. The interrupting tool must already be a\n * real in-process graphTool (the only kind whose body can reach\n * `interrupt()`). A name that resolves to a schema-only event stub (an\n * inherited `toolDefinition` with no executable instance, e.g. in a\n * self-spawned child that scrubs `graphTools`) stays event-dispatched\n * and the ordering is a no-op for it.\n *\n * ## Note on idempotency\n *\n * Same root cause as the resume re-execution above: LangGraph\n * re-runs the interrupted node from the start on resume, which\n * fires `PreToolUse` hooks again. Hooks that produce side effects\n * (logging, external calls) will see at least two invocations per\n * paused turn — exactly two for the interrupting tool, possibly\n * more across siblings.\n */\nexport interface HumanInTheLoopConfig {\n /**\n * Master switch. Defaults to `false` — omit the field (or pass\n * `false`) to keep HITL off, or set `true` to opt in once the host\n * UI is ready to render and resolve `tool_approval` interrupts.\n */\n enabled?: boolean;\n}\n\n/**\n * Snapshot of an in-flight interrupt surfaced from `Run.processStream`\n * via `run.getInterrupt()`. Hosts persist this alongside their job\n * record so they can later call `Run.resume(decisions)` against a Run\n * compiled with the same `thread_id` / checkpointer.\n *\n * The `payload` type defaults to `HumanInterruptPayload` (the SDK's\n * built-in `tool_approval` / `ask_user_question` discriminated union)\n * for ergonomic narrowing in the common case. Hosts that raise custom\n * interrupt payloads from custom graph nodes can pass the type\n * parameter (`run.getInterrupt<MyCustom>()` or\n * `RunInterruptResult<MyCustom>`) — the SDK does not validate the\n * runtime shape, it just transports whatever the node passed to\n * `interrupt()`. Use the `isToolApprovalInterrupt` /\n * `isAskUserQuestionInterrupt` guards (which accept `unknown`) when\n * the source of the interrupt isn't statically known.\n */\nexport interface RunInterruptResult<TPayload = HumanInterruptPayload> {\n /** Stable id of the LangGraph interrupt (from `Interrupt.id`). */\n interruptId: string;\n /** `thread_id` the run was bound to — required to resume. */\n threadId?: string;\n /** LangGraph checkpoint id that contains the paused interrupt task. */\n checkpointId?: string;\n /** LangGraph checkpoint namespace for the paused interrupt task. */\n checkpointNs?: string;\n /** Structured payload describing what needs human input. */\n payload: TPayload;\n}\n"],"mappings":";;;;;;AAyQA,SAAgB,2BACd,SAC4C;CAC5C,OACE,OAAO,YAAY,YACnB,YAAY,QACX,QAA+B,SAAS;AAE7C"}
|
|
@@ -36,6 +36,9 @@ function createHandlers(callbacks) {
|
|
|
36
36
|
});
|
|
37
37
|
callbacks?.onRunStepDelta?.(event, data);
|
|
38
38
|
} },
|
|
39
|
+
["on_run_step_closed"]: { handle: (event, data) => {
|
|
40
|
+
callbacks?.onRunStepClosed?.(event, data);
|
|
41
|
+
} },
|
|
39
42
|
["on_message_delta"]: { handle: (event, data) => {
|
|
40
43
|
aggregateContent({
|
|
41
44
|
event,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handlers.cjs","names":["createContentAggregator","ToolEndHandler","ModelEndHandler","ChatModelStreamHandler"],"sources":["../../../src/utils/handlers.ts"],"sourcesContent":["/**\n * Multi-Agent Handler Utilities\n *\n * Provides a simple helper to create handlers with content aggregation for multi-agent scripts.\n *\n * Usage:\n * ```typescript\n * const { contentParts, aggregateContent, handlers } = createHandlers();\n *\n * // With callbacks\n * const { contentParts, aggregateContent, handlers } = createHandlers({\n * onRunStep: (event, data) => console.log('Step:', data),\n * onRunStepCompleted: (event, data) => console.log('Completed:', data)\n * });\n * ```\n */\n\nimport type * as t from '@/types';\nimport { ChatModelStreamHandler, createContentAggregator } from '@/stream';\nimport { ToolEndHandler, ModelEndHandler } from '@/events';\nimport { GraphEvents } from '@/common';\n\ninterface HandlerCallbacks {\n onRunStep?: (event: GraphEvents.ON_RUN_STEP, data: t.StreamEventData) => void;\n onRunStepCompleted?: (\n event: GraphEvents.ON_RUN_STEP_COMPLETED,\n data: t.StreamEventData\n ) => void;\n onRunStepDelta?: (\n event: GraphEvents.ON_RUN_STEP_DELTA,\n data: t.StreamEventData\n ) => void;\n onMessageDelta?: (\n event: GraphEvents.ON_MESSAGE_DELTA,\n data: t.StreamEventData\n ) => void;\n}\n\n/**\n * Creates handlers with content aggregation for multi-agent scripts\n */\nexport function createHandlers(callbacks?: HandlerCallbacks): {\n contentParts: Array<t.MessageContentComplex | undefined>;\n aggregateContent: ReturnType<\n typeof createContentAggregator\n >['aggregateContent'];\n handlers: Record<string, t.EventHandler>;\n} {\n // Set up content aggregator\n const { contentParts, aggregateContent } = createContentAggregator();\n\n // Create the handlers object\n const handlers = {\n [GraphEvents.TOOL_END]: new ToolEndHandler(),\n [GraphEvents.CHAT_MODEL_END]: new ModelEndHandler(),\n [GraphEvents.CHAT_MODEL_STREAM]: new ChatModelStreamHandler(),\n\n [GraphEvents.ON_RUN_STEP]: {\n handle: (\n event: GraphEvents.ON_RUN_STEP,\n data: t.StreamEventData\n ): void => {\n aggregateContent({ event, data: data as t.RunStep });\n callbacks?.onRunStep?.(event, data);\n },\n },\n\n [GraphEvents.ON_RUN_STEP_COMPLETED]: {\n handle: (\n event: GraphEvents.ON_RUN_STEP_COMPLETED,\n data: t.StreamEventData\n ): void => {\n aggregateContent({\n event,\n data: data as unknown as { result: t.ToolEndEvent },\n });\n callbacks?.onRunStepCompleted?.(event, data);\n },\n },\n\n [GraphEvents.ON_RUN_STEP_DELTA]: {\n handle: (\n event: GraphEvents.ON_RUN_STEP_DELTA,\n data: t.StreamEventData\n ): void => {\n aggregateContent({ event, data: data as t.RunStepDeltaEvent });\n callbacks?.onRunStepDelta?.(event, data);\n },\n },\n\n [GraphEvents.ON_MESSAGE_DELTA]: {\n handle: (\n event: GraphEvents.ON_MESSAGE_DELTA,\n data: t.StreamEventData\n ): void => {\n aggregateContent({ event, data: data as t.MessageDeltaEvent });\n callbacks?.onMessageDelta?.(event, data);\n },\n },\n\n [GraphEvents.ON_SUMMARIZE_DELTA]: {\n handle: (event: string, data: t.StreamEventData): void => {\n aggregateContent({\n event: event as GraphEvents,\n data: data as t.SummarizeDeltaData,\n });\n },\n },\n\n [GraphEvents.ON_SUMMARIZE_COMPLETE]: {\n handle: (event: string, data: t.StreamEventData): void => {\n aggregateContent({\n event: event as GraphEvents,\n data: data as t.SummarizeCompleteEvent,\n });\n },\n },\n };\n\n return {\n contentParts,\n aggregateContent,\n handlers,\n };\n}\n"],"mappings":";;;;;;;;
|
|
1
|
+
{"version":3,"file":"handlers.cjs","names":["createContentAggregator","ToolEndHandler","ModelEndHandler","ChatModelStreamHandler"],"sources":["../../../src/utils/handlers.ts"],"sourcesContent":["/**\n * Multi-Agent Handler Utilities\n *\n * Provides a simple helper to create handlers with content aggregation for multi-agent scripts.\n *\n * Usage:\n * ```typescript\n * const { contentParts, aggregateContent, handlers } = createHandlers();\n *\n * // With callbacks\n * const { contentParts, aggregateContent, handlers } = createHandlers({\n * onRunStep: (event, data) => console.log('Step:', data),\n * onRunStepCompleted: (event, data) => console.log('Completed:', data)\n * });\n * ```\n */\n\nimport type * as t from '@/types';\nimport { ChatModelStreamHandler, createContentAggregator } from '@/stream';\nimport { ToolEndHandler, ModelEndHandler } from '@/events';\nimport { GraphEvents } from '@/common';\n\ninterface HandlerCallbacks {\n onRunStep?: (event: GraphEvents.ON_RUN_STEP, data: t.StreamEventData) => void;\n onRunStepCompleted?: (\n event: GraphEvents.ON_RUN_STEP_COMPLETED,\n data: t.StreamEventData\n ) => void;\n onRunStepDelta?: (\n event: GraphEvents.ON_RUN_STEP_DELTA,\n data: t.StreamEventData\n ) => void;\n onRunStepClosed?: (\n event: GraphEvents.ON_RUN_STEP_CLOSED,\n data: t.RunStepClosedEvent\n ) => void;\n onMessageDelta?: (\n event: GraphEvents.ON_MESSAGE_DELTA,\n data: t.StreamEventData\n ) => void;\n}\n\n/**\n * Creates handlers with content aggregation for multi-agent scripts\n */\nexport function createHandlers(callbacks?: HandlerCallbacks): {\n contentParts: Array<t.MessageContentComplex | undefined>;\n aggregateContent: ReturnType<\n typeof createContentAggregator\n >['aggregateContent'];\n handlers: Record<string, t.EventHandler>;\n} {\n // Set up content aggregator\n const { contentParts, aggregateContent } = createContentAggregator();\n\n // Create the handlers object\n const handlers = {\n [GraphEvents.TOOL_END]: new ToolEndHandler(),\n [GraphEvents.CHAT_MODEL_END]: new ModelEndHandler(),\n [GraphEvents.CHAT_MODEL_STREAM]: new ChatModelStreamHandler(),\n\n [GraphEvents.ON_RUN_STEP]: {\n handle: (\n event: GraphEvents.ON_RUN_STEP,\n data: t.StreamEventData\n ): void => {\n aggregateContent({ event, data: data as t.RunStep });\n callbacks?.onRunStep?.(event, data);\n },\n },\n\n [GraphEvents.ON_RUN_STEP_COMPLETED]: {\n handle: (\n event: GraphEvents.ON_RUN_STEP_COMPLETED,\n data: t.StreamEventData\n ): void => {\n aggregateContent({\n event,\n data: data as unknown as { result: t.ToolEndEvent },\n });\n callbacks?.onRunStepCompleted?.(event, data);\n },\n },\n\n [GraphEvents.ON_RUN_STEP_DELTA]: {\n handle: (\n event: GraphEvents.ON_RUN_STEP_DELTA,\n data: t.StreamEventData\n ): void => {\n aggregateContent({ event, data: data as t.RunStepDeltaEvent });\n callbacks?.onRunStepDelta?.(event, data);\n },\n },\n\n [GraphEvents.ON_RUN_STEP_CLOSED]: {\n handle: (\n event: GraphEvents.ON_RUN_STEP_CLOSED,\n data: t.StreamEventData\n ): void => {\n callbacks?.onRunStepClosed?.(event, data as t.RunStepClosedEvent);\n },\n },\n\n [GraphEvents.ON_MESSAGE_DELTA]: {\n handle: (\n event: GraphEvents.ON_MESSAGE_DELTA,\n data: t.StreamEventData\n ): void => {\n aggregateContent({ event, data: data as t.MessageDeltaEvent });\n callbacks?.onMessageDelta?.(event, data);\n },\n },\n\n [GraphEvents.ON_SUMMARIZE_DELTA]: {\n handle: (event: string, data: t.StreamEventData): void => {\n aggregateContent({\n event: event as GraphEvents,\n data: data as t.SummarizeDeltaData,\n });\n },\n },\n\n [GraphEvents.ON_SUMMARIZE_COMPLETE]: {\n handle: (event: string, data: t.StreamEventData): void => {\n aggregateContent({\n event: event as GraphEvents,\n data: data as t.SummarizeCompleteEvent,\n });\n },\n },\n };\n\n return {\n contentParts,\n aggregateContent,\n handlers,\n };\n}\n"],"mappings":";;;;;;;;AA6CA,SAAgB,eAAe,WAM7B;CAEA,MAAM,EAAE,cAAc,qBAAqBA,eAAAA,wBAAwB;CA+EnE,OAAO;EACL;EACA;EACA,UAAA;oBA9EwB,IAAIC,eAAAA,eAAe;0BACb,IAAIC,eAAAA,gBAAgB;6BACjB,IAAIC,eAAAA,uBAAuB;oBAEjC,EACzB,SACE,OACA,SACS;IACT,iBAAiB;KAAE;KAAa;IAAkB,CAAC;IACnD,WAAW,YAAY,OAAO,IAAI;GACpC,EACF;8BAEqC,EACnC,SACE,OACA,SACS;IACT,iBAAiB;KACf;KACM;IACR,CAAC;IACD,WAAW,qBAAqB,OAAO,IAAI;GAC7C,EACF;0BAEiC,EAC/B,SACE,OACA,SACS;IACT,iBAAiB;KAAE;KAAa;IAA4B,CAAC;IAC7D,WAAW,iBAAiB,OAAO,IAAI;GACzC,EACF;2BAEkC,EAChC,SACE,OACA,SACS;IACT,WAAW,kBAAkB,OAAO,IAA4B;GAClE,EACF;yBAEgC,EAC9B,SACE,OACA,SACS;IACT,iBAAiB;KAAE;KAAa;IAA4B,CAAC;IAC7D,WAAW,iBAAiB,OAAO,IAAI;GACzC,EACF;2BAEkC,EAChC,SAAS,OAAe,SAAkC;IACxD,iBAAiB;KACR;KACD;IACR,CAAC;GACH,EACF;8BAEqC,EACnC,SAAS,OAAe,SAAkC;IACxD,iBAAiB;KACR;KACD;IACR,CAAC;GACH,EACF;EAMO;CACT;AACF"}
|
package/dist/esm/common/enum.mjs
CHANGED
|
@@ -14,6 +14,8 @@ let GraphEvents = /* @__PURE__ */ function(GraphEvents) {
|
|
|
14
14
|
GraphEvents["ON_RUN_STEP_DELTA"] = "on_run_step_delta";
|
|
15
15
|
/** [Custom] Completed event for run steps (tool calls) */
|
|
16
16
|
GraphEvents["ON_RUN_STEP_COMPLETED"] = "on_run_step_completed";
|
|
17
|
+
/** [Custom] Terminal signal for a run step: closed with status + timestamps */
|
|
18
|
+
GraphEvents["ON_RUN_STEP_CLOSED"] = "on_run_step_closed";
|
|
17
19
|
/** [Custom] Delta events for messages */
|
|
18
20
|
GraphEvents["ON_MESSAGE_DELTA"] = "on_message_delta";
|
|
19
21
|
/** [Custom] Reasoning Delta events for messages */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"enum.mjs","names":[],"sources":["../../../src/common/enum.ts"],"sourcesContent":["/**\n * Enum representing the various event types emitted during the execution of runnables.\n * These events provide real-time information about the progress and state of different components.\n *\n * @enum {string}\n */\nexport enum GraphEvents {\n /* Custom Events */\n\n /** [Custom] Agent update event in multi-agent graph/workflow */\n ON_AGENT_UPDATE = 'on_agent_update',\n /** [Custom] Delta event for run steps (message creation and tool calls) */\n ON_RUN_STEP = 'on_run_step',\n /** [Custom] Delta event for run steps (tool calls) */\n ON_RUN_STEP_DELTA = 'on_run_step_delta',\n /** [Custom] Completed event for run steps (tool calls) */\n ON_RUN_STEP_COMPLETED = 'on_run_step_completed',\n /** [Custom] Delta events for messages */\n ON_MESSAGE_DELTA = 'on_message_delta',\n /** [Custom] Reasoning Delta events for messages */\n ON_REASONING_DELTA = 'on_reasoning_delta',\n /** [Custom] Request to execute tools - dispatched by ToolNode, handled by host */\n ON_TOOL_EXECUTE = 'on_tool_execute',\n /** [Custom] Emitted when the summarize node begins generating a summary */\n ON_SUMMARIZE_START = 'on_summarize_start',\n /** [Custom] Delta event carrying the completed summary content */\n ON_SUMMARIZE_DELTA = 'on_summarize_delta',\n /** [Custom] Emitted when the summarize node completes with the final summary */\n ON_SUMMARIZE_COMPLETE = 'on_summarize_complete',\n /** [Custom] Progress update from a running subagent (wraps child-graph events so hosts can display activity separately from parent). */\n ON_SUBAGENT_UPDATE = 'on_subagent_update',\n /** [Custom] Diagnostic logging event for context management observability */\n ON_AGENT_LOG = 'on_agent_log',\n /** [Custom] Per-model-call context window usage snapshot (post-prune token budget) */\n ON_CONTEXT_USAGE = 'on_context_usage',\n\n /* Official Events */\n\n /** Custom event, emitted by system */\n ON_CUSTOM_EVENT = 'on_custom_event',\n /** Emitted when a chat model starts processing. */\n CHAT_MODEL_START = 'on_chat_model_start',\n\n /** Emitted when a chat model streams a chunk of its response. */\n CHAT_MODEL_STREAM = 'on_chat_model_stream',\n\n /** Emitted when a chat model completes its processing. */\n CHAT_MODEL_END = 'on_chat_model_end',\n\n /** Emitted when a language model starts processing. */\n LLM_START = 'on_llm_start',\n\n /** Emitted when a language model streams a chunk of its response. */\n LLM_STREAM = 'on_llm_stream',\n\n /** Emitted when a language model completes its processing. */\n LLM_END = 'on_llm_end',\n\n /** Emitted when a chain starts processing. */\n CHAIN_START = 'on_chain_start',\n\n /** Emitted when a chain streams a chunk of its output. */\n CHAIN_STREAM = 'on_chain_stream',\n\n /** Emitted when a chain completes its processing. */\n CHAIN_END = 'on_chain_end',\n\n /** Emitted when a tool starts its operation. */\n TOOL_START = 'on_tool_start',\n\n /** Emitted when a tool completes its operation. */\n TOOL_END = 'on_tool_end',\n\n /** Emitted when a retriever starts its operation. */\n RETRIEVER_START = 'on_retriever_start',\n\n /** Emitted when a retriever completes its operation. */\n RETRIEVER_END = 'on_retriever_end',\n\n /** Emitted when a prompt starts processing. */\n PROMPT_START = 'on_prompt_start',\n\n /** Emitted when a prompt completes its processing. */\n PROMPT_END = 'on_prompt_end',\n}\n\nexport enum Providers {\n OPENAI = 'openAI',\n VERTEXAI = 'vertexai',\n BEDROCK = 'bedrock',\n ANTHROPIC = 'anthropic',\n MISTRALAI = 'mistralai',\n MISTRAL = 'mistral',\n GOOGLE = 'google',\n AZURE = 'azureOpenAI',\n DEEPSEEK = 'deepseek',\n OPENROUTER = 'openrouter',\n XAI = 'xai',\n MOONSHOT = 'moonshot',\n}\n\nexport enum GraphNodeKeys {\n TOOLS = 'tools=',\n AGENT = 'agent=',\n SUMMARIZE = 'summarize=',\n ROUTER = 'router',\n PRE_TOOLS = 'pre_tools',\n POST_TOOLS = 'post_tools',\n}\n\nexport enum GraphNodeActions {\n TOOL_NODE = 'tool_node',\n CALL_MODEL = 'call_model',\n ROUTE_MESSAGE = 'route_message',\n}\n\nexport enum CommonEvents {\n LANGGRAPH = 'LangGraph',\n}\n\nexport enum StepTypes {\n TOOL_CALLS = 'tool_calls',\n MESSAGE_CREATION = 'message_creation',\n}\n\nexport enum ContentTypes {\n TEXT = 'text',\n ERROR = 'error',\n THINK = 'think',\n TOOL_CALL = 'tool_call',\n IMAGE_URL = 'image_url',\n IMAGE_FILE = 'image_file',\n /** Anthropic */\n THINKING = 'thinking',\n /** Vertex AI / Google Common */\n REASONING = 'reasoning',\n /** Multi-Agent Switch */\n AGENT_UPDATE = 'agent_update',\n /** Framework-level conversation summary block */\n SUMMARY = 'summary',\n /** Bedrock */\n REASONING_CONTENT = 'reasoning_content',\n /** Mid-run user steer persisted inline in an assistant message; replayed as a user turn */\n STEER = 'steer',\n /** Fast-model activity label for a tool/reasoning block; UI-only, never model input */\n ACTIVITY_LABEL = 'activity_label',\n}\n\nexport enum ToolCallTypes {\n FUNCTION = 'function',\n RETRIEVAL = 'retrieval',\n FILE_SEARCH = 'file_search',\n CODE_INTERPRETER = 'code_interpreter',\n /* Agents Tool Call */\n TOOL_CALL = 'tool_call',\n}\n\nexport enum Callback {\n TOOL_ERROR = 'handleToolError',\n TOOL_START = 'handleToolStart',\n TOOL_END = 'handleToolEnd',\n CUSTOM_EVENT = 'handleCustomEvent',\n /*\n LLM_START = 'handleLLMStart',\n LLM_NEW_TOKEN = 'handleLLMNewToken',\n LLM_ERROR = 'handleLLMError',\n LLM_END = 'handleLLMEnd',\n CHAT_MODEL_START = 'handleChatModelStart',\n CHAIN_START = 'handleChainStart',\n CHAIN_ERROR = 'handleChainError',\n CHAIN_END = 'handleChainEnd',\n TEXT = 'handleText',\n AGENT_ACTION = 'handleAgentAction',\n AGENT_END = 'handleAgentEnd',\n RETRIEVER_START = 'handleRetrieverStart',\n RETRIEVER_END = 'handleRetrieverEnd',\n RETRIEVER_ERROR = 'handleRetrieverError',\n */\n}\n\nexport enum Constants {\n OFFICIAL_CODE_BASEURL = 'https://api.librechat.ai/v1',\n EXECUTE_CODE = 'execute_code',\n TOOL_SEARCH = 'tool_search',\n PROGRAMMATIC_TOOL_CALLING = 'run_tools_with_code',\n WEB_SEARCH = 'web_search',\n CONTENT_AND_ARTIFACT = 'content_and_artifact',\n LC_TRANSFER_TO_ = 'lc_transfer_to_',\n HANDOFF_PARALLEL_BATCH = '__handoff_parallel_batch',\n HANDOFF_GROUP_ID = '__handoff_group_id',\n /** Delimiter for MCP tools: toolName_mcp_serverName */\n MCP_DELIMITER = '_mcp_',\n /** Anthropic server tool ID prefix (web_search, code_execution, etc.) */\n ANTHROPIC_SERVER_TOOL_PREFIX = 'srvtoolu_',\n SKILL_TOOL = 'skill',\n /**\n * Callback-metadata keys stamped by `attemptInvoke` /\n * `tryFallbackProviders` carrying the provider (SDK `Providers` enum\n * value) and configured model that actually served a model invocation.\n * Unlike `ls_provider` — which derived providers inherit from their base\n * class (e.g. DeepSeek/OpenRouter report `'openai'`) — these reflect the\n * SDK's own routing, including fallback-provider calls. Consumed by the\n * subagent usage-capture handler to tag billing events.\n */\n INVOKED_PROVIDER = '__invoked_provider',\n INVOKED_MODEL = '__invoked_model',\n READ_FILE = 'read_file',\n BASH_TOOL = 'bash_tool',\n BASH_PROGRAMMATIC_TOOL_CALLING = 'run_tools_with_bash',\n SUBAGENT = 'subagent',\n /**\n * Local-engine coding tool names. Promoted to `Constants.*` (rather\n * than left as per-file `*ToolName` consts) so consumer UIs — most\n * importantly LibreChat's `getToolIconType` map — can match against\n * canonical strings instead of guessing. Existing matched names:\n * `bash_tool`, `read_file`, `execute_code`, `run_tools_with_code`.\n * The rest below are new and currently fall through to the generic\n * tool icon; once LibreChat adds icons keyed on the same names, the\n * wiring will work without an SDK change.\n */\n WRITE_FILE = 'write_file',\n EDIT_FILE = 'edit_file',\n GREP_SEARCH = 'grep_search',\n GLOB_SEARCH = 'glob_search',\n LIST_DIRECTORY = 'list_directory',\n COMPILE_CHECK = 'compile_check',\n}\n\n/** Tool names that use the code execution environment (shared session, file tracking). */\nexport const CODE_EXECUTION_TOOLS: ReadonlySet<string> = new Set([\n Constants.EXECUTE_CODE,\n Constants.BASH_TOOL,\n Constants.PROGRAMMATIC_TOOL_CALLING,\n Constants.BASH_PROGRAMMATIC_TOOL_CALLING,\n]);\n\n/**\n * Canonical names of the local-engine-specific coding tools — the\n * file/edit/search/typecheck surface that doesn't exist in the\n * remote (sandbox-API) engine. Single source of truth; the per-tool\n * factories, registry definitions, and `createWorkspacePolicyHook`\n * default extractors all key off these.\n *\n * `read_file` is on this list (the existing ReadFile tool is\n * remote-specific; the local engine's `read_file` is a parallel\n * implementation that shares the canonical name so consumer UIs\n * — most importantly LibreChat's `getToolIconType` — render both\n * with the same icon).\n */\nexport const LOCAL_CODING_TOOL_NAMES: readonly string[] = [\n Constants.READ_FILE,\n Constants.WRITE_FILE,\n Constants.EDIT_FILE,\n Constants.GREP_SEARCH,\n Constants.GLOB_SEARCH,\n Constants.LIST_DIRECTORY,\n Constants.COMPILE_CHECK,\n];\n\n/**\n * Every tool name the local coding bundle (`createLocalCodingTools`)\n * exposes — the local-specific tools above plus the bash/code/PTC\n * pair that the local engine wraps around the existing factories.\n * Tests pin against this so any addition/removal in the bundle is\n * accompanied by a deliberate canonical-name update here.\n */\nexport const LOCAL_CODING_BUNDLE_NAMES: readonly string[] = [\n ...LOCAL_CODING_TOOL_NAMES,\n Constants.BASH_TOOL,\n Constants.EXECUTE_CODE,\n Constants.PROGRAMMATIC_TOOL_CALLING,\n Constants.BASH_PROGRAMMATIC_TOOL_CALLING,\n];\n\nexport enum TitleMethod {\n STRUCTURED = 'structured',\n FUNCTIONS = 'functions',\n COMPLETION = 'completion',\n}\n\nexport enum EnvVar {\n CODE_BASEURL = 'LIBRECHAT_CODE_BASEURL',\n CODE_API_RUN_TIMEOUT_MS = 'CODE_API_RUN_TIMEOUT_MS',\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"enum.mjs","names":[],"sources":["../../../src/common/enum.ts"],"sourcesContent":["/**\n * Enum representing the various event types emitted during the execution of runnables.\n * These events provide real-time information about the progress and state of different components.\n *\n * @enum {string}\n */\nexport enum GraphEvents {\n /* Custom Events */\n\n /** [Custom] Agent update event in multi-agent graph/workflow */\n ON_AGENT_UPDATE = 'on_agent_update',\n /** [Custom] Delta event for run steps (message creation and tool calls) */\n ON_RUN_STEP = 'on_run_step',\n /** [Custom] Delta event for run steps (tool calls) */\n ON_RUN_STEP_DELTA = 'on_run_step_delta',\n /** [Custom] Completed event for run steps (tool calls) */\n ON_RUN_STEP_COMPLETED = 'on_run_step_completed',\n /** [Custom] Terminal signal for a run step: closed with status + timestamps */\n ON_RUN_STEP_CLOSED = 'on_run_step_closed',\n /** [Custom] Delta events for messages */\n ON_MESSAGE_DELTA = 'on_message_delta',\n /** [Custom] Reasoning Delta events for messages */\n ON_REASONING_DELTA = 'on_reasoning_delta',\n /** [Custom] Request to execute tools - dispatched by ToolNode, handled by host */\n ON_TOOL_EXECUTE = 'on_tool_execute',\n /** [Custom] Emitted when the summarize node begins generating a summary */\n ON_SUMMARIZE_START = 'on_summarize_start',\n /** [Custom] Delta event carrying the completed summary content */\n ON_SUMMARIZE_DELTA = 'on_summarize_delta',\n /** [Custom] Emitted when the summarize node completes with the final summary */\n ON_SUMMARIZE_COMPLETE = 'on_summarize_complete',\n /** [Custom] Progress update from a running subagent (wraps child-graph events so hosts can display activity separately from parent). */\n ON_SUBAGENT_UPDATE = 'on_subagent_update',\n /** [Custom] Diagnostic logging event for context management observability */\n ON_AGENT_LOG = 'on_agent_log',\n /** [Custom] Per-model-call context window usage snapshot (post-prune token budget) */\n ON_CONTEXT_USAGE = 'on_context_usage',\n\n /* Official Events */\n\n /** Custom event, emitted by system */\n ON_CUSTOM_EVENT = 'on_custom_event',\n /** Emitted when a chat model starts processing. */\n CHAT_MODEL_START = 'on_chat_model_start',\n\n /** Emitted when a chat model streams a chunk of its response. */\n CHAT_MODEL_STREAM = 'on_chat_model_stream',\n\n /** Emitted when a chat model completes its processing. */\n CHAT_MODEL_END = 'on_chat_model_end',\n\n /** Emitted when a language model starts processing. */\n LLM_START = 'on_llm_start',\n\n /** Emitted when a language model streams a chunk of its response. */\n LLM_STREAM = 'on_llm_stream',\n\n /** Emitted when a language model completes its processing. */\n LLM_END = 'on_llm_end',\n\n /** Emitted when a chain starts processing. */\n CHAIN_START = 'on_chain_start',\n\n /** Emitted when a chain streams a chunk of its output. */\n CHAIN_STREAM = 'on_chain_stream',\n\n /** Emitted when a chain completes its processing. */\n CHAIN_END = 'on_chain_end',\n\n /** Emitted when a tool starts its operation. */\n TOOL_START = 'on_tool_start',\n\n /** Emitted when a tool completes its operation. */\n TOOL_END = 'on_tool_end',\n\n /** Emitted when a retriever starts its operation. */\n RETRIEVER_START = 'on_retriever_start',\n\n /** Emitted when a retriever completes its operation. */\n RETRIEVER_END = 'on_retriever_end',\n\n /** Emitted when a prompt starts processing. */\n PROMPT_START = 'on_prompt_start',\n\n /** Emitted when a prompt completes its processing. */\n PROMPT_END = 'on_prompt_end',\n}\n\nexport enum Providers {\n OPENAI = 'openAI',\n VERTEXAI = 'vertexai',\n BEDROCK = 'bedrock',\n ANTHROPIC = 'anthropic',\n MISTRALAI = 'mistralai',\n MISTRAL = 'mistral',\n GOOGLE = 'google',\n AZURE = 'azureOpenAI',\n DEEPSEEK = 'deepseek',\n OPENROUTER = 'openrouter',\n XAI = 'xai',\n MOONSHOT = 'moonshot',\n}\n\nexport enum GraphNodeKeys {\n TOOLS = 'tools=',\n AGENT = 'agent=',\n SUMMARIZE = 'summarize=',\n ROUTER = 'router',\n PRE_TOOLS = 'pre_tools',\n POST_TOOLS = 'post_tools',\n}\n\nexport enum GraphNodeActions {\n TOOL_NODE = 'tool_node',\n CALL_MODEL = 'call_model',\n ROUTE_MESSAGE = 'route_message',\n}\n\nexport enum CommonEvents {\n LANGGRAPH = 'LangGraph',\n}\n\nexport enum StepTypes {\n TOOL_CALLS = 'tool_calls',\n MESSAGE_CREATION = 'message_creation',\n}\n\nexport enum ContentTypes {\n TEXT = 'text',\n ERROR = 'error',\n THINK = 'think',\n TOOL_CALL = 'tool_call',\n IMAGE_URL = 'image_url',\n IMAGE_FILE = 'image_file',\n /** Anthropic */\n THINKING = 'thinking',\n /** Vertex AI / Google Common */\n REASONING = 'reasoning',\n /** Multi-Agent Switch */\n AGENT_UPDATE = 'agent_update',\n /** Framework-level conversation summary block */\n SUMMARY = 'summary',\n /** Bedrock */\n REASONING_CONTENT = 'reasoning_content',\n /** Mid-run user steer persisted inline in an assistant message; replayed as a user turn */\n STEER = 'steer',\n /** Fast-model activity label for a tool/reasoning block; UI-only, never model input */\n ACTIVITY_LABEL = 'activity_label',\n}\n\nexport enum ToolCallTypes {\n FUNCTION = 'function',\n RETRIEVAL = 'retrieval',\n FILE_SEARCH = 'file_search',\n CODE_INTERPRETER = 'code_interpreter',\n /* Agents Tool Call */\n TOOL_CALL = 'tool_call',\n}\n\nexport enum Callback {\n TOOL_ERROR = 'handleToolError',\n TOOL_START = 'handleToolStart',\n TOOL_END = 'handleToolEnd',\n CUSTOM_EVENT = 'handleCustomEvent',\n /*\n LLM_START = 'handleLLMStart',\n LLM_NEW_TOKEN = 'handleLLMNewToken',\n LLM_ERROR = 'handleLLMError',\n LLM_END = 'handleLLMEnd',\n CHAT_MODEL_START = 'handleChatModelStart',\n CHAIN_START = 'handleChainStart',\n CHAIN_ERROR = 'handleChainError',\n CHAIN_END = 'handleChainEnd',\n TEXT = 'handleText',\n AGENT_ACTION = 'handleAgentAction',\n AGENT_END = 'handleAgentEnd',\n RETRIEVER_START = 'handleRetrieverStart',\n RETRIEVER_END = 'handleRetrieverEnd',\n RETRIEVER_ERROR = 'handleRetrieverError',\n */\n}\n\nexport enum Constants {\n OFFICIAL_CODE_BASEURL = 'https://api.librechat.ai/v1',\n EXECUTE_CODE = 'execute_code',\n TOOL_SEARCH = 'tool_search',\n PROGRAMMATIC_TOOL_CALLING = 'run_tools_with_code',\n WEB_SEARCH = 'web_search',\n CONTENT_AND_ARTIFACT = 'content_and_artifact',\n LC_TRANSFER_TO_ = 'lc_transfer_to_',\n HANDOFF_PARALLEL_BATCH = '__handoff_parallel_batch',\n HANDOFF_GROUP_ID = '__handoff_group_id',\n /** Delimiter for MCP tools: toolName_mcp_serverName */\n MCP_DELIMITER = '_mcp_',\n /** Anthropic server tool ID prefix (web_search, code_execution, etc.) */\n ANTHROPIC_SERVER_TOOL_PREFIX = 'srvtoolu_',\n SKILL_TOOL = 'skill',\n /**\n * Callback-metadata keys stamped by `attemptInvoke` /\n * `tryFallbackProviders` carrying the provider (SDK `Providers` enum\n * value) and configured model that actually served a model invocation.\n * Unlike `ls_provider` — which derived providers inherit from their base\n * class (e.g. DeepSeek/OpenRouter report `'openai'`) — these reflect the\n * SDK's own routing, including fallback-provider calls. Consumed by the\n * subagent usage-capture handler to tag billing events.\n */\n INVOKED_PROVIDER = '__invoked_provider',\n INVOKED_MODEL = '__invoked_model',\n READ_FILE = 'read_file',\n BASH_TOOL = 'bash_tool',\n BASH_PROGRAMMATIC_TOOL_CALLING = 'run_tools_with_bash',\n SUBAGENT = 'subagent',\n /**\n * Local-engine coding tool names. Promoted to `Constants.*` (rather\n * than left as per-file `*ToolName` consts) so consumer UIs — most\n * importantly LibreChat's `getToolIconType` map — can match against\n * canonical strings instead of guessing. Existing matched names:\n * `bash_tool`, `read_file`, `execute_code`, `run_tools_with_code`.\n * The rest below are new and currently fall through to the generic\n * tool icon; once LibreChat adds icons keyed on the same names, the\n * wiring will work without an SDK change.\n */\n WRITE_FILE = 'write_file',\n EDIT_FILE = 'edit_file',\n GREP_SEARCH = 'grep_search',\n GLOB_SEARCH = 'glob_search',\n LIST_DIRECTORY = 'list_directory',\n COMPILE_CHECK = 'compile_check',\n}\n\n/** Tool names that use the code execution environment (shared session, file tracking). */\nexport const CODE_EXECUTION_TOOLS: ReadonlySet<string> = new Set([\n Constants.EXECUTE_CODE,\n Constants.BASH_TOOL,\n Constants.PROGRAMMATIC_TOOL_CALLING,\n Constants.BASH_PROGRAMMATIC_TOOL_CALLING,\n]);\n\n/**\n * Canonical names of the local-engine-specific coding tools — the\n * file/edit/search/typecheck surface that doesn't exist in the\n * remote (sandbox-API) engine. Single source of truth; the per-tool\n * factories, registry definitions, and `createWorkspacePolicyHook`\n * default extractors all key off these.\n *\n * `read_file` is on this list (the existing ReadFile tool is\n * remote-specific; the local engine's `read_file` is a parallel\n * implementation that shares the canonical name so consumer UIs\n * — most importantly LibreChat's `getToolIconType` — render both\n * with the same icon).\n */\nexport const LOCAL_CODING_TOOL_NAMES: readonly string[] = [\n Constants.READ_FILE,\n Constants.WRITE_FILE,\n Constants.EDIT_FILE,\n Constants.GREP_SEARCH,\n Constants.GLOB_SEARCH,\n Constants.LIST_DIRECTORY,\n Constants.COMPILE_CHECK,\n];\n\n/**\n * Every tool name the local coding bundle (`createLocalCodingTools`)\n * exposes — the local-specific tools above plus the bash/code/PTC\n * pair that the local engine wraps around the existing factories.\n * Tests pin against this so any addition/removal in the bundle is\n * accompanied by a deliberate canonical-name update here.\n */\nexport const LOCAL_CODING_BUNDLE_NAMES: readonly string[] = [\n ...LOCAL_CODING_TOOL_NAMES,\n Constants.BASH_TOOL,\n Constants.EXECUTE_CODE,\n Constants.PROGRAMMATIC_TOOL_CALLING,\n Constants.BASH_PROGRAMMATIC_TOOL_CALLING,\n];\n\nexport enum TitleMethod {\n STRUCTURED = 'structured',\n FUNCTIONS = 'functions',\n COMPLETION = 'completion',\n}\n\nexport enum EnvVar {\n CODE_BASEURL = 'LIBRECHAT_CODE_BASEURL',\n CODE_API_RUN_TIMEOUT_MS = 'CODE_API_RUN_TIMEOUT_MS',\n}\n"],"mappings":"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"}
|
|
@@ -294,6 +294,17 @@ var Graph = class {
|
|
|
294
294
|
stepKeyIds = /* @__PURE__ */ new Map();
|
|
295
295
|
contentIndexMap = /* @__PURE__ */ new Map();
|
|
296
296
|
toolCallStepIds = /* @__PURE__ */ new Map();
|
|
297
|
+
/** Step ID -> tool call IDs whose completions have not yet arrived. */
|
|
298
|
+
pendingToolCallsByStep = /* @__PURE__ */ new Map();
|
|
299
|
+
/**
|
|
300
|
+
* Step ID -> latest producer completion time seen for that step. Parallel
|
|
301
|
+
* calls sharing a step can settle out of producer order when their host
|
|
302
|
+
* handlers differ in latency, so the call that happens to drain the set is
|
|
303
|
+
* not necessarily the one that finished last.
|
|
304
|
+
*/
|
|
305
|
+
latestCompletionByStep = /* @__PURE__ */ new Map();
|
|
306
|
+
/** Agent key ('' for single-agent) -> currently open MESSAGE_CREATION step ID. */
|
|
307
|
+
openMessageStepByAgent = /* @__PURE__ */ new Map();
|
|
297
308
|
/**
|
|
298
309
|
* Step IDs dispatched through the handler registry during this run.
|
|
299
310
|
* Event echo suppression is tracked separately so repeated deltas for
|
|
@@ -407,6 +418,9 @@ var Graph = class {
|
|
|
407
418
|
this.contentIndexMap = /* @__PURE__ */ new Map();
|
|
408
419
|
this.stepKeyIds = /* @__PURE__ */ new Map();
|
|
409
420
|
this.toolCallStepIds.clear();
|
|
421
|
+
this.pendingToolCallsByStep.clear();
|
|
422
|
+
this.latestCompletionByStep.clear();
|
|
423
|
+
this.openMessageStepByAgent.clear();
|
|
410
424
|
this.messageIdsByStepKey = /* @__PURE__ */ new Map();
|
|
411
425
|
this.messageStepHasTextDeltas = /* @__PURE__ */ new Set();
|
|
412
426
|
this.reasoningStepHasDeltas = /* @__PURE__ */ new Set();
|
|
@@ -454,6 +468,26 @@ var Graph = class {
|
|
|
454
468
|
this.eagerEventToolUsageCount.clear();
|
|
455
469
|
for (const usageCount of this.eagerEventToolUsageCountsByAgentId.values()) usageCount.clear();
|
|
456
470
|
}
|
|
471
|
+
/**
|
|
472
|
+
* Tracks a tool call whose completion must arrive before its step can be
|
|
473
|
+
* considered finished. Registered wherever `toolCallStepIds` gains entries,
|
|
474
|
+
* except cross-process subagent resume restoration, where pending state
|
|
475
|
+
* cannot be faithfully rebuilt and closes fall back to completions/sweep.
|
|
476
|
+
*/
|
|
477
|
+
registerPendingToolCall(toolCallId, stepId) {
|
|
478
|
+
if (!toolCallId || !stepId) return;
|
|
479
|
+
this.getPendingToolCallSet(stepId).add(toolCallId);
|
|
480
|
+
}
|
|
481
|
+
/** Lazily creates a step's pending-completions set; callers registering a
|
|
482
|
+
* batch hoist this lookup out of their per-call loop. */
|
|
483
|
+
getPendingToolCallSet(stepId) {
|
|
484
|
+
let pending = this.pendingToolCallsByStep.get(stepId);
|
|
485
|
+
if (!pending) {
|
|
486
|
+
pending = /* @__PURE__ */ new Set();
|
|
487
|
+
this.pendingToolCallsByStep.set(stepId, pending);
|
|
488
|
+
}
|
|
489
|
+
return pending;
|
|
490
|
+
}
|
|
457
491
|
markHandlerDispatchedEvent(eventName, stepId) {
|
|
458
492
|
const key = getHandlerDispatchedEventKey(eventName, stepId);
|
|
459
493
|
this.handlerDispatchedEventCounts.set(key, (this.handlerDispatchedEventCounts.get(key) ?? 0) + 1);
|
|
@@ -803,6 +837,9 @@ var StandardGraph = class StandardGraph extends Graph {
|
|
|
803
837
|
* a stale reference on 2nd+ processStream calls.
|
|
804
838
|
*/
|
|
805
839
|
this.toolCallStepIds.clear();
|
|
840
|
+
this.pendingToolCallsByStep.clear();
|
|
841
|
+
this.latestCompletionByStep.clear();
|
|
842
|
+
this.openMessageStepByAgent.clear();
|
|
806
843
|
this.runProducedAiMessageIds.clear();
|
|
807
844
|
this.eagerEventToolExecutions.clear();
|
|
808
845
|
this.clearEagerEventToolUsageCounts();
|
|
@@ -971,6 +1008,196 @@ var StandardGraph = class StandardGraph extends Graph {
|
|
|
971
1008
|
const index = this.contentIndexMap.get(stepId);
|
|
972
1009
|
if (index !== void 0) return this.contentData[index];
|
|
973
1010
|
}
|
|
1011
|
+
/**
|
|
1012
|
+
* Derives the same lane key `dispatchRunStep` stamps as `runStep.agentId`.
|
|
1013
|
+
* The multi-agent check gates the lookup because `getAgentContext` signals
|
|
1014
|
+
* a miss by throwing: single-agent graphs key every step under `''`, so
|
|
1015
|
+
* resolving the context could only ever produce a thrown-and-discarded
|
|
1016
|
+
* Error on a per-model-call path.
|
|
1017
|
+
*/
|
|
1018
|
+
getStepAgentKey(metadata) {
|
|
1019
|
+
if (!metadata || !this.isMultiAgentGraph()) return "";
|
|
1020
|
+
try {
|
|
1021
|
+
const agentContext = this.getAgentContext(metadata);
|
|
1022
|
+
if (agentContext.agentId) return agentContext.agentId;
|
|
1023
|
+
} catch (_e) {}
|
|
1024
|
+
return "";
|
|
1025
|
+
}
|
|
1026
|
+
/**
|
|
1027
|
+
* O(1) reverse lookup: both dispatch funnels key the open-message map by
|
|
1028
|
+
* `runStep.agentId ?? ''`, so the entry is addressable without scanning.
|
|
1029
|
+
*/
|
|
1030
|
+
untrackRunStep(runStep) {
|
|
1031
|
+
this.pendingToolCallsByStep.delete(runStep.id);
|
|
1032
|
+
this.latestCompletionByStep.delete(runStep.id);
|
|
1033
|
+
const agentKey = runStep.agentId ?? "";
|
|
1034
|
+
if (this.openMessageStepByAgent.get(agentKey) === runStep.id) this.openMessageStepByAgent.delete(agentKey);
|
|
1035
|
+
}
|
|
1036
|
+
/**
|
|
1037
|
+
* Shared step accounting for both dispatch funnels: a successor step in
|
|
1038
|
+
* the same agent lane marks the previous message step as finished — its
|
|
1039
|
+
* CLOSED event must precede the successor's ON_RUN_STEP so hosts observe
|
|
1040
|
+
* a consistent open-step timeline — then the step is registered in the
|
|
1041
|
+
* content maps and tracked as the lane's open message step when
|
|
1042
|
+
* applicable.
|
|
1043
|
+
*/
|
|
1044
|
+
async trackDispatchedRunStep(runStep, metadata, trackAsOpenMessageStep = true) {
|
|
1045
|
+
const agentKey = runStep.agentId ?? "";
|
|
1046
|
+
const openMessageStepId = this.openMessageStepByAgent.get(agentKey);
|
|
1047
|
+
/**
|
|
1048
|
+
* Reserve the content index and register the step SYNCHRONOUSLY, before
|
|
1049
|
+
* awaiting the predecessor's closure. Parallel agent lanes dispatch
|
|
1050
|
+
* successors concurrently: if both yielded here first, they would push
|
|
1051
|
+
* with the same `contentData.length` and `contentIndexMap` would resolve
|
|
1052
|
+
* one step id to the other lane's entry, so later deltas and completions
|
|
1053
|
+
* would mutate the wrong agent's step. Assigning the index at push time
|
|
1054
|
+
* also supersedes whatever the caller computed before this await point.
|
|
1055
|
+
*/
|
|
1056
|
+
runStep.index = this.contentData.length;
|
|
1057
|
+
this.contentData.push(runStep);
|
|
1058
|
+
this.contentIndexMap.set(runStep.id, runStep.index);
|
|
1059
|
+
if (trackAsOpenMessageStep && runStep.type === "message_creation") this.openMessageStepByAgent.set(agentKey, runStep.id);
|
|
1060
|
+
else this.openMessageStepByAgent.delete(agentKey);
|
|
1061
|
+
/**
|
|
1062
|
+
* Awaited after registration but before the caller dispatches this step's
|
|
1063
|
+
* ON_RUN_STEP, so the predecessor's CLOSED event still precedes the
|
|
1064
|
+
* successor's start event.
|
|
1065
|
+
*/
|
|
1066
|
+
if (openMessageStepId != null && openMessageStepId !== runStep.id)
|
|
1067
|
+
/**
|
|
1068
|
+
* Isolated: this step is already registered in `contentData`, so a
|
|
1069
|
+
* predecessor delivery failure rejecting here would abort the caller
|
|
1070
|
+
* before it publishes this step's ON_RUN_STEP — leaving the sweep to
|
|
1071
|
+
* emit a terminal event for a step that never announced a start.
|
|
1072
|
+
*/
|
|
1073
|
+
try {
|
|
1074
|
+
await this.closeRunStep(openMessageStepId, "completed", { metadata });
|
|
1075
|
+
} catch (_e) {}
|
|
1076
|
+
/**
|
|
1077
|
+
* Stamped last, after the predecessor's closure has been delivered and
|
|
1078
|
+
* immediately before the caller publishes this step's ON_RUN_STEP.
|
|
1079
|
+
* `created_at` documents when the step was dispatched, so it must not
|
|
1080
|
+
* absorb the latency of an arbitrarily slow predecessor handler.
|
|
1081
|
+
*/
|
|
1082
|
+
runStep.created_at = Date.now();
|
|
1083
|
+
}
|
|
1084
|
+
/**
|
|
1085
|
+
* Closes a run step: stamps its terminal status + timestamp on the stored
|
|
1086
|
+
* `RunStep` and emits `ON_RUN_STEP_CLOSED`. First close wins — later calls
|
|
1087
|
+
* are no-ops — except a `restamp` close, which lets a `completed`
|
|
1088
|
+
* TOOL_CALLS step refresh `completed_at` when a late-registered parallel
|
|
1089
|
+
* tool call finishes after the step already closed (the eager-execution
|
|
1090
|
+
* race). `cancelled`/`failed` are immutable once stamped.
|
|
1091
|
+
*/
|
|
1092
|
+
async closeRunStep(stepId, status, options) {
|
|
1093
|
+
if (!stepId) return false;
|
|
1094
|
+
const runStep = this.getRunStep(stepId);
|
|
1095
|
+
if (!runStep) return false;
|
|
1096
|
+
if (runStep.status != null && runStep.status !== "in_progress") {
|
|
1097
|
+
this.untrackRunStep(runStep);
|
|
1098
|
+
return false;
|
|
1099
|
+
}
|
|
1100
|
+
const closedAt = options?.at ?? Date.now();
|
|
1101
|
+
runStep.status = status;
|
|
1102
|
+
if (status === "completed") runStep.completed_at = closedAt;
|
|
1103
|
+
else if (status === "cancelled") runStep.cancelled_at = closedAt;
|
|
1104
|
+
else runStep.failed_at = closedAt;
|
|
1105
|
+
const closedEvent = {
|
|
1106
|
+
id: stepId,
|
|
1107
|
+
index: runStep.index,
|
|
1108
|
+
type: runStep.type,
|
|
1109
|
+
status,
|
|
1110
|
+
closed_at: closedAt
|
|
1111
|
+
};
|
|
1112
|
+
if (runStep.created_at != null) closedEvent.created_at = runStep.created_at;
|
|
1113
|
+
if (runStep.runId != null) closedEvent.runId = runStep.runId;
|
|
1114
|
+
if (runStep.agentId != null) closedEvent.agentId = runStep.agentId;
|
|
1115
|
+
if (runStep.groupId != null) closedEvent.groupId = runStep.groupId;
|
|
1116
|
+
if (runStep.stepIndex != null) closedEvent.stepIndex = runStep.stepIndex;
|
|
1117
|
+
this.untrackRunStep(runStep);
|
|
1118
|
+
const handler = this.handlerRegistry?.getHandler("on_run_step_closed");
|
|
1119
|
+
if (handler) {
|
|
1120
|
+
await handler.handle("on_run_step_closed", closedEvent, options?.metadata, this);
|
|
1121
|
+
this.handlerDispatchedStepIds.add(stepId);
|
|
1122
|
+
}
|
|
1123
|
+
const unmarkHandlerDispatchedEvent = handler ? this.markHandlerDispatchedEvent("on_run_step_closed", stepId) : void 0;
|
|
1124
|
+
try {
|
|
1125
|
+
if (this.config) await safeDispatchCustomEvent("on_run_step_closed", closedEvent, this.config);
|
|
1126
|
+
} finally {
|
|
1127
|
+
unmarkHandlerDispatchedEvent?.();
|
|
1128
|
+
}
|
|
1129
|
+
return true;
|
|
1130
|
+
}
|
|
1131
|
+
/**
|
|
1132
|
+
* Observes one `ON_RUN_STEP_COMPLETED` for a step and closes the step when
|
|
1133
|
+
* no registered tool calls remain pending. Steps without pending tracking
|
|
1134
|
+
* (summaries, cross-process resume) close on their first completion; the
|
|
1135
|
+
* terminal-status guard in `closeRunStep` absorbs duplicate echoes.
|
|
1136
|
+
*/
|
|
1137
|
+
async recordStepCompletion(stepId, options) {
|
|
1138
|
+
if (!stepId) return;
|
|
1139
|
+
const { toolCallId, metadata, at } = options ?? {};
|
|
1140
|
+
if (at != null) {
|
|
1141
|
+
const latest = this.latestCompletionByStep.get(stepId);
|
|
1142
|
+
if (latest == null || at > latest) this.latestCompletionByStep.set(stepId, at);
|
|
1143
|
+
}
|
|
1144
|
+
const closeAt = this.latestCompletionByStep.get(stepId) ?? at;
|
|
1145
|
+
const pending = this.pendingToolCallsByStep.get(stepId);
|
|
1146
|
+
if (pending == null) {
|
|
1147
|
+
await this.closeRunStep(stepId, "completed", {
|
|
1148
|
+
metadata,
|
|
1149
|
+
at: closeAt
|
|
1150
|
+
});
|
|
1151
|
+
return;
|
|
1152
|
+
}
|
|
1153
|
+
if (toolCallId != null && toolCallId !== "") pending.delete(toolCallId);
|
|
1154
|
+
if (pending.size > 0) return;
|
|
1155
|
+
await this.closeRunStep(stepId, "completed", {
|
|
1156
|
+
metadata,
|
|
1157
|
+
at: closeAt
|
|
1158
|
+
});
|
|
1159
|
+
}
|
|
1160
|
+
/**
|
|
1161
|
+
* Closes the tracked open MESSAGE_CREATION step for the event's agent lane.
|
|
1162
|
+
* Fires on every model end, so the empty-map check short-circuits ahead of
|
|
1163
|
+
* resolving the lane key — a turn whose message step already closed through
|
|
1164
|
+
* successor-close does no work here.
|
|
1165
|
+
*/
|
|
1166
|
+
async closeOpenMessageStep(metadata, at) {
|
|
1167
|
+
if (this.openMessageStepByAgent.size === 0) return;
|
|
1168
|
+
const agentKey = this.getStepAgentKey(metadata);
|
|
1169
|
+
const openStepId = this.openMessageStepByAgent.get(agentKey);
|
|
1170
|
+
if (openStepId == null) return;
|
|
1171
|
+
await this.closeRunStep(openStepId, "completed", {
|
|
1172
|
+
metadata,
|
|
1173
|
+
at
|
|
1174
|
+
});
|
|
1175
|
+
}
|
|
1176
|
+
/**
|
|
1177
|
+
* End-of-run sweep: closes every step that never reached a terminal
|
|
1178
|
+
* status. Dual-dispatches like any other close — the custom-event channel
|
|
1179
|
+
* is usually already torn down here and `safeDispatchCustomEvent` reports
|
|
1180
|
+
* that quietly, but callback-only subscribers still receive the terminal
|
|
1181
|
+
* signal whenever it is alive.
|
|
1182
|
+
*/
|
|
1183
|
+
async closeUnfinishedRunSteps(status, at) {
|
|
1184
|
+
const closedAt = at ?? Date.now();
|
|
1185
|
+
for (const runStep of this.contentData) {
|
|
1186
|
+
if (runStep.status != null && runStep.status !== "in_progress") continue;
|
|
1187
|
+
/**
|
|
1188
|
+
* Isolated per step: one host handler throwing must not strand the
|
|
1189
|
+
* remaining steps `in_progress` with no terminal event. The step is
|
|
1190
|
+
* stamped before dispatch either way, so a thrown handler still leaves
|
|
1191
|
+
* consistent state behind.
|
|
1192
|
+
*/
|
|
1193
|
+
try {
|
|
1194
|
+
await this.closeRunStep(runStep.id, status, { at: closedAt });
|
|
1195
|
+
} catch (_e) {}
|
|
1196
|
+
}
|
|
1197
|
+
this.pendingToolCallsByStep.clear();
|
|
1198
|
+
this.latestCompletionByStep.clear();
|
|
1199
|
+
this.openMessageStepByAgent.clear();
|
|
1200
|
+
}
|
|
974
1201
|
getAgentContext(metadata) {
|
|
975
1202
|
if (!metadata) throw new Error("No metadata provided to retrieve agent context");
|
|
976
1203
|
const currentNode = metadata.langgraph_node;
|
|
@@ -2644,8 +2871,8 @@ var StandardGraph = class StandardGraph extends Graph {
|
|
|
2644
2871
|
const groupId = this.resolveParallelGroupId(runStep.agentId, resolvedConfig?.metadata);
|
|
2645
2872
|
if (groupId != null) runStep.groupId = groupId;
|
|
2646
2873
|
}
|
|
2647
|
-
|
|
2648
|
-
this.
|
|
2874
|
+
runStep.status ??= "in_progress";
|
|
2875
|
+
await this.trackDispatchedRunStep(runStep, resolvedConfig?.metadata, false);
|
|
2649
2876
|
const handler = this.handlerRegistry?.getHandler("on_run_step");
|
|
2650
2877
|
if (handler) {
|
|
2651
2878
|
await handler.handle("on_run_step", runStep, resolvedConfig?.configurable, this);
|
|
@@ -2658,15 +2885,24 @@ var StandardGraph = class StandardGraph extends Graph {
|
|
|
2658
2885
|
unmarkHandlerDispatchedEvent?.();
|
|
2659
2886
|
}
|
|
2660
2887
|
},
|
|
2888
|
+
closeRunStep: async (stepId, status, nodeConfig) => {
|
|
2889
|
+
await this.closeRunStep(stepId, status, { metadata: (nodeConfig ?? this.config)?.metadata });
|
|
2890
|
+
},
|
|
2661
2891
|
dispatchRunStepCompleted: async (stepId, result, nodeConfig) => {
|
|
2662
2892
|
const resolvedConfig = nodeConfig ?? this.config;
|
|
2663
|
-
const
|
|
2893
|
+
const completedAt = Date.now();
|
|
2894
|
+
const runStep = this.getRunStep(stepId);
|
|
2664
2895
|
const handler = this.handlerRegistry?.getHandler("on_run_step_completed");
|
|
2665
2896
|
if (handler) await handler.handle("on_run_step_completed", { result: {
|
|
2666
2897
|
...result,
|
|
2667
2898
|
id: stepId,
|
|
2668
|
-
index: runStep?.index ?? 0
|
|
2899
|
+
index: runStep?.index ?? 0,
|
|
2900
|
+
completed_at: completedAt
|
|
2669
2901
|
} }, resolvedConfig?.configurable, this);
|
|
2902
|
+
await this.recordStepCompletion(stepId, {
|
|
2903
|
+
metadata: resolvedConfig?.metadata,
|
|
2904
|
+
at: completedAt
|
|
2905
|
+
});
|
|
2670
2906
|
}
|
|
2671
2907
|
},
|
|
2672
2908
|
generateStepId: (stepKey) => this.generateStepId(stepKey)
|
|
@@ -2714,10 +2950,15 @@ var StandardGraph = class StandardGraph extends Graph {
|
|
|
2714
2950
|
async dispatchRunStep(stepKey, stepDetails, metadata) {
|
|
2715
2951
|
if (!this.config) throw new Error("No config provided");
|
|
2716
2952
|
const [stepId, stepIndex] = this.generateStepId(stepKey);
|
|
2717
|
-
if (stepDetails.type === "tool_calls" && stepDetails.tool_calls)
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2953
|
+
if (stepDetails.type === "tool_calls" && stepDetails.tool_calls) {
|
|
2954
|
+
let pendingToolCalls;
|
|
2955
|
+
for (const tool_call of stepDetails.tool_calls) {
|
|
2956
|
+
const toolCallId = tool_call.id ?? "";
|
|
2957
|
+
if (!toolCallId || this.toolCallStepIds.has(toolCallId)) continue;
|
|
2958
|
+
this.toolCallStepIds.set(toolCallId, stepId);
|
|
2959
|
+
pendingToolCalls ??= this.getPendingToolCallSet(stepId);
|
|
2960
|
+
pendingToolCalls.add(toolCallId);
|
|
2961
|
+
}
|
|
2721
2962
|
}
|
|
2722
2963
|
const runStep = {
|
|
2723
2964
|
stepIndex,
|
|
@@ -2725,20 +2966,26 @@ var StandardGraph = class StandardGraph extends Graph {
|
|
|
2725
2966
|
type: stepDetails.type,
|
|
2726
2967
|
index: this.contentData.length,
|
|
2727
2968
|
stepDetails,
|
|
2728
|
-
usage: null
|
|
2969
|
+
usage: null,
|
|
2970
|
+
status: "in_progress"
|
|
2729
2971
|
};
|
|
2730
2972
|
const runId = this.runId ?? "";
|
|
2731
2973
|
if (runId) runStep.runId = runId;
|
|
2732
|
-
|
|
2974
|
+
/**
|
|
2975
|
+
* `agentId`/`groupId` are multi-agent-only, and `getAgentContext` signals a
|
|
2976
|
+
* miss by throwing — so for a single-agent graph the lookup could only ever
|
|
2977
|
+
* build and discard an Error while producing the same undefined fields.
|
|
2978
|
+
* The constant-time check gates it out of the per-step dispatch path.
|
|
2979
|
+
*/
|
|
2980
|
+
if (metadata && this.isMultiAgentGraph()) try {
|
|
2733
2981
|
const agentContext = this.getAgentContext(metadata);
|
|
2734
|
-
if (
|
|
2982
|
+
if (agentContext.agentId) {
|
|
2735
2983
|
runStep.agentId = agentContext.agentId;
|
|
2736
2984
|
const groupId = this.resolveParallelGroupId(agentContext.agentId, metadata);
|
|
2737
2985
|
if (groupId != null) runStep.groupId = groupId;
|
|
2738
2986
|
}
|
|
2739
2987
|
} catch (_e) {}
|
|
2740
|
-
this.
|
|
2741
|
-
this.contentIndexMap.set(stepId, runStep.index);
|
|
2988
|
+
await this.trackDispatchedRunStep(runStep, metadata);
|
|
2742
2989
|
const handler = this.handlerRegistry?.getHandler("on_run_step");
|
|
2743
2990
|
if (handler) {
|
|
2744
2991
|
await handler.handle("on_run_step", runStep, metadata, this);
|
|
@@ -2779,6 +3026,7 @@ var StandardGraph = class StandardGraph extends Graph {
|
|
|
2779
3026
|
const errorDetail = error?.message != null ? `: ${serializeToolContentBounded(error.message, Math.max(0, eventValueLimit - 21 - 2))}` : "";
|
|
2780
3027
|
const runStep = graph.getRunStep(stepId);
|
|
2781
3028
|
if (!runStep) return false;
|
|
3029
|
+
const completedAt = Date.now();
|
|
2782
3030
|
const tool_call = {
|
|
2783
3031
|
id: data.id,
|
|
2784
3032
|
name: name || "",
|
|
@@ -2792,8 +3040,14 @@ var StandardGraph = class StandardGraph extends Graph {
|
|
|
2792
3040
|
id: stepId,
|
|
2793
3041
|
index: runStep.index,
|
|
2794
3042
|
type: "tool_call",
|
|
2795
|
-
tool_call
|
|
3043
|
+
tool_call,
|
|
3044
|
+
completed_at: completedAt
|
|
2796
3045
|
} }, metadata, graph);
|
|
3046
|
+
await graph.recordStepCompletion(stepId, {
|
|
3047
|
+
toolCallId: data.id,
|
|
3048
|
+
metadata,
|
|
3049
|
+
at: completedAt
|
|
3050
|
+
});
|
|
2797
3051
|
return true;
|
|
2798
3052
|
}
|
|
2799
3053
|
/**
|