@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.
- package/CHANGELOG.md +44 -0
- package/README.md +386 -50
- package/development/docs/api/README.en.md +4 -0
- package/development/docs/api/README.md +4 -0
- package/development/docs/api/decorators.en.md +83 -0
- package/development/docs/api/decorators.md +108 -0
- package/development/docs/api/observability.en.md +37 -11
- package/development/docs/api/observability.md +64 -11
- package/development/docs/api/reference.en.md +469 -348
- package/development/docs/api/reference.md +225 -137
- package/development/docs/api/types.en.md +1 -0
- package/development/docs/api/types.md +1 -0
- package/development/docs/api/workflow-views.en.md +64 -0
- package/development/docs/api/workflow-views.md +78 -0
- package/development/docs/api/workflow.en.md +6 -2
- package/development/docs/api/workflow.md +6 -2
- package/development/skills/agent-core-app/SKILL.md +16 -0
- package/dist/cjs/capabilities/invocation.js +21 -1
- package/dist/cjs/capabilities/tool-runtime.js +8 -1
- package/dist/cjs/capabilities/tools.js +12 -17
- package/dist/cjs/diagnostics/index.js +51 -3
- package/dist/cjs/diagnostics/native-sub-flow.js +106 -0
- package/dist/cjs/diagnostics/sub-flow.js +278 -32
- package/dist/cjs/execution/engine.js +3 -2
- package/dist/cjs/execution/events.js +5 -5
- package/dist/cjs/execution/reporting.js +18 -28
- package/dist/cjs/execution/workflow-chain.js +211 -0
- package/dist/cjs/execution/workflow-graph.js +72 -0
- package/dist/cjs/execution/workflow.js +34 -6
- package/dist/cjs/kits/aggregation.js +17 -14
- package/dist/cjs/kits/answering/checks.js +2 -1
- package/dist/cjs/kits/answering/flow.js +24 -21
- package/dist/cjs/kits/answering/tasks.js +25 -20
- package/dist/cjs/kits/delivery.js +36 -24
- package/dist/cjs/kits/knowledge/catalog.js +33 -30
- package/dist/cjs/kits/policies.js +3 -2
- package/dist/cjs/kits/presentation/outputs.js +2 -1
- package/dist/cjs/kits/retrieval.js +7 -1
- package/dist/cjs/kits/tools.js +1 -0
- package/dist/cjs/model/decision-retry.js +2 -1
- package/dist/cjs/model/runner.js +20 -5
- package/dist/cjs/structs/execution/workflow-views.js +2 -0
- package/dist/cjs/support/context/nodes.js +48 -45
- package/dist/cjs/support/context/processing.js +6 -3
- package/dist/cjs/support/decorators/definition.js +42 -0
- package/dist/cjs/support/decorators/index.js +63 -0
- package/dist/cjs/support/decorators/pool.js +87 -0
- package/dist/cjs/support/decorators/types.js +2 -0
- package/dist/cjs/utils/invocation.js +1 -0
- package/dist/esm/capabilities/invocation.js +21 -1
- package/dist/esm/capabilities/tool-runtime.js +8 -1
- package/dist/esm/capabilities/tools.js +12 -17
- package/dist/esm/diagnostics/index.d.ts +1 -0
- package/dist/esm/diagnostics/index.js +48 -2
- package/dist/esm/diagnostics/native-sub-flow.d.ts +94 -0
- package/dist/esm/diagnostics/native-sub-flow.js +101 -0
- package/dist/esm/diagnostics/sub-flow.d.ts +56 -4
- package/dist/esm/diagnostics/sub-flow.js +271 -32
- package/dist/esm/execution/engine.d.ts +1 -0
- package/dist/esm/execution/engine.js +3 -2
- package/dist/esm/execution/events.js +5 -5
- package/dist/esm/execution/reporting.d.ts +6 -12
- package/dist/esm/execution/reporting.js +19 -29
- package/dist/esm/execution/workflow-chain.d.ts +3 -0
- package/dist/esm/execution/workflow-chain.js +208 -0
- package/dist/esm/execution/workflow-graph.d.ts +7 -0
- package/dist/esm/execution/workflow-graph.js +68 -0
- package/dist/esm/execution/workflow.d.ts +6 -0
- package/dist/esm/execution/workflow.js +34 -6
- package/dist/esm/kits/aggregation.d.ts +1 -1
- package/dist/esm/kits/aggregation.js +17 -14
- package/dist/esm/kits/answering/checks.js +2 -1
- package/dist/esm/kits/answering/flow.js +24 -21
- package/dist/esm/kits/answering/tasks.js +25 -20
- package/dist/esm/kits/delivery.d.ts +0 -9
- package/dist/esm/kits/delivery.js +36 -24
- package/dist/esm/kits/knowledge/catalog.js +33 -30
- package/dist/esm/kits/policies.d.ts +1 -1
- package/dist/esm/kits/policies.js +3 -2
- package/dist/esm/kits/presentation/outputs.js +2 -1
- package/dist/esm/kits/retrieval.js +7 -1
- package/dist/esm/kits/tools.js +1 -0
- package/dist/esm/model/decision-retry.d.ts +2 -0
- package/dist/esm/model/decision-retry.js +2 -1
- package/dist/esm/model/runner.d.ts +2 -0
- package/dist/esm/model/runner.js +20 -5
- package/dist/esm/structs/capabilities/invocation.d.ts +2 -0
- package/dist/esm/structs/capabilities/tools.d.ts +7 -3
- package/dist/esm/structs/common.d.ts +2 -0
- package/dist/esm/structs/debug.d.ts +36 -20
- package/dist/esm/structs/execution/workflow-views.d.ts +123 -0
- package/dist/esm/structs/execution/workflow-views.js +1 -0
- package/dist/esm/structs/execution/workflow.d.ts +4 -3
- package/dist/esm/structs/index.d.ts +1 -0
- package/dist/esm/structs/model/runtime.d.ts +2 -0
- package/dist/esm/support/context/nodes.js +48 -45
- package/dist/esm/support/context/processing.js +6 -3
- package/dist/esm/support/decorators/definition.d.ts +19 -0
- package/dist/esm/support/decorators/definition.js +38 -0
- package/dist/esm/support/decorators/index.d.ts +39 -0
- package/dist/esm/support/decorators/index.js +48 -0
- package/dist/esm/support/decorators/pool.d.ts +97 -0
- package/dist/esm/support/decorators/pool.js +83 -0
- package/dist/esm/support/decorators/types.d.ts +22 -0
- package/dist/esm/support/decorators/types.js +1 -0
- package/dist/esm/utils/invocation.d.ts +1 -0
- package/dist/esm/utils/invocation.js +1 -0
- package/package.json +10 -2
- package/dist/cjs/utils/debug.js +0 -30
- package/dist/esm/utils/debug.d.ts +0 -27
- package/dist/esm/utils/debug.js +0 -27
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ScopedSubFlowLogger = exports.SubFlowLogger = void 0;
|
|
4
|
+
exports.captureSubFlowSnapshot = captureSubFlowSnapshot;
|
|
3
5
|
exports.payloadSnapshot = payloadSnapshot;
|
|
6
|
+
exports.requireSubFlowLogger = requireSubFlowLogger;
|
|
7
|
+
exports.bindNativeSubFlowLogger = bindNativeSubFlowLogger;
|
|
4
8
|
exports.createSubFlowLogger = createSubFlowLogger;
|
|
5
9
|
exports.readSubFlowLogger = readSubFlowLogger;
|
|
10
|
+
exports.detachSubFlowLogger = detachSubFlowLogger;
|
|
6
11
|
/**
|
|
7
12
|
* Bounded, self-reported sub-flow logging.
|
|
8
13
|
*
|
|
@@ -13,7 +18,7 @@ exports.readSubFlowLogger = readSubFlowLogger;
|
|
|
13
18
|
* Issued loggers are tracked in a module-private registry, so only the execution that created
|
|
14
19
|
* one can read it. A lookalike object with a `readAll` method is never accepted.
|
|
15
20
|
*
|
|
16
|
-
* Exports: `payloadSnapshot`, `createSubFlowLogger`, `readSubFlowLogger`.
|
|
21
|
+
* Exports: `payloadSnapshot`, `createSubFlowLogger`, `readSubFlowLogger`, `captureSubFlowSnapshot`.
|
|
17
22
|
*/
|
|
18
23
|
const rules_js_1 = require("../utils/rules.js");
|
|
19
24
|
const invocation_js_1 = require("../utils/invocation.js");
|
|
@@ -23,12 +28,29 @@ const debug_js_1 = require("../structs/debug.js");
|
|
|
23
28
|
* private so a structurally identical object cannot impersonate a real logger.
|
|
24
29
|
*/
|
|
25
30
|
const instances = new WeakMap();
|
|
31
|
+
/** Only recorder-issued snapshots may enter a diagnostic fact, within their original span. */
|
|
32
|
+
const snapshots = new WeakMap();
|
|
33
|
+
/** Validate a committed snapshot without consulting its logger or invoking App code. */
|
|
34
|
+
function captureSubFlowSnapshot(steps, identity) {
|
|
35
|
+
const empty = { subSteps: Object.freeze([]), subStepsDropped: 0, subStepsTruncated: false };
|
|
36
|
+
if (steps === undefined)
|
|
37
|
+
return empty;
|
|
38
|
+
const scope = snapshots.get(steps);
|
|
39
|
+
if (!scope ||
|
|
40
|
+
(identity &&
|
|
41
|
+
(scope.identity?.runId !== identity.runId || scope.identity?.spanId !== identity.spanId)))
|
|
42
|
+
return { ...empty, subStepsDropped: 1 };
|
|
43
|
+
return { ...empty, subSteps: steps };
|
|
44
|
+
}
|
|
26
45
|
/** Fields accepted by `write`; anything else is rejected so a declaration cannot smuggle state. */
|
|
27
46
|
const writeFields = [
|
|
47
|
+
'type',
|
|
48
|
+
'subnodeId',
|
|
28
49
|
'name',
|
|
29
50
|
'kind',
|
|
30
51
|
'parentStepId',
|
|
31
52
|
'relatedDefinitionId',
|
|
53
|
+
'relatedInvocationId',
|
|
32
54
|
'status',
|
|
33
55
|
'usedModel',
|
|
34
56
|
'startedAtMs',
|
|
@@ -50,10 +72,13 @@ const fixFields = [
|
|
|
50
72
|
];
|
|
51
73
|
/** Per-field character caps, bounding what one reported step can add to a persisted record. */
|
|
52
74
|
const textLimits = {
|
|
75
|
+
type: 256,
|
|
76
|
+
subnodeId: 1024,
|
|
53
77
|
name: 256,
|
|
54
78
|
kind: 128,
|
|
55
79
|
parentStepId: 256,
|
|
56
80
|
relatedDefinitionId: 256,
|
|
81
|
+
relatedInvocationId: 256,
|
|
57
82
|
caseId: 256,
|
|
58
83
|
summary: 1024,
|
|
59
84
|
};
|
|
@@ -175,6 +200,126 @@ function fill(previous, patch) {
|
|
|
175
200
|
}
|
|
176
201
|
return Object.freeze(result);
|
|
177
202
|
}
|
|
203
|
+
/** Stable diagnostic reasons; untrusted error text and caller payloads never reach the console. */
|
|
204
|
+
const rejectionReasons = {
|
|
205
|
+
inactive: 'logger is detached or execution is cancelled',
|
|
206
|
+
scope: 'target does not belong to this subnode',
|
|
207
|
+
target: 'original write record was not found',
|
|
208
|
+
fields: 'input contains a forbidden field',
|
|
209
|
+
transition: 'status transition is not allowed',
|
|
210
|
+
sealed: 'an established fact cannot be overwritten',
|
|
211
|
+
limit: 'recording limit exceeded',
|
|
212
|
+
input: 'invalid input or result patch',
|
|
213
|
+
};
|
|
214
|
+
const rejectionMessages = {
|
|
215
|
+
'Unknown sub-flow field': 'fields',
|
|
216
|
+
'Unknown sub-flow step': 'target',
|
|
217
|
+
'Invalid sub-flow status transition': 'transition',
|
|
218
|
+
'Terminal sub-flow status is sealed': 'transition',
|
|
219
|
+
'Terminal sub-flow model use is sealed': 'sealed',
|
|
220
|
+
'Cannot undo reported model use': 'sealed',
|
|
221
|
+
'Cannot overwrite a sub-flow time': 'sealed',
|
|
222
|
+
'Cannot overwrite sub-flow successors': 'sealed',
|
|
223
|
+
'Cannot overwrite a sub-flow summary': 'sealed',
|
|
224
|
+
'Sub-flow log limit exceeded': 'limit',
|
|
225
|
+
};
|
|
226
|
+
/** Map only known own data messages; arbitrary thrown values are described generically. */
|
|
227
|
+
function rejectionReason(error) {
|
|
228
|
+
try {
|
|
229
|
+
const message = error instanceof Error ? Object.getOwnPropertyDescriptor(error, 'message')?.value : undefined;
|
|
230
|
+
return typeof message === 'string' && Object.hasOwn(rejectionMessages, message)
|
|
231
|
+
? rejectionMessages[message]
|
|
232
|
+
: 'input';
|
|
233
|
+
}
|
|
234
|
+
catch {
|
|
235
|
+
return 'input';
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
/** Unexported issuance token prevents constructing counterfeit full or scoped loggers. */
|
|
239
|
+
const loggerToken = Symbol('sub-flow-logger');
|
|
240
|
+
const stores = new WeakMap();
|
|
241
|
+
/** Full execution-owned recorder. Owns its store and may issue restricted delegates. */
|
|
242
|
+
class SubFlowLogger {
|
|
243
|
+
#store;
|
|
244
|
+
constructor(token, store) {
|
|
245
|
+
if (token !== loggerToken)
|
|
246
|
+
throw new TypeError('Use context.createSubFlowLogger()');
|
|
247
|
+
this.#store = store;
|
|
248
|
+
stores.set(this, store);
|
|
249
|
+
Object.freeze(this);
|
|
250
|
+
}
|
|
251
|
+
write(input) {
|
|
252
|
+
return this.#store.write(input);
|
|
253
|
+
}
|
|
254
|
+
fix(stepId, patch) {
|
|
255
|
+
return this.#store.fix(stepId, patch);
|
|
256
|
+
}
|
|
257
|
+
readAll() {
|
|
258
|
+
return this.#store.readAll();
|
|
259
|
+
}
|
|
260
|
+
/** Issue a delegate for an App-owned logical group; no execution or scheduling is implied. */
|
|
261
|
+
fork(subnodeId) {
|
|
262
|
+
if (typeof subnodeId !== 'string' || !subnodeId.trim() || subnodeId.length > 1024)
|
|
263
|
+
throw new TypeError('Invalid subnode id');
|
|
264
|
+
return new ScopedSubFlowLogger(loggerToken, this, subnodeId);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
exports.SubFlowLogger = SubFlowLogger;
|
|
268
|
+
/** Non-inheriting delegate. Stores only the original logger and its bound subnode identity. */
|
|
269
|
+
class ScopedSubFlowLogger {
|
|
270
|
+
#logger;
|
|
271
|
+
#subnodeId;
|
|
272
|
+
constructor(token, logger, subnodeId) {
|
|
273
|
+
if (token !== loggerToken)
|
|
274
|
+
throw new TypeError('Use logger.fork(subnodeId)');
|
|
275
|
+
this.#logger = logger;
|
|
276
|
+
this.#subnodeId = subnodeId;
|
|
277
|
+
Object.freeze(this);
|
|
278
|
+
}
|
|
279
|
+
write(input) {
|
|
280
|
+
if (!stores.get(this.#logger).isActive()) {
|
|
281
|
+
stores.get(this.#logger).warn('write', 'inactive');
|
|
282
|
+
return -1;
|
|
283
|
+
}
|
|
284
|
+
try {
|
|
285
|
+
fields(input, writeFields.filter((field) => field !== 'subnodeId'));
|
|
286
|
+
// Copy descriptors so private content getters remain subject to the original policy.
|
|
287
|
+
const bound = Object.defineProperties({}, Object.getOwnPropertyDescriptors(input));
|
|
288
|
+
Object.defineProperty(bound, 'subnodeId', { value: this.#subnodeId, enumerable: true });
|
|
289
|
+
return this.#logger.write(bound);
|
|
290
|
+
}
|
|
291
|
+
catch (error) {
|
|
292
|
+
stores.get(this.#logger).warn('write', rejectionReason(error));
|
|
293
|
+
return -1;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
fix(stepId, patch) {
|
|
297
|
+
const store = stores.get(this.#logger);
|
|
298
|
+
if (!store.isActive()) {
|
|
299
|
+
store.warn('fix', 'inactive');
|
|
300
|
+
return false;
|
|
301
|
+
}
|
|
302
|
+
if (!store.owns(stepId, this.#subnodeId)) {
|
|
303
|
+
store.warn('fix', 'scope');
|
|
304
|
+
return false;
|
|
305
|
+
}
|
|
306
|
+
return this.#logger.fix(stepId, patch);
|
|
307
|
+
}
|
|
308
|
+
readAll() {
|
|
309
|
+
return this.#logger.readAll();
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
exports.ScopedSubFlowLogger = ScopedSubFlowLogger;
|
|
313
|
+
/** Runtime check complements nominal invocation types; scoped or forged inputs cannot invoke. */
|
|
314
|
+
function requireSubFlowLogger(logger) {
|
|
315
|
+
if (logger !== undefined && (!(logger instanceof SubFlowLogger) || !instances.has(logger)))
|
|
316
|
+
throw new TypeError('Native invocation requires a full SubFlowLogger');
|
|
317
|
+
}
|
|
318
|
+
/** Allocate a short node-local group; the owning node visit already supplies the namespace. */
|
|
319
|
+
function bindNativeSubFlowLogger(logger, kind, definitionId) {
|
|
320
|
+
requireSubFlowLogger(logger);
|
|
321
|
+
return logger.fork(stores.get(logger).nextNativeSubnodeId(kind, definitionId));
|
|
322
|
+
}
|
|
178
323
|
/**
|
|
179
324
|
* Create a logger bound to one active execution visit.
|
|
180
325
|
*
|
|
@@ -183,51 +328,95 @@ function fill(previous, patch) {
|
|
|
183
328
|
*
|
|
184
329
|
* @param owner Object that owns the visit. `readSubFlowLogger` compares this identity before
|
|
185
330
|
* returning any step.
|
|
186
|
-
* @param active Reports whether the visit is still open; a
|
|
331
|
+
* @param active Reports whether the visit is still open; a detached logger returns write=-1 and fix=false.
|
|
187
332
|
* @param content Reports whether content-level detail is enabled. When false, summaries and
|
|
188
333
|
* payloads are dropped before anything is stored.
|
|
189
|
-
* @param changed
|
|
334
|
+
* @param changed Runtime receiver called after each accepted write/fix with its delta and immutable snapshot.
|
|
190
335
|
* @param identity Diagnostic run and span the logger belongs to; used to reject cross-scope reads.
|
|
191
336
|
* @returns A frozen logger instance.
|
|
192
337
|
*/
|
|
193
338
|
function createSubFlowLogger(owner, active, content, changed, identity) {
|
|
339
|
+
let root;
|
|
194
340
|
class Logger {
|
|
195
341
|
#steps = Object.freeze([]);
|
|
196
|
-
#
|
|
342
|
+
#records = Object.freeze([]);
|
|
343
|
+
#warnings = new Set();
|
|
344
|
+
#nativeCalls = new Map();
|
|
197
345
|
constructor() {
|
|
198
|
-
instances.set(this, {
|
|
346
|
+
instances.set(this, {
|
|
347
|
+
owner,
|
|
348
|
+
identity,
|
|
349
|
+
read: () => this.#records,
|
|
350
|
+
detach: () => {
|
|
351
|
+
// Retained read-only instances must not keep the runtime or App consumers alive.
|
|
352
|
+
owner = {};
|
|
353
|
+
instances.get(this).owner = owner;
|
|
354
|
+
active = () => false;
|
|
355
|
+
content = () => false;
|
|
356
|
+
changed = () => { };
|
|
357
|
+
},
|
|
358
|
+
});
|
|
359
|
+
snapshots.set(this.#records, { identity });
|
|
199
360
|
Object.freeze(this);
|
|
200
361
|
}
|
|
201
|
-
#assertActive() {
|
|
202
|
-
// A logger that outlived its execution would append evidence to a finished request.
|
|
203
|
-
(0, rules_js_1.breakWhile)(!active(), () => new TypeError('Sub-flow logger belongs to a closed execution'));
|
|
204
|
-
}
|
|
205
362
|
#save(step, operation) {
|
|
206
|
-
|
|
207
|
-
|
|
363
|
+
if (!active())
|
|
364
|
+
return false;
|
|
365
|
+
// Logical heads validate revisions; only the immutable ledger is published or queried.
|
|
208
366
|
const next = operation === 'write'
|
|
209
367
|
? [...this.#steps, step]
|
|
210
368
|
: this.#steps.map((item) => (item.stepId === step.stepId ? step : item));
|
|
211
369
|
// Successors must resolve inside this logger, otherwise the recorded graph would have
|
|
212
370
|
// dangling edges.
|
|
213
371
|
(0, rules_js_1.breakWhile)(step.nextStepIds?.some((id) => !next.some((item) => item.stepId === id)), () => new TypeError('Sub-flow successors must belong to this logger'));
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
(0, rules_js_1.breakWhile)(next.length > 256 || this.#changes >= 1024 || JSON.stringify(next).length > 65536, () => new RangeError('Sub-flow log limit exceeded'));
|
|
217
|
-
this.#steps = Object.freeze(next);
|
|
218
|
-
this.#changes++;
|
|
219
|
-
changed(this, {
|
|
372
|
+
const record = Object.freeze({
|
|
373
|
+
...step,
|
|
220
374
|
operation,
|
|
221
|
-
stepId: step.stepId,
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
createdAtMs:
|
|
225
|
-
status: step.status,
|
|
226
|
-
...(step.usedModel === undefined ? {} : { usedModel: step.usedModel }),
|
|
375
|
+
stepId: operation === 'write' ? step.stepId : (0, invocation_js_1.invocationId)(),
|
|
376
|
+
...(operation === 'fix' ? { targetStepId: step.stepId } : {}),
|
|
377
|
+
sequence: this.#records.length + 1,
|
|
378
|
+
createdAtMs: Date.now(),
|
|
227
379
|
});
|
|
380
|
+
const records = Object.freeze([...this.#records, record]);
|
|
381
|
+
(0, rules_js_1.breakWhile)(next.length > 256 || records.length > 1024 || JSON.stringify(records).length > 65536, () => new RangeError('Sub-flow log limit exceeded'));
|
|
382
|
+
if (!active())
|
|
383
|
+
return false;
|
|
384
|
+
this.#steps = Object.freeze(next);
|
|
385
|
+
this.#records = records;
|
|
386
|
+
snapshots.set(records, { identity });
|
|
387
|
+
changed(root, {
|
|
388
|
+
operation,
|
|
389
|
+
type: record.type,
|
|
390
|
+
subnodeId: record.subnodeId,
|
|
391
|
+
relatedInvocationId: record.relatedInvocationId,
|
|
392
|
+
stepId: record.stepId,
|
|
393
|
+
...(record.targetStepId ? { targetStepId: record.targetStepId } : {}),
|
|
394
|
+
sequence: record.sequence,
|
|
395
|
+
revision: record.revision,
|
|
396
|
+
createdAtMs: record.createdAtMs,
|
|
397
|
+
status: record.status,
|
|
398
|
+
...(record.usedModel === undefined ? {} : { usedModel: record.usedModel }),
|
|
399
|
+
}, records);
|
|
400
|
+
return true;
|
|
228
401
|
}
|
|
229
402
|
write(input) {
|
|
230
|
-
|
|
403
|
+
if (!active()) {
|
|
404
|
+
this.warn('write', 'inactive');
|
|
405
|
+
return -1;
|
|
406
|
+
}
|
|
407
|
+
try {
|
|
408
|
+
const id = this.#append(input);
|
|
409
|
+
if (id === -1)
|
|
410
|
+
this.warn('write', 'inactive');
|
|
411
|
+
return id;
|
|
412
|
+
}
|
|
413
|
+
catch (error) {
|
|
414
|
+
this.warn('write', rejectionReason(error));
|
|
415
|
+
return -1;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
/** Validate a new record before committing it; public write returns -1 on failure. */
|
|
419
|
+
#append(input) {
|
|
231
420
|
fields(input, writeFields);
|
|
232
421
|
(0, rules_js_1.breakWhile)(input.nextStepIds !== undefined && !Array.isArray(input.nextStepIds), () => new TypeError('Invalid sub-flow successors'));
|
|
233
422
|
const now = Date.now(), status = input.status === undefined ? 'planned' : input.status;
|
|
@@ -236,16 +425,21 @@ function createSubFlowLogger(owner, active, content, changed, identity) {
|
|
|
236
425
|
const step = {
|
|
237
426
|
origin: 'reported',
|
|
238
427
|
stepId: (0, invocation_js_1.invocationId)(),
|
|
239
|
-
sequence: this.#
|
|
428
|
+
sequence: this.#records.length + 1,
|
|
240
429
|
revision: 1,
|
|
241
430
|
createdAtMs: now,
|
|
431
|
+
type: input.type ?? input.kind,
|
|
432
|
+
...(input.subnodeId === undefined ? {} : { subnodeId: input.subnodeId }),
|
|
242
433
|
name: input.name,
|
|
243
|
-
kind: input.kind,
|
|
434
|
+
kind: input.kind ?? input.type,
|
|
244
435
|
status,
|
|
245
436
|
...(input.parentStepId === undefined ? {} : { parentStepId: input.parentStepId }),
|
|
246
437
|
...(input.relatedDefinitionId === undefined
|
|
247
438
|
? {}
|
|
248
439
|
: { relatedDefinitionId: input.relatedDefinitionId }),
|
|
440
|
+
...(input.relatedInvocationId === undefined
|
|
441
|
+
? {}
|
|
442
|
+
: { relatedInvocationId: input.relatedInvocationId }),
|
|
249
443
|
...(input.caseId === undefined ? {} : { caseId: input.caseId }),
|
|
250
444
|
...(input.usedModel === undefined ? {} : { usedModel: input.usedModel }),
|
|
251
445
|
...(input.startedAtMs === undefined && status !== 'running'
|
|
@@ -268,11 +462,30 @@ function createSubFlowLogger(owner, active, content, changed, identity) {
|
|
|
268
462
|
}
|
|
269
463
|
validate(step);
|
|
270
464
|
(0, rules_js_1.breakWhile)(step.parentStepId && !this.#steps.some((item) => item.stepId === step.parentStepId), () => new TypeError('Sub-flow parent must belong to this logger'));
|
|
271
|
-
this.#save(Object.freeze(step), 'write');
|
|
272
|
-
return step.stepId;
|
|
465
|
+
return this.#save(Object.freeze(step), 'write') ? step.stepId : -1;
|
|
273
466
|
}
|
|
274
467
|
fix(stepId, patch) {
|
|
275
|
-
|
|
468
|
+
if (!active()) {
|
|
469
|
+
this.warn('fix', 'inactive');
|
|
470
|
+
return false;
|
|
471
|
+
}
|
|
472
|
+
if (stepId === -1) {
|
|
473
|
+
this.warn('fix', 'target');
|
|
474
|
+
return false;
|
|
475
|
+
}
|
|
476
|
+
try {
|
|
477
|
+
const fixed = this.#applyFix(stepId, patch);
|
|
478
|
+
if (!fixed)
|
|
479
|
+
this.warn('fix', 'inactive');
|
|
480
|
+
return fixed;
|
|
481
|
+
}
|
|
482
|
+
catch (error) {
|
|
483
|
+
this.warn('fix', rejectionReason(error));
|
|
484
|
+
return false;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
/** Validate a revision completely before committing it; public fix reports failure as false. */
|
|
488
|
+
#applyFix(stepId, patch) {
|
|
276
489
|
fields(patch, fixFields);
|
|
277
490
|
const previous = this.#steps.find((step) => step.stepId === stepId);
|
|
278
491
|
// The assertion above only satisfies the type checker; this check rejects an unknown id.
|
|
@@ -328,13 +541,42 @@ function createSubFlowLogger(owner, active, content, changed, identity) {
|
|
|
328
541
|
});
|
|
329
542
|
}
|
|
330
543
|
validate(step);
|
|
331
|
-
this.#save(Object.freeze(step), 'fix');
|
|
544
|
+
return this.#save(Object.freeze(step), 'fix');
|
|
545
|
+
}
|
|
546
|
+
owns(stepId, subnodeId) {
|
|
547
|
+
return this.#steps.some((step) => step.stepId === stepId && step.subnodeId === subnodeId);
|
|
548
|
+
}
|
|
549
|
+
isActive() {
|
|
550
|
+
return active();
|
|
551
|
+
}
|
|
552
|
+
/** Count starts, including failed/cancelled calls; each node visit owns a fresh store. */
|
|
553
|
+
nextNativeSubnodeId(kind, definitionId) {
|
|
554
|
+
const key = JSON.stringify([kind, definitionId]);
|
|
555
|
+
const next = (this.#nativeCalls.get(key) ?? 0) + 1;
|
|
556
|
+
this.#nativeCalls.set(key, next);
|
|
557
|
+
return `${kind}:${encodeURIComponent(definitionId)}:${next}`;
|
|
558
|
+
}
|
|
559
|
+
/** Deduplicate safe developer warnings in the sole owning store, including delegated calls. */
|
|
560
|
+
warn(operation, reason) {
|
|
561
|
+
const key = `${operation}:${reason}`;
|
|
562
|
+
if (this.#warnings.has(key))
|
|
563
|
+
return;
|
|
564
|
+
this.#warnings.add(key);
|
|
565
|
+
try {
|
|
566
|
+
console.warn(`[agent-core:subflow] ${operation} rejected: ${rejectionReasons[reason]}`);
|
|
567
|
+
}
|
|
568
|
+
catch {
|
|
569
|
+
/* A console extension cannot change logging return values. */
|
|
570
|
+
}
|
|
332
571
|
}
|
|
333
572
|
readAll() {
|
|
334
|
-
return this.#
|
|
573
|
+
return this.#records;
|
|
335
574
|
}
|
|
336
575
|
}
|
|
337
|
-
|
|
576
|
+
const store = new Logger();
|
|
577
|
+
root = new SubFlowLogger(loggerToken, store);
|
|
578
|
+
instances.set(root, instances.get(store));
|
|
579
|
+
return root;
|
|
338
580
|
}
|
|
339
581
|
/**
|
|
340
582
|
* Read the steps recorded by a logger issued by this module.
|
|
@@ -357,3 +599,7 @@ function readSubFlowLogger(input, owner, identity) {
|
|
|
357
599
|
instance.identity?.spanId !== identity.spanId), () => new TypeError('Sub-flow logger belongs to another diagnostic scope'));
|
|
358
600
|
return instance.read();
|
|
359
601
|
}
|
|
602
|
+
/** Release execution callbacks while preserving the instance's immutable query history. */
|
|
603
|
+
function detachSubFlowLogger(logger) {
|
|
604
|
+
instances.get(logger)?.detach();
|
|
605
|
+
}
|
|
@@ -658,6 +658,7 @@ class AgentCoreEngine {
|
|
|
658
658
|
throw new errors_js_2.WorkflowContractError('Prepared model call belongs to another execution or model');
|
|
659
659
|
const runner = owner?.runner ?? runnerFor(request.modelId);
|
|
660
660
|
return runner.invoke({
|
|
661
|
+
logger: request.logger,
|
|
661
662
|
stageId: request.stageId,
|
|
662
663
|
maxOutputTokens: request.maxOutputTokens,
|
|
663
664
|
transport: request.transport,
|
|
@@ -897,14 +898,14 @@ class AgentCoreEngine {
|
|
|
897
898
|
try {
|
|
898
899
|
const result = (0, synchronous_js_1.synchronous)(report(), 'Workflow reports must be synchronous');
|
|
899
900
|
reports?.receive(result);
|
|
900
|
-
detail = { payload: structuredClone(result.payload)
|
|
901
|
+
detail = { payload: structuredClone(result.payload) };
|
|
901
902
|
}
|
|
902
903
|
catch {
|
|
903
904
|
/* A report cannot suppress the workflow event. */
|
|
904
905
|
}
|
|
905
906
|
}
|
|
906
907
|
reports?.close();
|
|
907
|
-
diagnostics?.emit('workflow.event', () => ({ event, ...(detail?.payload === undefined ? {} : { payload: detail.payload }) }), reports?.debug(
|
|
908
|
+
diagnostics?.emit('workflow.event', () => ({ event, ...(detail?.payload === undefined ? {} : { payload: detail.payload }) }), reports?.debug());
|
|
908
909
|
return event;
|
|
909
910
|
}
|
|
910
911
|
/**
|
|
@@ -12,7 +12,7 @@ exports.executionEvents = executionEvents;
|
|
|
12
12
|
*
|
|
13
13
|
* Exports: `createEventScope` and `executionEvents`.
|
|
14
14
|
*/
|
|
15
|
-
const
|
|
15
|
+
const sub_flow_js_1 = require("../diagnostics/sub-flow.js");
|
|
16
16
|
const invocation_js_1 = require("../utils/invocation.js");
|
|
17
17
|
const errors_js_1 = require("../errors.js");
|
|
18
18
|
const catalog_js_1 = require("../diagnostics/catalog.js");
|
|
@@ -66,9 +66,7 @@ function createEventScope(options = {}) {
|
|
|
66
66
|
};
|
|
67
67
|
const fact = (event, payload, debug) => {
|
|
68
68
|
const value = payload();
|
|
69
|
-
const reported = scope.enabled(event, 'content')
|
|
70
|
-
? (0, debug_js_1.captureSubSteps)(debug?.subSteps, identity)
|
|
71
|
-
: (0, debug_js_1.captureSubSteps)(undefined);
|
|
69
|
+
const reported = (0, sub_flow_js_1.captureSubFlowSnapshot)(scope.enabled(event, 'content') ? debug?.recordedSubSteps : undefined, identity);
|
|
72
70
|
return {
|
|
73
71
|
...identity,
|
|
74
72
|
...reported,
|
|
@@ -219,7 +217,9 @@ function metadataPayload(type, payload) {
|
|
|
219
217
|
function eventSnapshot(record, content = true) {
|
|
220
218
|
const metadata = () => ({
|
|
221
219
|
...record,
|
|
222
|
-
|
|
220
|
+
subSteps: [],
|
|
221
|
+
subStepsDropped: 0,
|
|
222
|
+
subStepsTruncated: false,
|
|
223
223
|
payload: metadataPayload(record.type, record.payload),
|
|
224
224
|
});
|
|
225
225
|
try {
|
|
@@ -26,6 +26,7 @@ class NodeReports {
|
|
|
26
26
|
active = true;
|
|
27
27
|
logger;
|
|
28
28
|
usedModel;
|
|
29
|
+
steps = Object.freeze([]);
|
|
29
30
|
/**
|
|
30
31
|
* @param diagnostics - Diagnostic scope of the visit; omitted when observation is off.
|
|
31
32
|
* @param signal - Execution signal; an aborted signal suppresses all further reports.
|
|
@@ -39,6 +40,9 @@ class NodeReports {
|
|
|
39
40
|
/** End the visit; later reports and logger creation are ignored or rejected. */
|
|
40
41
|
close() {
|
|
41
42
|
this.active = false;
|
|
43
|
+
if (this.logger)
|
|
44
|
+
(0, sub_flow_js_1.detachSubFlowLogger)(this.logger);
|
|
45
|
+
this.logger = undefined;
|
|
42
46
|
}
|
|
43
47
|
/**
|
|
44
48
|
* Validate a report and retain its model-use flag.
|
|
@@ -51,34 +55,18 @@ class NodeReports {
|
|
|
51
55
|
*/
|
|
52
56
|
receive(update) {
|
|
53
57
|
(0, rules_js_1.breakWhile)(!update || typeof update !== 'object', () => new TypeError('Invalid node report'));
|
|
58
|
+
(0, rules_js_1.breakWhile)(Object.hasOwn(update, 'subs'), () => new TypeError('Sub-flow logs are recorded automatically'));
|
|
54
59
|
const usedModel = update.usedModel;
|
|
55
60
|
(0, rules_js_1.breakWhile)(usedModel !== undefined &&
|
|
56
61
|
(typeof usedModel !== 'boolean' || (this.usedModel === true && !usedModel)), () => new TypeError('Invalid reported model use'));
|
|
57
62
|
if (usedModel !== undefined)
|
|
58
63
|
this.usedModel = usedModel;
|
|
59
64
|
}
|
|
60
|
-
/**
|
|
61
|
-
|
|
62
|
-
*
|
|
63
|
-
* Getters keep the values live while the fact is being built; `subSteps` prefers the
|
|
64
|
-
* logger supplied in the report and falls back to the visit's own logger.
|
|
65
|
-
*
|
|
66
|
-
* @param update - Optional report whose logger replaces the visit logger.
|
|
67
|
-
* @returns A debug output object consumed by the diagnostic pipeline.
|
|
68
|
-
*/
|
|
69
|
-
debug(update) {
|
|
70
|
-
const self = this;
|
|
65
|
+
/** Return the runtime-owned snapshot already received from write/fix. */
|
|
66
|
+
debug() {
|
|
71
67
|
return {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
},
|
|
75
|
-
subSteps: () => {
|
|
76
|
-
const supplied = update?.subs;
|
|
77
|
-
const logger = supplied === undefined ? this.logger : supplied;
|
|
78
|
-
if (logger !== undefined)
|
|
79
|
-
(0, sub_flow_js_1.readSubFlowLogger)(logger, this);
|
|
80
|
-
return logger;
|
|
81
|
-
},
|
|
68
|
+
usedModel: this.usedModel,
|
|
69
|
+
recordedSubSteps: this.steps.length ? this.steps : undefined,
|
|
82
70
|
};
|
|
83
71
|
}
|
|
84
72
|
/**
|
|
@@ -95,7 +83,7 @@ class NodeReports {
|
|
|
95
83
|
return;
|
|
96
84
|
try {
|
|
97
85
|
this.receive(update);
|
|
98
|
-
this.diagnostics?.emit('node.progress', () => this.diagnostics?.enabled('node.progress', 'content') ? { payload: update.payload } : {}, this.debug(
|
|
86
|
+
this.diagnostics?.emit('node.progress', () => this.diagnostics?.enabled('node.progress', 'content') ? { payload: update.payload } : {}, this.debug());
|
|
99
87
|
}
|
|
100
88
|
catch {
|
|
101
89
|
this.diagnostics?.emit('node.progress', () => ({ reportError: 'INVALID_NODE_REPORT' }));
|
|
@@ -104,8 +92,9 @@ class NodeReports {
|
|
|
104
92
|
/**
|
|
105
93
|
* Create (once) the logger that records sub-steps for this visit.
|
|
106
94
|
*
|
|
107
|
-
*
|
|
108
|
-
*
|
|
95
|
+
* Status metadata is retained for the active visit. Summary and payload capture require
|
|
96
|
+
* content permission from progress or the terminal event. The bound runtime receives
|
|
97
|
+
* every accepted revision before notifying consumers.
|
|
109
98
|
*
|
|
110
99
|
* @returns The visit's logger, identical on repeated calls.
|
|
111
100
|
* @throws TypeError When the visit is already closed or the execution was aborted.
|
|
@@ -113,10 +102,11 @@ class NodeReports {
|
|
|
113
102
|
createSubFlowLogger() {
|
|
114
103
|
(0, rules_js_1.breakWhile)(!this.active || this.signal?.aborted, () => new TypeError('Cannot create a logger for a closed execution'));
|
|
115
104
|
return (this.logger ??= (0, sub_flow_js_1.createSubFlowLogger)(this, () => this.active && !this.signal?.aborted, () => this.diagnostics?.enabled('node.progress', 'content') === true ||
|
|
116
|
-
this.diagnostics?.enabled(this.terminalEvent, 'content') === true, (
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
105
|
+
this.diagnostics?.enabled(this.terminalEvent, 'content') === true, (_logger, progress, steps) => {
|
|
106
|
+
// Commit before notifying consumers; failed listeners cannot lose the accepted revision.
|
|
107
|
+
this.steps = steps;
|
|
108
|
+
this.diagnostics?.emit('node.progress', () => ({ subStep: progress }), this.debug());
|
|
109
|
+
}, this.diagnostics?.identity));
|
|
120
110
|
}
|
|
121
111
|
}
|
|
122
112
|
exports.NodeReports = NodeReports;
|