@codemation/core-nodes 1.0.0 → 1.0.2
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/CHANGELOG.md +45 -0
- package/dist/index.cjs +184 -31
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +170 -68
- package/dist/index.d.ts +170 -68
- package/dist/index.js +185 -32
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/nodes/AIAgentNode.ts +101 -3
- package/src/nodes/AgentToolExecutionCoordinator.ts +29 -3
- package/src/nodes/NodeBackedToolRuntime.ts +40 -4
- package/src/nodes/WebhookTriggerFactory.ts +1 -1
- package/src/nodes/aggregate.ts +1 -1
- package/src/nodes/aiAgentSupport.types.ts +18 -3
- package/src/nodes/httpRequest.ts +1 -0
- package/src/nodes/if.ts +1 -1
- package/src/nodes/mapData.ts +1 -0
- package/src/nodes/merge.ts +1 -1
- package/src/nodes/noOp.ts +1 -0
- package/src/nodes/split.ts +1 -1
- package/src/nodes/wait.ts +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codemation/core-nodes",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"@ai-sdk/provider": "^3.0.8",
|
|
33
33
|
"ai": "^6.0.168",
|
|
34
34
|
"lucide-react": "^0.577.0",
|
|
35
|
-
"@codemation/core": "1.0.
|
|
35
|
+
"@codemation/core": "1.0.1"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@types/node": "^25.3.5",
|
package/src/nodes/AIAgentNode.ts
CHANGED
|
@@ -356,7 +356,7 @@ export class AIAgentNode implements RunnableNode<AIAgent<any, any>> {
|
|
|
356
356
|
return {
|
|
357
357
|
config: entry.config,
|
|
358
358
|
inputSchema: entry.runtime.inputSchema,
|
|
359
|
-
execute: async (input
|
|
359
|
+
execute: async (input, hooks): Promise<unknown> => {
|
|
360
360
|
const validated = entry.runtime.inputSchema.parse(input) as unknown;
|
|
361
361
|
return await entry.runtime.execute({
|
|
362
362
|
config: entry.config,
|
|
@@ -365,6 +365,7 @@ export class AIAgentNode implements RunnableNode<AIAgent<any, any>> {
|
|
|
365
365
|
item,
|
|
366
366
|
itemIndex,
|
|
367
367
|
items,
|
|
368
|
+
hooks,
|
|
368
369
|
});
|
|
369
370
|
},
|
|
370
371
|
} satisfies ItemScopedToolBinding;
|
|
@@ -421,11 +422,36 @@ export class AIAgentNode implements RunnableNode<AIAgent<any, any>> {
|
|
|
421
422
|
activationId: ctx.activationId,
|
|
422
423
|
inputsByPort: itemInputsByPort,
|
|
423
424
|
});
|
|
425
|
+
await ctx.nodeState?.appendConnectionInvocation({
|
|
426
|
+
invocationId,
|
|
427
|
+
connectionNodeId: languageModelConnectionNodeId,
|
|
428
|
+
parentAgentNodeId: ctx.nodeId,
|
|
429
|
+
parentAgentActivationId: ctx.activationId,
|
|
430
|
+
status: "queued",
|
|
431
|
+
managedInput: summarizedInput,
|
|
432
|
+
queuedAt: startedAt.toISOString(),
|
|
433
|
+
iterationId: ctx.iterationId,
|
|
434
|
+
itemIndex: ctx.itemIndex,
|
|
435
|
+
parentInvocationId: ctx.parentInvocationId,
|
|
436
|
+
});
|
|
424
437
|
await ctx.nodeState?.markRunning({
|
|
425
438
|
nodeId: languageModelConnectionNodeId,
|
|
426
439
|
activationId: ctx.activationId,
|
|
427
440
|
inputsByPort: itemInputsByPort,
|
|
428
441
|
});
|
|
442
|
+
await ctx.nodeState?.appendConnectionInvocation({
|
|
443
|
+
invocationId,
|
|
444
|
+
connectionNodeId: languageModelConnectionNodeId,
|
|
445
|
+
parentAgentNodeId: ctx.nodeId,
|
|
446
|
+
parentAgentActivationId: ctx.activationId,
|
|
447
|
+
status: "running",
|
|
448
|
+
managedInput: summarizedInput,
|
|
449
|
+
queuedAt: startedAt.toISOString(),
|
|
450
|
+
startedAt: startedAt.toISOString(),
|
|
451
|
+
iterationId: ctx.iterationId,
|
|
452
|
+
itemIndex: ctx.itemIndex,
|
|
453
|
+
parentInvocationId: ctx.parentInvocationId,
|
|
454
|
+
});
|
|
429
455
|
try {
|
|
430
456
|
const tools = this.buildToolSet(itemScopedTools);
|
|
431
457
|
const callOptions = this.resolveCallOptions(model, guardrails.modelInvocationOptions);
|
|
@@ -441,11 +467,12 @@ export class AIAgentNode implements RunnableNode<AIAgent<any, any>> {
|
|
|
441
467
|
});
|
|
442
468
|
const turnResult = this.extractTurnResult(result as AnyGenerateTextResult);
|
|
443
469
|
const finishedAt = new Date();
|
|
470
|
+
const managedOutput = this.summarizeTurnOutput(turnResult);
|
|
444
471
|
await ctx.nodeState?.markCompleted({
|
|
445
472
|
nodeId: languageModelConnectionNodeId,
|
|
446
473
|
activationId: ctx.activationId,
|
|
447
474
|
inputsByPort: itemInputsByPort,
|
|
448
|
-
outputs: AgentOutputFactory.fromUnknown(
|
|
475
|
+
outputs: AgentOutputFactory.fromUnknown(managedOutput),
|
|
449
476
|
});
|
|
450
477
|
await span.attachArtifact({ kind: "ai.messages", contentType: "application/json", previewJson: summarizedInput });
|
|
451
478
|
await span.attachArtifact({ kind: "ai.response", contentType: "application/json", previewJson: turnResult.text });
|
|
@@ -458,10 +485,13 @@ export class AIAgentNode implements RunnableNode<AIAgent<any, any>> {
|
|
|
458
485
|
parentAgentActivationId: ctx.activationId,
|
|
459
486
|
status: "completed",
|
|
460
487
|
managedInput: summarizedInput,
|
|
461
|
-
managedOutput
|
|
488
|
+
managedOutput,
|
|
462
489
|
queuedAt: startedAt.toISOString(),
|
|
463
490
|
startedAt: startedAt.toISOString(),
|
|
464
491
|
finishedAt: finishedAt.toISOString(),
|
|
492
|
+
iterationId: ctx.iterationId,
|
|
493
|
+
itemIndex: ctx.itemIndex,
|
|
494
|
+
parentInvocationId: ctx.parentInvocationId,
|
|
465
495
|
});
|
|
466
496
|
return turnResult;
|
|
467
497
|
} catch (error) {
|
|
@@ -504,11 +534,36 @@ export class AIAgentNode implements RunnableNode<AIAgent<any, any>> {
|
|
|
504
534
|
activationId: ctx.activationId,
|
|
505
535
|
inputsByPort: itemInputsByPort,
|
|
506
536
|
});
|
|
537
|
+
await ctx.nodeState?.appendConnectionInvocation({
|
|
538
|
+
invocationId,
|
|
539
|
+
connectionNodeId: languageModelConnectionNodeId,
|
|
540
|
+
parentAgentNodeId: ctx.nodeId,
|
|
541
|
+
parentAgentActivationId: ctx.activationId,
|
|
542
|
+
status: "queued",
|
|
543
|
+
managedInput: summarizedInput,
|
|
544
|
+
queuedAt: startedAt.toISOString(),
|
|
545
|
+
iterationId: ctx.iterationId,
|
|
546
|
+
itemIndex: ctx.itemIndex,
|
|
547
|
+
parentInvocationId: ctx.parentInvocationId,
|
|
548
|
+
});
|
|
507
549
|
await ctx.nodeState?.markRunning({
|
|
508
550
|
nodeId: languageModelConnectionNodeId,
|
|
509
551
|
activationId: ctx.activationId,
|
|
510
552
|
inputsByPort: itemInputsByPort,
|
|
511
553
|
});
|
|
554
|
+
await ctx.nodeState?.appendConnectionInvocation({
|
|
555
|
+
invocationId,
|
|
556
|
+
connectionNodeId: languageModelConnectionNodeId,
|
|
557
|
+
parentAgentNodeId: ctx.nodeId,
|
|
558
|
+
parentAgentActivationId: ctx.activationId,
|
|
559
|
+
status: "running",
|
|
560
|
+
managedInput: summarizedInput,
|
|
561
|
+
queuedAt: startedAt.toISOString(),
|
|
562
|
+
startedAt: startedAt.toISOString(),
|
|
563
|
+
iterationId: ctx.iterationId,
|
|
564
|
+
itemIndex: ctx.itemIndex,
|
|
565
|
+
parentInvocationId: ctx.parentInvocationId,
|
|
566
|
+
});
|
|
512
567
|
try {
|
|
513
568
|
const callOptions = this.resolveCallOptions(model, guardrails.modelInvocationOptions);
|
|
514
569
|
const outputSchema =
|
|
@@ -551,6 +606,9 @@ export class AIAgentNode implements RunnableNode<AIAgent<any, any>> {
|
|
|
551
606
|
queuedAt: startedAt.toISOString(),
|
|
552
607
|
startedAt: startedAt.toISOString(),
|
|
553
608
|
finishedAt: finishedAt.toISOString(),
|
|
609
|
+
iterationId: ctx.iterationId,
|
|
610
|
+
itemIndex: ctx.itemIndex,
|
|
611
|
+
parentInvocationId: ctx.parentInvocationId,
|
|
554
612
|
});
|
|
555
613
|
return result.experimental_output;
|
|
556
614
|
} catch (error) {
|
|
@@ -589,6 +647,22 @@ export class AIAgentNode implements RunnableNode<AIAgent<any, any>> {
|
|
|
589
647
|
};
|
|
590
648
|
}
|
|
591
649
|
|
|
650
|
+
/**
|
|
651
|
+
* Build a no-code-friendly output payload for an LLM round.
|
|
652
|
+
*
|
|
653
|
+
* Always includes `content` (matching the canvas snapshot shape used elsewhere) and adds a
|
|
654
|
+
* `toolCalls` array when the round produced tool calls so the execution inspector surfaces the
|
|
655
|
+
* planned calls instead of just an empty `""` for tool-only rounds.
|
|
656
|
+
*/
|
|
657
|
+
private summarizeTurnOutput(turnResult: TurnResult): JsonValue {
|
|
658
|
+
if (turnResult.toolCalls.length === 0) return { content: turnResult.text };
|
|
659
|
+
const toolCalls = turnResult.toolCalls.map((toolCall) => ({
|
|
660
|
+
name: toolCall.name,
|
|
661
|
+
args: this.resultToJsonValue(toolCall.input) ?? null,
|
|
662
|
+
}));
|
|
663
|
+
return { content: turnResult.text, toolCalls };
|
|
664
|
+
}
|
|
665
|
+
|
|
592
666
|
private extractTurnResult(result: AnyGenerateTextResult): TurnResult {
|
|
593
667
|
const usage = this.extractUsageFromResult(result);
|
|
594
668
|
const text = result.text;
|
|
@@ -642,6 +716,13 @@ export class AIAgentNode implements RunnableNode<AIAgent<any, any>> {
|
|
|
642
716
|
[CodemationTelemetryAttributeNames.connectionInvocationId]: invocationId,
|
|
643
717
|
[GenAiTelemetryAttributeNames.operationName]: "chat",
|
|
644
718
|
[GenAiTelemetryAttributeNames.requestModel]: this.resolveChatModelName(ctx.config.chatModel),
|
|
719
|
+
...(ctx.iterationId ? { [CodemationTelemetryAttributeNames.iterationId]: ctx.iterationId } : {}),
|
|
720
|
+
...(typeof ctx.itemIndex === "number"
|
|
721
|
+
? { [CodemationTelemetryAttributeNames.iterationIndex]: ctx.itemIndex }
|
|
722
|
+
: {}),
|
|
723
|
+
...(ctx.parentInvocationId
|
|
724
|
+
? { [CodemationTelemetryAttributeNames.parentInvocationId]: ctx.parentInvocationId }
|
|
725
|
+
: {}),
|
|
645
726
|
},
|
|
646
727
|
});
|
|
647
728
|
}
|
|
@@ -707,12 +788,25 @@ export class AIAgentNode implements RunnableNode<AIAgent<any, any>> {
|
|
|
707
788
|
plannedToolCalls: ReadonlyArray<PlannedToolCall>,
|
|
708
789
|
ctx: NodeExecutionContext<AIAgent<any, any>>,
|
|
709
790
|
): Promise<void> {
|
|
791
|
+
const queuedAt = new Date().toISOString();
|
|
710
792
|
for (const plannedToolCall of plannedToolCalls) {
|
|
711
793
|
await ctx.nodeState?.markQueued({
|
|
712
794
|
nodeId: plannedToolCall.nodeId,
|
|
713
795
|
activationId: ctx.activationId,
|
|
714
796
|
inputsByPort: AgentToolCallPortMap.fromInput(plannedToolCall.toolCall.input ?? {}),
|
|
715
797
|
});
|
|
798
|
+
await ctx.nodeState?.appendConnectionInvocation({
|
|
799
|
+
invocationId: plannedToolCall.invocationId,
|
|
800
|
+
connectionNodeId: plannedToolCall.nodeId,
|
|
801
|
+
parentAgentNodeId: ctx.nodeId,
|
|
802
|
+
parentAgentActivationId: ctx.activationId,
|
|
803
|
+
status: "queued",
|
|
804
|
+
managedInput: this.resultToJsonValue(plannedToolCall.toolCall.input),
|
|
805
|
+
queuedAt,
|
|
806
|
+
iterationId: ctx.iterationId,
|
|
807
|
+
itemIndex: ctx.itemIndex,
|
|
808
|
+
parentInvocationId: ctx.parentInvocationId,
|
|
809
|
+
});
|
|
716
810
|
}
|
|
717
811
|
}
|
|
718
812
|
|
|
@@ -732,6 +826,7 @@ export class AIAgentNode implements RunnableNode<AIAgent<any, any>> {
|
|
|
732
826
|
toolCall,
|
|
733
827
|
invocationIndex,
|
|
734
828
|
nodeId: ConnectionNodeIdFactory.toolConnectionNodeId(parentNodeId, binding.config.name),
|
|
829
|
+
invocationId: ConnectionInvocationIdFactory.create(),
|
|
735
830
|
} satisfies PlannedToolCall;
|
|
736
831
|
});
|
|
737
832
|
}
|
|
@@ -771,6 +866,9 @@ export class AIAgentNode implements RunnableNode<AIAgent<any, any>> {
|
|
|
771
866
|
queuedAt: args.startedAt.toISOString(),
|
|
772
867
|
startedAt: args.startedAt.toISOString(),
|
|
773
868
|
finishedAt: finishedAt.toISOString(),
|
|
869
|
+
iterationId: args.ctx.iterationId,
|
|
870
|
+
itemIndex: args.ctx.itemIndex,
|
|
871
|
+
parentInvocationId: args.ctx.parentInvocationId,
|
|
774
872
|
});
|
|
775
873
|
return effectiveError;
|
|
776
874
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { JsonValue, NodeExecutionContext } from "@codemation/core";
|
|
2
|
-
import { CodemationTelemetryAttributeNames,
|
|
2
|
+
import { CodemationTelemetryAttributeNames, inject, injectable } from "@codemation/core";
|
|
3
3
|
|
|
4
4
|
import type { AIAgent } from "./AIAgentConfig";
|
|
5
5
|
import { AgentOutputFactory } from "./AgentOutputFactory";
|
|
@@ -53,7 +53,7 @@ export class AgentToolExecutionCoordinator {
|
|
|
53
53
|
): Promise<ExecutedToolCall> {
|
|
54
54
|
const { plannedToolCall, ctx } = args;
|
|
55
55
|
const toolCallInputsByPort = AgentToolCallPortMap.fromInput(plannedToolCall.toolCall.input ?? {});
|
|
56
|
-
const invocationId =
|
|
56
|
+
const invocationId = plannedToolCall.invocationId;
|
|
57
57
|
const startedAt = new Date();
|
|
58
58
|
const span = ctx.telemetry.startChildSpan({
|
|
59
59
|
name: "agent.tool.call",
|
|
@@ -62,6 +62,13 @@ export class AgentToolExecutionCoordinator {
|
|
|
62
62
|
attributes: {
|
|
63
63
|
[CodemationTelemetryAttributeNames.connectionInvocationId]: invocationId,
|
|
64
64
|
[CodemationTelemetryAttributeNames.toolName]: plannedToolCall.binding.config.name,
|
|
65
|
+
...(ctx.iterationId ? { [CodemationTelemetryAttributeNames.iterationId]: ctx.iterationId } : {}),
|
|
66
|
+
...(typeof ctx.itemIndex === "number"
|
|
67
|
+
? { [CodemationTelemetryAttributeNames.iterationIndex]: ctx.itemIndex }
|
|
68
|
+
: {}),
|
|
69
|
+
...(ctx.parentInvocationId
|
|
70
|
+
? { [CodemationTelemetryAttributeNames.parentInvocationId]: ctx.parentInvocationId }
|
|
71
|
+
: {}),
|
|
65
72
|
},
|
|
66
73
|
});
|
|
67
74
|
await ctx.nodeState?.markRunning({
|
|
@@ -69,9 +76,24 @@ export class AgentToolExecutionCoordinator {
|
|
|
69
76
|
activationId: ctx.activationId,
|
|
70
77
|
inputsByPort: toolCallInputsByPort,
|
|
71
78
|
});
|
|
79
|
+
await ctx.nodeState?.appendConnectionInvocation({
|
|
80
|
+
invocationId,
|
|
81
|
+
connectionNodeId: plannedToolCall.nodeId,
|
|
82
|
+
parentAgentNodeId: ctx.nodeId,
|
|
83
|
+
parentAgentActivationId: ctx.activationId,
|
|
84
|
+
status: "running",
|
|
85
|
+
managedInput: this.toJsonValue(plannedToolCall.toolCall.input),
|
|
86
|
+
queuedAt: startedAt.toISOString(),
|
|
87
|
+
startedAt: startedAt.toISOString(),
|
|
88
|
+
iterationId: ctx.iterationId,
|
|
89
|
+
parentInvocationId: ctx.parentInvocationId,
|
|
90
|
+
});
|
|
72
91
|
|
|
73
92
|
try {
|
|
74
|
-
const result = await plannedToolCall.binding.execute(plannedToolCall.toolCall.input ?? {}
|
|
93
|
+
const result = await plannedToolCall.binding.execute(plannedToolCall.toolCall.input ?? {}, {
|
|
94
|
+
parentSpan: span,
|
|
95
|
+
parentInvocationId: invocationId,
|
|
96
|
+
});
|
|
75
97
|
const serialized = typeof result === "string" ? result : JSON.stringify(result);
|
|
76
98
|
const finishedAt = new Date();
|
|
77
99
|
await ctx.nodeState?.markCompleted({
|
|
@@ -102,6 +124,8 @@ export class AgentToolExecutionCoordinator {
|
|
|
102
124
|
queuedAt: startedAt.toISOString(),
|
|
103
125
|
startedAt: startedAt.toISOString(),
|
|
104
126
|
finishedAt: finishedAt.toISOString(),
|
|
127
|
+
iterationId: ctx.iterationId,
|
|
128
|
+
parentInvocationId: ctx.parentInvocationId,
|
|
105
129
|
});
|
|
106
130
|
return {
|
|
107
131
|
toolName: plannedToolCall.binding.config.name,
|
|
@@ -262,6 +286,8 @@ export class AgentToolExecutionCoordinator {
|
|
|
262
286
|
queuedAt: args.startedAt.toISOString(),
|
|
263
287
|
startedAt: args.startedAt.toISOString(),
|
|
264
288
|
finishedAt: finishedAt.toISOString(),
|
|
289
|
+
iterationId: args.ctx.iterationId,
|
|
290
|
+
parentInvocationId: args.ctx.parentInvocationId,
|
|
265
291
|
});
|
|
266
292
|
}
|
|
267
293
|
|
|
@@ -11,6 +11,8 @@ import type {
|
|
|
11
11
|
ZodSchemaAny,
|
|
12
12
|
} from "@codemation/core";
|
|
13
13
|
import {
|
|
14
|
+
AgentConfigInspector,
|
|
15
|
+
ChildExecutionScopeFactory,
|
|
14
16
|
CoreTokens,
|
|
15
17
|
inject,
|
|
16
18
|
injectable,
|
|
@@ -31,6 +33,8 @@ export class NodeBackedToolRuntime {
|
|
|
31
33
|
private readonly outputNormalizer: NodeOutputNormalizer,
|
|
32
34
|
@inject(RunnableOutputBehaviorResolver)
|
|
33
35
|
private readonly outputBehaviorResolver: RunnableOutputBehaviorResolver,
|
|
36
|
+
@inject(ChildExecutionScopeFactory)
|
|
37
|
+
private readonly childExecutionScopeFactory: ChildExecutionScopeFactory,
|
|
34
38
|
) {}
|
|
35
39
|
|
|
36
40
|
async execute(
|
|
@@ -45,10 +49,7 @@ export class NodeBackedToolRuntime {
|
|
|
45
49
|
ctx: args.ctx,
|
|
46
50
|
node: config.node,
|
|
47
51
|
});
|
|
48
|
-
const nodeCtx =
|
|
49
|
-
...args.ctx,
|
|
50
|
-
config: config.node,
|
|
51
|
-
} as NodeExecutionContext<RunnableNodeConfig>;
|
|
52
|
+
const nodeCtx = this.resolveNodeCtx(config, args);
|
|
52
53
|
const resolvedNode = this.nodeResolver.resolve(config.node.type);
|
|
53
54
|
const outputs = await this.executeResolvedNode(resolvedNode, nodeInput, nodeCtx);
|
|
54
55
|
return config.toToolOutput({
|
|
@@ -62,6 +63,41 @@ export class NodeBackedToolRuntime {
|
|
|
62
63
|
});
|
|
63
64
|
}
|
|
64
65
|
|
|
66
|
+
/**
|
|
67
|
+
* Returns a re-rooted child ctx for nested-agent tools (so their LLM/tool connection ids derive
|
|
68
|
+
* from the tool connection node, telemetry parents under the tool-call span, and connection
|
|
69
|
+
* invocations carry `parentInvocationId`). Plain runnable tools (non-agent) keep the orchestrator
|
|
70
|
+
* ctx with only `config` swapped — no nesting concern.
|
|
71
|
+
*
|
|
72
|
+
* The caller (`AIAgentNode.createItemScopedTools`) already wraps the orchestrator ctx via
|
|
73
|
+
* `ConnectionCredentialExecutionContextFactory.forConnectionNode`, so `args.ctx.nodeId` is the
|
|
74
|
+
* tool's own connection node id (e.g. `AIAgentNode:2__conn__tool__searchInMail`). We pass that
|
|
75
|
+
* through as the sub-agent's `nodeId`; deriving another `toolConnectionNodeId(args.ctx.nodeId,
|
|
76
|
+
* config.name)` here would prepend a duplicate `__conn__tool__<name>` segment and exponentially
|
|
77
|
+
* deepen ids on each invocation, which also breaks credential resolution because user-provided
|
|
78
|
+
* bindings sit on the single-level connection node id.
|
|
79
|
+
*/
|
|
80
|
+
private resolveNodeCtx(
|
|
81
|
+
config: NodeBackedToolConfig<any, ZodSchemaAny, ZodSchemaAny>,
|
|
82
|
+
args: ToolExecuteArgs,
|
|
83
|
+
): NodeExecutionContext<RunnableNodeConfig> {
|
|
84
|
+
const isNestedAgent = AgentConfigInspector.isAgentNodeConfig(config.node);
|
|
85
|
+
const hooks = args.hooks;
|
|
86
|
+
if (!isNestedAgent || !hooks?.parentSpan || !hooks.parentInvocationId) {
|
|
87
|
+
return {
|
|
88
|
+
...args.ctx,
|
|
89
|
+
config: config.node,
|
|
90
|
+
} as NodeExecutionContext<RunnableNodeConfig>;
|
|
91
|
+
}
|
|
92
|
+
return this.childExecutionScopeFactory.forSubAgent({
|
|
93
|
+
parentCtx: args.ctx as NodeExecutionContext<RunnableNodeConfig>,
|
|
94
|
+
childNodeId: args.ctx.nodeId,
|
|
95
|
+
childConfig: config.node as unknown as RunnableNodeConfig,
|
|
96
|
+
parentInvocationId: hooks.parentInvocationId,
|
|
97
|
+
parentSpan: hooks.parentSpan,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
65
101
|
private async executeResolvedNode(
|
|
66
102
|
resolvedNode: unknown,
|
|
67
103
|
nodeInput: ToolExecuteArgs["item"],
|
|
@@ -13,7 +13,7 @@ export class WebhookTrigger<
|
|
|
13
13
|
> implements TriggerNodeConfig<unknown> {
|
|
14
14
|
readonly kind = "trigger" as const;
|
|
15
15
|
readonly type: TypeToken<unknown> = WebhookTriggerNode;
|
|
16
|
-
readonly icon = "lucide:
|
|
16
|
+
readonly icon = "lucide:webhook";
|
|
17
17
|
|
|
18
18
|
constructor(
|
|
19
19
|
public readonly name: string,
|
package/src/nodes/aggregate.ts
CHANGED
|
@@ -7,7 +7,7 @@ export class Aggregate<TIn = unknown, TOut = unknown> implements RunnableNodeCon
|
|
|
7
7
|
readonly type: TypeToken<unknown> = AggregateNode;
|
|
8
8
|
readonly execution = { hint: "local" } as const;
|
|
9
9
|
readonly keepBinaries = true as const;
|
|
10
|
-
readonly icon = "
|
|
10
|
+
readonly icon = "builtin:aggregate-rows" as const;
|
|
11
11
|
|
|
12
12
|
constructor(
|
|
13
13
|
public readonly name: string,
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
AgentToolCall,
|
|
3
|
+
ConnectionInvocationId,
|
|
3
4
|
Item,
|
|
4
5
|
NodeInputsByPort,
|
|
6
|
+
TelemetrySpanScope,
|
|
5
7
|
ToolConfig,
|
|
6
8
|
ToolExecuteArgs,
|
|
7
9
|
ZodSchemaAny,
|
|
@@ -24,13 +26,24 @@ export type ResolvedTool = Readonly<{
|
|
|
24
26
|
|
|
25
27
|
/**
|
|
26
28
|
* Per-item binding of a tool: the user config plus the resolved runtime and a snapshot of the
|
|
27
|
-
* original Zod `inputSchema
|
|
28
|
-
*
|
|
29
|
+
* original Zod `inputSchema`.
|
|
30
|
+
*
|
|
31
|
+
* `execute` accepts optional `hooks` so the agent coordinator can pass the live `agent.tool.call`
|
|
32
|
+
* span and the planned tool-call's `invocationId`. Node-backed sub-agent tools use these hooks
|
|
33
|
+
* via {@link ChildExecutionScopeFactory} to re-root their runtime ctx under the tool-call boundary
|
|
34
|
+
* (fresh activationId, telemetry parented at the tool-call span, `parentInvocationId` set).
|
|
29
35
|
*/
|
|
30
36
|
export type ItemScopedToolBinding = Readonly<{
|
|
31
37
|
config: ToolConfig;
|
|
32
38
|
inputSchema: ZodSchemaAny;
|
|
33
|
-
execute(input: unknown): Promise<unknown>;
|
|
39
|
+
execute(input: unknown, hooks?: ItemScopedToolCallHooks): Promise<unknown>;
|
|
40
|
+
}>;
|
|
41
|
+
|
|
42
|
+
export type ItemScopedToolCallHooks = Readonly<{
|
|
43
|
+
/** Live agent.tool.call span (used to parent sub-agent telemetry). */
|
|
44
|
+
parentSpan?: TelemetrySpanScope;
|
|
45
|
+
/** invocationId of the parent tool call (used to thread `parentInvocationId` through ctx). */
|
|
46
|
+
parentInvocationId?: ConnectionInvocationId;
|
|
34
47
|
}>;
|
|
35
48
|
|
|
36
49
|
export type PlannedToolCall = Readonly<{
|
|
@@ -38,6 +51,8 @@ export type PlannedToolCall = Readonly<{
|
|
|
38
51
|
toolCall: AgentToolCall;
|
|
39
52
|
invocationIndex: number;
|
|
40
53
|
nodeId: string;
|
|
54
|
+
/** Stable id reused across queued / running / completed connection invocation rows for this tool call. */
|
|
55
|
+
invocationId: string;
|
|
41
56
|
}>;
|
|
42
57
|
|
|
43
58
|
export type ExecutedToolCall = Readonly<{
|
package/src/nodes/httpRequest.ts
CHANGED
|
@@ -23,6 +23,7 @@ export class HttpRequest<
|
|
|
23
23
|
readonly kind = "node" as const;
|
|
24
24
|
readonly type: TypeToken<unknown> = HttpRequestNode;
|
|
25
25
|
readonly execution = { hint: "local" } as const;
|
|
26
|
+
readonly icon = "lucide:globe" as const;
|
|
26
27
|
|
|
27
28
|
constructor(
|
|
28
29
|
public readonly name: string,
|
package/src/nodes/if.ts
CHANGED
|
@@ -6,7 +6,7 @@ export class If<TInputJson = unknown> implements RunnableNodeConfig<TInputJson,
|
|
|
6
6
|
readonly kind = "node" as const;
|
|
7
7
|
readonly type: TypeToken<unknown> = IfNode;
|
|
8
8
|
readonly execution = { hint: "local" } as const;
|
|
9
|
-
readonly icon = "lucide:split" as const;
|
|
9
|
+
readonly icon = "lucide:split@rot=90" as const;
|
|
10
10
|
readonly declaredOutputPorts = ["true", "false"] as const;
|
|
11
11
|
constructor(
|
|
12
12
|
public readonly name: string,
|
package/src/nodes/mapData.ts
CHANGED
|
@@ -16,6 +16,7 @@ export class MapData<TInputJson = unknown, TOutputJson = unknown> implements Run
|
|
|
16
16
|
readonly execution = { hint: "local" } as const;
|
|
17
17
|
/** Zero mapped items should still allow downstream nodes to run. */
|
|
18
18
|
readonly continueWhenEmptyOutput = true as const;
|
|
19
|
+
readonly icon = "lucide:square-pen" as const;
|
|
19
20
|
readonly keepBinaries: boolean;
|
|
20
21
|
|
|
21
22
|
constructor(
|
package/src/nodes/merge.ts
CHANGED
|
@@ -10,7 +10,7 @@ export class Merge<TInputJson = unknown, TOutputJson = TInputJson> implements Ru
|
|
|
10
10
|
> {
|
|
11
11
|
readonly kind = "node" as const;
|
|
12
12
|
readonly type: TypeToken<unknown> = MergeNode;
|
|
13
|
-
readonly icon = "lucide:
|
|
13
|
+
readonly icon = "lucide:merge@rot=90" as const;
|
|
14
14
|
|
|
15
15
|
constructor(
|
|
16
16
|
public readonly name: string,
|
package/src/nodes/noOp.ts
CHANGED
|
@@ -6,6 +6,7 @@ export class NoOp<TItemJson = unknown> implements RunnableNodeConfig<TItemJson,
|
|
|
6
6
|
readonly kind = "node" as const;
|
|
7
7
|
readonly type: TypeToken<unknown> = NoOpNode;
|
|
8
8
|
readonly execution = { hint: "local" } as const;
|
|
9
|
+
readonly icon = "lucide:circle-dashed" as const;
|
|
9
10
|
|
|
10
11
|
constructor(
|
|
11
12
|
public readonly name: string = "NoOp",
|
package/src/nodes/split.ts
CHANGED
|
@@ -12,7 +12,7 @@ export class Split<TIn = unknown, TElem = unknown> implements RunnableNodeConfig
|
|
|
12
12
|
* Mirrors {@link MapData}'s empty-output behavior.
|
|
13
13
|
*/
|
|
14
14
|
readonly continueWhenEmptyOutput = true as const;
|
|
15
|
-
readonly icon = "
|
|
15
|
+
readonly icon = "builtin:split-rows" as const;
|
|
16
16
|
|
|
17
17
|
constructor(
|
|
18
18
|
public readonly name: string,
|
package/src/nodes/wait.ts
CHANGED
|
@@ -8,6 +8,7 @@ export class Wait<TItemJson = unknown> implements RunnableNodeConfig<TItemJson,
|
|
|
8
8
|
readonly execution = { hint: "local" } as const;
|
|
9
9
|
/** Pass-through empty batches should still advance to downstream nodes. */
|
|
10
10
|
readonly continueWhenEmptyOutput = true as const;
|
|
11
|
+
readonly icon = "lucide:hourglass" as const;
|
|
11
12
|
|
|
12
13
|
constructor(
|
|
13
14
|
public readonly name: string,
|