@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/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,50 @@
|
|
|
1
1
|
# @codemation/core-nodes
|
|
2
2
|
|
|
3
|
+
## 1.0.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`ed75183`](https://github.com/MadeRelevant/codemation/commit/ed75183f51ae71b06aa2e57ae4fc48ce9db2e4ce) - Establish "per Item per Call" identity end-to-end so the workflow run inspector reports, visualizes, and dashboards multi-item AI agents correctly.
|
|
8
|
+
|
|
9
|
+
Previously, an orchestrator agent that processed N items emitted one flat list of LLM rounds and tool calls — the bottom execution tree, the right-panel agent timeline, cost dashboards, and the realtime event stream all collapsed iterations into one bucket, making sub-agent fan-outs (and parallel item processing in general) unreadable.
|
|
10
|
+
|
|
11
|
+
**What changed**
|
|
12
|
+
- **Engine** (`@codemation/core`): `NodeExecutor` mints a `NodeIterationId` per item inside per-item runnable activations and stamps it (with `itemIndex`) onto `NodeExecutionContext`. Connection invocations, telemetry spans (`gen_ai.chat.completion`, `agent.tool.call`), metric points (`codemation.cost.estimated`, `codemation.agent.turns`, `codemation.agent.tool_calls`), and run events all carry the per-item identity. New `ChildExecutionScopeFactory` re-roots `NodeExecutionContext` for sub-agents so credentials and iteration ids resolve correctly across the orchestrator → tool → sub-agent boundary.
|
|
13
|
+
- **Sub-agent credentials** (`@codemation/core-nodes`): `NodeBackedToolRuntime.resolveNodeCtx` no longer re-wraps `args.ctx.nodeId` with `ConnectionNodeIdFactory.toolConnectionNodeId` — the caller already pre-wraps it. The previous double-nesting produced exponentially deep node ids (`AIAgentNode:2__conn__tool__conn__searchInMail__conn__tool__conn__searchInMail__conn__llm`) that didn't match user-bound credential slots. Sub-agent OpenAI / API-key slots resolve again.
|
|
14
|
+
- **Realtime events**: new `connectionInvocationStarted` / `connectionInvocationCompleted` / `connectionInvocationFailed` events carry the full `ConnectionInvocationRecord` (incl. `iterationId`, `itemIndex`, `parentInvocationId`) and surgical reducers update the run cache without waiting for a coarse `runSaved` snapshot. Run-query polling dropped from 250 ms → 5 s now that WebSocket events drive most updates.
|
|
15
|
+
- **Persistence** (`@codemation/host`): Prisma `ExecutionInstance` model gains `iteration_id`, `item_index`, `parent_invocation_id` columns + index (sqlite + postgres migrations); `PrismaWorkflowRunRepository` round-trips them on read/save and via `ExecutionInstanceDto`. Without this the cold reload of a finished run silently flattens the per-item tree because `runSaved` events stream through Prisma. Telemetry tables already carried these columns from Phase 4; both sides now agree.
|
|
16
|
+
- **Iteration projection / cost queries** (`@codemation/host`): new `RunIterationProjectionFactory` projects `RunIterationRecord`s from connection invocations + iteration cost metrics and `GetIterationCostQueryHandler` serves per-iteration cost rollups for dashboards.
|
|
17
|
+
- **Inspector view model** (`@codemation/next-host`): `NodeInspectorTelemetryPresenter` groups LLM and tool spans by `iterationId` into "Item N" accordion entries (single-item agents fall back to flat layout). New `FocusedInvocationModelFactory` powers item-level prev/next navigation when a specific invocation is selected — the breadcrumb shows "Item X of Y" and nav targets the first invocation of adjacent items. Tool spans now interleave chronologically with LLM rounds (request → tools → response) instead of LLM rounds first then orphan tools at the bottom.
|
|
18
|
+
- **Bottom execution tree** (`@codemation/next-host`): new `ExecutionTreeItemGroupInjector` injects synthetic "Item N" parent rows between an agent and its connection invocations when the agent processed 2+ items. Single-item activations are left untouched; sub-agent invocations whose `parentInvocationId` already points at a tool-call row stay nested under the orchestrator's specific tool call.
|
|
19
|
+
- **Sub-agent credential boundary**: `ChildExecutionScopeFactory.forSubAgent` ensures sub-agent `NodeExecutionContext` keeps the parent invocation id and span context intact so trace nesting and credential resolution agree on the connection-node id.
|
|
20
|
+
- **Tests**: new unit + UI suites for each layer (sub-agent scope, item-group injector, focused invocation model, agent timeline per-item grouping, chronological ordering, Prisma iterationId round trip, item-aware properties panel, connection-invocation event publisher) and a runnable `apps/test-dev` sample (`agentSubAgentToolFanout`) that exercises the orchestrator → sub-agent fan-out across 2 items end-to-end.
|
|
21
|
+
|
|
22
|
+
- Updated dependencies [[`ed75183`](https://github.com/MadeRelevant/codemation/commit/ed75183f51ae71b06aa2e57ae4fc48ce9db2e4ce)]:
|
|
23
|
+
- @codemation/core@1.0.1
|
|
24
|
+
|
|
25
|
+
## 1.0.1
|
|
26
|
+
|
|
27
|
+
### Patch Changes
|
|
28
|
+
|
|
29
|
+
- [#95](https://github.com/MadeRelevant/codemation/pull/95) [`328c975`](https://github.com/MadeRelevant/codemation/commit/328c9759d45b711c177ea9a360ed4960ffdf5ffa) Thanks [@cblokland90](https://github.com/cblokland90)! - Workflow-canvas icon system redesign: LTR-oriented control-flow icons, pixel-perfect Split / Aggregate SVGs, and a single icon renderer shared by the canvas and the execution tree panel.
|
|
30
|
+
|
|
31
|
+
**Why**
|
|
32
|
+
|
|
33
|
+
The canvas reads left-to-right, but Lucide's `split`, `merge`, and `git-*` family are oriented vertically (top-to-bottom git-graph convention), so `If` and `Merge` nodes rendered 90° off from the flow direction. The execution-tree panel also ran a parallel icon-rendering path that only understood Lucide names — every time a plugin node set an icon as `builtin:<id>`, `si:<slug>`, or a URL, the tree panel silently fell back to a type-substring-guessed Lucide glyph (e.g. `"wait".includes("ai")` → the agent Bot icon). That duplication is gone.
|
|
34
|
+
|
|
35
|
+
**What changed**
|
|
36
|
+
- **Rotation suffix**: `NodeConfigBase.icon` now accepts an optional `@rot=<0|90|180|270>` tail modifier on any icon token (`lucide:`, `builtin:`, `si:`, or URL). Parsed by a strict tail regex so URLs with `@` (for example `http://user@host/icon.svg`) are unaffected, and non-orthogonal angles are rejected so glyphs stay pixel-crisp.
|
|
37
|
+
- **LTR control-flow icons**:
|
|
38
|
+
- `If`: `lucide:split@rot=90` — Y-fork with the single leg on the left and two arms fanning right.
|
|
39
|
+
- `Merge`: `lucide:merge@rot=90` — chevron pointing right, two arms merging from the left.
|
|
40
|
+
- **Pixel-perfect builtin SVGs** shipped under `packages/next-host/public/canvas-icons/builtin/`:
|
|
41
|
+
- `builtin:split-rows`: 1 source square on the left, tree trunk / spine, 3 output lines on the right.
|
|
42
|
+
- `builtin:aggregate-rows`: mirror — 3 input lines on the left converging through a spine into 1 summary square on the right.
|
|
43
|
+
- Stroke-based SVGs (matching Lucide stroke weight next to them on the canvas).
|
|
44
|
+
- **`@codemation/core-nodes` built-in node icon updates** that plug into the above: `If` (rotated split), `Merge` (rotated merge), `Split` (`builtin:split-rows`), `Aggregate` (`builtin:aggregate-rows`), `Wait` (`lucide:hourglass`), `MapData` (`lucide:square-pen`), `HttpRequest` (`lucide:globe`), `WebhookTrigger` (`lucide:webhook`), `NoOp` (`lucide:circle-dashed`).
|
|
45
|
+
- **One renderer, role-only fallback**: `WorkflowExecutionInspectorTreePanelContent` now renders `<WorkflowCanvasNodeIcon />` so `builtin:`, `si:`, URLs, and `@rot=…` resolve identically in the execution tree panel and on the canvas. `WorkflowNodeIconResolver.resolveFallback` shrank from 11 branches of type-substring guessing to 4 role mappings (`agent` / `nestedAgent` → `Bot`, `languageModel` → `Brain`, `tool` → `Wrench`, else → `Boxes`). Plugin nodes that forget to set an icon now get a generic `Boxes` — a clear visual signal rather than a silent substring mismatch.
|
|
46
|
+
- **New test file** `test/canvas/workflowCanvasNodeIcon.test.tsx` pins 12 behaviours across the rotation parser and the role-only fallback, including a regression case that the pre-fix `"wait".includes("ai")` mis-mapping is impossible now.
|
|
47
|
+
|
|
3
48
|
## 1.0.0
|
|
4
49
|
|
|
5
50
|
### Major Changes
|
package/dist/index.cjs
CHANGED
|
@@ -2883,7 +2883,7 @@ let AgentToolExecutionCoordinator = class AgentToolExecutionCoordinator$1 {
|
|
|
2883
2883
|
async executePlannedToolCall(args) {
|
|
2884
2884
|
const { plannedToolCall, ctx } = args;
|
|
2885
2885
|
const toolCallInputsByPort = AgentToolCallPortMap.fromInput(plannedToolCall.toolCall.input ?? {});
|
|
2886
|
-
const invocationId =
|
|
2886
|
+
const invocationId = plannedToolCall.invocationId;
|
|
2887
2887
|
const startedAt = /* @__PURE__ */ new Date();
|
|
2888
2888
|
const span = ctx.telemetry.startChildSpan({
|
|
2889
2889
|
name: "agent.tool.call",
|
|
@@ -2891,7 +2891,10 @@ let AgentToolExecutionCoordinator = class AgentToolExecutionCoordinator$1 {
|
|
|
2891
2891
|
startedAt,
|
|
2892
2892
|
attributes: {
|
|
2893
2893
|
[__codemation_core.CodemationTelemetryAttributeNames.connectionInvocationId]: invocationId,
|
|
2894
|
-
[__codemation_core.CodemationTelemetryAttributeNames.toolName]: plannedToolCall.binding.config.name
|
|
2894
|
+
[__codemation_core.CodemationTelemetryAttributeNames.toolName]: plannedToolCall.binding.config.name,
|
|
2895
|
+
...ctx.iterationId ? { [__codemation_core.CodemationTelemetryAttributeNames.iterationId]: ctx.iterationId } : {},
|
|
2896
|
+
...typeof ctx.itemIndex === "number" ? { [__codemation_core.CodemationTelemetryAttributeNames.iterationIndex]: ctx.itemIndex } : {},
|
|
2897
|
+
...ctx.parentInvocationId ? { [__codemation_core.CodemationTelemetryAttributeNames.parentInvocationId]: ctx.parentInvocationId } : {}
|
|
2895
2898
|
}
|
|
2896
2899
|
});
|
|
2897
2900
|
await ctx.nodeState?.markRunning({
|
|
@@ -2899,8 +2902,23 @@ let AgentToolExecutionCoordinator = class AgentToolExecutionCoordinator$1 {
|
|
|
2899
2902
|
activationId: ctx.activationId,
|
|
2900
2903
|
inputsByPort: toolCallInputsByPort
|
|
2901
2904
|
});
|
|
2905
|
+
await ctx.nodeState?.appendConnectionInvocation({
|
|
2906
|
+
invocationId,
|
|
2907
|
+
connectionNodeId: plannedToolCall.nodeId,
|
|
2908
|
+
parentAgentNodeId: ctx.nodeId,
|
|
2909
|
+
parentAgentActivationId: ctx.activationId,
|
|
2910
|
+
status: "running",
|
|
2911
|
+
managedInput: this.toJsonValue(plannedToolCall.toolCall.input),
|
|
2912
|
+
queuedAt: startedAt.toISOString(),
|
|
2913
|
+
startedAt: startedAt.toISOString(),
|
|
2914
|
+
iterationId: ctx.iterationId,
|
|
2915
|
+
parentInvocationId: ctx.parentInvocationId
|
|
2916
|
+
});
|
|
2902
2917
|
try {
|
|
2903
|
-
const result = await plannedToolCall.binding.execute(plannedToolCall.toolCall.input ?? {}
|
|
2918
|
+
const result = await plannedToolCall.binding.execute(plannedToolCall.toolCall.input ?? {}, {
|
|
2919
|
+
parentSpan: span,
|
|
2920
|
+
parentInvocationId: invocationId
|
|
2921
|
+
});
|
|
2904
2922
|
const serialized = typeof result === "string" ? result : JSON.stringify(result);
|
|
2905
2923
|
const finishedAt = /* @__PURE__ */ new Date();
|
|
2906
2924
|
await ctx.nodeState?.markCompleted({
|
|
@@ -2933,7 +2951,9 @@ let AgentToolExecutionCoordinator = class AgentToolExecutionCoordinator$1 {
|
|
|
2933
2951
|
managedOutput: this.toJsonValue(result),
|
|
2934
2952
|
queuedAt: startedAt.toISOString(),
|
|
2935
2953
|
startedAt: startedAt.toISOString(),
|
|
2936
|
-
finishedAt: finishedAt.toISOString()
|
|
2954
|
+
finishedAt: finishedAt.toISOString(),
|
|
2955
|
+
iterationId: ctx.iterationId,
|
|
2956
|
+
parentInvocationId: ctx.parentInvocationId
|
|
2937
2957
|
});
|
|
2938
2958
|
return {
|
|
2939
2959
|
toolName: plannedToolCall.binding.config.name,
|
|
@@ -3074,7 +3094,9 @@ let AgentToolExecutionCoordinator = class AgentToolExecutionCoordinator$1 {
|
|
|
3074
3094
|
},
|
|
3075
3095
|
queuedAt: args.startedAt.toISOString(),
|
|
3076
3096
|
startedAt: args.startedAt.toISOString(),
|
|
3077
|
-
finishedAt: finishedAt.toISOString()
|
|
3097
|
+
finishedAt: finishedAt.toISOString(),
|
|
3098
|
+
iterationId: args.ctx.iterationId,
|
|
3099
|
+
parentInvocationId: args.ctx.parentInvocationId
|
|
3078
3100
|
});
|
|
3079
3101
|
}
|
|
3080
3102
|
createRepairPayload(args) {
|
|
@@ -3135,13 +3157,14 @@ AgentToolExecutionCoordinator = __decorate([
|
|
|
3135
3157
|
|
|
3136
3158
|
//#endregion
|
|
3137
3159
|
//#region src/nodes/NodeBackedToolRuntime.ts
|
|
3138
|
-
var _ref$1, _ref2$1, _ref3$1;
|
|
3160
|
+
var _ref$1, _ref2$1, _ref3$1, _ref4$1;
|
|
3139
3161
|
let NodeBackedToolRuntime = class NodeBackedToolRuntime$1 {
|
|
3140
|
-
constructor(nodeResolver, itemExprResolver, outputNormalizer, outputBehaviorResolver) {
|
|
3162
|
+
constructor(nodeResolver, itemExprResolver, outputNormalizer, outputBehaviorResolver, childExecutionScopeFactory) {
|
|
3141
3163
|
this.nodeResolver = nodeResolver;
|
|
3142
3164
|
this.itemExprResolver = itemExprResolver;
|
|
3143
3165
|
this.outputNormalizer = outputNormalizer;
|
|
3144
3166
|
this.outputBehaviorResolver = outputBehaviorResolver;
|
|
3167
|
+
this.childExecutionScopeFactory = childExecutionScopeFactory;
|
|
3145
3168
|
}
|
|
3146
3169
|
async execute(config$1, args) {
|
|
3147
3170
|
const nodeInput = config$1.toNodeItem({
|
|
@@ -3152,10 +3175,7 @@ let NodeBackedToolRuntime = class NodeBackedToolRuntime$1 {
|
|
|
3152
3175
|
ctx: args.ctx,
|
|
3153
3176
|
node: config$1.node
|
|
3154
3177
|
});
|
|
3155
|
-
const nodeCtx =
|
|
3156
|
-
...args.ctx,
|
|
3157
|
-
config: config$1.node
|
|
3158
|
-
};
|
|
3178
|
+
const nodeCtx = this.resolveNodeCtx(config$1, args);
|
|
3159
3179
|
const resolvedNode = this.nodeResolver.resolve(config$1.node.type);
|
|
3160
3180
|
const outputs = await this.executeResolvedNode(resolvedNode, nodeInput, nodeCtx);
|
|
3161
3181
|
return config$1.toToolOutput({
|
|
@@ -3168,6 +3188,35 @@ let NodeBackedToolRuntime = class NodeBackedToolRuntime$1 {
|
|
|
3168
3188
|
outputs
|
|
3169
3189
|
});
|
|
3170
3190
|
}
|
|
3191
|
+
/**
|
|
3192
|
+
* Returns a re-rooted child ctx for nested-agent tools (so their LLM/tool connection ids derive
|
|
3193
|
+
* from the tool connection node, telemetry parents under the tool-call span, and connection
|
|
3194
|
+
* invocations carry `parentInvocationId`). Plain runnable tools (non-agent) keep the orchestrator
|
|
3195
|
+
* ctx with only `config` swapped — no nesting concern.
|
|
3196
|
+
*
|
|
3197
|
+
* The caller (`AIAgentNode.createItemScopedTools`) already wraps the orchestrator ctx via
|
|
3198
|
+
* `ConnectionCredentialExecutionContextFactory.forConnectionNode`, so `args.ctx.nodeId` is the
|
|
3199
|
+
* tool's own connection node id (e.g. `AIAgentNode:2__conn__tool__searchInMail`). We pass that
|
|
3200
|
+
* through as the sub-agent's `nodeId`; deriving another `toolConnectionNodeId(args.ctx.nodeId,
|
|
3201
|
+
* config.name)` here would prepend a duplicate `__conn__tool__<name>` segment and exponentially
|
|
3202
|
+
* deepen ids on each invocation, which also breaks credential resolution because user-provided
|
|
3203
|
+
* bindings sit on the single-level connection node id.
|
|
3204
|
+
*/
|
|
3205
|
+
resolveNodeCtx(config$1, args) {
|
|
3206
|
+
const isNestedAgent = __codemation_core.AgentConfigInspector.isAgentNodeConfig(config$1.node);
|
|
3207
|
+
const hooks = args.hooks;
|
|
3208
|
+
if (!isNestedAgent || !hooks?.parentSpan || !hooks.parentInvocationId) return {
|
|
3209
|
+
...args.ctx,
|
|
3210
|
+
config: config$1.node
|
|
3211
|
+
};
|
|
3212
|
+
return this.childExecutionScopeFactory.forSubAgent({
|
|
3213
|
+
parentCtx: args.ctx,
|
|
3214
|
+
childNodeId: args.ctx.nodeId,
|
|
3215
|
+
childConfig: config$1.node,
|
|
3216
|
+
parentInvocationId: hooks.parentInvocationId,
|
|
3217
|
+
parentSpan: hooks.parentSpan
|
|
3218
|
+
});
|
|
3219
|
+
}
|
|
3171
3220
|
async executeResolvedNode(resolvedNode, nodeInput, ctx) {
|
|
3172
3221
|
if (this.isMultiInputNode(resolvedNode)) return await resolvedNode.executeMulti({ in: [nodeInput] }, ctx);
|
|
3173
3222
|
if (this.isRunnableNode(resolvedNode)) {
|
|
@@ -3205,11 +3254,13 @@ NodeBackedToolRuntime = __decorate([
|
|
|
3205
3254
|
__decorateParam(1, (0, __codemation_core.inject)(__codemation_core.ItemExprResolver)),
|
|
3206
3255
|
__decorateParam(2, (0, __codemation_core.inject)(__codemation_core.NodeOutputNormalizer)),
|
|
3207
3256
|
__decorateParam(3, (0, __codemation_core.inject)(__codemation_core.RunnableOutputBehaviorResolver)),
|
|
3257
|
+
__decorateParam(4, (0, __codemation_core.inject)(__codemation_core.ChildExecutionScopeFactory)),
|
|
3208
3258
|
__decorateMetadata("design:paramtypes", [
|
|
3209
3259
|
Object,
|
|
3210
3260
|
typeof (_ref$1 = typeof __codemation_core.ItemExprResolver !== "undefined" && __codemation_core.ItemExprResolver) === "function" ? _ref$1 : Object,
|
|
3211
3261
|
typeof (_ref2$1 = typeof __codemation_core.NodeOutputNormalizer !== "undefined" && __codemation_core.NodeOutputNormalizer) === "function" ? _ref2$1 : Object,
|
|
3212
|
-
typeof (_ref3$1 = typeof __codemation_core.RunnableOutputBehaviorResolver !== "undefined" && __codemation_core.RunnableOutputBehaviorResolver) === "function" ? _ref3$1 : Object
|
|
3262
|
+
typeof (_ref3$1 = typeof __codemation_core.RunnableOutputBehaviorResolver !== "undefined" && __codemation_core.RunnableOutputBehaviorResolver) === "function" ? _ref3$1 : Object,
|
|
3263
|
+
typeof (_ref4$1 = typeof __codemation_core.ChildExecutionScopeFactory !== "undefined" && __codemation_core.ChildExecutionScopeFactory) === "function" ? _ref4$1 : Object
|
|
3213
3264
|
])
|
|
3214
3265
|
], NodeBackedToolRuntime);
|
|
3215
3266
|
|
|
@@ -3412,7 +3463,7 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
3412
3463
|
return {
|
|
3413
3464
|
config: entry.config,
|
|
3414
3465
|
inputSchema: entry.runtime.inputSchema,
|
|
3415
|
-
execute: async (input) => {
|
|
3466
|
+
execute: async (input, hooks) => {
|
|
3416
3467
|
const validated = entry.runtime.inputSchema.parse(input);
|
|
3417
3468
|
return await entry.runtime.execute({
|
|
3418
3469
|
config: entry.config,
|
|
@@ -3420,7 +3471,8 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
3420
3471
|
ctx: toolCredentialContext,
|
|
3421
3472
|
item,
|
|
3422
3473
|
itemIndex,
|
|
3423
|
-
items
|
|
3474
|
+
items,
|
|
3475
|
+
hooks
|
|
3424
3476
|
});
|
|
3425
3477
|
}
|
|
3426
3478
|
};
|
|
@@ -3470,11 +3522,36 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
3470
3522
|
activationId: ctx.activationId,
|
|
3471
3523
|
inputsByPort: itemInputsByPort
|
|
3472
3524
|
});
|
|
3525
|
+
await ctx.nodeState?.appendConnectionInvocation({
|
|
3526
|
+
invocationId,
|
|
3527
|
+
connectionNodeId: languageModelConnectionNodeId,
|
|
3528
|
+
parentAgentNodeId: ctx.nodeId,
|
|
3529
|
+
parentAgentActivationId: ctx.activationId,
|
|
3530
|
+
status: "queued",
|
|
3531
|
+
managedInput: summarizedInput,
|
|
3532
|
+
queuedAt: startedAt.toISOString(),
|
|
3533
|
+
iterationId: ctx.iterationId,
|
|
3534
|
+
itemIndex: ctx.itemIndex,
|
|
3535
|
+
parentInvocationId: ctx.parentInvocationId
|
|
3536
|
+
});
|
|
3473
3537
|
await ctx.nodeState?.markRunning({
|
|
3474
3538
|
nodeId: languageModelConnectionNodeId,
|
|
3475
3539
|
activationId: ctx.activationId,
|
|
3476
3540
|
inputsByPort: itemInputsByPort
|
|
3477
3541
|
});
|
|
3542
|
+
await ctx.nodeState?.appendConnectionInvocation({
|
|
3543
|
+
invocationId,
|
|
3544
|
+
connectionNodeId: languageModelConnectionNodeId,
|
|
3545
|
+
parentAgentNodeId: ctx.nodeId,
|
|
3546
|
+
parentAgentActivationId: ctx.activationId,
|
|
3547
|
+
status: "running",
|
|
3548
|
+
managedInput: summarizedInput,
|
|
3549
|
+
queuedAt: startedAt.toISOString(),
|
|
3550
|
+
startedAt: startedAt.toISOString(),
|
|
3551
|
+
iterationId: ctx.iterationId,
|
|
3552
|
+
itemIndex: ctx.itemIndex,
|
|
3553
|
+
parentInvocationId: ctx.parentInvocationId
|
|
3554
|
+
});
|
|
3478
3555
|
try {
|
|
3479
3556
|
const tools = this.buildToolSet(itemScopedTools);
|
|
3480
3557
|
const callOptions = this.resolveCallOptions(model, guardrails.modelInvocationOptions);
|
|
@@ -3490,11 +3567,12 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
3490
3567
|
});
|
|
3491
3568
|
const turnResult = this.extractTurnResult(result);
|
|
3492
3569
|
const finishedAt = /* @__PURE__ */ new Date();
|
|
3570
|
+
const managedOutput = this.summarizeTurnOutput(turnResult);
|
|
3493
3571
|
await ctx.nodeState?.markCompleted({
|
|
3494
3572
|
nodeId: languageModelConnectionNodeId,
|
|
3495
3573
|
activationId: ctx.activationId,
|
|
3496
3574
|
inputsByPort: itemInputsByPort,
|
|
3497
|
-
outputs: AgentOutputFactory.fromUnknown(
|
|
3575
|
+
outputs: AgentOutputFactory.fromUnknown(managedOutput)
|
|
3498
3576
|
});
|
|
3499
3577
|
await span.attachArtifact({
|
|
3500
3578
|
kind: "ai.messages",
|
|
@@ -3518,10 +3596,13 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
3518
3596
|
parentAgentActivationId: ctx.activationId,
|
|
3519
3597
|
status: "completed",
|
|
3520
3598
|
managedInput: summarizedInput,
|
|
3521
|
-
managedOutput
|
|
3599
|
+
managedOutput,
|
|
3522
3600
|
queuedAt: startedAt.toISOString(),
|
|
3523
3601
|
startedAt: startedAt.toISOString(),
|
|
3524
|
-
finishedAt: finishedAt.toISOString()
|
|
3602
|
+
finishedAt: finishedAt.toISOString(),
|
|
3603
|
+
iterationId: ctx.iterationId,
|
|
3604
|
+
itemIndex: ctx.itemIndex,
|
|
3605
|
+
parentInvocationId: ctx.parentInvocationId
|
|
3525
3606
|
});
|
|
3526
3607
|
return turnResult;
|
|
3527
3608
|
} catch (error) {
|
|
@@ -3557,11 +3638,36 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
3557
3638
|
activationId: ctx.activationId,
|
|
3558
3639
|
inputsByPort: itemInputsByPort
|
|
3559
3640
|
});
|
|
3641
|
+
await ctx.nodeState?.appendConnectionInvocation({
|
|
3642
|
+
invocationId,
|
|
3643
|
+
connectionNodeId: languageModelConnectionNodeId,
|
|
3644
|
+
parentAgentNodeId: ctx.nodeId,
|
|
3645
|
+
parentAgentActivationId: ctx.activationId,
|
|
3646
|
+
status: "queued",
|
|
3647
|
+
managedInput: summarizedInput,
|
|
3648
|
+
queuedAt: startedAt.toISOString(),
|
|
3649
|
+
iterationId: ctx.iterationId,
|
|
3650
|
+
itemIndex: ctx.itemIndex,
|
|
3651
|
+
parentInvocationId: ctx.parentInvocationId
|
|
3652
|
+
});
|
|
3560
3653
|
await ctx.nodeState?.markRunning({
|
|
3561
3654
|
nodeId: languageModelConnectionNodeId,
|
|
3562
3655
|
activationId: ctx.activationId,
|
|
3563
3656
|
inputsByPort: itemInputsByPort
|
|
3564
3657
|
});
|
|
3658
|
+
await ctx.nodeState?.appendConnectionInvocation({
|
|
3659
|
+
invocationId,
|
|
3660
|
+
connectionNodeId: languageModelConnectionNodeId,
|
|
3661
|
+
parentAgentNodeId: ctx.nodeId,
|
|
3662
|
+
parentAgentActivationId: ctx.activationId,
|
|
3663
|
+
status: "running",
|
|
3664
|
+
managedInput: summarizedInput,
|
|
3665
|
+
queuedAt: startedAt.toISOString(),
|
|
3666
|
+
startedAt: startedAt.toISOString(),
|
|
3667
|
+
iterationId: ctx.iterationId,
|
|
3668
|
+
itemIndex: ctx.itemIndex,
|
|
3669
|
+
parentInvocationId: ctx.parentInvocationId
|
|
3670
|
+
});
|
|
3565
3671
|
try {
|
|
3566
3672
|
const callOptions = this.resolveCallOptions(model, guardrails.modelInvocationOptions);
|
|
3567
3673
|
const outputSchema = structuredOptions?.strict && !this.isZodSchema(schema) ? ai.Output.object({ schema: (0, ai.jsonSchema)(schema) }) : ai.Output.object({ schema });
|
|
@@ -3607,7 +3713,10 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
3607
3713
|
managedOutput: this.resultToJsonValue(result.experimental_output),
|
|
3608
3714
|
queuedAt: startedAt.toISOString(),
|
|
3609
3715
|
startedAt: startedAt.toISOString(),
|
|
3610
|
-
finishedAt: finishedAt.toISOString()
|
|
3716
|
+
finishedAt: finishedAt.toISOString(),
|
|
3717
|
+
iterationId: ctx.iterationId,
|
|
3718
|
+
itemIndex: ctx.itemIndex,
|
|
3719
|
+
parentInvocationId: ctx.parentInvocationId
|
|
3611
3720
|
});
|
|
3612
3721
|
return result.experimental_output;
|
|
3613
3722
|
} catch (error) {
|
|
@@ -3638,6 +3747,24 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
3638
3747
|
providerOptions: overrides?.providerOptions ?? defaults.providerOptions
|
|
3639
3748
|
};
|
|
3640
3749
|
}
|
|
3750
|
+
/**
|
|
3751
|
+
* Build a no-code-friendly output payload for an LLM round.
|
|
3752
|
+
*
|
|
3753
|
+
* Always includes `content` (matching the canvas snapshot shape used elsewhere) and adds a
|
|
3754
|
+
* `toolCalls` array when the round produced tool calls so the execution inspector surfaces the
|
|
3755
|
+
* planned calls instead of just an empty `""` for tool-only rounds.
|
|
3756
|
+
*/
|
|
3757
|
+
summarizeTurnOutput(turnResult) {
|
|
3758
|
+
if (turnResult.toolCalls.length === 0) return { content: turnResult.text };
|
|
3759
|
+
const toolCalls = turnResult.toolCalls.map((toolCall) => ({
|
|
3760
|
+
name: toolCall.name,
|
|
3761
|
+
args: this.resultToJsonValue(toolCall.input) ?? null
|
|
3762
|
+
}));
|
|
3763
|
+
return {
|
|
3764
|
+
content: turnResult.text,
|
|
3765
|
+
toolCalls
|
|
3766
|
+
};
|
|
3767
|
+
}
|
|
3641
3768
|
extractTurnResult(result) {
|
|
3642
3769
|
const usage = this.extractUsageFromResult(result);
|
|
3643
3770
|
const text = result.text;
|
|
@@ -3681,7 +3808,10 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
3681
3808
|
attributes: {
|
|
3682
3809
|
[__codemation_core.CodemationTelemetryAttributeNames.connectionInvocationId]: invocationId,
|
|
3683
3810
|
[__codemation_core.GenAiTelemetryAttributeNames.operationName]: "chat",
|
|
3684
|
-
[__codemation_core.GenAiTelemetryAttributeNames.requestModel]: this.resolveChatModelName(ctx.config.chatModel)
|
|
3811
|
+
[__codemation_core.GenAiTelemetryAttributeNames.requestModel]: this.resolveChatModelName(ctx.config.chatModel),
|
|
3812
|
+
...ctx.iterationId ? { [__codemation_core.CodemationTelemetryAttributeNames.iterationId]: ctx.iterationId } : {},
|
|
3813
|
+
...typeof ctx.itemIndex === "number" ? { [__codemation_core.CodemationTelemetryAttributeNames.iterationIndex]: ctx.itemIndex } : {},
|
|
3814
|
+
...ctx.parentInvocationId ? { [__codemation_core.CodemationTelemetryAttributeNames.parentInvocationId]: ctx.parentInvocationId } : {}
|
|
3685
3815
|
}
|
|
3686
3816
|
});
|
|
3687
3817
|
}
|
|
@@ -3731,11 +3861,26 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
3731
3861
|
return chatModel$1.modelName ?? chatModel$1.name;
|
|
3732
3862
|
}
|
|
3733
3863
|
async markQueuedTools(plannedToolCalls, ctx) {
|
|
3734
|
-
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
|
|
3864
|
+
const queuedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3865
|
+
for (const plannedToolCall of plannedToolCalls) {
|
|
3866
|
+
await ctx.nodeState?.markQueued({
|
|
3867
|
+
nodeId: plannedToolCall.nodeId,
|
|
3868
|
+
activationId: ctx.activationId,
|
|
3869
|
+
inputsByPort: AgentToolCallPortMap.fromInput(plannedToolCall.toolCall.input ?? {})
|
|
3870
|
+
});
|
|
3871
|
+
await ctx.nodeState?.appendConnectionInvocation({
|
|
3872
|
+
invocationId: plannedToolCall.invocationId,
|
|
3873
|
+
connectionNodeId: plannedToolCall.nodeId,
|
|
3874
|
+
parentAgentNodeId: ctx.nodeId,
|
|
3875
|
+
parentAgentActivationId: ctx.activationId,
|
|
3876
|
+
status: "queued",
|
|
3877
|
+
managedInput: this.resultToJsonValue(plannedToolCall.toolCall.input),
|
|
3878
|
+
queuedAt,
|
|
3879
|
+
iterationId: ctx.iterationId,
|
|
3880
|
+
itemIndex: ctx.itemIndex,
|
|
3881
|
+
parentInvocationId: ctx.parentInvocationId
|
|
3882
|
+
});
|
|
3883
|
+
}
|
|
3739
3884
|
}
|
|
3740
3885
|
planToolCalls(bindings, toolCalls, parentNodeId) {
|
|
3741
3886
|
const invocationCountByToolName = /* @__PURE__ */ new Map();
|
|
@@ -3748,7 +3893,8 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
3748
3893
|
binding,
|
|
3749
3894
|
toolCall,
|
|
3750
3895
|
invocationIndex,
|
|
3751
|
-
nodeId: __codemation_core.ConnectionNodeIdFactory.toolConnectionNodeId(parentNodeId, binding.config.name)
|
|
3896
|
+
nodeId: __codemation_core.ConnectionNodeIdFactory.toolConnectionNodeId(parentNodeId, binding.config.name),
|
|
3897
|
+
invocationId: __codemation_core.ConnectionInvocationIdFactory.create()
|
|
3752
3898
|
};
|
|
3753
3899
|
});
|
|
3754
3900
|
}
|
|
@@ -3776,7 +3922,10 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
3776
3922
|
},
|
|
3777
3923
|
queuedAt: args.startedAt.toISOString(),
|
|
3778
3924
|
startedAt: args.startedAt.toISOString(),
|
|
3779
|
-
finishedAt: finishedAt.toISOString()
|
|
3925
|
+
finishedAt: finishedAt.toISOString(),
|
|
3926
|
+
iterationId: args.ctx.iterationId,
|
|
3927
|
+
itemIndex: args.ctx.itemIndex,
|
|
3928
|
+
parentInvocationId: args.ctx.parentInvocationId
|
|
3780
3929
|
});
|
|
3781
3930
|
return effectiveError;
|
|
3782
3931
|
}
|
|
@@ -4049,6 +4198,7 @@ var HttpRequest = class {
|
|
|
4049
4198
|
kind = "node";
|
|
4050
4199
|
type = HttpRequestNode;
|
|
4051
4200
|
execution = { hint: "local" };
|
|
4201
|
+
icon = "lucide:globe";
|
|
4052
4202
|
constructor(name, args = {}, retryPolicy = __codemation_core.RetryPolicy.defaultForHttp) {
|
|
4053
4203
|
this.name = name;
|
|
4054
4204
|
this.args = args;
|
|
@@ -4090,7 +4240,7 @@ var Aggregate = class {
|
|
|
4090
4240
|
type = AggregateNode;
|
|
4091
4241
|
execution = { hint: "local" };
|
|
4092
4242
|
keepBinaries = true;
|
|
4093
|
-
icon = "
|
|
4243
|
+
icon = "builtin:aggregate-rows";
|
|
4094
4244
|
constructor(name, aggregate, id) {
|
|
4095
4245
|
this.name = name;
|
|
4096
4246
|
this.aggregate = aggregate;
|
|
@@ -4185,7 +4335,7 @@ var If = class {
|
|
|
4185
4335
|
kind = "node";
|
|
4186
4336
|
type = IfNode;
|
|
4187
4337
|
execution = { hint: "local" };
|
|
4188
|
-
icon = "lucide:split";
|
|
4338
|
+
icon = "lucide:split@rot=90";
|
|
4189
4339
|
declaredOutputPorts = ["true", "false"];
|
|
4190
4340
|
constructor(name, predicate, id) {
|
|
4191
4341
|
this.name = name;
|
|
@@ -4250,7 +4400,7 @@ var Split = class {
|
|
|
4250
4400
|
* Mirrors {@link MapData}'s empty-output behavior.
|
|
4251
4401
|
*/
|
|
4252
4402
|
continueWhenEmptyOutput = true;
|
|
4253
|
-
icon = "
|
|
4403
|
+
icon = "builtin:split-rows";
|
|
4254
4404
|
constructor(name, getElements, id) {
|
|
4255
4405
|
this.name = name;
|
|
4256
4406
|
this.getElements = getElements;
|
|
@@ -4321,6 +4471,7 @@ var MapData = class {
|
|
|
4321
4471
|
execution = { hint: "local" };
|
|
4322
4472
|
/** Zero mapped items should still allow downstream nodes to run. */
|
|
4323
4473
|
continueWhenEmptyOutput = true;
|
|
4474
|
+
icon = "lucide:square-pen";
|
|
4324
4475
|
keepBinaries;
|
|
4325
4476
|
constructor(name, map, options = {}) {
|
|
4326
4477
|
this.name = name;
|
|
@@ -4390,7 +4541,7 @@ MergeNode = __decorate([(0, __codemation_core.node)({ packageName: "@codemation/
|
|
|
4390
4541
|
var Merge = class {
|
|
4391
4542
|
kind = "node";
|
|
4392
4543
|
type = MergeNode;
|
|
4393
|
-
icon = "lucide:
|
|
4544
|
+
icon = "lucide:merge@rot=90";
|
|
4394
4545
|
constructor(name, cfg = { mode: "passThrough" }, id) {
|
|
4395
4546
|
this.name = name;
|
|
4396
4547
|
this.cfg = cfg;
|
|
@@ -4415,6 +4566,7 @@ var NoOp = class {
|
|
|
4415
4566
|
kind = "node";
|
|
4416
4567
|
type = NoOpNode;
|
|
4417
4568
|
execution = { hint: "local" };
|
|
4569
|
+
icon = "lucide:circle-dashed";
|
|
4418
4570
|
constructor(name = "NoOp", id) {
|
|
4419
4571
|
this.name = name;
|
|
4420
4572
|
this.id = id;
|
|
@@ -4520,6 +4672,7 @@ var Wait = class {
|
|
|
4520
4672
|
execution = { hint: "local" };
|
|
4521
4673
|
/** Pass-through empty batches should still advance to downstream nodes. */
|
|
4522
4674
|
continueWhenEmptyOutput = true;
|
|
4675
|
+
icon = "lucide:hourglass";
|
|
4523
4676
|
constructor(name, milliseconds, id) {
|
|
4524
4677
|
this.name = name;
|
|
4525
4678
|
this.milliseconds = milliseconds;
|
|
@@ -4570,7 +4723,7 @@ WebhookTriggerNode = __decorate([(0, __codemation_core.node)({ packageName: "@co
|
|
|
4570
4723
|
var WebhookTrigger = class WebhookTrigger {
|
|
4571
4724
|
kind = "trigger";
|
|
4572
4725
|
type = WebhookTriggerNode;
|
|
4573
|
-
icon = "lucide:
|
|
4726
|
+
icon = "lucide:webhook";
|
|
4574
4727
|
constructor(name, args, handler = WebhookTrigger.defaultHandler, id) {
|
|
4575
4728
|
this.name = name;
|
|
4576
4729
|
this.args = args;
|