@jangts/agent-core 0.2.5 → 0.3.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/README.md +386 -50
  3. package/development/docs/api/README.en.md +4 -0
  4. package/development/docs/api/README.md +4 -0
  5. package/development/docs/api/decorators.en.md +83 -0
  6. package/development/docs/api/decorators.md +108 -0
  7. package/development/docs/api/observability.en.md +37 -11
  8. package/development/docs/api/observability.md +64 -11
  9. package/development/docs/api/reference.en.md +469 -348
  10. package/development/docs/api/reference.md +225 -137
  11. package/development/docs/api/types.en.md +1 -0
  12. package/development/docs/api/types.md +1 -0
  13. package/development/docs/api/workflow-views.en.md +64 -0
  14. package/development/docs/api/workflow-views.md +78 -0
  15. package/development/docs/api/workflow.en.md +6 -2
  16. package/development/docs/api/workflow.md +6 -2
  17. package/development/skills/agent-core-app/SKILL.md +16 -0
  18. package/dist/cjs/capabilities/invocation.js +21 -1
  19. package/dist/cjs/capabilities/tool-runtime.js +8 -1
  20. package/dist/cjs/capabilities/tools.js +12 -17
  21. package/dist/cjs/diagnostics/index.js +51 -3
  22. package/dist/cjs/diagnostics/native-sub-flow.js +106 -0
  23. package/dist/cjs/diagnostics/sub-flow.js +278 -32
  24. package/dist/cjs/execution/engine.js +3 -2
  25. package/dist/cjs/execution/events.js +5 -5
  26. package/dist/cjs/execution/reporting.js +18 -28
  27. package/dist/cjs/execution/workflow-chain.js +211 -0
  28. package/dist/cjs/execution/workflow-graph.js +72 -0
  29. package/dist/cjs/execution/workflow.js +34 -6
  30. package/dist/cjs/kits/aggregation.js +17 -14
  31. package/dist/cjs/kits/answering/checks.js +2 -1
  32. package/dist/cjs/kits/answering/flow.js +24 -21
  33. package/dist/cjs/kits/answering/tasks.js +25 -20
  34. package/dist/cjs/kits/delivery.js +36 -24
  35. package/dist/cjs/kits/knowledge/catalog.js +33 -30
  36. package/dist/cjs/kits/policies.js +3 -2
  37. package/dist/cjs/kits/presentation/outputs.js +2 -1
  38. package/dist/cjs/kits/retrieval.js +7 -1
  39. package/dist/cjs/kits/tools.js +1 -0
  40. package/dist/cjs/model/decision-retry.js +2 -1
  41. package/dist/cjs/model/runner.js +20 -5
  42. package/dist/cjs/structs/execution/workflow-views.js +2 -0
  43. package/dist/cjs/support/context/nodes.js +48 -45
  44. package/dist/cjs/support/context/processing.js +6 -3
  45. package/dist/cjs/support/decorators/definition.js +42 -0
  46. package/dist/cjs/support/decorators/index.js +63 -0
  47. package/dist/cjs/support/decorators/pool.js +87 -0
  48. package/dist/cjs/support/decorators/types.js +2 -0
  49. package/dist/cjs/utils/invocation.js +1 -0
  50. package/dist/esm/capabilities/invocation.js +21 -1
  51. package/dist/esm/capabilities/tool-runtime.js +8 -1
  52. package/dist/esm/capabilities/tools.js +12 -17
  53. package/dist/esm/diagnostics/index.d.ts +1 -0
  54. package/dist/esm/diagnostics/index.js +48 -2
  55. package/dist/esm/diagnostics/native-sub-flow.d.ts +94 -0
  56. package/dist/esm/diagnostics/native-sub-flow.js +101 -0
  57. package/dist/esm/diagnostics/sub-flow.d.ts +56 -4
  58. package/dist/esm/diagnostics/sub-flow.js +271 -32
  59. package/dist/esm/execution/engine.d.ts +1 -0
  60. package/dist/esm/execution/engine.js +3 -2
  61. package/dist/esm/execution/events.js +5 -5
  62. package/dist/esm/execution/reporting.d.ts +6 -12
  63. package/dist/esm/execution/reporting.js +19 -29
  64. package/dist/esm/execution/workflow-chain.d.ts +3 -0
  65. package/dist/esm/execution/workflow-chain.js +208 -0
  66. package/dist/esm/execution/workflow-graph.d.ts +7 -0
  67. package/dist/esm/execution/workflow-graph.js +68 -0
  68. package/dist/esm/execution/workflow.d.ts +6 -0
  69. package/dist/esm/execution/workflow.js +34 -6
  70. package/dist/esm/kits/aggregation.d.ts +1 -1
  71. package/dist/esm/kits/aggregation.js +17 -14
  72. package/dist/esm/kits/answering/checks.js +2 -1
  73. package/dist/esm/kits/answering/flow.js +24 -21
  74. package/dist/esm/kits/answering/tasks.js +25 -20
  75. package/dist/esm/kits/delivery.d.ts +0 -9
  76. package/dist/esm/kits/delivery.js +36 -24
  77. package/dist/esm/kits/knowledge/catalog.js +33 -30
  78. package/dist/esm/kits/policies.d.ts +1 -1
  79. package/dist/esm/kits/policies.js +3 -2
  80. package/dist/esm/kits/presentation/outputs.js +2 -1
  81. package/dist/esm/kits/retrieval.js +7 -1
  82. package/dist/esm/kits/tools.js +1 -0
  83. package/dist/esm/model/decision-retry.d.ts +2 -0
  84. package/dist/esm/model/decision-retry.js +2 -1
  85. package/dist/esm/model/runner.d.ts +2 -0
  86. package/dist/esm/model/runner.js +20 -5
  87. package/dist/esm/structs/capabilities/invocation.d.ts +2 -0
  88. package/dist/esm/structs/capabilities/tools.d.ts +7 -3
  89. package/dist/esm/structs/common.d.ts +2 -0
  90. package/dist/esm/structs/debug.d.ts +36 -20
  91. package/dist/esm/structs/execution/workflow-views.d.ts +123 -0
  92. package/dist/esm/structs/execution/workflow-views.js +1 -0
  93. package/dist/esm/structs/execution/workflow.d.ts +4 -3
  94. package/dist/esm/structs/index.d.ts +1 -0
  95. package/dist/esm/structs/model/runtime.d.ts +2 -0
  96. package/dist/esm/support/context/nodes.js +48 -45
  97. package/dist/esm/support/context/processing.js +6 -3
  98. package/dist/esm/support/decorators/definition.d.ts +19 -0
  99. package/dist/esm/support/decorators/definition.js +38 -0
  100. package/dist/esm/support/decorators/index.d.ts +39 -0
  101. package/dist/esm/support/decorators/index.js +48 -0
  102. package/dist/esm/support/decorators/pool.d.ts +97 -0
  103. package/dist/esm/support/decorators/pool.js +83 -0
  104. package/dist/esm/support/decorators/types.d.ts +22 -0
  105. package/dist/esm/support/decorators/types.js +1 -0
  106. package/dist/esm/utils/invocation.d.ts +1 -0
  107. package/dist/esm/utils/invocation.js +1 -0
  108. package/package.json +10 -2
  109. package/dist/cjs/utils/debug.js +0 -30
  110. package/dist/esm/utils/debug.d.ts +0 -27
  111. package/dist/esm/utils/debug.js +0 -27
@@ -0,0 +1,211 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.projectWorkflowChain = projectWorkflowChain;
4
+ /** Read-side projection of captured workflow facts; never dispatches or reconstructs topology. */
5
+ const index_js_1 = require("../diagnostics/index.js");
6
+ const workflow_graph_js_1 = require("./workflow-graph.js");
7
+ const lifecycleTypes = new Set(['enter', 'transition', 'complete', 'error']);
8
+ /** Aggregate main visits by span and keep raw subflow records in their own append order. */
9
+ function projectWorkflowChain(workflowId, runId, input) {
10
+ const coverage = structuredClone(input.coverage);
11
+ const issues = [...coverage.issues];
12
+ const issue = (code, spanId, eventId) => issues.push({ code, spanId, eventId });
13
+ const events = input.events
14
+ .map(index_js_1.normalizeDiagnosticEvent)
15
+ .filter((e) => e.runId === runId)
16
+ .sort((a, b) => a.producerSequence - b.producerSequence);
17
+ const visits = new Map();
18
+ const ledgers = new Map();
19
+ const transitions = [];
20
+ const seen = new Map();
21
+ for (const raw of events) {
22
+ const payload = raw.payload && typeof raw.payload === 'object'
23
+ ? raw.payload
24
+ : {};
25
+ const candidate = payload.event;
26
+ const fact = raw.type === 'workflow.event' && candidate && typeof candidate === 'object'
27
+ ? candidate
28
+ : undefined;
29
+ const belongs = fact?.workflowId === workflowId ||
30
+ (raw.nodeRole === 'workflow-node' && raw.definitionId === workflowId);
31
+ if (!belongs || (raw.nodeRole !== undefined && raw.nodeRole !== 'workflow-node'))
32
+ continue;
33
+ if (raw.type === 'workflow.event' && !fact) {
34
+ issue('invalid-workflow-event', raw.spanId, raw.eventId);
35
+ continue;
36
+ }
37
+ const identity = JSON.stringify(raw);
38
+ if (seen.has(raw.eventId)) {
39
+ if (seen.get(raw.eventId) !== identity)
40
+ issue('conflicting-event', raw.spanId, raw.eventId);
41
+ continue;
42
+ }
43
+ seen.set(raw.eventId, identity);
44
+ if (fact &&
45
+ (!lifecycleTypes.has(fact.type) ||
46
+ fact.eventId !== raw.eventId ||
47
+ fact.workflowId !== workflowId ||
48
+ (raw.workflowInvocationId !== undefined &&
49
+ fact.workflowInvocationId !== raw.workflowInvocationId) ||
50
+ [
51
+ 'targetSpanId',
52
+ 'targetNodeId',
53
+ 'routeId',
54
+ 'conditionId',
55
+ 'errorCode',
56
+ 'errorEventId',
57
+ ].some((key) => {
58
+ const value = fact[key];
59
+ return value !== undefined && typeof value !== 'string';
60
+ }) ||
61
+ (fact.route !== undefined && (!Number.isSafeInteger(fact.route) || fact.route < 0)) ||
62
+ (fact.durationMs !== undefined &&
63
+ (!Number.isFinite(fact.durationMs) || fact.durationMs < 0)) ||
64
+ !Number.isSafeInteger(fact.roundIndex) ||
65
+ typeof fact.workflowInvocationId !== 'string' ||
66
+ fact.nodeId !== raw.nodeId ||
67
+ (fact.spanId !== undefined && fact.spanId !== raw.spanId) ||
68
+ (fact.runId !== undefined && fact.runId !== runId))) {
69
+ issue('invalid-workflow-event', raw.spanId, raw.eventId);
70
+ continue;
71
+ }
72
+ if (!raw.nodeId) {
73
+ issue('missing-node-id', raw.spanId, raw.eventId);
74
+ continue;
75
+ }
76
+ let visit = visits.get(raw.spanId);
77
+ if (!visit) {
78
+ visit = {
79
+ spanId: raw.spanId,
80
+ nodeId: raw.nodeId,
81
+ workflowInvocationId: raw.workflowInvocationId ?? fact?.workflowInvocationId,
82
+ definitionRevision: raw.definitionRevision,
83
+ name: raw.nodeName,
84
+ description: raw.nodeDescription,
85
+ kind: raw.nodeKind,
86
+ roundIndex: fact?.roundIndex,
87
+ firstEventSequence: raw.producerSequence,
88
+ lastEventSequence: raw.producerSequence,
89
+ status: 'unknown',
90
+ events: [],
91
+ subflow: [],
92
+ };
93
+ visits.set(raw.spanId, visit);
94
+ ledgers.set(raw.spanId, new Map());
95
+ }
96
+ if (visit.nodeId !== raw.nodeId ||
97
+ visit.workflowInvocationId !== (raw.workflowInvocationId ?? fact?.workflowInvocationId)) {
98
+ issue('conflicting-span', raw.spanId, raw.eventId);
99
+ continue;
100
+ }
101
+ visit.lastEventSequence = raw.producerSequence;
102
+ if (raw.usedModel !== undefined)
103
+ visit.usedModel = raw.usedModel;
104
+ if (raw.subStepsDropped || raw.subStepsTruncated)
105
+ issue('subflow-truncated', raw.spanId, raw.eventId);
106
+ const ledger = ledgers.get(raw.spanId);
107
+ for (const step of raw.subSteps ?? []) {
108
+ const previous = ledger.get(step.stepId);
109
+ if (!previous)
110
+ ledger.set(step.stepId, structuredClone(step));
111
+ else if (JSON.stringify({ ...previous, sourceEventId: undefined, producerSequence: undefined }) !==
112
+ JSON.stringify(step))
113
+ issue('conflicting-subflow', raw.spanId, raw.eventId);
114
+ }
115
+ const progress = payload.subStep;
116
+ if (raw.type === 'node.progress' && progress) {
117
+ const record = progress.stepId ? ledger.get(progress.stepId) : undefined;
118
+ if (record) {
119
+ ledger.set(record.stepId, {
120
+ ...record,
121
+ sourceEventId: raw.eventId,
122
+ producerSequence: raw.producerSequence,
123
+ });
124
+ }
125
+ else if (typeof progress.stepId === 'string' && Number.isSafeInteger(progress.sequence)) {
126
+ ledger.set(progress.stepId, {
127
+ ...structuredClone(progress),
128
+ origin: 'reported',
129
+ sourceEventId: raw.eventId,
130
+ producerSequence: raw.producerSequence,
131
+ });
132
+ }
133
+ else
134
+ issue('invalid-subflow-progress', raw.spanId, raw.eventId);
135
+ continue;
136
+ }
137
+ if (fact || raw.type === 'node.progress') {
138
+ visit.events.push({
139
+ eventId: raw.eventId,
140
+ producerSequence: raw.producerSequence,
141
+ occurredAtMs: raw.occurredAtMs,
142
+ type: fact?.type ?? 'progress',
143
+ phase: fact?.phase,
144
+ routeId: fact?.routeId,
145
+ conditionId: fact?.conditionId,
146
+ errorCode: fact?.errorCode,
147
+ errorEventId: fact?.errorEventId,
148
+ payload: payload.payload,
149
+ });
150
+ }
151
+ if (!fact)
152
+ continue;
153
+ visit.roundIndex ??= fact.roundIndex;
154
+ if (fact.type === 'enter') {
155
+ visit.enteredAtMs = raw.occurredAtMs;
156
+ visit.status = 'running';
157
+ }
158
+ if (fact.type === 'error')
159
+ visit.status =
160
+ fact.errorCode === 'CANCELLED' || fact.errorCode === 'TIMEOUT' ? 'cancelled' : 'error';
161
+ if (fact.type !== 'enter') {
162
+ visit.endedAtMs = raw.occurredAtMs;
163
+ visit.durationMs = fact.durationMs;
164
+ if (fact.type !== 'error' && visit.status !== 'error' && visit.status !== 'cancelled')
165
+ visit.status = 'success';
166
+ }
167
+ if (fact.type === 'transition' ||
168
+ fact.type === 'complete' ||
169
+ (fact.type === 'error' && fact.targetNodeId !== undefined))
170
+ transitions.push({
171
+ eventId: raw.eventId,
172
+ producerSequence: raw.producerSequence,
173
+ occurredAtMs: raw.occurredAtMs,
174
+ type: fact.type === 'complete' ? 'complete' : 'transition',
175
+ sourceSpanId: raw.spanId,
176
+ targetSpanId: fact.targetSpanId,
177
+ targetNodeId: fact.targetNodeId,
178
+ routeOrder: fact.route,
179
+ routeId: fact.routeId,
180
+ conditionId: fact.conditionId,
181
+ errorEventId: fact.type === 'error' ? raw.eventId : fact.errorEventId,
182
+ });
183
+ }
184
+ if (!visits.size)
185
+ throw new RangeError(`No captured workflow visits: ${workflowId} / ${runId}`);
186
+ for (const visit of visits.values()) {
187
+ visit.subflow = [...ledgers.get(visit.spanId).values()].sort((a, b) => a.sequence - b.sequence);
188
+ if (visit.enteredAtMs === undefined)
189
+ issue('missing-enter', visit.spanId);
190
+ if (visit.endedAtMs === undefined && coverage.complete === true) {
191
+ visit.status = 'unknown';
192
+ issue('missing-exit', visit.spanId);
193
+ }
194
+ for (const step of visit.subflow)
195
+ if (step.operation === undefined ||
196
+ step.revision === undefined ||
197
+ step.createdAtMs === undefined)
198
+ issue('historical-subflow-fields', visit.spanId);
199
+ }
200
+ for (const edge of transitions)
201
+ if (edge.targetSpanId && !visits.has(edge.targetSpanId))
202
+ issue('missing-target-enter', edge.targetSpanId, edge.eventId);
203
+ return (0, workflow_graph_js_1.freezeView)({
204
+ schemaVersion: 1,
205
+ workflowId,
206
+ runId,
207
+ nodes: [...visits.values()],
208
+ transitions,
209
+ coverage: { ...coverage, complete: issues.length ? false : coverage.complete, issues },
210
+ });
211
+ }
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.projectWorkflowGraph = projectWorkflowGraph;
4
+ exports.freezeView = freezeView;
5
+ /** Edge identity depends on topology, never on a route label or callback. */
6
+ function projectWorkflowGraph(workflowId, entryNodeId, revision, definitions) {
7
+ const edges = new Map();
8
+ const nodes = definitions.map((node, order) => {
9
+ const edgeFor = (kind, targetNodeId) => {
10
+ const id = JSON.stringify([node.id, kind, targetNodeId ?? null]);
11
+ if (!edges.has(id))
12
+ edges.set(id, {
13
+ id,
14
+ kind,
15
+ sourceNodeId: node.id,
16
+ targetNodeId,
17
+ routeOrders: [],
18
+ ...(kind === 'error' ? { errorRouteId: node.onErrorRouteId } : {}),
19
+ });
20
+ return edges.get(id);
21
+ };
22
+ const routes = node.routes.map((route, order) => {
23
+ const edge = edgeFor(route.complete ? 'complete' : 'transition', route.targetNodeId);
24
+ edge.routeOrders.push(order);
25
+ return {
26
+ order,
27
+ routeId: route.id,
28
+ description: route.description,
29
+ conditional: !!route.when,
30
+ conditionId: route.conditionId,
31
+ edgeId: edge.id,
32
+ };
33
+ });
34
+ if (node.onErrorNodeId)
35
+ edgeFor('error', node.onErrorNodeId);
36
+ return {
37
+ id: node.id,
38
+ name: node.name ?? node.id,
39
+ description: node.description,
40
+ order,
41
+ startsRound: !!node.startsRound,
42
+ requires: [...(node.requires ?? [])],
43
+ provides: [...(node.provides ?? [])],
44
+ routes,
45
+ incomingEdgeIds: [],
46
+ outgoingEdgeIds: [],
47
+ };
48
+ });
49
+ const byId = new Map(nodes.map((node) => [node.id, node]));
50
+ for (const edge of edges.values()) {
51
+ byId.get(edge.sourceNodeId).outgoingEdgeIds.push(edge.id);
52
+ if (edge.targetNodeId)
53
+ byId.get(edge.targetNodeId).incomingEdgeIds.push(edge.id);
54
+ }
55
+ return freezeView({
56
+ schemaVersion: 1,
57
+ workflowId,
58
+ revision,
59
+ entryNodeId,
60
+ nodes,
61
+ edges: [...edges.values()],
62
+ });
63
+ }
64
+ /** Freeze detached JSON data recursively so callers cannot alter another consumer's view. */
65
+ function freezeView(value) {
66
+ if (value && typeof value === 'object') {
67
+ for (const child of Object.values(value))
68
+ freezeView(child);
69
+ Object.freeze(value);
70
+ }
71
+ return value;
72
+ }
@@ -2,6 +2,19 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.WorkflowContractError = exports.route = exports.requireWorkflowValue = exports.AttemptBudget = exports.AgentWorkflow = void 0;
4
4
  exports.defineWorkflow = defineWorkflow;
5
+ /**
6
+ * Validated workflow catalog and round accounting.
7
+ *
8
+ * A workflow is declared as data (nodes plus routes) and validated before any handler
9
+ * runs: references must resolve, every node must be reachable from the entry, and every
10
+ * cycle must cross a round boundary so the engine's budget stays authoritative. This
11
+ * module owns the catalog; `engine.ts` owns traversal and execution.
12
+ *
13
+ * Exports: `AgentWorkflow`, `defineWorkflow`, `AttemptBudget`, the re-exported
14
+ * `requireWorkflowValue` and `route`, and the workflow contract types.
15
+ */
16
+ const workflow_graph_js_1 = require("./workflow-graph.js");
17
+ const workflow_chain_js_1 = require("./workflow-chain.js");
5
18
  /**
6
19
  * Derive unique route ids for one node.
7
20
  *
@@ -55,6 +68,7 @@ class AgentWorkflow {
55
68
  revision;
56
69
  /** Optional result guard; when present it must return exactly true. */
57
70
  isResult;
71
+ chainSource;
58
72
  nodes = new Map();
59
73
  /**
60
74
  * Build and validate a workflow.
@@ -72,6 +86,7 @@ class AgentWorkflow {
72
86
  if (!definition.id?.trim() || !entry?.trim() || !definition.nodes.length) {
73
87
  throw new TypeError('Invalid workflow');
74
88
  }
89
+ this.chainSource = definition.chainSource?.bind(definition);
75
90
  this.id = definition.id;
76
91
  this.entryNodeId = entry;
77
92
  this.revision = definition.revision;
@@ -108,6 +123,7 @@ class AgentWorkflow {
108
123
  return new AgentWorkflow({
109
124
  id: this.id,
110
125
  revision: patch.revision,
126
+ chainSource: this.chainSource,
111
127
  entryNodeId: this.entryNodeId,
112
128
  isResult: this.isResult,
113
129
  nodes: nodes.map((node) => ({
@@ -125,6 +141,18 @@ class AgentWorkflow {
125
141
  })),
126
142
  });
127
143
  }
144
+ /** Export topology from the registered snapshot without executing callbacks. */
145
+ toGraph() {
146
+ return (0, workflow_graph_js_1.projectWorkflowGraph)(this.id, this.entryNodeId, this.revision, this.definitions);
147
+ }
148
+ /** Read an authorized run through the bound App source, or an explicit per-call source. */
149
+ async toChain(runId, source = this.chainSource) {
150
+ if (!runId.trim())
151
+ throw new TypeError('A runId is required');
152
+ if (!source)
153
+ throw new TypeError('Workflow chainSource is not configured');
154
+ return (0, workflow_chain_js_1.projectWorkflowChain)(this.id, runId, await source(runId));
155
+ }
128
156
  /**
129
157
  * Look up a node that must exist.
130
158
  *
@@ -207,25 +235,26 @@ class AgentWorkflow {
207
235
  * @throws TypeError When the node, a route or a state contract is invalid.
208
236
  */
209
237
  register(node) {
238
+ const declaredRoutes = node.routes;
210
239
  if (!node.id?.trim() ||
211
240
  !node.description?.trim() ||
212
241
  this.nodes.has(node.id) ||
213
- !node.routes.length ||
242
+ !declaredRoutes.length ||
214
243
  (node.run !== undefined && typeof node.run !== 'function')) {
215
244
  throw new TypeError('Invalid or duplicate workflow node');
216
245
  }
217
246
  validateNodeFields(node);
218
247
  if (node.onErrorRouteId !== undefined && (!node.onErrorRouteId.trim() || !node.onErrorNodeId))
219
248
  throw new TypeError(`Invalid workflow error route identity: ${node.id}`);
220
- const ids = assignRouteIds(node.routes);
221
- for (const [index, route] of node.routes.entries()) {
249
+ const ids = assignRouteIds(declaredRoutes);
250
+ for (const [index, route] of declaredRoutes.entries()) {
222
251
  if ('to' in route ||
223
252
  (route.id !== undefined && !route.id.trim()) ||
224
253
  (route.conditionId !== undefined && (!route.conditionId.trim() || !route.when)))
225
254
  throw new TypeError(`Invalid workflow route identity: ${node.id}`);
226
255
  const hasTarget = typeof route.targetNodeId === 'string';
227
256
  const hasCompletion = typeof route.complete === 'function';
228
- const shadowsFollowingRoutes = !route.when && index !== node.routes.length - 1;
257
+ const shadowsFollowingRoutes = !route.when && index !== declaredRoutes.length - 1;
229
258
  if (hasTarget === hasCompletion ||
230
259
  shadowsFollowingRoutes ||
231
260
  (route.when !== undefined && typeof route.when !== 'function')) {
@@ -239,7 +268,7 @@ class AgentWorkflow {
239
268
  throw new TypeError(`Invalid workflow state contract: ${node.id}`);
240
269
  }
241
270
  }
242
- const routes = Object.freeze(node.routes.map((route, index) => {
271
+ const routes = Object.freeze(declaredRoutes.map((route, index) => {
243
272
  const id = ids[index];
244
273
  const conditionId = route.conditionId?.replace(/\{(nid|rid)\}/g, (_, key) => {
245
274
  if (key === 'nid')
@@ -262,7 +291,6 @@ class AgentWorkflow {
262
291
  // only when it declares it explicitly.
263
292
  const startsRound = node.startsRound ?? (node.id === this.entryNodeId ? true : undefined);
264
293
  this.nodes.set(node.id, Object.freeze({
265
- ...node,
266
294
  id: node.id,
267
295
  description: node.description,
268
296
  name: node.name,
@@ -11,6 +11,7 @@ exports.defineQueryAggregation = defineQueryAggregation;
11
11
  *
12
12
  * Exports: `mergeByKey`, `defineQueryAggregation`, `Aggregation`.
13
13
  */
14
+ const native_sub_flow_js_1 = require("../diagnostics/native-sub-flow.js");
14
15
  const synchronous_js_1 = require("../utils/synchronous.js");
15
16
  /**
16
17
  * Merge duplicates without moving their first position; selection policy remains host-owned.
@@ -56,21 +57,23 @@ function defineQueryAggregation(options) {
56
57
  * @param context - Host context forwarded unchanged to `execute`, `key` and `merge`.
57
58
  * @returns The merged items and their count.
58
59
  */
59
- collect(inputs, context) {
60
- const rows = [], merged = new Map();
61
- for (const input of inputs)
62
- for (const item of (0, synchronous_js_1.synchronous)(execute(input, context), 'Query aggregation must be synchronous')) {
63
- if (!keyFor) {
64
- rows.push(item);
65
- continue;
60
+ collect(inputs, context, execution = {}) {
61
+ return (0, native_sub_flow_js_1.nativeSubFlowSync)(execution, 'aggregation', 'query-aggregation', () => {
62
+ const rows = [], merged = new Map();
63
+ for (const input of inputs)
64
+ for (const item of (0, synchronous_js_1.synchronous)(execute(input, context), 'Query aggregation must be synchronous')) {
65
+ if (!keyFor) {
66
+ rows.push(item);
67
+ continue;
68
+ }
69
+ const key = (0, synchronous_js_1.synchronous)(keyFor(item, input, context), 'Aggregation keys must be synchronous');
70
+ merged.set(key, merge
71
+ ? (0, synchronous_js_1.synchronous)(merge(item, merged.get(key), input, context), 'Aggregation merge must be synchronous')
72
+ : item);
66
73
  }
67
- const key = (0, synchronous_js_1.synchronous)(keyFor(item, input, context), 'Aggregation keys must be synchronous');
68
- merged.set(key, merge
69
- ? (0, synchronous_js_1.synchronous)(merge(item, merged.get(key), input, context), 'Aggregation merge must be synchronous')
70
- : item);
71
- }
72
- const items = keyFor ? [...merged.values()] : rows;
73
- return { total: items.length, items };
74
+ const items = keyFor ? [...merged.values()] : rows;
75
+ return { total: items.length, items };
76
+ });
74
77
  },
75
78
  });
76
79
  }
@@ -9,6 +9,7 @@ exports.defineAnswerCheckCatalog = defineAnswerCheckCatalog;
9
9
  *
10
10
  * Exports: `defineAnswerCheckCatalog`, and the answer checker types.
11
11
  */
12
+ const native_sub_flow_js_1 = require("../../diagnostics/native-sub-flow.js");
12
13
  const check_result_js_1 = require("./check-result.js");
13
14
  const catalog_js_1 = require("../../utils/catalog.js");
14
15
  const synchronous_js_1 = require("../../utils/synchronous.js");
@@ -40,7 +41,7 @@ function defineAnswerCheckCatalog(definitions) {
40
41
  throw new TypeError('Duplicate selected check id');
41
42
  for (const id of request.checkIds) {
42
43
  context.signal.throwIfAborted();
43
- const result = (0, check_result_js_1.snapshotAnswerCheck)((0, synchronous_js_1.synchronous)(checks.require(id).check(value, context), 'Answer checkers must be synchronous'), id);
44
+ const result = (0, native_sub_flow_js_1.nativeSubFlowSync)(context, 'check', id, () => (0, check_result_js_1.snapshotAnswerCheck)((0, synchronous_js_1.synchronous)(checks.require(id).check(value, context), 'Answer checkers must be synchronous'), id));
44
45
  context.signal.throwIfAborted();
45
46
  if (Object.hasOwn(result, 'value'))
46
47
  value = result.value;
@@ -10,6 +10,7 @@ exports.defineAnswerFlow = defineAnswerFlow;
10
10
  *
11
11
  * Exports: `defineAnswerFlow`, and the answer flow/item/result/check types.
12
12
  */
13
+ const native_sub_flow_js_1 = require("../../diagnostics/native-sub-flow.js");
13
14
  const synchronous_js_1 = require("../../utils/synchronous.js");
14
15
  const cancellation_js_1 = require("../../utils/cancellation.js");
15
16
  const engine_js_1 = require("../../execution/engine.js");
@@ -282,27 +283,29 @@ function defineAnswerFlow(definition) {
282
283
  * exhausted without a final result.
283
284
  */
284
285
  async run(request, context) {
285
- context.signal.throwIfAborted();
286
- (0, batch_js_1.validateAnswerBatch)(request.inputIds, request.items, maxItems);
287
- const cells = request.items.map((item) => ({
288
- item: Object.freeze({
289
- id: item.id,
290
- data: item.data,
291
- sourceStatus: item.sourceStatus,
292
- inputIds: Object.freeze([...item.inputIds]),
293
- }),
294
- hasValue: false,
295
- local: { status: 'not-run', issues: [] },
296
- review: { status: 'not-run', issues: [] },
297
- knownReviewFailure: false,
298
- }));
299
- return new engine_js_1.AgentCoreEngine({
300
- maxRounds: maxRepairs + 1,
301
- errors: { exhausted: () => new TypeError('Answer flow exhausted') },
302
- execution: { signal: context.signal, diagnostics: context.diagnostics },
303
- })
304
- .bindWorkflow(workflow, context.app)
305
- .run({ cells, pending: [], repairBudget: new workflow_js_1.AttemptBudget(maxRepairs) });
286
+ return (0, native_sub_flow_js_1.nativeSubFlowAsync)(context, 'answer', 'answer-flow', async () => {
287
+ context.signal.throwIfAborted();
288
+ (0, batch_js_1.validateAnswerBatch)(request.inputIds, request.items, maxItems);
289
+ const cells = request.items.map((item) => ({
290
+ item: Object.freeze({
291
+ id: item.id,
292
+ data: item.data,
293
+ sourceStatus: item.sourceStatus,
294
+ inputIds: Object.freeze([...item.inputIds]),
295
+ }),
296
+ hasValue: false,
297
+ local: { status: 'not-run', issues: [] },
298
+ review: { status: 'not-run', issues: [] },
299
+ knownReviewFailure: false,
300
+ }));
301
+ return new engine_js_1.AgentCoreEngine({
302
+ maxRounds: maxRepairs + 1,
303
+ errors: { exhausted: () => new TypeError('Answer flow exhausted') },
304
+ execution: { signal: context.signal, diagnostics: context.diagnostics },
305
+ })
306
+ .bindWorkflow(workflow, context.app)
307
+ .run({ cells, pending: [], repairBudget: new workflow_js_1.AttemptBudget(maxRepairs) });
308
+ });
306
309
  },
307
310
  });
308
311
  }
@@ -11,6 +11,7 @@ exports.defineAnswerTaskCatalog = defineAnswerTaskCatalog;
11
11
  *
12
12
  * Exports: `defineAnswerReaderCatalog`, `defineAnswerTaskCatalog`, and the task/reader types.
13
13
  */
14
+ const native_sub_flow_js_1 = require("../../diagnostics/native-sub-flow.js");
14
15
  const catalog_js_1 = require("../../utils/catalog.js");
15
16
  const synchronous_js_1 = require("../../utils/synchronous.js");
16
17
  const object_js_1 = require("../../utils/object.js");
@@ -100,16 +101,18 @@ function defineAnswerTaskCatalog(definitions, ports) {
100
101
  * @throws TypeError For an unknown task, a parse failure, or a reader returning a Promise.
101
102
  */
102
103
  read(request, context) {
103
- const { input, task, reader } = prepare(request, context);
104
- const value = (0, synchronous_js_1.synchronous)(reader.read(input, {
105
- ...context,
106
- app: context.app,
107
- signal: context.signal,
108
- diagnostics: context.diagnostics,
109
- task,
110
- }), 'Synchronous answer readers must not return a Promise');
111
- context.signal.throwIfAborted();
112
- return value;
104
+ return (0, native_sub_flow_js_1.nativeSubFlowSync)(context, 'task', request.taskId, () => {
105
+ const { input, task, reader } = prepare(request, context);
106
+ const value = (0, synchronous_js_1.synchronous)(reader.read(input, {
107
+ ...context,
108
+ app: context.app,
109
+ signal: context.signal,
110
+ diagnostics: context.diagnostics,
111
+ task,
112
+ }), 'Synchronous answer readers must not return a Promise');
113
+ context.signal.throwIfAborted();
114
+ return value;
115
+ });
113
116
  },
114
117
  /**
115
118
  * Read evidence through a task's registered reader, awaiting an async reader.
@@ -117,16 +120,18 @@ function defineAnswerTaskCatalog(definitions, ports) {
117
120
  * @throws TypeError For an unknown task or a parse failure; aborts with the caller's signal.
118
121
  */
119
122
  async readAsync(request, context) {
120
- const { input, task, reader } = prepare(request, context);
121
- const value = await (0, cancellation_js_1.awaitWithSignal)(reader.read(input, {
122
- ...context,
123
- app: context.app,
124
- signal: context.signal,
125
- diagnostics: context.diagnostics,
126
- task,
127
- }), context.signal);
128
- context.signal.throwIfAborted();
129
- return value;
123
+ return (0, native_sub_flow_js_1.nativeSubFlowAsync)(context, 'task', request.taskId, async () => {
124
+ const { input, task, reader } = prepare(request, context);
125
+ const value = await (0, cancellation_js_1.awaitWithSignal)(reader.read(input, {
126
+ ...context,
127
+ app: context.app,
128
+ signal: context.signal,
129
+ diagnostics: context.diagnostics,
130
+ task,
131
+ }), context.signal);
132
+ context.signal.throwIfAborted();
133
+ return value;
134
+ });
130
135
  },
131
136
  });
132
137
  }