@jangts/agent-core 0.3.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 +21 -0
- package/README.md +28 -26
- package/development/docs/api/README.en.md +2 -0
- package/development/docs/api/README.md +2 -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 +193 -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 +12 -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/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/utils/invocation.d.ts +1 -0
- package/dist/esm/utils/invocation.js +1 -0
- package/package.json +1 -1
- 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,3 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Delivery Kit: run an ordered, synchronous stage pipeline that ends with one result.
|
|
3
|
+
*
|
|
4
|
+
* Place in the package: an optional App-side delivery pipeline (for example formatting and
|
|
5
|
+
* sending one reply). The Kit owns ordering, cancellation checks and outcome validation; the
|
|
6
|
+
* stages, their state and the actual side effects belong to the App.
|
|
7
|
+
*
|
|
8
|
+
* Exports: `defineDelivery`, `Delivery`, and the delivery option/outcome types.
|
|
9
|
+
*/
|
|
10
|
+
import { nativeSubFlowSync } from '../diagnostics/native-sub-flow.js';
|
|
1
11
|
import { synchronous } from '../utils/synchronous.js';
|
|
2
12
|
/**
|
|
3
13
|
* Compile a delivery pipeline from its declared stages.
|
|
@@ -38,33 +48,35 @@ export function defineDelivery(options) {
|
|
|
38
48
|
* handler results.
|
|
39
49
|
*/
|
|
40
50
|
deliver(input, execution) {
|
|
41
|
-
execution
|
|
42
|
-
const state = synchronous(createState(input, execution), 'Delivery state creation must be synchronous');
|
|
43
|
-
const context = {
|
|
44
|
-
...execution,
|
|
45
|
-
signal: execution.signal,
|
|
46
|
-
diagnostics: execution.diagnostics,
|
|
47
|
-
app: input,
|
|
48
|
-
};
|
|
49
|
-
for (const stage of stages) {
|
|
51
|
+
return nativeSubFlowSync(execution, 'delivery', 'delivery', () => {
|
|
50
52
|
execution.signal.throwIfAborted();
|
|
51
|
-
synchronous(
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
53
|
+
const state = synchronous(createState(input, execution), 'Delivery state creation must be synchronous');
|
|
54
|
+
const context = {
|
|
55
|
+
...execution,
|
|
56
|
+
signal: execution.signal,
|
|
57
|
+
diagnostics: execution.diagnostics,
|
|
58
|
+
app: input,
|
|
59
|
+
};
|
|
60
|
+
for (const stage of stages) {
|
|
61
|
+
execution.signal.throwIfAborted();
|
|
62
|
+
synchronous(beforeStage?.({ stageId: stage.id, state }, context), 'Delivery hooks must be synchronous');
|
|
63
|
+
execution.signal.throwIfAborted();
|
|
64
|
+
if (!stage.handle)
|
|
65
|
+
continue;
|
|
66
|
+
const outcome = synchronous(stage.handle(state, context), 'Delivery handlers must be synchronous');
|
|
63
67
|
execution.signal.throwIfAborted();
|
|
64
|
-
|
|
68
|
+
if (!outcome ||
|
|
69
|
+
!['continue', 'complete'].includes(outcome.type) ||
|
|
70
|
+
(outcome.type === 'complete' && !('value' in outcome)))
|
|
71
|
+
throw new TypeError('Delivery handlers require explicit continue/complete');
|
|
72
|
+
if (outcome.type === 'complete') {
|
|
73
|
+
const value = outcome.value;
|
|
74
|
+
execution.signal.throwIfAborted();
|
|
75
|
+
return value;
|
|
76
|
+
}
|
|
65
77
|
}
|
|
66
|
-
|
|
67
|
-
|
|
78
|
+
throw synchronous(missingResult(), 'Delivery errors must be synchronous');
|
|
79
|
+
});
|
|
68
80
|
},
|
|
69
81
|
});
|
|
70
82
|
}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*
|
|
8
8
|
* Exports: `defineKnowledgeCatalog`, and the knowledge document/read/snapshot types.
|
|
9
9
|
*/
|
|
10
|
+
import { nativeSubFlowSync } from '../../diagnostics/native-sub-flow.js';
|
|
10
11
|
import { readonlyCatalog } from '../../utils/catalog.js';
|
|
11
12
|
import { compileKnowledgeDocument, knowledgeText } from './sections.js';
|
|
12
13
|
import { observeSafely } from '../../utils/observer.js';
|
|
@@ -65,37 +66,39 @@ export function defineKnowledgeCatalog(documents, options = {}) {
|
|
|
65
66
|
...readonlyCatalog([...catalog.values()].map((entry) => entry.document), (id) => new TypeError(`Unknown knowledge document: ${id}`)),
|
|
66
67
|
sectionIds,
|
|
67
68
|
assertSelection,
|
|
68
|
-
read(input) {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
69
|
+
read(input, context = {}) {
|
|
70
|
+
return nativeSubFlowSync(context, 'knowledge', input.documentId, () => {
|
|
71
|
+
const { read, entry } = resolve(input), raw = read.selection;
|
|
72
|
+
const selection = raw.mode === 'document'
|
|
73
|
+
? Object.freeze({ mode: 'document' })
|
|
74
|
+
: Object.freeze({ mode: raw.mode, sectionIds: Object.freeze([...raw.sectionIds]) });
|
|
75
|
+
const content = (selection.mode === 'document'
|
|
76
|
+
? knowledgeText(entry.document.parts)
|
|
77
|
+
: selection.sectionIds
|
|
78
|
+
.map((id) => knowledgeText(entry.sections.get(id).parts, selection.mode === 'subtree'))
|
|
79
|
+
.join('\n\n')).trim();
|
|
80
|
+
if (maxChars !== undefined && content.length > maxChars)
|
|
81
|
+
throw new RangeError('Knowledge selection exceeds its budget');
|
|
82
|
+
// Preserve the existing opaque snapshot identity format.
|
|
83
|
+
const identitySelection = selection.mode === 'document'
|
|
84
|
+
? selection
|
|
85
|
+
: { mode: selection.mode, sections: selection.sectionIds };
|
|
86
|
+
const snapshot = Object.freeze({
|
|
87
|
+
documentId: read.documentId,
|
|
88
|
+
selection,
|
|
89
|
+
revision: entry.document.revision,
|
|
90
|
+
locale: entry.document.locale,
|
|
91
|
+
content,
|
|
92
|
+
id: JSON.stringify([
|
|
93
|
+
read.documentId,
|
|
94
|
+
entry.document.revision,
|
|
95
|
+
entry.document.locale,
|
|
96
|
+
identitySelection,
|
|
97
|
+
]),
|
|
98
|
+
});
|
|
99
|
+
observeSafely(observe, snapshot);
|
|
100
|
+
return snapshot;
|
|
96
101
|
});
|
|
97
|
-
observeSafely(observe, snapshot);
|
|
98
|
-
return snapshot;
|
|
99
102
|
},
|
|
100
103
|
});
|
|
101
104
|
}
|
|
@@ -38,7 +38,7 @@ export declare function definePolicySet<Input, Kind extends string>(definitions:
|
|
|
38
38
|
* boolean `handled` or omits `value` while claiming to have handled the input.
|
|
39
39
|
*/
|
|
40
40
|
export declare function defineRecoveryChain<Input, Result>(definitions: readonly RecoveryDefinition<Input, Result>[]): Readonly<{
|
|
41
|
-
tryRecover: (input: Input) => RecoveryResult<Result>;
|
|
41
|
+
tryRecover: (input: Input, execution?: Partial<import("../structs/common.js").ExecutionContext>) => RecoveryResult<Result>;
|
|
42
42
|
definitions: readonly Readonly<{
|
|
43
43
|
id: string;
|
|
44
44
|
description: string;
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*
|
|
8
8
|
* Exports: `definePolicySet`, `defineRecoveryChain`, `Policies`, and the policy types.
|
|
9
9
|
*/
|
|
10
|
+
import { nativeSubFlowSync } from '../diagnostics/native-sub-flow.js';
|
|
10
11
|
import { readonlyCatalog } from '../utils/catalog.js';
|
|
11
12
|
import { synchronous, booleanResult } from '../utils/synchronous.js';
|
|
12
13
|
import { breakWhile } from '../utils/rules.js';
|
|
@@ -65,9 +66,9 @@ export function defineRecoveryChain(definitions) {
|
|
|
65
66
|
},
|
|
66
67
|
});
|
|
67
68
|
});
|
|
68
|
-
const tryRecover = (input) => {
|
|
69
|
+
const tryRecover = (input, execution = {}) => {
|
|
69
70
|
for (const policy of catalog) {
|
|
70
|
-
const result = policy.tryRecover(input);
|
|
71
|
+
const result = nativeSubFlowSync(execution, 'recovery', policy.id, () => policy.tryRecover(input));
|
|
71
72
|
if (result.handled)
|
|
72
73
|
return result;
|
|
73
74
|
}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*
|
|
8
8
|
* Exports: `defineOutputCatalog`, and the output definition/field/provider/result types.
|
|
9
9
|
*/
|
|
10
|
+
import { nativeSubFlowSync } from '../../diagnostics/native-sub-flow.js';
|
|
10
11
|
import { snapshotSchemaAdapter } from '../../utils/schema-adapter.js';
|
|
11
12
|
import { readonlyCatalog } from '../../utils/catalog.js';
|
|
12
13
|
import { synchronous } from '../../utils/synchronous.js';
|
|
@@ -321,7 +322,7 @@ export function defineOutputCatalog(definitions, options = {}) {
|
|
|
321
322
|
throw new TypeError('Use a named output request');
|
|
322
323
|
if (!context?.signal)
|
|
323
324
|
throw new TypeError('Output composition requires OperationContext');
|
|
324
|
-
return composeInternal(request.outputId, request.input, context);
|
|
325
|
+
return nativeSubFlowSync(context, 'output', request.outputId, () => composeInternal(request.outputId, request.input, context));
|
|
325
326
|
},
|
|
326
327
|
});
|
|
327
328
|
}
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*
|
|
9
9
|
* Exports: `defineRetrieval`, `retrievalQueryKey`, `Retrieval`, and the retrieval port types.
|
|
10
10
|
*/
|
|
11
|
+
import { nativeSubFlowSync } from '../diagnostics/native-sub-flow.js';
|
|
11
12
|
import { assertion, synchronous } from '../utils/synchronous.js';
|
|
12
13
|
import { retrievalQueryKey } from '../utils/query-key.js';
|
|
13
14
|
import { breakWhile } from '../utils/rules.js';
|
|
@@ -144,7 +145,12 @@ export function defineRetrieval(definition) {
|
|
|
144
145
|
}
|
|
145
146
|
return receipt;
|
|
146
147
|
}
|
|
147
|
-
return Object.freeze({
|
|
148
|
+
return Object.freeze({
|
|
149
|
+
id,
|
|
150
|
+
description,
|
|
151
|
+
effects,
|
|
152
|
+
run: (input, context) => nativeSubFlowSync(context, 'retrieval', id, () => run(input, context)),
|
|
153
|
+
});
|
|
148
154
|
}
|
|
149
155
|
function reason(value, query, context) {
|
|
150
156
|
return () => (typeof value === 'function' ? value(query, context) : value);
|
package/dist/esm/kits/tools.js
CHANGED
|
@@ -89,6 +89,7 @@ export class ToolLifecycleRegistry {
|
|
|
89
89
|
});
|
|
90
90
|
this.diagnostics = this.options.execution?.diagnostics;
|
|
91
91
|
this.invocationPorts = Object.freeze({
|
|
92
|
+
logger: this.options.logger,
|
|
92
93
|
diagnostics: this.options.diagnostics ?? (() => this.diagnostics),
|
|
93
94
|
signal: this.options.signal,
|
|
94
95
|
journal: this.options.journal,
|
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
* @throws The original error when it is not a multi-call rejection, or the fallback error.
|
|
10
10
|
*/
|
|
11
11
|
export declare function retryMultipleModelCalls<Value>(request: {
|
|
12
|
+
readonly logger?: import('../structs/debug.js').SubFlowLogger;
|
|
13
|
+
readonly signal?: AbortSignal;
|
|
12
14
|
readonly invoke: () => Promise<Value>;
|
|
13
15
|
readonly retry: (error: unknown) => Promise<Value>;
|
|
14
16
|
}): Promise<Value>;
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*
|
|
8
8
|
* Exports: `retryMultipleModelCalls`.
|
|
9
9
|
*/
|
|
10
|
+
import { nativeSubFlowAsync } from '../diagnostics/native-sub-flow.js';
|
|
10
11
|
import { getErrorInfo } from '../errors.js';
|
|
11
12
|
/**
|
|
12
13
|
* Retry once when the failure is a rejected multi-call proposal.
|
|
@@ -25,6 +26,6 @@ export async function retryMultipleModelCalls(request) {
|
|
|
25
26
|
catch (error) {
|
|
26
27
|
if (!getErrorInfo(error).codes.includes('MODEL_MULTIPLE_CALLS'))
|
|
27
28
|
throw error;
|
|
28
|
-
return request.retry(error);
|
|
29
|
+
return nativeSubFlowAsync(request, 'retry', 'MODEL_MULTIPLE_CALLS', () => request.retry(error));
|
|
29
30
|
}
|
|
30
31
|
}
|
|
@@ -145,6 +145,8 @@ export declare class ModelRunner<TransportId extends string = string, const Tran
|
|
|
145
145
|
}): Promise<ModelResult<import('../structs/model/runtime.js').RegisteredTransportOutput<Transports, Id>>>;
|
|
146
146
|
/** Invoke without narrowing the transport type. */
|
|
147
147
|
invoke(request: ModelCallRequest<TransportId | ModelTransportDefinition>): Promise<ModelResult<unknown>>;
|
|
148
|
+
/** Validate and execute a single request; optional recording is owned by invoke. */
|
|
149
|
+
private invokeRequest;
|
|
148
150
|
/**
|
|
149
151
|
* Acquire the per-runner call slot and execute one call.
|
|
150
152
|
*
|
package/dist/esm/model/runner.js
CHANGED
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
* Exports: `ModelRunner`, the re-exported `BudgetError`, and the runtime/config type
|
|
11
11
|
* re-exports at the bottom of the file.
|
|
12
12
|
*/
|
|
13
|
+
import { requireSubFlowLogger } from '../diagnostics/sub-flow.js';
|
|
14
|
+
import { nativeSubFlowAsync } from '../diagnostics/native-sub-flow.js';
|
|
13
15
|
import { executionEvents } from '../execution/events.js';
|
|
14
16
|
import { ModelCallBudget, parentModelBudget } from './call-budget.js';
|
|
15
17
|
import { synchronous } from '../utils/synchronous.js';
|
|
@@ -317,6 +319,19 @@ export class ModelRunner {
|
|
|
317
319
|
return snapshot;
|
|
318
320
|
}
|
|
319
321
|
async invoke(request) {
|
|
322
|
+
const context = typeof request === 'string' ? { logger: undefined, signal: undefined } : request;
|
|
323
|
+
requireSubFlowLogger(context.logger);
|
|
324
|
+
const callId = invocationId();
|
|
325
|
+
return nativeSubFlowAsync({
|
|
326
|
+
logger: context.logger,
|
|
327
|
+
signal: combineSignals(this.signal, context.signal),
|
|
328
|
+
invocationId: callId,
|
|
329
|
+
}, 'model', typeof request === 'string'
|
|
330
|
+
? modelRunnerDefaults.stage
|
|
331
|
+
: (request.stageId ?? request.prepared?.stageId ?? modelRunnerDefaults.stage), () => this.invokeRequest(request, callId));
|
|
332
|
+
}
|
|
333
|
+
/** Validate and execute a single request; optional recording is owned by invoke. */
|
|
334
|
+
async invokeRequest(request, callId) {
|
|
320
335
|
const call = typeof request === 'string' ? { input: request } : request;
|
|
321
336
|
const state = call.prepared === undefined ? undefined : this.preparedCalls.get(call.prepared);
|
|
322
337
|
if (call.prepared !== undefined && (!state || state.used))
|
|
@@ -380,7 +395,7 @@ export class ModelRunner {
|
|
|
380
395
|
throw invalid('Prepared model request settings cannot be replaced');
|
|
381
396
|
if (state)
|
|
382
397
|
state.used = true;
|
|
383
|
-
return this.invokePrepared(stage, input, tokens, transportId, call.diagnostics, prepared, call.signal);
|
|
398
|
+
return this.invokePrepared(stage, input, tokens, transportId, call.diagnostics, prepared, call.signal, callId);
|
|
384
399
|
}
|
|
385
400
|
/**
|
|
386
401
|
* Acquire the per-runner call slot and execute one call.
|
|
@@ -399,7 +414,7 @@ export class ModelRunner {
|
|
|
399
414
|
* @returns The parsed result with invocation metadata.
|
|
400
415
|
* @throws Error When a call is already in flight or the request capacity is exhausted.
|
|
401
416
|
*/
|
|
402
|
-
async invokePrepared(stage, input, tokens, transportId, diagnostics = this.ports.diagnostics, prepared, requestSignal) {
|
|
417
|
+
async invokePrepared(stage, input, tokens, transportId, diagnostics = this.ports.diagnostics, prepared, requestSignal, callId = invocationId()) {
|
|
403
418
|
const configuredRun = this.ports.diagnostics?.identity?.runId, selectedRun = diagnostics?.identity?.runId;
|
|
404
419
|
if (configuredRun && selectedRun && configuredRun !== selectedRun)
|
|
405
420
|
throw recordErrorContext(new TypeError('Model observation parent belongs to a different run'), { stageId: stage, codes: ['MODEL_CONFIG'], isRetryable: false });
|
|
@@ -409,7 +424,7 @@ export class ModelRunner {
|
|
|
409
424
|
this.requestGate.active++;
|
|
410
425
|
let failure;
|
|
411
426
|
try {
|
|
412
|
-
return await this.execute(stage, input, tokens, transportId, combineSignals(this.signal, requestSignal), diagnostics, prepared);
|
|
427
|
+
return await this.execute(stage, input, tokens, transportId, combineSignals(this.signal, requestSignal), diagnostics, prepared, callId);
|
|
413
428
|
}
|
|
414
429
|
catch (error) {
|
|
415
430
|
failure = recordErrorContext(error, { stageId: stage });
|
|
@@ -440,8 +455,8 @@ export class ModelRunner {
|
|
|
440
455
|
* @returns The parsed result with invocation metadata.
|
|
441
456
|
* @throws Error or ValidationError from any phase, decorated with stage context.
|
|
442
457
|
*/
|
|
443
|
-
async execute(stage, input, tokens, transportId, signal, parentDiagnostics = this.ports.diagnostics, prepared) {
|
|
444
|
-
const id =
|
|
458
|
+
async execute(stage, input, tokens, transportId, signal, parentDiagnostics = this.ports.diagnostics, prepared, callId = invocationId()) {
|
|
459
|
+
const id = callId, startedAtMs = Date.now();
|
|
445
460
|
this.activeCall = undefined;
|
|
446
461
|
const invocationDiagnostics = parentDiagnostics?.child(id, {
|
|
447
462
|
invocationId: id,
|
|
@@ -49,6 +49,8 @@ export interface InvocationHandler<Input, Context> {
|
|
|
49
49
|
}
|
|
50
50
|
/** Optional capabilities the lifecycle resolves from the host context for one invocation. */
|
|
51
51
|
export interface ToolInvocationPorts<Context> {
|
|
52
|
+
/** Explicit recorder selected for this invocation only. */
|
|
53
|
+
readonly logger?: (context: Context) => import('../debug.js').SubFlowLogger | undefined;
|
|
52
54
|
/** Diagnostic scope; omitted disables recording for this tool. */
|
|
53
55
|
readonly diagnostics?: (context: Context) => import('../diagnostics.js').DiagnosticContext | undefined;
|
|
54
56
|
/** Extra cancellation; must not replace the tool execution signal. */
|
|
@@ -27,13 +27,15 @@ export interface ToolDefinition<Input = unknown, Output = unknown, App = unknown
|
|
|
27
27
|
authorize(input: Input, context: OperationContext<App>): MaybePromise<void>;
|
|
28
28
|
/** Executes the already-authorized input; must not repeat authorization decisions. */
|
|
29
29
|
execute(input: Input, context: OperationContext<App> & import('../debug.js').NodeReportingContext): MaybePromise<Output>;
|
|
30
|
-
/** Optional
|
|
31
|
-
debugSteps?(result: Output, context: import('../debug.js').NodeReportingContext):
|
|
30
|
+
/** Optional result hook; writes through context.createSubFlowLogger() are recorded immediately. */
|
|
31
|
+
debugSteps?(result: Output, context: import('../debug.js').NodeReportingContext): void;
|
|
32
32
|
/** Validates untrusted output (for example a cached replay) before it is returned. */
|
|
33
33
|
parseOutput(value: unknown): Output;
|
|
34
34
|
}
|
|
35
35
|
/** Execution facts handed to a tool for one invocation. */
|
|
36
36
|
export interface ToolExecutionContext extends ExecutionContext, NodeReportingContext {
|
|
37
|
+
/** Delegate bound by Core to this actual invocation. */
|
|
38
|
+
readonly logger?: import('../debug.js').ScopedSubFlowLogger;
|
|
37
39
|
/** Diagnostic scope of the invocation, when recording is enabled. */
|
|
38
40
|
readonly diagnostics?: DiagnosticContext;
|
|
39
41
|
/** Declared execution discipline; mirrors whether the registry accepted an async tool. */
|
|
@@ -49,7 +51,7 @@ export interface ExecutableTool {
|
|
|
49
51
|
readonly kind: string;
|
|
50
52
|
/** Optional here because the erased view does not require model-facing prose. */
|
|
51
53
|
readonly description?: string;
|
|
52
|
-
readonly debugSteps?: (result: unknown, context: NodeReportingContext) =>
|
|
54
|
+
readonly debugSteps?: (result: unknown, context: NodeReportingContext) => void;
|
|
53
55
|
parseInput(value: unknown): unknown;
|
|
54
56
|
parseOutput(value: unknown): unknown;
|
|
55
57
|
execute(input: unknown, context: ToolExecutionContext): unknown;
|
|
@@ -79,6 +81,8 @@ export interface ToolRegistryPorts {
|
|
|
79
81
|
}
|
|
80
82
|
/** One invocation request; the registry supplies signal and diagnostics when omitted. */
|
|
81
83
|
export interface ToolInvocationRequest<Id extends string = string> {
|
|
84
|
+
/** Explicit recorder for this call, also available to its handler for deliberate use. */
|
|
85
|
+
readonly logger?: import('../debug.js').SubFlowLogger;
|
|
82
86
|
readonly toolId: Id;
|
|
83
87
|
/** Raw model arguments; parsed by the tool's input schema before authorization. */
|
|
84
88
|
readonly input: unknown;
|
|
@@ -93,6 +93,8 @@ export interface ExecutionOptions {
|
|
|
93
93
|
}
|
|
94
94
|
/** Required execution facts available inside a running call. */
|
|
95
95
|
export interface ExecutionContext {
|
|
96
|
+
/** Explicit optional recorder for this operation; never inferred from diagnostics. */
|
|
97
|
+
readonly logger?: import('./debug.js').SubFlowLog;
|
|
96
98
|
/** Non-optional cancellation signal; never `undefined` once execution has started. */
|
|
97
99
|
readonly signal: AbortSignal;
|
|
98
100
|
/** Diagnostic scope of the enclosing call, when recording is enabled. */
|
|
@@ -55,12 +55,18 @@ export declare const reportedStepStatuses: readonly (keyof typeof reportedStepSt
|
|
|
55
55
|
export type ReportedStepStatus = keyof typeof reportedStepStatusCatalog;
|
|
56
56
|
/** One step as reported by a node; fields beyond identity are optional detail. */
|
|
57
57
|
export interface ReportedStep {
|
|
58
|
-
/**
|
|
58
|
+
/** App-defined business log type; never a write/fix operation enum. */
|
|
59
|
+
readonly type?: string;
|
|
60
|
+
/** Logical subnode grouping within this node visit; independent of individual log ids. */
|
|
61
|
+
readonly subnodeId?: string;
|
|
62
|
+
/** Log record identity, unique within one node visit; not a subnode id. */
|
|
59
63
|
readonly stepId: string;
|
|
60
64
|
/** Reported parent step, forming a tree; absent for a top-level step. */
|
|
61
65
|
readonly parentStepId?: string;
|
|
62
66
|
/** Self-reported reference only; never a workflow dispatch identity. */
|
|
63
67
|
readonly relatedDefinitionId?: string;
|
|
68
|
+
/** Real model/tool invocation identity, kept separate from the node-local subnode id. */
|
|
69
|
+
readonly relatedInvocationId?: string;
|
|
64
70
|
/** Short reported label. */
|
|
65
71
|
readonly name: string;
|
|
66
72
|
/** Reported step kind (for example a tool or model call); free-form and node-owned. */
|
|
@@ -88,7 +94,11 @@ export type SubFlowValue = null | boolean | number | string | readonly SubFlowVa
|
|
|
88
94
|
export interface RecordedSubStep extends ReportedStep {
|
|
89
95
|
/** Historical marker: only self-reported steps are recorded. */
|
|
90
96
|
readonly origin: 'reported';
|
|
91
|
-
/**
|
|
97
|
+
/** Append-only logging operation, separate from the App-owned business type. */
|
|
98
|
+
readonly operation?: 'write' | 'fix';
|
|
99
|
+
/** Original write record revised by this fix; fixes have their own stepId. */
|
|
100
|
+
readonly targetStepId?: string;
|
|
101
|
+
/** One-based append order within the node visit, including every fix. */
|
|
92
102
|
readonly sequence: number;
|
|
93
103
|
/** Optional only for historical records produced before the logger contract. */
|
|
94
104
|
readonly createdAtMs?: number;
|
|
@@ -98,10 +108,14 @@ export interface RecordedSubStep extends ReportedStep {
|
|
|
98
108
|
readonly payload?: Readonly<Record<string, SubFlowValue>>;
|
|
99
109
|
}
|
|
100
110
|
/** Input accepted by `write`; the logger assigns the step id and sequence. */
|
|
101
|
-
export interface SubFlowStepInput extends Omit<ReportedStep, 'stepId'> {
|
|
111
|
+
export interface SubFlowStepInput extends Omit<ReportedStep, 'stepId' | 'kind'> {
|
|
112
|
+
/** Optional family classification; defaults to the custom type when omitted. */
|
|
113
|
+
readonly kind?: string;
|
|
102
114
|
readonly payload?: Readonly<Record<string, SubFlowValue>>;
|
|
103
115
|
}
|
|
104
|
-
/**
|
|
116
|
+
/** Scoped writes cannot supply or override the identity bound by their delegate. */
|
|
117
|
+
export type ScopedSubFlowStepInput = Omit<SubFlowStepInput, 'subnodeId'>;
|
|
118
|
+
/** Explicit revision whitelist: identity, grouping and business type are never patchable. */
|
|
105
119
|
export interface SubFlowStepFix {
|
|
106
120
|
readonly status?: ReportedStepStatus;
|
|
107
121
|
readonly usedModel?: boolean;
|
|
@@ -112,31 +126,34 @@ export interface SubFlowStepFix {
|
|
|
112
126
|
/** Fill missing result fields; existing facts cannot be replaced. */
|
|
113
127
|
readonly payload?: Readonly<Record<string, SubFlowValue>>;
|
|
114
128
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
/** Appends one step and returns its assigned step id. */
|
|
121
|
-
write(input: SubFlowStepInput): string;
|
|
122
|
-
/** Applies a partial update; illegal status transitions and replacements are rejected. */
|
|
123
|
-
fix(stepId: string, patch: SubFlowStepFix): void;
|
|
124
|
-
/** Snapshot of the steps recorded so far, in write order. */
|
|
129
|
+
/** Common read/write surface for both independently implemented logger classes. */
|
|
130
|
+
export interface SubFlowLog {
|
|
131
|
+
write(input: ScopedSubFlowStepInput): string | -1;
|
|
132
|
+
fix(stepId: string | -1, patch: SubFlowStepFix): boolean;
|
|
133
|
+
/** Both logger classes return the same complete immutable ledger. */
|
|
125
134
|
readAll(): readonly Readonly<RecordedSubStep>[];
|
|
126
135
|
}
|
|
136
|
+
/** Nominal classes: only the full logger is accepted by native invocation requests. */
|
|
137
|
+
export type { SubFlowLogger, ScopedSubFlowLogger } from '../diagnostics/sub-flow.js';
|
|
138
|
+
import type { SubFlowLogger } from '../diagnostics/sub-flow.js';
|
|
127
139
|
/** Reporting surface handed to a node or tool for the duration of one visit. */
|
|
128
140
|
export interface NodeReportingContext {
|
|
129
141
|
/** Reports progress for the active node; Core supplies the execution identity. */
|
|
130
142
|
report(update: import('./execution/workflow.js').WorkflowNodeReport): void;
|
|
131
|
-
/**
|
|
143
|
+
/** Bound to this context and owned by runtime; repeated calls in one visit return the same instance. */
|
|
132
144
|
createSubFlowLogger(): SubFlowLogger;
|
|
133
145
|
}
|
|
134
146
|
/** Compact progress notification emitted for each accepted sub-step write or fix. */
|
|
135
147
|
export interface SubStepProgress {
|
|
148
|
+
readonly relatedInvocationId?: string;
|
|
136
149
|
readonly operation: 'write' | 'fix';
|
|
137
|
-
|
|
150
|
+
readonly type?: string;
|
|
151
|
+
readonly subnodeId?: string;
|
|
152
|
+
/** Original write record when this operation is a fix. */
|
|
153
|
+
readonly targetStepId?: string;
|
|
154
|
+
/** Unique id of this append-only record. */
|
|
138
155
|
readonly stepId: string;
|
|
139
|
-
/**
|
|
156
|
+
/** Append sequence of this record within the node visit, including every fix. */
|
|
140
157
|
readonly sequence: number;
|
|
141
158
|
/** Revision after the operation; `write` starts at 1. */
|
|
142
159
|
readonly revision: number;
|
|
@@ -151,7 +168,6 @@ export interface SubStepProgress {
|
|
|
151
168
|
export interface DebugStepsOutput {
|
|
152
169
|
/** Model use reported by this handler; omitted when unknown. */
|
|
153
170
|
readonly usedModel?: boolean;
|
|
154
|
-
/**
|
|
155
|
-
readonly
|
|
171
|
+
/** Runtime-owned immutable snapshot, committed when a logger writes or fixes a step. */
|
|
172
|
+
readonly recordedSubSteps?: readonly Readonly<RecordedSubStep>[];
|
|
156
173
|
}
|
|
157
|
-
export {};
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/** Serializable topology and observed execution contracts; layout belongs to consumers. */
|
|
2
|
+
import type { CoreDiagnosticRecord } from '../diagnostics.js';
|
|
3
|
+
import type { RecordedSubStep, SubFlowValue } from '../debug.js';
|
|
4
|
+
import type { WorkflowEvent } from './workflow.js';
|
|
5
|
+
/** Pure registered topology; routes sharing a target share an edge within one kind. */
|
|
6
|
+
export interface WorkflowGraph {
|
|
7
|
+
readonly schemaVersion: 1;
|
|
8
|
+
readonly workflowId: string;
|
|
9
|
+
readonly revision?: string;
|
|
10
|
+
readonly entryNodeId: string;
|
|
11
|
+
readonly nodes: readonly WorkflowGraphNode[];
|
|
12
|
+
readonly edges: readonly WorkflowGraphEdge[];
|
|
13
|
+
}
|
|
14
|
+
/** Node metadata and derived incident edges, without executable callbacks. */
|
|
15
|
+
export interface WorkflowGraphNode {
|
|
16
|
+
readonly id: string;
|
|
17
|
+
readonly name: string;
|
|
18
|
+
readonly description: string;
|
|
19
|
+
readonly order: number;
|
|
20
|
+
readonly startsRound: boolean;
|
|
21
|
+
readonly requires: readonly string[];
|
|
22
|
+
readonly provides: readonly string[];
|
|
23
|
+
readonly routes: readonly WorkflowGraphRoute[];
|
|
24
|
+
readonly incomingEdgeIds: readonly string[];
|
|
25
|
+
readonly outgoingEdgeIds: readonly string[];
|
|
26
|
+
}
|
|
27
|
+
/** Declaration order is retained even when multiple routes share a topology edge. */
|
|
28
|
+
export interface WorkflowGraphRoute {
|
|
29
|
+
readonly order: number;
|
|
30
|
+
readonly routeId?: string;
|
|
31
|
+
readonly description?: string;
|
|
32
|
+
readonly conditional: boolean;
|
|
33
|
+
readonly conditionId?: string;
|
|
34
|
+
readonly edgeId: string;
|
|
35
|
+
}
|
|
36
|
+
/** Completion has no target node; recovery edges remain distinct from normal edges. */
|
|
37
|
+
export interface WorkflowGraphEdge {
|
|
38
|
+
readonly id: string;
|
|
39
|
+
readonly kind: 'transition' | 'error' | 'complete';
|
|
40
|
+
readonly sourceNodeId: string;
|
|
41
|
+
readonly targetNodeId?: string;
|
|
42
|
+
readonly routeOrders: readonly number[];
|
|
43
|
+
readonly errorRouteId?: string;
|
|
44
|
+
}
|
|
45
|
+
/** Capture completeness is independent of whether diagnostic content was included. */
|
|
46
|
+
export interface WorkflowChainCoverage {
|
|
47
|
+
readonly complete: boolean | 'unknown';
|
|
48
|
+
readonly content: 'included' | 'omitted' | 'mixed' | 'unknown';
|
|
49
|
+
readonly issues: readonly {
|
|
50
|
+
readonly code: string;
|
|
51
|
+
readonly spanId?: string;
|
|
52
|
+
readonly eventId?: string;
|
|
53
|
+
readonly count?: number;
|
|
54
|
+
}[];
|
|
55
|
+
}
|
|
56
|
+
/** App resolves authorization, retention, pagination and storage paths before returning. */
|
|
57
|
+
export type WorkflowChainSource = (runId: string) => Promise<{
|
|
58
|
+
readonly events: readonly CoreDiagnosticRecord[];
|
|
59
|
+
readonly coverage: WorkflowChainCoverage;
|
|
60
|
+
}>;
|
|
61
|
+
/** Observed visits, never merged by static node id or synthesized from adjacency. */
|
|
62
|
+
export interface WorkflowChain {
|
|
63
|
+
readonly schemaVersion: 1;
|
|
64
|
+
readonly workflowId: string;
|
|
65
|
+
readonly runId: string;
|
|
66
|
+
readonly nodes: readonly WorkflowChainNode[];
|
|
67
|
+
readonly transitions: readonly WorkflowChainTransition[];
|
|
68
|
+
readonly coverage: WorkflowChainCoverage;
|
|
69
|
+
}
|
|
70
|
+
/** Historical subflow snapshots may omit recorder fields or descriptive content. */
|
|
71
|
+
export type WorkflowSubflowRecord = Omit<RecordedSubStep, 'name' | 'kind'> & {
|
|
72
|
+
readonly name?: string;
|
|
73
|
+
readonly kind?: string;
|
|
74
|
+
readonly sourceEventId?: string;
|
|
75
|
+
readonly producerSequence?: number;
|
|
76
|
+
};
|
|
77
|
+
/** A single node visit, with flat ordered subflow records including every independent fix. */
|
|
78
|
+
export interface WorkflowChainNode {
|
|
79
|
+
readonly spanId: string;
|
|
80
|
+
readonly nodeId: string;
|
|
81
|
+
readonly workflowInvocationId?: string;
|
|
82
|
+
readonly definitionRevision?: string;
|
|
83
|
+
readonly name?: string;
|
|
84
|
+
readonly description?: string;
|
|
85
|
+
readonly kind?: string;
|
|
86
|
+
readonly roundIndex?: number;
|
|
87
|
+
readonly firstEventSequence: number;
|
|
88
|
+
readonly lastEventSequence: number;
|
|
89
|
+
readonly enteredAtMs?: number;
|
|
90
|
+
readonly endedAtMs?: number;
|
|
91
|
+
readonly durationMs?: number;
|
|
92
|
+
readonly status: 'running' | 'success' | 'error' | 'cancelled' | 'unknown';
|
|
93
|
+
readonly usedModel?: boolean;
|
|
94
|
+
readonly events: readonly WorkflowChainNodeEvent[];
|
|
95
|
+
readonly subflow: readonly WorkflowSubflowRecord[];
|
|
96
|
+
}
|
|
97
|
+
/** Node-local lifecycle facts; payload remains subject to the source's content policy. */
|
|
98
|
+
export interface WorkflowChainNodeEvent {
|
|
99
|
+
readonly eventId: string;
|
|
100
|
+
readonly producerSequence: number;
|
|
101
|
+
readonly occurredAtMs: number;
|
|
102
|
+
readonly type: WorkflowEvent['type'] | 'progress';
|
|
103
|
+
readonly phase?: WorkflowEvent['phase'];
|
|
104
|
+
readonly routeId?: string;
|
|
105
|
+
readonly conditionId?: string;
|
|
106
|
+
readonly errorCode?: string;
|
|
107
|
+
readonly errorEventId?: string;
|
|
108
|
+
readonly payload?: SubFlowValue;
|
|
109
|
+
}
|
|
110
|
+
/** Actual handoff evidence; a target span does not prove its enter event was captured. */
|
|
111
|
+
export interface WorkflowChainTransition {
|
|
112
|
+
readonly eventId: string;
|
|
113
|
+
readonly producerSequence: number;
|
|
114
|
+
readonly occurredAtMs: number;
|
|
115
|
+
readonly type: 'transition' | 'complete';
|
|
116
|
+
readonly sourceSpanId: string;
|
|
117
|
+
readonly targetSpanId?: string;
|
|
118
|
+
readonly targetNodeId?: string;
|
|
119
|
+
readonly routeOrder?: number;
|
|
120
|
+
readonly routeId?: string;
|
|
121
|
+
readonly conditionId?: string;
|
|
122
|
+
readonly errorEventId?: string;
|
|
123
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|