@illuma-ai/agents 1.1.16 → 1.1.18

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.
@@ -29,6 +29,8 @@ export enum GraphEvents {
29
29
  ON_CONTEXT_PRESSURE = 'on_context_pressure',
30
30
  /** [Custom] Tool requires human approval before execution - dispatched by ToolNode HITL */
31
31
  ON_TOOL_APPROVAL_REQUIRED = 'on_tool_approval_required',
32
+ /** [Custom] Agent transition event — dispatched when control passes between agents */
33
+ ON_AGENT_TRANSITION = 'on_agent_transition',
32
34
 
33
35
  /* Official Events */
34
36
 
@@ -25,8 +25,10 @@ import { StandardGraph } from './Graph';
25
25
  import {
26
26
  Constants,
27
27
  EdgeType,
28
+ GraphEvents,
28
29
  DEFAULT_HANDOFF_MAX_RESULT_CHARS,
29
30
  } from '@/common';
31
+ import { safeDispatchCustomEvent } from '@/utils/events';
30
32
 
31
33
  /** Pattern to extract instructions from transfer ToolMessage content */
32
34
  const TRANSFER_INSTRUCTIONS_PATTERN = /(?:Instructions?|Context):\s*(.+)/is;
@@ -694,6 +696,19 @@ export class MultiAgentGraph extends StandardGraph {
694
696
  `${truncatedResult.length < resultText.length ? `, truncated to ${truncatedResult.length}` : ''})`
695
697
  );
696
698
 
699
+ await safeDispatchCustomEvent(
700
+ GraphEvents.ON_AGENT_TRANSITION,
701
+ {
702
+ sourceAgentId: sourceAgentId,
703
+ sourceAgentName: this.agentContexts.get(sourceAgentId)?.name ?? sourceAgentId,
704
+ destinationAgentId: destination,
705
+ destinationAgentName: destContext?.name ?? destination,
706
+ edgeType: EdgeType.HANDOFF,
707
+ timestamp: Date.now(),
708
+ },
709
+ config
710
+ );
711
+
697
712
  return truncatedResult;
698
713
  } catch (err) {
699
714
  const errorMessage =
@@ -1508,6 +1523,19 @@ export class MultiAgentGraph extends StandardGraph {
1508
1523
  }
1509
1524
  }
1510
1525
 
1526
+ await safeDispatchCustomEvent(
1527
+ GraphEvents.ON_AGENT_TRANSITION,
1528
+ {
1529
+ sourceAgentId: agentId,
1530
+ sourceAgentName: this.agentContexts.get(agentId)?.name ?? agentId,
1531
+ destinationAgentId: transferDest,
1532
+ destinationAgentName: this.agentContexts.get(transferDest)?.name ?? transferDest,
1533
+ edgeType: EdgeType.TRANSFER,
1534
+ timestamp: Date.now(),
1535
+ },
1536
+ config
1537
+ );
1538
+
1511
1539
  return new Command({
1512
1540
  update: result,
1513
1541
  goto: transferDest,
@@ -1518,6 +1546,20 @@ export class MultiAgentGraph extends StandardGraph {
1518
1546
  `[MultiAgentGraph] Command routing: "${agentId}" -> no transfer, following sequence edges: [${Array.from(sequenceDestinations).join(', ')}]`
1519
1547
  );
1520
1548
  const directDests = Array.from(sequenceDestinations);
1549
+ for (const dest of directDests) {
1550
+ await safeDispatchCustomEvent(
1551
+ GraphEvents.ON_AGENT_TRANSITION,
1552
+ {
1553
+ sourceAgentId: agentId,
1554
+ sourceAgentName: this.agentContexts.get(agentId)?.name ?? agentId,
1555
+ destinationAgentId: dest,
1556
+ destinationAgentName: this.agentContexts.get(dest)?.name ?? dest,
1557
+ edgeType: EdgeType.SEQUENCE,
1558
+ timestamp: Date.now(),
1559
+ },
1560
+ config
1561
+ );
1562
+ }
1521
1563
  if (directDests.length === 1) {
1522
1564
  return new Command({
1523
1565
  update: result,
@@ -177,11 +177,30 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
177
177
  return false;
178
178
  }
179
179
 
180
- const { defaultPolicy, overrides, executionContext } =
181
- this.toolApprovalConfig;
180
+ const {
181
+ defaultPolicy,
182
+ overrides,
183
+ executionContext,
184
+ agentExecutionContextOverrides,
185
+ } = this.toolApprovalConfig;
186
+
187
+ // Resolve the effective execution context for this agent.
188
+ // Per-agent overrides take precedence — allows handoff agents to bypass HITL
189
+ // while the primary agent retains interactive approval.
190
+ const effectiveContext = (
191
+ this.agentId && agentExecutionContextOverrides?.[this.agentId]
192
+ ) ?? executionContext;
193
+
194
+ // Scheduled executions bypass all approval checks — no user is present
195
+ if (effectiveContext === ExecutionContext.SCHEDULED) {
196
+ return false;
197
+ }
182
198
 
183
- // Scheduled executions bypass all approval checks
184
- if (executionContext === ExecutionContext.SCHEDULED) {
199
+ // Handoff/delegate/workflow agents bypass HITL approval — these agents run
200
+ // autonomously as part of a multi-agent pipeline. Blocking on user approval
201
+ // would stall the entire graph since all agents share the same SSE stream.
202
+ // The primary (user-facing) agent retains HITL; only delegated agents skip it.
203
+ if (effectiveContext === ExecutionContext.HANDOFF) {
185
204
  return false;
186
205
  }
187
206
 
@@ -16,10 +16,14 @@
16
16
  *
17
17
  * - `INTERACTIVE`: User is present in the chat session — approval prompts are shown.
18
18
  * - `SCHEDULED`: Automated/scheduled execution — all approvals are auto-granted.
19
+ * - `HANDOFF`: Agent is running as a handoff/delegate/workflow target — approvals
20
+ * are auto-granted so the autonomous multi-agent pipeline isn't blocked.
21
+ * The primary agent retains HITL; only handoff agents bypass it.
19
22
  */
20
23
  export enum ExecutionContext {
21
24
  INTERACTIVE = 'interactive',
22
25
  SCHEDULED = 'scheduled',
26
+ HANDOFF = 'handoff',
23
27
  }
24
28
 
25
29
  // ============================================================================
@@ -48,6 +48,16 @@ export interface ChatModel {
48
48
  ) => Promise<AIMessageChunk>;
49
49
  }
50
50
 
51
+ /** Payload for ON_AGENT_TRANSITION events */
52
+ export type AgentTransitionEvent = {
53
+ sourceAgentId?: string;
54
+ sourceAgentName?: string;
55
+ destinationAgentId: string;
56
+ destinationAgentName: string;
57
+ edgeType: string; // 'handoff' | 'transfer' | 'sequence'
58
+ timestamp: number;
59
+ };
60
+
51
61
  export type GraphNode = GraphNodeKeys | typeof START;
52
62
  export type ClientCallback<T extends unknown[]> = (
53
63
  graph: StandardGraph,
@@ -258,9 +258,29 @@ export type ToolApprovalConfig = {
258
258
  overrides?: ToolApprovalOverrides;
259
259
  /**
260
260
  * Execution context. When 'scheduled', all approvals are auto-granted.
261
+ * When 'handoff', approvals are auto-granted for autonomous multi-agent pipelines.
261
262
  * @default 'interactive'
262
263
  */
263
264
  executionContext?: `${ExecutionContext}`;
265
+ /**
266
+ * Per-agent execution context overrides. Keys are agent IDs.
267
+ * Used to give handoff/delegate agents a different execution context than the
268
+ * primary agent. For example, the primary agent stays 'interactive' (HITL enabled)
269
+ * while all handoff agents are set to 'handoff' (HITL bypassed).
270
+ *
271
+ * When a ToolNode's agentId is found in this map, the override takes precedence
272
+ * over the top-level `executionContext`.
273
+ *
274
+ * @example
275
+ * {
276
+ * executionContext: 'interactive', // primary agent: HITL on
277
+ * agentExecutionContextOverrides: {
278
+ * 'research-agent': 'handoff', // handoff agent: HITL off
279
+ * 'data-agent': 'handoff', // handoff agent: HITL off
280
+ * },
281
+ * }
282
+ */
283
+ agentExecutionContextOverrides?: Record<string, `${ExecutionContext}`>;
264
284
  };
265
285
 
266
286
  /**