@illuma-ai/agents 1.1.19 → 1.1.20

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.
Files changed (53) hide show
  1. package/dist/cjs/common/enum.cjs +2 -0
  2. package/dist/cjs/common/enum.cjs.map +1 -1
  3. package/dist/cjs/graphs/MultiAgentGraph.cjs +87 -1
  4. package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
  5. package/dist/cjs/main.cjs +3 -0
  6. package/dist/cjs/main.cjs.map +1 -1
  7. package/dist/cjs/nodes/ApprovalGateNode.cjs +75 -0
  8. package/dist/cjs/nodes/ApprovalGateNode.cjs.map +1 -0
  9. package/dist/cjs/run.cjs +45 -0
  10. package/dist/cjs/run.cjs.map +1 -1
  11. package/dist/cjs/tools/ToolNode.cjs +21 -18
  12. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  13. package/dist/cjs/types/graph.cjs.map +1 -1
  14. package/dist/cjs/utils/run.cjs +6 -1
  15. package/dist/cjs/utils/run.cjs.map +1 -1
  16. package/dist/esm/common/enum.mjs +2 -0
  17. package/dist/esm/common/enum.mjs.map +1 -1
  18. package/dist/esm/graphs/MultiAgentGraph.mjs +87 -1
  19. package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
  20. package/dist/esm/main.mjs +1 -0
  21. package/dist/esm/main.mjs.map +1 -1
  22. package/dist/esm/nodes/ApprovalGateNode.mjs +72 -0
  23. package/dist/esm/nodes/ApprovalGateNode.mjs.map +1 -0
  24. package/dist/esm/run.mjs +45 -0
  25. package/dist/esm/run.mjs.map +1 -1
  26. package/dist/esm/tools/ToolNode.mjs +22 -19
  27. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  28. package/dist/esm/types/graph.mjs.map +1 -1
  29. package/dist/esm/utils/run.mjs +6 -1
  30. package/dist/esm/utils/run.mjs.map +1 -1
  31. package/dist/types/common/enum.d.ts +2 -0
  32. package/dist/types/index.d.ts +1 -0
  33. package/dist/types/nodes/ApprovalGateNode.d.ts +49 -0
  34. package/dist/types/nodes/index.d.ts +2 -0
  35. package/dist/types/run.d.ts +25 -1
  36. package/dist/types/tools/ToolNode.d.ts +7 -5
  37. package/dist/types/types/graph.d.ts +31 -0
  38. package/dist/types/types/tools.d.ts +7 -9
  39. package/package.json +1 -1
  40. package/src/common/enum.ts +2 -0
  41. package/src/graphs/MultiAgentGraph.ts +108 -1
  42. package/src/index.ts +3 -0
  43. package/src/nodes/ApprovalGateNode.ts +117 -0
  44. package/src/nodes/__tests__/ApprovalGateNode.test.ts +206 -0
  45. package/src/nodes/index.ts +5 -0
  46. package/src/run.ts +55 -1
  47. package/src/specs/agent-handoffs-bedrock.integration.test.ts +2 -2
  48. package/src/specs/agent-handoffs.test.ts +153 -6
  49. package/src/tools/ToolNode.ts +28 -23
  50. package/src/tools/__tests__/ToolApproval.test.ts +162 -325
  51. package/src/types/graph.ts +32 -0
  52. package/src/types/tools.ts +7 -9
  53. package/src/utils/run.ts +9 -1
@@ -0,0 +1,72 @@
1
+ import { interrupt } from '@langchain/langgraph';
2
+ import { GraphEvents } from '../common/enum.mjs';
3
+ import '../tools/approval/constants.mjs';
4
+ import { safeDispatchCustomEvent } from '../utils/events.mjs';
5
+
6
+ /**
7
+ * Creates a graph node function that acts as an approval gate.
8
+ *
9
+ * Unlike tool approval (which respects ExecutionContext and can be auto-approved
10
+ * in scheduled/handoff modes), approval gates ALWAYS fire. They are placed by
11
+ * the builder between agents in a sequence and represent explicit human
12
+ * checkpoints that cannot be bypassed.
13
+ *
14
+ * Flow:
15
+ * 1. Dispatch ON_APPROVAL_GATE notification (for SSE/persistence)
16
+ * 2. Call interrupt() — graph pauses, state is checkpointed
17
+ * 3. On resume, interrupt() returns the ToolApprovalResponse
18
+ * 4. If approved, pass state through (next agent runs)
19
+ * 5. If denied, return state as-is (routing handled by conditional edge)
20
+ *
21
+ * @param config - The approval gate configuration from the edge definition
22
+ * @param sourceAgentId - The agent that precedes this gate
23
+ * @param destinationAgentId - The agent that follows this gate
24
+ */
25
+ function createApprovalGateNode(config, sourceAgentId, destinationAgentId) {
26
+ const { gateId, channel = 'chat', prompt, approver, timeoutMs, } = config;
27
+ /**
28
+ * The gate node function. Receives the current graph state,
29
+ * dispatches a notification, calls interrupt(), and returns
30
+ * the state with an approval result annotation.
31
+ */
32
+ return async function approvalGateNode(state, runnableConfig) {
33
+ const interruptPayload = {
34
+ type: 'approval_gate',
35
+ gateId,
36
+ channel,
37
+ prompt,
38
+ approver,
39
+ timeoutMs,
40
+ sourceAgentId,
41
+ destinationAgentId,
42
+ };
43
+ // Dispatch notification event so the host can:
44
+ // 1. Persist the approval request to MongoDB
45
+ // 2. Route to the appropriate channel adapter
46
+ // 3. Emit SSE event for chat UI
47
+ safeDispatchCustomEvent(GraphEvents.ON_APPROVAL_GATE, interruptPayload, runnableConfig);
48
+ // Pause the graph — state is checkpointed by the MongoDBSaver.
49
+ // On resume via Command({ resume: ToolApprovalResponse }), interrupt()
50
+ // returns the response value.
51
+ const response = interrupt(interruptPayload);
52
+ // Return empty state update — the graph structure (conditional edges)
53
+ // handles routing based on the approval result. We store the response
54
+ // in a message so downstream nodes can access it if needed.
55
+ if (response.approved) {
56
+ return {};
57
+ }
58
+ // On denial, we could add a system message noting the denial.
59
+ // The conditional edge after this node will route to END or skip.
60
+ return {};
61
+ };
62
+ }
63
+ /**
64
+ * Node ID for an approval gate, derived from the gate configuration.
65
+ * Used by MultiAgentGraph when inserting gate nodes into the graph.
66
+ */
67
+ function getApprovalGateNodeId(gateId) {
68
+ return `approval_gate_${gateId}`;
69
+ }
70
+
71
+ export { createApprovalGateNode, getApprovalGateNodeId };
72
+ //# sourceMappingURL=ApprovalGateNode.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ApprovalGateNode.mjs","sources":["../../../src/nodes/ApprovalGateNode.ts"],"sourcesContent":["import { interrupt } from '@langchain/langgraph';\nimport type { RunnableConfig } from '@langchain/core/runnables';\nimport type { ApprovalGateConfig, BaseGraphState } from '@/types/graph';\nimport type { ToolApprovalResponse } from '@/types/tools';\nimport { GraphEvents } from '@/common';\nimport { safeDispatchCustomEvent } from '@/utils/events';\n\n/**\n * Interrupt payload for approval gate nodes.\n * Passed to interrupt() and persisted in the checkpoint.\n */\nexport interface ApprovalGateInterrupt {\n /** Discriminator to distinguish from tool approval interrupts */\n type: 'approval_gate';\n /** Unique gate identifier */\n gateId: string;\n /** Approval channel (chat, outlook, telegram) */\n channel: string;\n /** Human-readable prompt for the approver */\n prompt?: string;\n /** Approver identifier */\n approver?: string;\n /** Timeout in ms */\n timeoutMs?: number;\n /** Source agent ID (who just finished) */\n sourceAgentId?: string;\n /** Destination agent ID (who will run next if approved) */\n destinationAgentId?: string;\n}\n\n/**\n * Creates a graph node function that acts as an approval gate.\n *\n * Unlike tool approval (which respects ExecutionContext and can be auto-approved\n * in scheduled/handoff modes), approval gates ALWAYS fire. They are placed by\n * the builder between agents in a sequence and represent explicit human\n * checkpoints that cannot be bypassed.\n *\n * Flow:\n * 1. Dispatch ON_APPROVAL_GATE notification (for SSE/persistence)\n * 2. Call interrupt() — graph pauses, state is checkpointed\n * 3. On resume, interrupt() returns the ToolApprovalResponse\n * 4. If approved, pass state through (next agent runs)\n * 5. If denied, return state as-is (routing handled by conditional edge)\n *\n * @param config - The approval gate configuration from the edge definition\n * @param sourceAgentId - The agent that precedes this gate\n * @param destinationAgentId - The agent that follows this gate\n */\nexport function createApprovalGateNode(\n config: ApprovalGateConfig,\n sourceAgentId: string,\n destinationAgentId: string,\n) {\n const {\n gateId,\n channel = 'chat',\n prompt,\n approver,\n timeoutMs,\n } = config;\n\n /**\n * The gate node function. Receives the current graph state,\n * dispatches a notification, calls interrupt(), and returns\n * the state with an approval result annotation.\n */\n return async function approvalGateNode(\n state: BaseGraphState,\n runnableConfig?: RunnableConfig,\n ): Promise<Partial<BaseGraphState>> {\n const interruptPayload: ApprovalGateInterrupt = {\n type: 'approval_gate',\n gateId,\n channel,\n prompt,\n approver,\n timeoutMs,\n sourceAgentId,\n destinationAgentId,\n };\n\n // Dispatch notification event so the host can:\n // 1. Persist the approval request to MongoDB\n // 2. Route to the appropriate channel adapter\n // 3. Emit SSE event for chat UI\n safeDispatchCustomEvent(\n GraphEvents.ON_APPROVAL_GATE,\n interruptPayload,\n runnableConfig,\n );\n\n // Pause the graph — state is checkpointed by the MongoDBSaver.\n // On resume via Command({ resume: ToolApprovalResponse }), interrupt()\n // returns the response value.\n const response = interrupt(interruptPayload) as ToolApprovalResponse;\n\n // Return empty state update — the graph structure (conditional edges)\n // handles routing based on the approval result. We store the response\n // in a message so downstream nodes can access it if needed.\n if (response.approved) {\n return {};\n }\n\n // On denial, we could add a system message noting the denial.\n // The conditional edge after this node will route to END or skip.\n return {};\n };\n}\n\n/**\n * Node ID for an approval gate, derived from the gate configuration.\n * Used by MultiAgentGraph when inserting gate nodes into the graph.\n */\nexport function getApprovalGateNodeId(gateId: string): string {\n return `approval_gate_${gateId}`;\n}\n"],"names":[],"mappings":";;;;;AA8BA;;;;;;;;;;;;;;;;;;AAkBG;SACa,sBAAsB,CACpC,MAA0B,EAC1B,aAAqB,EACrB,kBAA0B,EAAA;AAE1B,IAAA,MAAM,EACJ,MAAM,EACN,OAAO,GAAG,MAAM,EAChB,MAAM,EACN,QAAQ,EACR,SAAS,GACV,GAAG,MAAM;AAEV;;;;AAIG;AACH,IAAA,OAAO,eAAe,gBAAgB,CACpC,KAAqB,EACrB,cAA+B,EAAA;AAE/B,QAAA,MAAM,gBAAgB,GAA0B;AAC9C,YAAA,IAAI,EAAE,eAAe;YACrB,MAAM;YACN,OAAO;YACP,MAAM;YACN,QAAQ;YACR,SAAS;YACT,aAAa;YACb,kBAAkB;SACnB;;;;;QAMD,uBAAuB,CACrB,WAAW,CAAC,gBAAgB,EAC5B,gBAAgB,EAChB,cAAc,CACf;;;;AAKD,QAAA,MAAM,QAAQ,GAAG,SAAS,CAAC,gBAAgB,CAAyB;;;;AAKpE,QAAA,IAAI,QAAQ,CAAC,QAAQ,EAAE;AACrB,YAAA,OAAO,EAAE;QACX;;;AAIA,QAAA,OAAO,EAAE;AACX,IAAA,CAAC;AACH;AAEA;;;AAGG;AACG,SAAU,qBAAqB,CAAC,MAAc,EAAA;IAClD,OAAO,CAAA,cAAA,EAAiB,MAAM,CAAA,CAAE;AAClC;;;;"}
package/dist/esm/run.mjs CHANGED
@@ -184,6 +184,14 @@ class Run {
184
184
  }
185
185
  };
186
186
  }
187
+ /**
188
+ * Processes the graph stream for a given input.
189
+ *
190
+ * @param inputs - Either the initial state (IState) for a new run, or a
191
+ * Command (e.g., `new Command({ resume: ... })`) to resume from an interrupt.
192
+ * @param config - Runnable config with version and optional run_id.
193
+ * @param streamOptions - Optional stream event callbacks and options.
194
+ */
187
195
  async processStream(inputs, config, streamOptions) {
188
196
  if (this.graphRunnable == null) {
189
197
  throw new Error('Run not initialized. Make sure to use Run.create() to instantiate the Run.');
@@ -304,6 +312,43 @@ class Run {
304
312
  }
305
313
  });
306
314
  }
315
+ /**
316
+ * Checks whether the graph was interrupted (e.g., by HITL tool approval).
317
+ * Call after processStream() returns to determine if the graph is waiting
318
+ * for a resume via Command({ resume }).
319
+ *
320
+ * Requires a checkpointer to be configured — without one, interrupt state
321
+ * is not persisted and this always returns false.
322
+ */
323
+ async hasInterrupts(config) {
324
+ if (!this.graphRunnable) {
325
+ return false;
326
+ }
327
+ try {
328
+ const state = await this.graphRunnable.getState(config);
329
+ return state.tasks?.some((task) => task.interrupts?.length > 0) ?? false;
330
+ }
331
+ catch {
332
+ return false;
333
+ }
334
+ }
335
+ /**
336
+ * Returns the interrupt values from the graph state.
337
+ * Each interrupt's `value` contains the data passed to `interrupt()` by the node
338
+ * (e.g., a ToolApprovalRequest for HITL).
339
+ */
340
+ async getInterruptValues(config) {
341
+ if (!this.graphRunnable) {
342
+ return [];
343
+ }
344
+ try {
345
+ const state = await this.graphRunnable.getState(config);
346
+ return (state.tasks?.flatMap((task) => (task.interrupts ?? []).map((i) => i.value)) ?? []);
347
+ }
348
+ catch {
349
+ return [];
350
+ }
351
+ }
307
352
  getCallbacks(clientCallbacks) {
308
353
  return {
309
354
  [Callback.TOOL_ERROR]: this.createSystemCallback(clientCallbacks, Callback.TOOL_ERROR),
@@ -1 +1 @@
1
- {"version":3,"file":"run.mjs","sources":["../../src/run.ts"],"sourcesContent":["// src/run.ts\nimport './instrumentation';\nimport { ObservabilityCallbackHandler } from '@illuma-ai/observability-langchain';\nimport { PromptTemplate } from '@langchain/core/prompts';\nimport { RunnableLambda } from '@langchain/core/runnables';\nimport { AzureChatOpenAI, ChatOpenAI } from '@langchain/openai';\nimport { BaseCallbackHandler } from '@langchain/core/callbacks/base';\nimport type {\n MessageContentComplex,\n BaseMessage,\n} from '@langchain/core/messages';\nimport type { StringPromptValue } from '@langchain/core/prompt_values';\nimport type { RunnableConfig } from '@langchain/core/runnables';\nimport type * as t from '@/types';\nimport {\n createCompletionTitleRunnable,\n createTitleRunnable,\n} from '@/utils/title';\nimport { createTokenCounter, encodingForModel } from '@/utils/tokens';\nimport { GraphEvents, Callback, TitleMethod } from '@/common';\nimport { MultiAgentGraph } from '@/graphs/MultiAgentGraph';\nimport { StandardGraph } from '@/graphs/Graph';\nimport { HandlerRegistry } from '@/events';\nimport { isOpenAILike } from '@/utils/llm';\nimport { isPresent } from '@/utils/misc';\n\nexport const defaultOmitOptions = new Set([\n 'stream',\n 'thinking',\n 'streaming',\n 'maxTokens',\n 'clientOptions',\n 'thinkingConfig',\n 'thinkingBudget',\n 'includeThoughts',\n 'maxOutputTokens',\n 'additionalModelRequestFields',\n]);\n\nexport class Run<_T extends t.BaseGraphState> {\n id: string;\n private tokenCounter?: t.TokenCounter;\n private handlerRegistry?: HandlerRegistry;\n private indexTokenCountMap?: Record<string, number>;\n graphRunnable?: t.CompiledStateWorkflow;\n Graph: StandardGraph | MultiAgentGraph | undefined;\n returnContent: boolean = false;\n private skipCleanup: boolean = false;\n\n private constructor(config: Partial<t.RunConfig>) {\n const runId = config.runId ?? '';\n if (!runId) {\n throw new Error('Run ID not provided');\n }\n\n this.id = runId;\n this.tokenCounter = config.tokenCounter;\n this.indexTokenCountMap = config.indexTokenCountMap;\n\n const handlerRegistry = new HandlerRegistry();\n\n if (config.customHandlers) {\n for (const [eventType, handler] of Object.entries(\n config.customHandlers\n )) {\n handlerRegistry.register(eventType, handler);\n }\n }\n\n this.handlerRegistry = handlerRegistry;\n\n if (!config.graphConfig) {\n throw new Error('Graph config not provided');\n }\n\n /** Handle different graph types */\n if (config.graphConfig.type === 'multi-agent') {\n this.graphRunnable = this.createMultiAgentGraph(config.graphConfig);\n if (this.Graph) {\n this.Graph.handlerRegistry = handlerRegistry;\n }\n } else {\n /** Default to legacy graph for 'standard' or undefined type */\n this.graphRunnable = this.createLegacyGraph(config.graphConfig);\n if (this.Graph) {\n this.Graph.compileOptions =\n config.graphConfig.compileOptions ?? this.Graph.compileOptions;\n this.Graph.handlerRegistry = handlerRegistry;\n }\n }\n\n this.returnContent = config.returnContent ?? false;\n this.skipCleanup = config.skipCleanup ?? false;\n }\n\n private createLegacyGraph(\n config: t.LegacyGraphConfig | t.StandardGraphConfig\n ): t.CompiledStateWorkflow {\n let agentConfig: t.AgentInputs;\n let signal: AbortSignal | undefined;\n\n /** Check if this is a multi-agent style config (has agents array) */\n if ('agents' in config && Array.isArray(config.agents)) {\n if (config.agents.length === 0) {\n throw new Error('At least one agent must be provided');\n }\n agentConfig = config.agents[0];\n signal = config.signal;\n } else {\n /** Legacy path: build agent config from llmConfig */\n const {\n type: _type,\n llmConfig,\n signal: legacySignal,\n tools = [],\n ...agentInputs\n } = config as t.LegacyGraphConfig;\n const { provider, ...clientOptions } = llmConfig;\n\n agentConfig = {\n ...agentInputs,\n tools,\n provider,\n clientOptions,\n agentId: 'default',\n };\n signal = legacySignal;\n }\n\n const standardGraph = new StandardGraph({\n signal,\n runId: this.id,\n agents: [agentConfig],\n tokenCounter: this.tokenCounter,\n indexTokenCountMap: this.indexTokenCountMap,\n });\n /** Propagate compile options from graph config */\n standardGraph.compileOptions = config.compileOptions;\n this.Graph = standardGraph;\n return standardGraph.createWorkflow();\n }\n\n private createMultiAgentGraph(\n config: t.MultiAgentGraphConfig\n ): t.CompiledStateWorkflow {\n const { agents, edges, compileOptions, resumeFromAgentId } = config;\n\n const multiAgentGraph = new MultiAgentGraph({\n runId: this.id,\n agents,\n edges,\n resumeFromAgentId,\n tokenCounter: this.tokenCounter,\n indexTokenCountMap: this.indexTokenCountMap,\n });\n\n if (compileOptions != null) {\n multiAgentGraph.compileOptions = compileOptions;\n }\n\n this.Graph = multiAgentGraph;\n return multiAgentGraph.createWorkflow();\n }\n\n static async create<T extends t.BaseGraphState>(\n config: t.RunConfig\n ): Promise<Run<T>> {\n /** Create tokenCounter if indexTokenCountMap is provided but tokenCounter is not */\n if (config.indexTokenCountMap && !config.tokenCounter) {\n const gc = config.graphConfig;\n const clientOpts =\n 'agents' in gc ? gc.agents[0]?.clientOptions : gc.clientOptions;\n const model = (clientOpts as { model?: string } | undefined)?.model ?? '';\n config.tokenCounter = await createTokenCounter(encodingForModel(model));\n }\n return new Run<T>(config);\n }\n\n getRunMessages(): BaseMessage[] | undefined {\n if (!this.Graph) {\n throw new Error(\n 'Graph not initialized. Make sure to use Run.create() to instantiate the Run.'\n );\n }\n return this.Graph.getRunMessages();\n }\n\n /**\n * Manually trigger cleanup of heavy state (messages, config, etc.).\n * Call this after all continuations are complete when using skipCleanup=true.\n */\n clearState(): void {\n if (this.Graph) {\n this.Graph.clearHeavyState();\n }\n }\n\n /**\n * Returns the normalized finish/stop reason from the last LLM invocation.\n * Delegates to the underlying Graph instance.\n */\n getLastFinishReason(): string | undefined {\n if (this.Graph && 'getLastFinishReason' in this.Graph) {\n return (this.Graph as StandardGraph).getLastFinishReason();\n }\n return undefined;\n }\n\n /**\n * Returns the ID of the last agent that produced output in a multi-agent graph.\n * Used by auto-continuation to determine which agent's context to preserve\n * when a response is truncated after an agent handoff.\n * Returns undefined for single-agent graphs.\n */\n getLastActiveAgentId(): string | undefined {\n if (this.Graph && this.Graph instanceof MultiAgentGraph) {\n return this.Graph.getLastActiveAgentId();\n }\n return undefined;\n }\n\n /**\n * Creates a custom event callback handler that intercepts custom events\n * and processes them through our handler registry instead of EventStreamCallbackHandler\n */\n private createCustomEventCallback() {\n return async (\n eventName: string,\n data: unknown,\n runId: string,\n tags?: string[],\n metadata?: Record<string, unknown>\n ): Promise<void> => {\n const handler = this.handlerRegistry?.getHandler(eventName);\n if (handler && this.Graph) {\n return await handler.handle(\n eventName,\n data as\n | t.StreamEventData\n | t.ModelEndData\n | t.RunStep\n | t.RunStepDeltaEvent\n | t.MessageDeltaEvent\n | t.ReasoningDeltaEvent\n | { result: t.ToolEndEvent },\n metadata,\n this.Graph\n );\n }\n };\n }\n\n async processStream(\n inputs: t.IState,\n config: Partial<RunnableConfig> & { version: 'v1' | 'v2'; run_id?: string },\n streamOptions?: t.EventStreamOptions\n ): Promise<MessageContentComplex[] | undefined> {\n if (this.graphRunnable == null) {\n throw new Error(\n 'Run not initialized. Make sure to use Run.create() to instantiate the Run.'\n );\n }\n if (!this.Graph) {\n throw new Error(\n 'Graph not initialized. Make sure to use Run.create() to instantiate the Run.'\n );\n }\n\n this.Graph.resetValues(streamOptions?.keepContent);\n\n /** Custom event callback to intercept and handle custom events */\n const customEventCallback = this.createCustomEventCallback();\n\n const baseCallbacks = (config.callbacks as t.ProvidedCallbacks) ?? [];\n const streamCallbacks = streamOptions?.callbacks\n ? this.getCallbacks(streamOptions.callbacks)\n : [];\n\n const customHandler = BaseCallbackHandler.fromMethods({\n [Callback.CUSTOM_EVENT]: customEventCallback,\n });\n customHandler.awaitHandlers = true;\n\n config.callbacks = baseCallbacks\n .concat(streamCallbacks)\n .concat(customHandler);\n\n const illumaSecretKey = process.env.ILLUMA_SECRET_KEY;\n const illumaPublicKey = process.env.ILLUMA_PUBLIC_KEY;\n const illumaBaseUrl = process.env.ILLUMA_BASE_URL;\n\n if (\n isPresent(illumaSecretKey) &&\n isPresent(illumaPublicKey) &&\n isPresent(illumaBaseUrl)\n ) {\n try {\n const userId = config.configurable?.user_id;\n const sessionId = config.configurable?.thread_id;\n const handler = new ObservabilityCallbackHandler({\n clientOptions: {\n secretKey: illumaSecretKey!,\n publicKey: illumaPublicKey!,\n baseUrl: illumaBaseUrl!,\n },\n userId,\n sessionId,\n metadata: {\n messageId: this.id,\n parentMessageId: config.configurable?.requestBody?.parentMessageId,\n },\n });\n config.callbacks = (\n (config.callbacks as t.ProvidedCallbacks) ?? []\n ).concat([handler]);\n } catch {\n // Gracefully skip if @illuma-ai/observability-node is not installed\n }\n }\n\n if (!this.id) {\n throw new Error('Run ID not provided');\n }\n\n config.run_id = this.id;\n config.configurable = Object.assign(config.configurable ?? {}, {\n run_id: this.id,\n });\n\n const stream = this.graphRunnable.streamEvents(inputs, config, {\n raiseError: true,\n /**\n * Prevent EventStreamCallbackHandler from processing custom events.\n * Custom events are already handled via our createCustomEventCallback()\n * which routes them through the handlerRegistry.\n * Without this flag, EventStreamCallbackHandler throws errors when\n * custom events are dispatched for run IDs not in its internal map\n * (due to timing issues in parallel execution or after run cleanup).\n */\n ignoreCustomEvent: true,\n });\n\n for await (const event of stream) {\n const { data, metadata, ...info } = event;\n\n const eventName: t.EventName = info.event;\n\n /** Skip custom events as they're handled by our callback */\n if (eventName === GraphEvents.ON_CUSTOM_EVENT) {\n continue;\n }\n\n const handler = this.handlerRegistry?.getHandler(eventName);\n if (handler) {\n await handler.handle(eventName, data, metadata, this.Graph);\n }\n }\n\n /**\n * Break the reference chain that keeps heavy data alive via\n * LangGraph's internal `__pregel_scratchpad.currentTaskInput` →\n * `@langchain/core` `RunTree.extra[lc:child_config]` →\n * Node.js `AsyncLocalStorage` context captured by timers/promises.\n *\n * Without this, base64-encoded images/PDFs in message content remain\n * reachable from lingering `Timeout` handles until GC runs.\n */\n if (!this.skipCleanup) {\n if (\n (config.configurable as Record<string, unknown> | undefined) != null\n ) {\n for (const key of Object.getOwnPropertySymbols(config.configurable)) {\n const val = config.configurable[key as unknown as string] as\n | Record<string, unknown>\n | undefined;\n if (\n val != null &&\n typeof val === 'object' &&\n 'currentTaskInput' in val\n ) {\n (val as Record<string, unknown>).currentTaskInput = undefined;\n }\n delete config.configurable[key as unknown as string];\n }\n config.configurable = undefined;\n }\n config.callbacks = undefined;\n }\n\n const result = this.returnContent\n ? this.Graph.getContentParts()\n : undefined;\n\n if (!this.skipCleanup) {\n this.Graph.clearHeavyState();\n }\n return result;\n }\n\n private createSystemCallback<K extends keyof t.ClientCallbacks>(\n clientCallbacks: t.ClientCallbacks,\n key: K\n ): t.SystemCallbacks[K] {\n return ((...args: unknown[]) => {\n const clientCallback = clientCallbacks[key];\n if (clientCallback && this.Graph) {\n (clientCallback as (...args: unknown[]) => void)(this.Graph, ...args);\n }\n }) as t.SystemCallbacks[K];\n }\n\n getCallbacks(clientCallbacks: t.ClientCallbacks): t.SystemCallbacks {\n return {\n [Callback.TOOL_ERROR]: this.createSystemCallback(\n clientCallbacks,\n Callback.TOOL_ERROR\n ),\n [Callback.TOOL_START]: this.createSystemCallback(\n clientCallbacks,\n Callback.TOOL_START\n ),\n [Callback.TOOL_END]: this.createSystemCallback(\n clientCallbacks,\n Callback.TOOL_END\n ),\n };\n }\n\n async generateTitle({\n provider,\n inputText,\n contentParts,\n titlePrompt,\n clientOptions,\n chainOptions,\n skipLanguage,\n titleMethod = TitleMethod.COMPLETION,\n titlePromptTemplate,\n }: t.RunTitleOptions): Promise<{ language?: string; title?: string }> {\n const titleSecretKey = process.env.ILLUMA_SECRET_KEY;\n const titlePublicKey = process.env.ILLUMA_PUBLIC_KEY;\n const titleBaseUrl = process.env.ILLUMA_BASE_URL;\n\n if (\n chainOptions != null &&\n isPresent(titleSecretKey) &&\n isPresent(titlePublicKey) &&\n isPresent(titleBaseUrl)\n ) {\n try {\n const userId = chainOptions.configurable?.user_id;\n const sessionId = chainOptions.configurable?.thread_id;\n const handler = new ObservabilityCallbackHandler({\n clientOptions: {\n secretKey: titleSecretKey!,\n publicKey: titlePublicKey!,\n baseUrl: titleBaseUrl!,\n },\n userId,\n sessionId,\n metadata: {\n messageId: 'title-' + this.id,\n },\n });\n chainOptions.callbacks = (\n (chainOptions.callbacks as t.ProvidedCallbacks) ?? []\n ).concat([handler]);\n } catch {\n // Gracefully skip if @illuma-ai/observability-node is not installed\n }\n }\n\n const convoTemplate = PromptTemplate.fromTemplate(\n titlePromptTemplate ?? 'User: {input}\\nAI: {output}'\n );\n\n const response = contentParts\n .map((part) => {\n if (part?.type === 'text') return part.text;\n return '';\n })\n .join('\\n');\n\n const model = this.Graph?.getNewModel({\n provider,\n clientOptions,\n });\n if (!model) {\n return { language: '', title: '' };\n }\n if (\n isOpenAILike(provider) &&\n (model instanceof ChatOpenAI || model instanceof AzureChatOpenAI)\n ) {\n model.temperature = (clientOptions as t.OpenAIClientOptions | undefined)\n ?.temperature as number;\n model.topP = (clientOptions as t.OpenAIClientOptions | undefined)\n ?.topP as number;\n model.frequencyPenalty = (\n clientOptions as t.OpenAIClientOptions | undefined\n )?.frequencyPenalty as number;\n model.presencePenalty = (\n clientOptions as t.OpenAIClientOptions | undefined\n )?.presencePenalty as number;\n model.n = (clientOptions as t.OpenAIClientOptions | undefined)\n ?.n as number;\n }\n\n const convoToTitleInput = new RunnableLambda({\n func: (\n promptValue: StringPromptValue\n ): { convo: string; inputText: string; skipLanguage?: boolean } => ({\n convo: promptValue.value,\n inputText,\n skipLanguage,\n }),\n }).withConfig({ runName: 'ConvoTransform' });\n\n const titleChain =\n titleMethod === TitleMethod.COMPLETION\n ? await createCompletionTitleRunnable(model, titlePrompt)\n : await createTitleRunnable(model, titlePrompt);\n\n /** Pipes `convoTemplate` -> `transformer` -> `titleChain` */\n const fullChain = convoTemplate\n .withConfig({ runName: 'ConvoTemplate' })\n .pipe(convoToTitleInput)\n .pipe(titleChain)\n .withConfig({ runName: 'TitleChain' });\n\n const invokeConfig = Object.assign({}, chainOptions, {\n run_id: this.id,\n runId: this.id,\n });\n\n try {\n return await fullChain.invoke(\n { input: inputText, output: response },\n invokeConfig\n );\n } catch (_e) {\n // Fallback: strip callbacks to avoid EventStream tracer errors in certain environments\n // But preserve observability handler if it exists\n const observabilityHandler = (\n invokeConfig.callbacks as t.ProvidedCallbacks\n )?.find((cb) => cb instanceof ObservabilityCallbackHandler);\n const { callbacks: _cb, ...rest } = invokeConfig;\n const safeConfig = Object.assign({}, rest, {\n callbacks: observabilityHandler ? [observabilityHandler] : [],\n });\n return await fullChain.invoke(\n { input: inputText, output: response },\n safeConfig as Partial<RunnableConfig>\n );\n }\n }\n}\n"],"names":["ChatOpenAI","AzureChatOpenAI"],"mappings":"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"}
1
+ {"version":3,"file":"run.mjs","sources":["../../src/run.ts"],"sourcesContent":["// src/run.ts\nimport './instrumentation';\nimport { ObservabilityCallbackHandler } from '@illuma-ai/observability-langchain';\nimport { Command } from '@langchain/langgraph';\nimport { PromptTemplate } from '@langchain/core/prompts';\nimport { RunnableLambda } from '@langchain/core/runnables';\nimport { AzureChatOpenAI, ChatOpenAI } from '@langchain/openai';\nimport { BaseCallbackHandler } from '@langchain/core/callbacks/base';\nimport type {\n MessageContentComplex,\n BaseMessage,\n} from '@langchain/core/messages';\nimport type { StringPromptValue } from '@langchain/core/prompt_values';\nimport type { RunnableConfig } from '@langchain/core/runnables';\nimport type * as t from '@/types';\nimport {\n createCompletionTitleRunnable,\n createTitleRunnable,\n} from '@/utils/title';\nimport { createTokenCounter, encodingForModel } from '@/utils/tokens';\nimport { GraphEvents, Callback, TitleMethod } from '@/common';\nimport { MultiAgentGraph } from '@/graphs/MultiAgentGraph';\nimport { StandardGraph } from '@/graphs/Graph';\nimport { HandlerRegistry } from '@/events';\nimport { isOpenAILike } from '@/utils/llm';\nimport { isPresent } from '@/utils/misc';\n\nexport const defaultOmitOptions = new Set([\n 'stream',\n 'thinking',\n 'streaming',\n 'maxTokens',\n 'clientOptions',\n 'thinkingConfig',\n 'thinkingBudget',\n 'includeThoughts',\n 'maxOutputTokens',\n 'additionalModelRequestFields',\n]);\n\nexport class Run<_T extends t.BaseGraphState> {\n id: string;\n private tokenCounter?: t.TokenCounter;\n private handlerRegistry?: HandlerRegistry;\n private indexTokenCountMap?: Record<string, number>;\n graphRunnable?: t.CompiledStateWorkflow;\n Graph: StandardGraph | MultiAgentGraph | undefined;\n returnContent: boolean = false;\n private skipCleanup: boolean = false;\n\n private constructor(config: Partial<t.RunConfig>) {\n const runId = config.runId ?? '';\n if (!runId) {\n throw new Error('Run ID not provided');\n }\n\n this.id = runId;\n this.tokenCounter = config.tokenCounter;\n this.indexTokenCountMap = config.indexTokenCountMap;\n\n const handlerRegistry = new HandlerRegistry();\n\n if (config.customHandlers) {\n for (const [eventType, handler] of Object.entries(\n config.customHandlers\n )) {\n handlerRegistry.register(eventType, handler);\n }\n }\n\n this.handlerRegistry = handlerRegistry;\n\n if (!config.graphConfig) {\n throw new Error('Graph config not provided');\n }\n\n /** Handle different graph types */\n if (config.graphConfig.type === 'multi-agent') {\n this.graphRunnable = this.createMultiAgentGraph(config.graphConfig);\n if (this.Graph) {\n this.Graph.handlerRegistry = handlerRegistry;\n }\n } else {\n /** Default to legacy graph for 'standard' or undefined type */\n this.graphRunnable = this.createLegacyGraph(config.graphConfig);\n if (this.Graph) {\n this.Graph.compileOptions =\n config.graphConfig.compileOptions ?? this.Graph.compileOptions;\n this.Graph.handlerRegistry = handlerRegistry;\n }\n }\n\n this.returnContent = config.returnContent ?? false;\n this.skipCleanup = config.skipCleanup ?? false;\n }\n\n private createLegacyGraph(\n config: t.LegacyGraphConfig | t.StandardGraphConfig\n ): t.CompiledStateWorkflow {\n let agentConfig: t.AgentInputs;\n let signal: AbortSignal | undefined;\n\n /** Check if this is a multi-agent style config (has agents array) */\n if ('agents' in config && Array.isArray(config.agents)) {\n if (config.agents.length === 0) {\n throw new Error('At least one agent must be provided');\n }\n agentConfig = config.agents[0];\n signal = config.signal;\n } else {\n /** Legacy path: build agent config from llmConfig */\n const {\n type: _type,\n llmConfig,\n signal: legacySignal,\n tools = [],\n ...agentInputs\n } = config as t.LegacyGraphConfig;\n const { provider, ...clientOptions } = llmConfig;\n\n agentConfig = {\n ...agentInputs,\n tools,\n provider,\n clientOptions,\n agentId: 'default',\n };\n signal = legacySignal;\n }\n\n const standardGraph = new StandardGraph({\n signal,\n runId: this.id,\n agents: [agentConfig],\n tokenCounter: this.tokenCounter,\n indexTokenCountMap: this.indexTokenCountMap,\n });\n /** Propagate compile options from graph config */\n standardGraph.compileOptions = config.compileOptions;\n this.Graph = standardGraph;\n return standardGraph.createWorkflow();\n }\n\n private createMultiAgentGraph(\n config: t.MultiAgentGraphConfig\n ): t.CompiledStateWorkflow {\n const { agents, edges, compileOptions, resumeFromAgentId } = config;\n\n const multiAgentGraph = new MultiAgentGraph({\n runId: this.id,\n agents,\n edges,\n resumeFromAgentId,\n tokenCounter: this.tokenCounter,\n indexTokenCountMap: this.indexTokenCountMap,\n });\n\n if (compileOptions != null) {\n multiAgentGraph.compileOptions = compileOptions;\n }\n\n this.Graph = multiAgentGraph;\n return multiAgentGraph.createWorkflow();\n }\n\n static async create<T extends t.BaseGraphState>(\n config: t.RunConfig\n ): Promise<Run<T>> {\n /** Create tokenCounter if indexTokenCountMap is provided but tokenCounter is not */\n if (config.indexTokenCountMap && !config.tokenCounter) {\n const gc = config.graphConfig;\n const clientOpts =\n 'agents' in gc ? gc.agents[0]?.clientOptions : gc.clientOptions;\n const model = (clientOpts as { model?: string } | undefined)?.model ?? '';\n config.tokenCounter = await createTokenCounter(encodingForModel(model));\n }\n return new Run<T>(config);\n }\n\n getRunMessages(): BaseMessage[] | undefined {\n if (!this.Graph) {\n throw new Error(\n 'Graph not initialized. Make sure to use Run.create() to instantiate the Run.'\n );\n }\n return this.Graph.getRunMessages();\n }\n\n /**\n * Manually trigger cleanup of heavy state (messages, config, etc.).\n * Call this after all continuations are complete when using skipCleanup=true.\n */\n clearState(): void {\n if (this.Graph) {\n this.Graph.clearHeavyState();\n }\n }\n\n /**\n * Returns the normalized finish/stop reason from the last LLM invocation.\n * Delegates to the underlying Graph instance.\n */\n getLastFinishReason(): string | undefined {\n if (this.Graph && 'getLastFinishReason' in this.Graph) {\n return (this.Graph as StandardGraph).getLastFinishReason();\n }\n return undefined;\n }\n\n /**\n * Returns the ID of the last agent that produced output in a multi-agent graph.\n * Used by auto-continuation to determine which agent's context to preserve\n * when a response is truncated after an agent handoff.\n * Returns undefined for single-agent graphs.\n */\n getLastActiveAgentId(): string | undefined {\n if (this.Graph && this.Graph instanceof MultiAgentGraph) {\n return this.Graph.getLastActiveAgentId();\n }\n return undefined;\n }\n\n /**\n * Creates a custom event callback handler that intercepts custom events\n * and processes them through our handler registry instead of EventStreamCallbackHandler\n */\n private createCustomEventCallback() {\n return async (\n eventName: string,\n data: unknown,\n runId: string,\n tags?: string[],\n metadata?: Record<string, unknown>\n ): Promise<void> => {\n const handler = this.handlerRegistry?.getHandler(eventName);\n if (handler && this.Graph) {\n return await handler.handle(\n eventName,\n data as\n | t.StreamEventData\n | t.ModelEndData\n | t.RunStep\n | t.RunStepDeltaEvent\n | t.MessageDeltaEvent\n | t.ReasoningDeltaEvent\n | { result: t.ToolEndEvent },\n metadata,\n this.Graph\n );\n }\n };\n }\n\n /**\n * Processes the graph stream for a given input.\n *\n * @param inputs - Either the initial state (IState) for a new run, or a\n * Command (e.g., `new Command({ resume: ... })`) to resume from an interrupt.\n * @param config - Runnable config with version and optional run_id.\n * @param streamOptions - Optional stream event callbacks and options.\n */\n async processStream(\n inputs: t.IState | Command,\n config: Partial<RunnableConfig> & { version: 'v1' | 'v2'; run_id?: string },\n streamOptions?: t.EventStreamOptions\n ): Promise<MessageContentComplex[] | undefined> {\n if (this.graphRunnable == null) {\n throw new Error(\n 'Run not initialized. Make sure to use Run.create() to instantiate the Run.'\n );\n }\n if (!this.Graph) {\n throw new Error(\n 'Graph not initialized. Make sure to use Run.create() to instantiate the Run.'\n );\n }\n\n this.Graph.resetValues(streamOptions?.keepContent);\n\n /** Custom event callback to intercept and handle custom events */\n const customEventCallback = this.createCustomEventCallback();\n\n const baseCallbacks = (config.callbacks as t.ProvidedCallbacks) ?? [];\n const streamCallbacks = streamOptions?.callbacks\n ? this.getCallbacks(streamOptions.callbacks)\n : [];\n\n const customHandler = BaseCallbackHandler.fromMethods({\n [Callback.CUSTOM_EVENT]: customEventCallback,\n });\n customHandler.awaitHandlers = true;\n\n config.callbacks = baseCallbacks\n .concat(streamCallbacks)\n .concat(customHandler);\n\n const illumaSecretKey = process.env.ILLUMA_SECRET_KEY;\n const illumaPublicKey = process.env.ILLUMA_PUBLIC_KEY;\n const illumaBaseUrl = process.env.ILLUMA_BASE_URL;\n\n if (\n isPresent(illumaSecretKey) &&\n isPresent(illumaPublicKey) &&\n isPresent(illumaBaseUrl)\n ) {\n try {\n const userId = config.configurable?.user_id;\n const sessionId = config.configurable?.thread_id;\n const handler = new ObservabilityCallbackHandler({\n clientOptions: {\n secretKey: illumaSecretKey!,\n publicKey: illumaPublicKey!,\n baseUrl: illumaBaseUrl!,\n },\n userId,\n sessionId,\n metadata: {\n messageId: this.id,\n parentMessageId: config.configurable?.requestBody?.parentMessageId,\n },\n });\n config.callbacks = (\n (config.callbacks as t.ProvidedCallbacks) ?? []\n ).concat([handler]);\n } catch {\n // Gracefully skip if @illuma-ai/observability-node is not installed\n }\n }\n\n if (!this.id) {\n throw new Error('Run ID not provided');\n }\n\n config.run_id = this.id;\n config.configurable = Object.assign(config.configurable ?? {}, {\n run_id: this.id,\n });\n\n const stream = this.graphRunnable.streamEvents(inputs, config, {\n raiseError: true,\n /**\n * Prevent EventStreamCallbackHandler from processing custom events.\n * Custom events are already handled via our createCustomEventCallback()\n * which routes them through the handlerRegistry.\n * Without this flag, EventStreamCallbackHandler throws errors when\n * custom events are dispatched for run IDs not in its internal map\n * (due to timing issues in parallel execution or after run cleanup).\n */\n ignoreCustomEvent: true,\n });\n\n for await (const event of stream) {\n const { data, metadata, ...info } = event;\n\n const eventName: t.EventName = info.event;\n\n /** Skip custom events as they're handled by our callback */\n if (eventName === GraphEvents.ON_CUSTOM_EVENT) {\n continue;\n }\n\n const handler = this.handlerRegistry?.getHandler(eventName);\n if (handler) {\n await handler.handle(eventName, data, metadata, this.Graph);\n }\n }\n\n /**\n * Break the reference chain that keeps heavy data alive via\n * LangGraph's internal `__pregel_scratchpad.currentTaskInput` →\n * `@langchain/core` `RunTree.extra[lc:child_config]` →\n * Node.js `AsyncLocalStorage` context captured by timers/promises.\n *\n * Without this, base64-encoded images/PDFs in message content remain\n * reachable from lingering `Timeout` handles until GC runs.\n */\n if (!this.skipCleanup) {\n if (\n (config.configurable as Record<string, unknown> | undefined) != null\n ) {\n for (const key of Object.getOwnPropertySymbols(config.configurable)) {\n const val = config.configurable[key as unknown as string] as\n | Record<string, unknown>\n | undefined;\n if (\n val != null &&\n typeof val === 'object' &&\n 'currentTaskInput' in val\n ) {\n (val as Record<string, unknown>).currentTaskInput = undefined;\n }\n delete config.configurable[key as unknown as string];\n }\n config.configurable = undefined;\n }\n config.callbacks = undefined;\n }\n\n const result = this.returnContent\n ? this.Graph.getContentParts()\n : undefined;\n\n if (!this.skipCleanup) {\n this.Graph.clearHeavyState();\n }\n return result;\n }\n\n private createSystemCallback<K extends keyof t.ClientCallbacks>(\n clientCallbacks: t.ClientCallbacks,\n key: K\n ): t.SystemCallbacks[K] {\n return ((...args: unknown[]) => {\n const clientCallback = clientCallbacks[key];\n if (clientCallback && this.Graph) {\n (clientCallback as (...args: unknown[]) => void)(this.Graph, ...args);\n }\n }) as t.SystemCallbacks[K];\n }\n\n /**\n * Checks whether the graph was interrupted (e.g., by HITL tool approval).\n * Call after processStream() returns to determine if the graph is waiting\n * for a resume via Command({ resume }).\n *\n * Requires a checkpointer to be configured — without one, interrupt state\n * is not persisted and this always returns false.\n */\n async hasInterrupts(\n config: Partial<RunnableConfig>\n ): Promise<boolean> {\n if (!this.graphRunnable) {\n return false;\n }\n try {\n const state = await this.graphRunnable.getState(config);\n return state.tasks?.some((task) => task.interrupts?.length > 0) ?? false;\n } catch {\n return false;\n }\n }\n\n /**\n * Returns the interrupt values from the graph state.\n * Each interrupt's `value` contains the data passed to `interrupt()` by the node\n * (e.g., a ToolApprovalRequest for HITL).\n */\n async getInterruptValues(\n config: Partial<RunnableConfig>\n ): Promise<unknown[]> {\n if (!this.graphRunnable) {\n return [];\n }\n try {\n const state = await this.graphRunnable.getState(config);\n return (\n state.tasks?.flatMap((task) =>\n (task.interrupts ?? []).map((i) => i.value)\n ) ?? []\n );\n } catch {\n return [];\n }\n }\n\n getCallbacks(clientCallbacks: t.ClientCallbacks): t.SystemCallbacks {\n return {\n [Callback.TOOL_ERROR]: this.createSystemCallback(\n clientCallbacks,\n Callback.TOOL_ERROR\n ),\n [Callback.TOOL_START]: this.createSystemCallback(\n clientCallbacks,\n Callback.TOOL_START\n ),\n [Callback.TOOL_END]: this.createSystemCallback(\n clientCallbacks,\n Callback.TOOL_END\n ),\n };\n }\n\n async generateTitle({\n provider,\n inputText,\n contentParts,\n titlePrompt,\n clientOptions,\n chainOptions,\n skipLanguage,\n titleMethod = TitleMethod.COMPLETION,\n titlePromptTemplate,\n }: t.RunTitleOptions): Promise<{ language?: string; title?: string }> {\n const titleSecretKey = process.env.ILLUMA_SECRET_KEY;\n const titlePublicKey = process.env.ILLUMA_PUBLIC_KEY;\n const titleBaseUrl = process.env.ILLUMA_BASE_URL;\n\n if (\n chainOptions != null &&\n isPresent(titleSecretKey) &&\n isPresent(titlePublicKey) &&\n isPresent(titleBaseUrl)\n ) {\n try {\n const userId = chainOptions.configurable?.user_id;\n const sessionId = chainOptions.configurable?.thread_id;\n const handler = new ObservabilityCallbackHandler({\n clientOptions: {\n secretKey: titleSecretKey!,\n publicKey: titlePublicKey!,\n baseUrl: titleBaseUrl!,\n },\n userId,\n sessionId,\n metadata: {\n messageId: 'title-' + this.id,\n },\n });\n chainOptions.callbacks = (\n (chainOptions.callbacks as t.ProvidedCallbacks) ?? []\n ).concat([handler]);\n } catch {\n // Gracefully skip if @illuma-ai/observability-node is not installed\n }\n }\n\n const convoTemplate = PromptTemplate.fromTemplate(\n titlePromptTemplate ?? 'User: {input}\\nAI: {output}'\n );\n\n const response = contentParts\n .map((part) => {\n if (part?.type === 'text') return part.text;\n return '';\n })\n .join('\\n');\n\n const model = this.Graph?.getNewModel({\n provider,\n clientOptions,\n });\n if (!model) {\n return { language: '', title: '' };\n }\n if (\n isOpenAILike(provider) &&\n (model instanceof ChatOpenAI || model instanceof AzureChatOpenAI)\n ) {\n model.temperature = (clientOptions as t.OpenAIClientOptions | undefined)\n ?.temperature as number;\n model.topP = (clientOptions as t.OpenAIClientOptions | undefined)\n ?.topP as number;\n model.frequencyPenalty = (\n clientOptions as t.OpenAIClientOptions | undefined\n )?.frequencyPenalty as number;\n model.presencePenalty = (\n clientOptions as t.OpenAIClientOptions | undefined\n )?.presencePenalty as number;\n model.n = (clientOptions as t.OpenAIClientOptions | undefined)\n ?.n as number;\n }\n\n const convoToTitleInput = new RunnableLambda({\n func: (\n promptValue: StringPromptValue\n ): { convo: string; inputText: string; skipLanguage?: boolean } => ({\n convo: promptValue.value,\n inputText,\n skipLanguage,\n }),\n }).withConfig({ runName: 'ConvoTransform' });\n\n const titleChain =\n titleMethod === TitleMethod.COMPLETION\n ? await createCompletionTitleRunnable(model, titlePrompt)\n : await createTitleRunnable(model, titlePrompt);\n\n /** Pipes `convoTemplate` -> `transformer` -> `titleChain` */\n const fullChain = convoTemplate\n .withConfig({ runName: 'ConvoTemplate' })\n .pipe(convoToTitleInput)\n .pipe(titleChain)\n .withConfig({ runName: 'TitleChain' });\n\n const invokeConfig = Object.assign({}, chainOptions, {\n run_id: this.id,\n runId: this.id,\n });\n\n try {\n return await fullChain.invoke(\n { input: inputText, output: response },\n invokeConfig\n );\n } catch (_e) {\n // Fallback: strip callbacks to avoid EventStream tracer errors in certain environments\n // But preserve observability handler if it exists\n const observabilityHandler = (\n invokeConfig.callbacks as t.ProvidedCallbacks\n )?.find((cb) => cb instanceof ObservabilityCallbackHandler);\n const { callbacks: _cb, ...rest } = invokeConfig;\n const safeConfig = Object.assign({}, rest, {\n callbacks: observabilityHandler ? [observabilityHandler] : [],\n });\n return await fullChain.invoke(\n { input: inputText, output: response },\n safeConfig as Partial<RunnableConfig>\n );\n }\n }\n}\n"],"names":["ChatOpenAI","AzureChatOpenAI"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { ToolMessage, isBaseMessage, isAIMessage } from '@langchain/core/messages';
2
- import { isCommand, isGraphInterrupt, Command, Send, END } from '@langchain/langgraph';
2
+ import { interrupt, isCommand, isGraphInterrupt, Command, Send, END } from '@langchain/langgraph';
3
3
  import { ExecutionContext } from './approval/constants.mjs';
4
4
  import { GraphEvents, Constants } from '../common/enum.mjs';
5
5
  import 'nanoid';
@@ -170,18 +170,20 @@ class ToolNode extends RunnableCallable {
170
170
  }
171
171
  }
172
172
  /**
173
- * Requests human approval for a tool call via event dispatch.
174
- * Dispatches an ON_TOOL_APPROVAL_REQUIRED event and waits for the host
175
- * to resolve the promise with an approval response.
173
+ * Requests human approval for a tool call using LangGraph's native interrupt().
176
174
  *
177
- * This uses the same pattern as ON_TOOL_EXECUTE: a promise-based event
178
- * dispatch where the host calls resolve/reject when the human responds.
175
+ * Flow:
176
+ * 1. Dispatches ON_TOOL_APPROVAL_REQUIRED notification (no resolve/reject data only)
177
+ * so the host can persist the request and send UI events.
178
+ * 2. Calls interrupt() which checkpoints graph state and pauses execution.
179
+ * 3. When the host resumes via Command({ resume: ToolApprovalResponse }),
180
+ * interrupt() returns the response synchronously.
179
181
  *
180
182
  * @param call - The tool call requiring approval
181
183
  * @param config - The runnable config for event dispatch
182
184
  * @returns The approval response from the human
183
185
  */
184
- async requestApproval(call, config) {
186
+ requestApproval(call, config) {
185
187
  const approvalRequest = {
186
188
  type: 'tool_approval_required',
187
189
  toolCallId: call.id ?? '',
@@ -190,13 +192,13 @@ class ToolNode extends RunnableCallable {
190
192
  agentId: this.agentId,
191
193
  description: `Tool "${call.name}" wants to execute with the provided arguments.`,
192
194
  };
193
- return new Promise((resolve, reject) => {
194
- safeDispatchCustomEvent(GraphEvents.ON_TOOL_APPROVAL_REQUIRED, {
195
- ...approvalRequest,
196
- resolve,
197
- reject,
198
- }, config);
199
- });
195
+ // Notify host before interrupting — allows SSE event emission and DB persistence.
196
+ // This is a fire-and-forget notification, NOT the approval mechanism.
197
+ safeDispatchCustomEvent(GraphEvents.ON_TOOL_APPROVAL_REQUIRED, approvalRequest, config);
198
+ // interrupt() throws GraphInterrupt on first call (checkpoints state),
199
+ // returns the resume value on re-execution after Command({ resume }).
200
+ const response = interrupt(approvalRequest);
201
+ return response;
200
202
  }
201
203
  /**
202
204
  * Runs a single tool call with error handling
@@ -286,11 +288,11 @@ class ToolNode extends RunnableCallable {
286
288
  }
287
289
  // ========================================================================
288
290
  // HITL: Check if this tool requires human approval before execution.
289
- // Uses event-driven pattern: dispatches ON_TOOL_APPROVAL_REQUIRED event
290
- // and waits for the host to resolve/reject with a ToolApprovalResponse.
291
+ // Uses LangGraph interrupt() checkpoints state and pauses the graph.
292
+ // Resumes when host sends Command({ resume: ToolApprovalResponse }).
291
293
  // ========================================================================
292
294
  if (this.requiresApproval(call.name, call.args)) {
293
- const approvalResponse = await this.requestApproval(call, config);
295
+ const approvalResponse = this.requestApproval(call, config);
294
296
  if (!approvalResponse.approved) {
295
297
  // Human denied the tool call - return a denial message
296
298
  return new ToolMessage({
@@ -648,13 +650,14 @@ class ToolNode extends RunnableCallable {
648
650
  async dispatchToolEvents(toolCalls, config) {
649
651
  // ========================================================================
650
652
  // HITL: Check approval for event-dispatched tools (browser, MCP, etc.)
651
- // before dispatching. Denied tools return denial messages immediately.
653
+ // before dispatching. Uses LangGraph interrupt() for each tool needing
654
+ // approval — counter-based matching handles sequential interrupts.
652
655
  // ========================================================================
653
656
  const approvedCalls = [];
654
657
  const denialMessages = [];
655
658
  for (const call of toolCalls) {
656
659
  if (this.requiresApproval(call.name, call.args)) {
657
- const approvalResponse = await this.requestApproval(call, config);
660
+ const approvalResponse = this.requestApproval(call, config);
658
661
  if (!approvalResponse.approved) {
659
662
  denialMessages.push(new ToolMessage({
660
663
  status: 'error',