@jangts/agent-core 0.3.5 → 0.5.0
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 +41 -0
- package/README.md +33 -31
- package/development/docs/README.en.md +2 -2
- package/development/docs/acceptance.en.md +2 -2
- package/development/docs/agent-development.en.md +1 -1
- package/development/docs/api/README.en.md +6 -2
- package/development/docs/api/README.md +4 -0
- package/development/docs/api/configuration-in-workflow.en.md +403 -0
- package/development/docs/api/configuration-in-workflow.md +403 -0
- package/development/docs/api/core.en.md +7 -7
- package/development/docs/api/core.md +6 -6
- package/development/docs/api/decorators.en.md +20 -0
- package/development/docs/api/decorators.md +20 -0
- package/development/docs/api/examples.en.md +1 -1
- package/development/docs/api/examples.md +1 -1
- package/development/docs/api/kits.en.md +22 -4
- package/development/docs/api/kits.md +22 -4
- package/development/docs/api/knowledge-answering.en.md +4 -1
- package/development/docs/api/knowledge-answering.md +4 -1
- package/development/docs/api/mock.en.md +2 -0
- package/development/docs/api/mock.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 +876 -657
- package/development/docs/api/reference.md +662 -508
- package/development/docs/api/support.en.md +55 -9
- package/development/docs/api/support.md +53 -7
- package/development/docs/api/types.en.md +36 -1
- package/development/docs/api/types.md +44 -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/docs/basic-usage.en.md +1 -1
- package/development/docs/basic-usage.md +1 -1
- package/development/docs/create-app.en.md +1 -1
- package/development/docs/examples/registration/associated.mjs +8 -0
- package/development/docs/examples/registration/independent.mjs +20 -0
- package/development/docs/examples/registration/protocol.mjs +48 -0
- package/development/docs/examples/registration/run.mjs +47 -0
- package/development/docs/examples/registration/tools.mjs +75 -0
- package/development/docs/examples/registration/workflow.mjs +109 -0
- package/development/docs/flows/knowledge-maintenance.en.md +1 -1
- package/development/docs/materials.en.md +1 -1
- package/development/docs/mechanisms.en.md +2 -2
- package/development/docs/mechanisms.md +1 -1
- package/development/docs/operations.en.md +1 -1
- package/development/docs/records.en.md +1 -1
- package/development/docs/registration.en.md +4 -4
- package/development/docs/registration.md +2 -2
- package/development/skills/agent-core-app/SKILL.md +16 -4
- package/development/skills/agent-core-app/templates/node-ts/README.md.tpl +1 -1
- package/development/skills/agent-core-app/templates/node-ts/src/catalog.ts.tpl +2 -2
- package/development/skills/agent-core-app/templates/node-ts/test/app.test.mjs.tpl +1 -1
- package/dist/cjs/capabilities/index.js +5 -12
- package/dist/cjs/capabilities/invocation.js +21 -1
- package/dist/cjs/capabilities/model.js +56 -12
- package/dist/cjs/capabilities/tool-conditions.js +65 -0
- package/dist/cjs/capabilities/tool-runtime.js +18 -4
- package/dist/cjs/capabilities/tools.js +12 -17
- package/dist/cjs/context/audit.js +5 -8
- package/dist/cjs/context/index.js +3 -4
- 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/index.js +21 -17
- 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 +30 -25
- package/dist/cjs/kits/delivery.js +36 -24
- package/dist/cjs/kits/domains/catalog.js +113 -0
- package/dist/cjs/kits/domains/index.js +20 -0
- package/dist/cjs/kits/{intents → domains}/registry.js +77 -31
- package/dist/cjs/kits/{intents → domains}/scopes.js +21 -21
- package/dist/cjs/kits/index.js +14 -13
- package/dist/cjs/kits/knowledge/catalog.js +33 -30
- package/dist/cjs/kits/policies.js +3 -2
- package/dist/cjs/kits/presentation/formats.js +110 -0
- package/dist/cjs/kits/presentation/index.js +10 -5
- package/dist/cjs/kits/presentation/outputs.js +2 -1
- package/dist/cjs/kits/reflection.js +2 -2
- package/dist/cjs/kits/retrieval.js +7 -1
- package/dist/cjs/kits/text-blocks.js +101 -0
- package/dist/cjs/kits/tools.js +247 -158
- package/dist/cjs/model/decision-retry.js +2 -1
- package/dist/cjs/model/index.js +2 -6
- package/dist/cjs/model/interactions/calls.js +61 -0
- package/dist/cjs/model/interactions/json-object.js +11 -11
- package/dist/cjs/model/interactions/native-functions.js +17 -23
- package/dist/cjs/model/message-diagnostics.js +13 -3
- package/dist/cjs/model/options.js +2 -0
- package/dist/cjs/model/preparation.js +39 -6
- package/dist/cjs/model/runner.js +34 -5
- package/dist/cjs/model/selection-transport.js +4 -4
- package/dist/cjs/model/token-budget.js +3 -0
- package/dist/cjs/model/transports.js +3 -2
- package/dist/cjs/structs/index.js +7 -6
- package/dist/cjs/structs/kits/domains/scopes.js +2 -0
- package/dist/cjs/structs/kits/presentation/formats.js +2 -0
- package/dist/cjs/structs/kits/text-blocks.js +11 -0
- package/dist/cjs/structs/model/session.js +11 -0
- package/dist/cjs/structs/support/associations.js +2 -0
- package/dist/cjs/support/associations.js +79 -0
- package/dist/cjs/support/context/nodes.js +48 -45
- package/dist/cjs/support/context/processing.js +6 -3
- package/dist/cjs/support/context/snapshot.js +11 -1
- package/dist/cjs/support/decorators/index.js +45 -3
- package/dist/cjs/support/index.js +1 -0
- package/dist/cjs/support/model/capabilities.js +35 -5
- package/dist/cjs/support/model/chat-completions.js +11 -2
- package/dist/cjs/support/model/completion/calls.js +10 -56
- package/dist/cjs/support/model/preview-rules.js +11 -11
- package/dist/cjs/support/model/reply-preview.js +3 -3
- package/dist/cjs/support/model/session.js +152 -0
- package/dist/cjs/support/model/stage-transport.js +8 -8
- package/dist/cjs/support/model/transports.js +5 -2
- package/dist/cjs/support/tools.js +42 -17
- package/dist/cjs/utils/invocation.js +1 -0
- package/dist/esm/capabilities/index.d.ts +5 -10
- package/dist/esm/capabilities/index.js +5 -9
- package/dist/esm/capabilities/invocation.js +21 -1
- package/dist/esm/capabilities/model.d.ts +17 -13
- package/dist/esm/capabilities/model.js +54 -10
- package/dist/esm/capabilities/tool-conditions.d.ts +9 -0
- package/dist/esm/capabilities/tool-conditions.js +59 -0
- package/dist/esm/capabilities/tool-runtime.js +18 -4
- package/dist/esm/capabilities/tools.d.ts +3 -3
- package/dist/esm/capabilities/tools.js +12 -17
- package/dist/esm/context/audit.js +5 -8
- package/dist/esm/context/index.d.ts +3 -4
- package/dist/esm/context/index.js +3 -4
- 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/index.d.ts +19 -13
- package/dist/esm/index.js +18 -15
- 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.d.ts +6 -6
- package/dist/esm/kits/answering/tasks.js +30 -25
- package/dist/esm/kits/delivery.d.ts +0 -9
- package/dist/esm/kits/delivery.js +36 -24
- package/dist/esm/kits/domains/catalog.d.ts +42 -0
- package/dist/esm/kits/domains/catalog.js +110 -0
- package/dist/esm/kits/domains/index.d.ts +4 -0
- package/dist/esm/kits/domains/index.js +4 -0
- package/dist/esm/kits/domains/registry.d.ts +100 -0
- package/dist/esm/kits/{intents → domains}/registry.js +77 -31
- package/dist/esm/kits/{intents → domains}/scopes.d.ts +18 -18
- package/dist/esm/kits/{intents → domains}/scopes.js +21 -21
- package/dist/esm/kits/index.d.ts +6 -5
- package/dist/esm/kits/index.js +6 -5
- 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/formats.d.ts +42 -0
- package/dist/esm/kits/presentation/formats.js +105 -0
- package/dist/esm/kits/presentation/index.d.ts +14 -5
- package/dist/esm/kits/presentation/index.js +10 -5
- package/dist/esm/kits/presentation/outputs.js +2 -1
- package/dist/esm/kits/reflection.js +3 -3
- package/dist/esm/kits/retrieval.js +7 -1
- package/dist/esm/kits/text-blocks.d.ts +24 -0
- package/dist/esm/kits/text-blocks.js +97 -0
- package/dist/esm/kits/tools.d.ts +69 -52
- package/dist/esm/kits/tools.js +247 -158
- package/dist/esm/model/decision-retry.d.ts +2 -0
- package/dist/esm/model/decision-retry.js +2 -1
- package/dist/esm/model/index.d.ts +2 -6
- package/dist/esm/model/index.js +2 -6
- package/dist/esm/model/interactions/calls.d.ts +40 -0
- package/dist/esm/model/interactions/calls.js +58 -0
- package/dist/esm/model/interactions/json-object.js +11 -11
- package/dist/esm/model/interactions/native-functions.js +17 -23
- package/dist/esm/model/message-diagnostics.d.ts +6 -5
- package/dist/esm/model/message-diagnostics.js +13 -3
- package/dist/esm/model/options.js +2 -0
- package/dist/esm/model/preparation.js +39 -6
- package/dist/esm/model/runner.d.ts +2 -0
- package/dist/esm/model/runner.js +34 -5
- package/dist/esm/model/selection-transport.js +4 -4
- package/dist/esm/model/token-budget.js +3 -0
- package/dist/esm/model/transports.d.ts +2 -1
- package/dist/esm/model/transports.js +3 -2
- package/dist/esm/structs/capabilities/invocation.d.ts +3 -1
- package/dist/esm/structs/capabilities/model.d.ts +42 -11
- package/dist/esm/structs/capabilities/tools.d.ts +16 -7
- package/dist/esm/structs/common.d.ts +3 -1
- 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.d.ts +4 -3
- package/dist/esm/structs/index.d.ts +8 -6
- package/dist/esm/structs/index.js +7 -6
- package/dist/esm/structs/kits/answering/tasks.d.ts +2 -2
- package/dist/esm/structs/kits/{intents → domains}/catalog.d.ts +20 -20
- package/dist/esm/structs/kits/{intents → domains}/registry.d.ts +24 -21
- package/dist/esm/structs/kits/{intents → domains}/scopes.d.ts +9 -9
- package/dist/esm/structs/kits/domains/scopes.js +1 -0
- package/dist/esm/structs/kits/presentation/formats.d.ts +42 -0
- package/dist/esm/structs/kits/presentation/formats.js +1 -0
- package/dist/esm/structs/kits/text-blocks.d.ts +25 -0
- package/dist/esm/structs/kits/text-blocks.js +10 -0
- package/dist/esm/structs/kits/tools.d.ts +82 -83
- package/dist/esm/structs/model/interactions.d.ts +18 -11
- package/dist/esm/structs/model/messages.d.ts +34 -2
- package/dist/esm/structs/model/runtime.d.ts +9 -0
- package/dist/esm/structs/model/session.d.ts +29 -0
- package/dist/esm/structs/model/session.js +10 -0
- package/dist/esm/structs/support/associations.d.ts +22 -0
- package/dist/esm/structs/support/associations.js +1 -0
- package/dist/esm/structs/support/model/interactions.d.ts +2 -2
- package/dist/esm/structs/support/model/messages.d.ts +6 -13
- package/dist/esm/structs/support/tools.d.ts +23 -23
- package/dist/esm/support/associations.d.ts +26 -0
- package/dist/esm/support/associations.js +76 -0
- package/dist/esm/support/context/nodes.js +48 -45
- package/dist/esm/support/context/processing.js +6 -3
- package/dist/esm/support/context/snapshot.js +11 -1
- package/dist/esm/support/decorators/index.d.ts +13 -2
- package/dist/esm/support/decorators/index.js +40 -3
- package/dist/esm/support/decorators/pool.d.ts +6 -5
- package/dist/esm/support/decorators/types.d.ts +3 -1
- package/dist/esm/support/index.d.ts +1 -0
- package/dist/esm/support/index.js +1 -0
- package/dist/esm/support/model/capabilities.d.ts +8 -3
- package/dist/esm/support/model/capabilities.js +35 -5
- package/dist/esm/support/model/chat-completions.js +11 -2
- package/dist/esm/support/model/completion/calls.d.ts +6 -34
- package/dist/esm/support/model/completion/calls.js +8 -55
- package/dist/esm/support/model/index.d.ts +1 -1
- package/dist/esm/support/model/preview-rules.d.ts +13 -8
- package/dist/esm/support/model/preview-rules.js +11 -11
- package/dist/esm/support/model/reply-preview.d.ts +2 -2
- package/dist/esm/support/model/reply-preview.js +3 -3
- package/dist/esm/support/model/session.d.ts +61 -0
- package/dist/esm/support/model/session.js +148 -0
- package/dist/esm/support/model/stage-transport.d.ts +1 -1
- package/dist/esm/support/model/stage-transport.js +9 -9
- package/dist/esm/support/model/transports.d.ts +2 -0
- package/dist/esm/support/model/transports.js +2 -1
- package/dist/esm/support/tools.d.ts +11 -3
- package/dist/esm/support/tools.js +43 -18
- package/dist/esm/utils/catalog.d.ts +1 -1
- package/dist/esm/utils/invocation.d.ts +1 -0
- package/dist/esm/utils/invocation.js +1 -0
- package/package.json +20 -6
- package/dist/cjs/execution/index.js +0 -30
- package/dist/cjs/kits/intents/catalog.js +0 -82
- package/dist/cjs/kits/intents/index.js +0 -35
- package/dist/cjs/utils/debug.js +0 -30
- package/dist/esm/execution/index.d.ts +0 -11
- package/dist/esm/execution/index.js +0 -11
- package/dist/esm/kits/intents/catalog.d.ts +0 -39
- package/dist/esm/kits/intents/catalog.js +0 -79
- package/dist/esm/kits/intents/index.d.ts +0 -22
- package/dist/esm/kits/intents/index.js +0 -18
- package/dist/esm/kits/intents/registry.d.ts +0 -88
- package/dist/esm/utils/debug.d.ts +0 -27
- package/dist/esm/utils/debug.js +0 -27
- /package/dist/cjs/structs/{kits/intents/catalog.js → execution/workflow-views.js} +0 -0
- /package/dist/cjs/structs/kits/{intents/registry.js → domains/catalog.js} +0 -0
- /package/dist/cjs/structs/kits/{intents/scopes.js → domains/registry.js} +0 -0
- /package/dist/esm/structs/{kits/intents/catalog.js → execution/workflow-views.js} +0 -0
- /package/dist/esm/structs/kits/{intents/registry.js → domains/catalog.js} +0 -0
- /package/dist/esm/structs/kits/{intents/scopes.js → domains/registry.js} +0 -0
package/dist/esm/kits/tools.js
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Tools Kit: a lifecycle registry for
|
|
2
|
+
* Tools Kit: a lifecycle registry for App-declared tools.
|
|
3
3
|
*
|
|
4
4
|
* Place in the package: an optional App-side library for tools that need more than a plain
|
|
5
|
-
* definition — availability,
|
|
5
|
+
* definition — availability, one lifecycle hook set, output ownership, context fields, task rules
|
|
6
6
|
* and delivery hooks. Actual invocation is delegated to Core (`ToolRegistry` and
|
|
7
7
|
* `executeInvocation`); domain predicates, phase names and vocabulary stay App configuration.
|
|
8
8
|
*
|
|
9
|
+
* A tool claims an invocation through `lifecycle.id` and, when a node dispatches by name, a
|
|
10
|
+
* delegate id through `delegate`. Both indexes live here, so dispatch is one lookup.
|
|
11
|
+
*
|
|
9
12
|
* Exports: `ToolLifecycleRegistry` (also exported as `Tools`) and the tool lifecycle types.
|
|
10
13
|
*/
|
|
11
14
|
import { assertion, booleanResult, synchronous } from '../utils/synchronous.js';
|
|
@@ -15,12 +18,34 @@ import { ToolRegistry } from '../capabilities/tools.js';
|
|
|
15
18
|
import { executeInvocation } from '../capabilities/invocation.js';
|
|
16
19
|
import { ProtocolRegistry } from '../utils/protocol-registry.js';
|
|
17
20
|
import { registrationData } from '../utils/registration.js';
|
|
21
|
+
import { ModelToolRegistry } from '../capabilities/model.js';
|
|
22
|
+
import { toolActive } from '../capabilities/tool-conditions.js';
|
|
23
|
+
/** Hooks that must finish with `undefined`; every other hook only rejects thenables. */
|
|
24
|
+
const assertionHooks = ['authorize', 'inspect', 'finalize'];
|
|
25
|
+
/** Every hook a lifecycle block may declare, in binding order. */
|
|
26
|
+
const lifecycleHooks = [
|
|
27
|
+
'call',
|
|
28
|
+
'authorize',
|
|
29
|
+
'resume',
|
|
30
|
+
'complete',
|
|
31
|
+
'inspect',
|
|
32
|
+
'finalize',
|
|
33
|
+
'rules',
|
|
34
|
+
'modelFields',
|
|
35
|
+
'visibleText',
|
|
36
|
+
'reviewDraft',
|
|
37
|
+
'composition',
|
|
38
|
+
'recoveryContext',
|
|
39
|
+
'projectContext',
|
|
40
|
+
'missingInteractionAllowed',
|
|
41
|
+
'supportsGoal',
|
|
42
|
+
];
|
|
18
43
|
/** Pin contract methods and configuration without cloning the App services they use. */
|
|
19
44
|
function snapshotLifecycleOptions(options) {
|
|
20
|
-
const { receiver, execution, errors, registration,
|
|
21
|
-
if (typeof
|
|
22
|
-
throw new TypeError('An
|
|
23
|
-
const { kinds, isValidId, audiences, effects,
|
|
45
|
+
const { receiver, execution, errors, registration, invocationId, outputs, modelOutputs, unavailableInvocation, forbidden, contextFragment, goalIds, diagnostics, signal, journal, checkpoint, receipt, receiptError, } = options;
|
|
46
|
+
if (typeof goalIds !== 'function')
|
|
47
|
+
throw new TypeError('An goalIds provider is required');
|
|
48
|
+
const { kinds, isValidId, audiences, effects, goalExists, protocolVersion, tags, invalidTool, invalidInvocation, } = registration;
|
|
24
49
|
return Object.freeze({
|
|
25
50
|
receiver: receiver?.bind(options),
|
|
26
51
|
execution: execution && Object.freeze({ signal: execution.signal, diagnostics: execution.diagnostics }),
|
|
@@ -34,7 +59,7 @@ function snapshotLifecycleOptions(options) {
|
|
|
34
59
|
audiences: Object.freeze([...audiences]),
|
|
35
60
|
effects: Object.freeze([...effects]),
|
|
36
61
|
isValidId: isValidId.bind(registration),
|
|
37
|
-
|
|
62
|
+
goalExists: goalExists.bind(registration),
|
|
38
63
|
protocolVersion,
|
|
39
64
|
tags: tags && Object.freeze([...tags]),
|
|
40
65
|
invalidTool: typeof invalidTool === 'function' ? invalidTool.bind(registration) : invalidTool,
|
|
@@ -42,13 +67,13 @@ function snapshotLifecycleOptions(options) {
|
|
|
42
67
|
? invalidInvocation.bind(registration)
|
|
43
68
|
: invalidInvocation,
|
|
44
69
|
}),
|
|
45
|
-
|
|
70
|
+
invocationId: invocationId.bind(options),
|
|
46
71
|
outputs: outputs.bind(options),
|
|
47
72
|
modelOutputs: modelOutputs?.bind(options),
|
|
48
|
-
|
|
73
|
+
unavailableInvocation: unavailableInvocation?.bind(options),
|
|
49
74
|
forbidden: forbidden?.bind(options),
|
|
50
75
|
contextFragment: contextFragment.bind(options),
|
|
51
|
-
|
|
76
|
+
goalIds: goalIds.bind(options),
|
|
52
77
|
diagnostics: diagnostics?.bind(options),
|
|
53
78
|
signal: signal?.bind(options),
|
|
54
79
|
journal: journal?.bind(options),
|
|
@@ -57,18 +82,38 @@ function snapshotLifecycleOptions(options) {
|
|
|
57
82
|
receiptError: receiptError?.bind(options),
|
|
58
83
|
});
|
|
59
84
|
}
|
|
85
|
+
/** Bind every declared hook to its owner and freeze the result. */
|
|
86
|
+
function bindLifecycle(value) {
|
|
87
|
+
return Object.freeze({
|
|
88
|
+
...registrationData(value, [...lifecycleHooks, 'id', 'protocol']),
|
|
89
|
+
id: value.id,
|
|
90
|
+
protocol: value.protocol,
|
|
91
|
+
...Object.fromEntries(lifecycleHooks.map((key) => {
|
|
92
|
+
const handler = value[key];
|
|
93
|
+
return [
|
|
94
|
+
key,
|
|
95
|
+
typeof handler === 'function'
|
|
96
|
+
? (...args) => (assertionHooks.includes(key) ? assertion : synchronous)(handler.apply(value, args), `Tool lifecycle ${key} must finish synchronously with the declared result`)
|
|
97
|
+
: handler,
|
|
98
|
+
];
|
|
99
|
+
})),
|
|
100
|
+
});
|
|
101
|
+
}
|
|
60
102
|
/**
|
|
61
|
-
* Registry of tools whose lifecycle, protocols and contributions
|
|
103
|
+
* Registry of tools whose lifecycle, protocols and context contributions the App declares.
|
|
62
104
|
*
|
|
63
105
|
* Registration is only possible before `seal()`; afterwards the catalog is read-only, so a run
|
|
64
|
-
* cannot change which tools exist. Every
|
|
65
|
-
*
|
|
106
|
+
* cannot change which tools exist. Every hook is optional and is applied in registration order,
|
|
107
|
+
* which is why `registerAll` preserves iteration order.
|
|
66
108
|
*/
|
|
67
109
|
export class ToolLifecycleRegistry {
|
|
68
110
|
tools;
|
|
69
111
|
registered = new Map();
|
|
70
112
|
sources = new Map();
|
|
113
|
+
/** Tools that own an invocation id; dispatch by decision next reads this index. */
|
|
71
114
|
invocations = new Map();
|
|
115
|
+
/** Tools that answer a delegate id; dispatch by node reads this index. */
|
|
116
|
+
delegateIndex = new Map();
|
|
72
117
|
normalizer;
|
|
73
118
|
finalizer;
|
|
74
119
|
sealed = false;
|
|
@@ -79,7 +124,7 @@ export class ToolLifecycleRegistry {
|
|
|
79
124
|
/**
|
|
80
125
|
* @param options - Receiver, execution context, error wording, registration policy, protocol
|
|
81
126
|
* providers and optional journal/receipt ports; contract methods are bound to their owner.
|
|
82
|
-
* @throws TypeError When no `
|
|
127
|
+
* @throws TypeError When no `goalIds` provider is supplied.
|
|
83
128
|
*/
|
|
84
129
|
constructor(options) {
|
|
85
130
|
this.options = snapshotLifecycleOptions(options);
|
|
@@ -89,6 +134,7 @@ export class ToolLifecycleRegistry {
|
|
|
89
134
|
});
|
|
90
135
|
this.diagnostics = this.options.execution?.diagnostics;
|
|
91
136
|
this.invocationPorts = Object.freeze({
|
|
137
|
+
logger: this.options.logger,
|
|
92
138
|
diagnostics: this.options.diagnostics ?? (() => this.diagnostics),
|
|
93
139
|
signal: this.options.signal,
|
|
94
140
|
journal: this.options.journal,
|
|
@@ -113,17 +159,31 @@ export class ToolLifecycleRegistry {
|
|
|
113
159
|
* hooks must return `void` synchronously, while the remaining handlers are checked for
|
|
114
160
|
* accidental thenables only.
|
|
115
161
|
*
|
|
116
|
-
* @param input - Tool definition with identity, audience, effect and its
|
|
162
|
+
* @param input - Tool definition with identity, audience, effect and its hooks.
|
|
117
163
|
* @returns This registry, for chaining.
|
|
118
164
|
* @throws A configured registration error for a sealed registry, invalid identity,
|
|
119
|
-
* description, audience, effect,
|
|
120
|
-
* incomplete
|
|
165
|
+
* description, audience, effect, goal ids, missing handlers, duplicate tool id,
|
|
166
|
+
* duplicate delegate id, incomplete lifecycle contract, protocol version mismatch or
|
|
167
|
+
* duplicate invocation id.
|
|
121
168
|
*/
|
|
122
169
|
register(input) {
|
|
123
|
-
const { id, kind, description, audience, effect,
|
|
170
|
+
const { id, kind, description, audience, effect, goalIds, delegate, lifecycle, parseInput: inputParser, parseOutput: outputParser, active: availability, execute, debugSteps, } = input;
|
|
124
171
|
const parseInput = inputParser?.bind(input), parseOutput = outputParser?.bind(input);
|
|
125
|
-
const available = availability
|
|
126
|
-
const
|
|
172
|
+
const available = typeof availability === 'function' ? availability.bind(input) : availability;
|
|
173
|
+
const frozenGoalIds = Array.isArray(goalIds) ? Object.freeze([...goalIds]) : goalIds;
|
|
174
|
+
// Validate the declaration before projecting it into a model tool: a sealed or invalid
|
|
175
|
+
// registration must report its configured error instead of a schema-projection failure.
|
|
176
|
+
this.assertRegistration(input, { parseInput, parseOutput, execute });
|
|
177
|
+
const modelTool = new ModelToolRegistry([
|
|
178
|
+
{
|
|
179
|
+
id,
|
|
180
|
+
description,
|
|
181
|
+
inputSchema: input.inputSchema ?? { jsonSchema: { type: 'object' }, parse: parseInput },
|
|
182
|
+
resolve: input.resolve?.bind(input) ?? ((value) => ({ toolId: id, input: value })),
|
|
183
|
+
active: typeof input.active === 'function' ? input.active.bind(input) : input.active,
|
|
184
|
+
asIntent: typeof input.asIntent === 'function' ? input.asIntent.bind(input) : input.asIntent,
|
|
185
|
+
},
|
|
186
|
+
]).require(id);
|
|
127
187
|
const tool = {
|
|
128
188
|
...registrationData(input, [
|
|
129
189
|
'id',
|
|
@@ -131,148 +191,148 @@ export class ToolLifecycleRegistry {
|
|
|
131
191
|
'description',
|
|
132
192
|
'audience',
|
|
133
193
|
'effect',
|
|
134
|
-
'
|
|
135
|
-
'
|
|
136
|
-
'
|
|
194
|
+
'goalIds',
|
|
195
|
+
'delegate',
|
|
196
|
+
'lifecycle',
|
|
137
197
|
'parseInput',
|
|
138
198
|
'parseOutput',
|
|
139
199
|
'execute',
|
|
140
200
|
'debugSteps',
|
|
141
|
-
'
|
|
201
|
+
'active',
|
|
142
202
|
]),
|
|
203
|
+
...modelTool,
|
|
143
204
|
id,
|
|
144
205
|
kind,
|
|
145
206
|
description,
|
|
146
207
|
audience,
|
|
147
208
|
effect,
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
209
|
+
delegate,
|
|
210
|
+
lifecycle,
|
|
211
|
+
goalIds: frozenGoalIds,
|
|
151
212
|
parseInput,
|
|
152
213
|
parseOutput,
|
|
153
214
|
debugSteps: debugSteps?.bind(input),
|
|
154
215
|
execute: execute?.bind(input),
|
|
155
|
-
|
|
156
|
-
? (context) => booleanResult(available(context), 'Tool availability must be synchronous', 'Tool availability must return boolean')
|
|
157
|
-
: undefined,
|
|
216
|
+
active: available,
|
|
158
217
|
};
|
|
159
|
-
const bindMethods = (value, keys, data = {}) => Object.freeze({
|
|
160
|
-
...registrationData(value, [...keys, ...Reflect.ownKeys(data)]),
|
|
161
|
-
...data,
|
|
162
|
-
...Object.fromEntries(keys.map((key) => {
|
|
163
|
-
const handler = value[key];
|
|
164
|
-
return [
|
|
165
|
-
key,
|
|
166
|
-
typeof handler === 'function'
|
|
167
|
-
? (...args) => (['authorize', 'inspect', 'finalize'].includes(String(key))
|
|
168
|
-
? assertion
|
|
169
|
-
: synchronous)(handler.apply(value, args), `Tool lifecycle ${String(key)} must finish synchronously with the declared result`)
|
|
170
|
-
: handler,
|
|
171
|
-
];
|
|
172
|
-
})),
|
|
173
|
-
});
|
|
174
218
|
const registered = Object.freeze({
|
|
175
219
|
...tool,
|
|
176
|
-
...(
|
|
177
|
-
? {
|
|
178
|
-
invocation: bindMethods(invocation, ['call', 'inspect', 'authorize', 'resume', 'complete'], { step: invocation.step, protocol: invocation.protocol }),
|
|
179
|
-
}
|
|
180
|
-
: {}),
|
|
181
|
-
...(contribution
|
|
182
|
-
? {
|
|
183
|
-
contribution: bindMethods(contribution, [
|
|
184
|
-
'composition',
|
|
185
|
-
'inspect',
|
|
186
|
-
'recoveryContext',
|
|
187
|
-
'projectContext',
|
|
188
|
-
'visibleText',
|
|
189
|
-
'reviewDraft',
|
|
190
|
-
'finalize',
|
|
191
|
-
'missingInteractionAllowed',
|
|
192
|
-
'rules',
|
|
193
|
-
'modelFields',
|
|
194
|
-
'supportsIntent',
|
|
195
|
-
]),
|
|
196
|
-
}
|
|
197
|
-
: {}),
|
|
220
|
+
...(lifecycle ? { lifecycle: bindLifecycle(lifecycle) } : {}),
|
|
198
221
|
});
|
|
199
|
-
return this.install(input, registered);
|
|
222
|
+
return this.install(input, registered, true);
|
|
200
223
|
}
|
|
201
|
-
|
|
202
|
-
|
|
224
|
+
/** Validate one declaration in the fixed order documented by `register`. */
|
|
225
|
+
assertRegistration(input, handlers) {
|
|
203
226
|
const policy = this.options.registration;
|
|
204
227
|
const identityPredicate = policy.isValidId;
|
|
205
228
|
const isValidId = () => booleanResult(identityPredicate(input), 'Tool identity predicates must be synchronous', 'Tool identity predicates must return boolean');
|
|
206
229
|
const invalid = policy.invalidTool ?? 'Invalid registered tool';
|
|
207
230
|
const invalidInvocation = policy.invalidInvocation ?? 'Invalid or duplicate tool invocation';
|
|
231
|
+
const { lifecycle } = input;
|
|
232
|
+
// A lifecycle block may declare hooks only. Declaring any invocation part makes all three
|
|
233
|
+
// required, so a partial declaration fails instead of silently never running.
|
|
234
|
+
const ownsInvocation = !!lifecycle &&
|
|
235
|
+
(lifecycle.id !== undefined ||
|
|
236
|
+
lifecycle.protocol !== undefined ||
|
|
237
|
+
lifecycle.call !== undefined);
|
|
238
|
+
const { parseInput, parseOutput, execute } = handlers;
|
|
208
239
|
breakWhile([
|
|
240
|
+
{ occur: () => this.sealed, error: 'Tool registry is sealed', tags: ['sealed'] },
|
|
209
241
|
{
|
|
210
|
-
occur: [() => !policy.kinds.includes(
|
|
242
|
+
occur: [() => !policy.kinds.includes(input.kind), () => !isValidId()],
|
|
211
243
|
error: invalid,
|
|
212
244
|
tags: ['identity'],
|
|
213
245
|
},
|
|
214
|
-
{
|
|
246
|
+
{
|
|
247
|
+
occur: () => typeof input.description !== 'string' || !input.description.trim(),
|
|
248
|
+
error: invalid,
|
|
249
|
+
tags: ['description'],
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
occur: () => input.active !== undefined &&
|
|
253
|
+
typeof input.active !== 'boolean' &&
|
|
254
|
+
typeof input.active !== 'function',
|
|
255
|
+
error: invalid,
|
|
256
|
+
tags: ['active'],
|
|
257
|
+
},
|
|
215
258
|
{
|
|
216
259
|
occur: [
|
|
217
|
-
() => !policy.audiences.includes(
|
|
218
|
-
() => !policy.effects.includes(
|
|
260
|
+
() => !policy.audiences.includes(input.audience),
|
|
261
|
+
() => !policy.effects.includes(input.effect),
|
|
219
262
|
],
|
|
220
263
|
error: invalid,
|
|
221
264
|
tags: ['execution-contract'],
|
|
222
265
|
},
|
|
223
266
|
{
|
|
224
267
|
occur: [
|
|
225
|
-
() =>
|
|
226
|
-
() =>
|
|
227
|
-
() =>
|
|
228
|
-
|
|
229
|
-
() =>
|
|
268
|
+
() => input.goalIds !== 'all' && !Array.isArray(input.goalIds),
|
|
269
|
+
() => input.goalIds !== 'all' && !input.goalIds.length,
|
|
270
|
+
() => input.goalIds !== 'all' &&
|
|
271
|
+
input.goalIds.some((goal) => !booleanResult(policy.goalExists(goal), 'Goal lookup must return boolean synchronously')),
|
|
272
|
+
() => input.goalIds !== 'all' && new Set(input.goalIds).size !== input.goalIds.length,
|
|
230
273
|
],
|
|
231
274
|
error: invalid,
|
|
232
|
-
tags: ['
|
|
275
|
+
tags: ['goals'],
|
|
233
276
|
},
|
|
234
277
|
{
|
|
235
|
-
occur: () => ![
|
|
278
|
+
occur: () => ![parseInput, parseOutput, execute].every((handler) => typeof handler === 'function'),
|
|
236
279
|
error: invalid,
|
|
237
280
|
tags: ['handlers'],
|
|
238
281
|
},
|
|
239
282
|
{
|
|
240
|
-
occur: () => this.tools.has(
|
|
241
|
-
error: () => new Error(`Duplicate registered tool: ${
|
|
283
|
+
occur: () => this.tools.has(input.id),
|
|
284
|
+
error: () => new Error(`Duplicate registered tool: ${input.id}`),
|
|
242
285
|
tags: ['duplicate-name'],
|
|
243
286
|
},
|
|
244
287
|
{
|
|
245
|
-
occur: () =>
|
|
288
|
+
occur: () => !!input.delegate && this.delegateIndex.has(input.delegate),
|
|
289
|
+
error: () => new Error(`Duplicate registered delegate: ${input.delegate}`),
|
|
290
|
+
tags: ['delegate', 'duplicate-id'],
|
|
291
|
+
},
|
|
292
|
+
{
|
|
293
|
+
occur: () => ownsInvocation && !lifecycle.id,
|
|
246
294
|
error: invalidInvocation,
|
|
247
|
-
tags: ['
|
|
295
|
+
tags: ['lifecycle', 'identity'],
|
|
248
296
|
},
|
|
249
297
|
{
|
|
250
|
-
occur:
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
298
|
+
occur: () => ownsInvocation && typeof lifecycle.call !== 'function',
|
|
299
|
+
error: invalidInvocation,
|
|
300
|
+
tags: ['lifecycle', 'contract'],
|
|
301
|
+
},
|
|
302
|
+
{
|
|
303
|
+
occur: () => ownsInvocation && typeof lifecycle.protocol?.keys !== 'function',
|
|
254
304
|
error: invalidInvocation,
|
|
255
|
-
tags: ['
|
|
305
|
+
tags: ['lifecycle', 'contract'],
|
|
256
306
|
},
|
|
257
307
|
{
|
|
258
|
-
occur: () =>
|
|
308
|
+
occur: () => ownsInvocation &&
|
|
259
309
|
policy.protocolVersion !== undefined &&
|
|
260
|
-
|
|
261
|
-
|
|
310
|
+
lifecycle.protocol?.version !== undefined &&
|
|
311
|
+
lifecycle.protocol.version !== policy.protocolVersion,
|
|
262
312
|
error: invalidInvocation,
|
|
263
|
-
tags: ['
|
|
313
|
+
tags: ['lifecycle', 'version'],
|
|
264
314
|
},
|
|
265
315
|
{
|
|
266
|
-
occur: () =>
|
|
316
|
+
occur: () => !!lifecycle?.id && this.invocations.has(lifecycle.id),
|
|
267
317
|
error: invalidInvocation,
|
|
268
|
-
tags: ['
|
|
318
|
+
tags: ['lifecycle', 'duplicate-id'],
|
|
269
319
|
},
|
|
270
320
|
], policy.tags);
|
|
321
|
+
}
|
|
322
|
+
install(input, tool, validated = false) {
|
|
323
|
+
if (!validated)
|
|
324
|
+
this.assertRegistration(input, {
|
|
325
|
+
parseInput: tool.parseInput,
|
|
326
|
+
parseOutput: tool.parseOutput,
|
|
327
|
+
execute: tool.execute,
|
|
328
|
+
});
|
|
271
329
|
this.tools.register(tool);
|
|
272
330
|
this.registered.set(tool.id, tool);
|
|
273
331
|
this.sources.set(tool.id, input);
|
|
274
|
-
if (tool.
|
|
275
|
-
this.invocations.set(tool.
|
|
332
|
+
if (tool.lifecycle?.id)
|
|
333
|
+
this.invocations.set(tool.lifecycle.id, tool);
|
|
334
|
+
if (tool.delegate)
|
|
335
|
+
this.delegateIndex.set(tool.delegate, tool);
|
|
276
336
|
return this;
|
|
277
337
|
}
|
|
278
338
|
/** Register every tool in iteration order. */
|
|
@@ -324,6 +384,10 @@ export class ToolLifecycleRegistry {
|
|
|
324
384
|
values() {
|
|
325
385
|
return this.registered.values();
|
|
326
386
|
}
|
|
387
|
+
/** Whether a delegate id is claimed by a registered tool. */
|
|
388
|
+
hasDelegate(id) {
|
|
389
|
+
return this.delegateIndex.has(id);
|
|
390
|
+
}
|
|
327
391
|
/**
|
|
328
392
|
* Install the single normalization handler.
|
|
329
393
|
*
|
|
@@ -359,134 +423,142 @@ export class ToolLifecycleRegistry {
|
|
|
359
423
|
return synchronous(this.finalizer.call(receiver, receiver, decision, context), 'Tool finalization must be synchronous');
|
|
360
424
|
}
|
|
361
425
|
/**
|
|
362
|
-
*
|
|
426
|
+
* Run the invocation that answers one decision next.
|
|
427
|
+
*
|
|
428
|
+
* Delegation is the App's choice; this method only executes the tool that claimed the id, so a
|
|
429
|
+
* purely programmatic node, a model choice and a mixed pipeline all dispatch the same way.
|
|
363
430
|
*
|
|
364
431
|
* Diagnostics are swapped to the invocation's own sink for the duration of the call and
|
|
365
432
|
* restored afterwards, so nested tool calls report under the innermost scope.
|
|
366
433
|
*
|
|
367
|
-
* @param
|
|
434
|
+
* @param next - Decision next carrying the claimed delegate id in `type`.
|
|
368
435
|
* @param context - Execution context forwarded to Core's invocation executor.
|
|
369
|
-
* @throws A configured error when the
|
|
436
|
+
* @throws A configured error when the delegate is unknown or the tool is unavailable.
|
|
370
437
|
*/
|
|
371
|
-
call(
|
|
372
|
-
const tool = this.invocations.get(
|
|
438
|
+
call(next, context) {
|
|
439
|
+
const tool = this.delegateIndex.get(next.type) ?? this.invocations.get(next.type);
|
|
373
440
|
breakWhile([
|
|
374
441
|
{
|
|
375
|
-
occur: () => !tool?.
|
|
376
|
-
error: () => this.options.
|
|
377
|
-
new Error(`Tool
|
|
442
|
+
occur: () => !tool?.lifecycle,
|
|
443
|
+
error: () => this.options.unavailableInvocation?.(next.type) ??
|
|
444
|
+
new Error(`Tool invocation is not registered: ${next.type}`),
|
|
378
445
|
},
|
|
379
446
|
{
|
|
380
|
-
occur: () => tool.
|
|
447
|
+
occur: () => !toolActive(tool.active, context),
|
|
381
448
|
error: () => this.options.forbidden?.() ?? new Error('Tool is not available'),
|
|
382
449
|
},
|
|
383
450
|
]);
|
|
384
451
|
executeInvocation({
|
|
385
452
|
id: tool.id,
|
|
386
|
-
call: (
|
|
453
|
+
call: (input, executionContext, execution) => {
|
|
387
454
|
const previous = this.activeDiagnostics;
|
|
388
455
|
this.activeDiagnostics = execution.diagnostics;
|
|
389
456
|
try {
|
|
390
|
-
tool.
|
|
457
|
+
tool.lifecycle.call(input, executionContext, execution);
|
|
391
458
|
}
|
|
392
459
|
finally {
|
|
393
460
|
this.activeDiagnostics = previous;
|
|
394
461
|
}
|
|
395
462
|
},
|
|
396
|
-
},
|
|
463
|
+
}, next, context, this.invocationPorts);
|
|
397
464
|
}
|
|
398
|
-
/** Output protocols plus one protocol per registered invocation
|
|
465
|
+
/** Output protocols plus one protocol per registered invocation. */
|
|
399
466
|
protocols() {
|
|
400
|
-
return new ProtocolRegistry([
|
|
401
|
-
...this.options.outputs(),
|
|
402
|
-
...[...this.invocations].map(([step, tool]) => [step, tool.invocation.protocol]),
|
|
403
|
-
]);
|
|
467
|
+
return new ProtocolRegistry([...this.options.outputs(), ...this.invocationProtocols()]);
|
|
404
468
|
}
|
|
405
469
|
/**
|
|
406
|
-
* Protocols exposed to the model: output protocols plus
|
|
470
|
+
* Protocols exposed to the model: output protocols plus invocations whose audience is `model` and
|
|
407
471
|
* which are currently available in `context`.
|
|
408
472
|
*/
|
|
409
473
|
modelProtocols(context) {
|
|
410
474
|
return new ProtocolRegistry([
|
|
411
475
|
...(this.options.modelOutputs?.(context) ?? this.options.outputs()),
|
|
412
|
-
...
|
|
413
|
-
.filter(([, tool]) => tool.audience === 'model' && (!tool.available || tool.available(context)))
|
|
414
|
-
.map(([step, tool]) => [step, tool.invocation.protocol]),
|
|
476
|
+
...this.invocationProtocols((tool) => tool.audience === 'model' && toolActive(tool.active, context)),
|
|
415
477
|
]);
|
|
416
478
|
}
|
|
417
|
-
/** Whether the protocol registered for this
|
|
418
|
-
isTerminal(
|
|
419
|
-
return this.protocols().get(
|
|
479
|
+
/** Whether the protocol registered for this invocation is declared terminal. */
|
|
480
|
+
isTerminal(next) {
|
|
481
|
+
return this.protocols().get(next.type)?.terminal === true;
|
|
482
|
+
}
|
|
483
|
+
/** `[invocationId, protocol]` pairs, optionally filtered by the owning tool. */
|
|
484
|
+
invocationProtocols(keep) {
|
|
485
|
+
return [...this.invocations]
|
|
486
|
+
.filter(([, tool]) => !keep || keep(tool))
|
|
487
|
+
.map(([id, tool]) => [id, tool.lifecycle.protocol]);
|
|
420
488
|
}
|
|
421
|
-
/**
|
|
489
|
+
/**
|
|
490
|
+
* Run `inspect` for every registered tool in registration order.
|
|
491
|
+
*
|
|
492
|
+
* `inspect` belongs to the all-tools group, so the selected tool participates exactly once
|
|
493
|
+
* through the same loop rather than through a second selected-only pass.
|
|
494
|
+
*/
|
|
422
495
|
assertDecision(decision, context) {
|
|
423
496
|
for (const tool of this.registered.values())
|
|
424
|
-
tool.
|
|
425
|
-
this.selected(decision)?.invocation?.inspect?.(decision, context);
|
|
497
|
+
tool.lifecycle?.inspect?.(decision, context);
|
|
426
498
|
}
|
|
427
499
|
/** Run the selected invocation's authorization hook, if any. */
|
|
428
500
|
authorize(decision, context) {
|
|
429
|
-
this.selected(decision)?.
|
|
501
|
+
this.selected(decision)?.lifecycle?.authorize?.(decision, context);
|
|
430
502
|
}
|
|
431
503
|
/** Whether the selected invocation should resume rather than start fresh; defaults to false. */
|
|
432
504
|
resume(decision, context) {
|
|
433
|
-
const
|
|
434
|
-
return
|
|
435
|
-
? booleanResult(
|
|
505
|
+
const lifecycle = this.selected(decision)?.lifecycle;
|
|
506
|
+
return lifecycle?.resume
|
|
507
|
+
? booleanResult(lifecycle.resume(decision, context), 'Tool resume must return boolean synchronously')
|
|
436
508
|
: false;
|
|
437
509
|
}
|
|
438
510
|
/** Let the selected invocation rewrite the decision on completion, if it declares a hook. */
|
|
439
511
|
complete(decision, context) {
|
|
440
|
-
return this.selected(decision)?.
|
|
512
|
+
return this.selected(decision)?.lifecycle?.complete?.(decision, context);
|
|
441
513
|
}
|
|
442
|
-
/** Fold every
|
|
514
|
+
/** Fold every tool's context projection over the value, in registration order. */
|
|
443
515
|
projectContext(value) {
|
|
444
|
-
return [...this.registered.values()].reduce((current, tool) => tool.
|
|
516
|
+
return [...this.registered.values()].reduce((current, tool) => tool.lifecycle?.projectContext?.(current) ?? current, value);
|
|
445
517
|
}
|
|
446
|
-
/** Fold every
|
|
518
|
+
/** Fold every tool's visible-text transform over the text. */
|
|
447
519
|
visibleText(text, context) {
|
|
448
|
-
return [...this.registered.values()].reduce((value, tool) => tool.
|
|
520
|
+
return [...this.registered.values()].reduce((value, tool) => tool.lifecycle?.visibleText?.(value, context) ?? value, text);
|
|
449
521
|
}
|
|
450
|
-
/** Fold every
|
|
522
|
+
/** Fold every tool's review transform over the draft. */
|
|
451
523
|
reviewDraft(draft, context) {
|
|
452
|
-
return [...this.registered.values()].reduce((value, tool) => tool.
|
|
524
|
+
return [...this.registered.values()].reduce((value, tool) => tool.lifecycle?.reviewDraft?.(value, context) ?? value, draft);
|
|
453
525
|
}
|
|
454
526
|
/** Collected recovery context values; `undefined` results are omitted. */
|
|
455
527
|
recoveryContext(context) {
|
|
456
528
|
return [...this.registered.values()].flatMap((tool) => {
|
|
457
|
-
const value = tool.
|
|
529
|
+
const value = tool.lifecycle?.recoveryContext?.(context);
|
|
458
530
|
return value === undefined ? [] : [value];
|
|
459
531
|
});
|
|
460
532
|
}
|
|
461
533
|
/**
|
|
462
534
|
* The single output template that owns this delivery.
|
|
463
535
|
*
|
|
464
|
-
* @throws An error when more than one
|
|
465
|
-
*
|
|
536
|
+
* @throws An error when more than one tool claims composition, because delivery would
|
|
537
|
+
* otherwise depend on registration order.
|
|
466
538
|
*/
|
|
467
539
|
composition(context) {
|
|
468
540
|
const values = [...this.registered.values()].flatMap((tool) => {
|
|
469
|
-
const value = tool.
|
|
541
|
+
const value = tool.lifecycle?.composition?.(context);
|
|
470
542
|
return value ? [value] : [];
|
|
471
543
|
});
|
|
472
544
|
breakWhile(values.length > 1, 'More than one output template owns this delivery');
|
|
473
545
|
return values[0];
|
|
474
546
|
}
|
|
475
|
-
/** Run every
|
|
547
|
+
/** Run every tool's finalize hook for the given phase. */
|
|
476
548
|
finalize(decision, context, phase) {
|
|
477
549
|
for (const tool of this.registered.values())
|
|
478
|
-
tool.
|
|
550
|
+
tool.lifecycle?.finalize?.(decision, context, phase);
|
|
479
551
|
}
|
|
480
|
-
/** Whether any
|
|
552
|
+
/** Whether any tool permits the interaction to be missing. */
|
|
481
553
|
missingInteractionAllowed(decision, context) {
|
|
482
|
-
return [...this.registered.values()].some((tool) => tool.
|
|
483
|
-
? booleanResult(tool.
|
|
554
|
+
return [...this.registered.values()].some((tool) => tool.lifecycle?.missingInteractionAllowed
|
|
555
|
+
? booleanResult(tool.lifecycle.missingInteractionAllowed(decision, context), 'Missing interaction predicates must return boolean synchronously')
|
|
484
556
|
: false);
|
|
485
557
|
}
|
|
486
558
|
/** Task rules ordered by their declared `order`, joined into one prompt block. */
|
|
487
559
|
taskRules(context) {
|
|
488
560
|
return [...this.registered.values()]
|
|
489
|
-
.flatMap((tool) => tool.
|
|
561
|
+
.flatMap((tool) => tool.lifecycle?.rules?.(context) ?? [])
|
|
490
562
|
.sort((left, right) => left.order - right.order)
|
|
491
563
|
.map((rule) => rule.text)
|
|
492
564
|
.filter(Boolean)
|
|
@@ -495,7 +567,7 @@ export class ToolLifecycleRegistry {
|
|
|
495
567
|
/** Model-context fragments for one placement, ordered by each field's `order`. */
|
|
496
568
|
contextFields(context, placement) {
|
|
497
569
|
return [...this.registered.values()]
|
|
498
|
-
.flatMap((tool) => (tool.
|
|
570
|
+
.flatMap((tool) => (tool.lifecycle?.modelFields?.(context, placement) ?? [])
|
|
499
571
|
.filter((field) => field.placement === placement)
|
|
500
572
|
.map((field) => ({ tool, field })))
|
|
501
573
|
.sort((left, right) => left.field.order - right.field.order)
|
|
@@ -503,11 +575,25 @@ export class ToolLifecycleRegistry {
|
|
|
503
575
|
}
|
|
504
576
|
/** Merged model-context values for one placement; later fragments win per key. */
|
|
505
577
|
modelFields(context, placement) {
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
578
|
+
const fields = {};
|
|
579
|
+
for (const { value } of this.contextFields(context, placement)) {
|
|
580
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
581
|
+
continue;
|
|
582
|
+
for (const [key, entry] of Object.entries(value))
|
|
583
|
+
// Own data properties keep keys such as `__proto__` from rewriting the merged result's
|
|
584
|
+
// prototype, matching how context assembly writes structured fields.
|
|
585
|
+
Object.defineProperty(fields, key, {
|
|
586
|
+
value: entry,
|
|
587
|
+
enumerable: true,
|
|
588
|
+
configurable: true,
|
|
589
|
+
writable: true,
|
|
590
|
+
});
|
|
591
|
+
}
|
|
592
|
+
return fields;
|
|
593
|
+
}
|
|
594
|
+
/** A tool may veto a goal by returning `false`; silence means support. */
|
|
595
|
+
supportsGoal(intentId, context) {
|
|
596
|
+
return [...this.registered.values()].every((tool) => tool.lifecycle?.supportsGoal?.(intentId, context) !== false);
|
|
511
597
|
}
|
|
512
598
|
/**
|
|
513
599
|
* Describe registered tools, optionally limited by audience and availability.
|
|
@@ -517,19 +603,22 @@ export class ToolLifecycleRegistry {
|
|
|
517
603
|
*
|
|
518
604
|
* @param audience - Audience to keep; omit to include every audience.
|
|
519
605
|
* @param context - Context for availability checks.
|
|
520
|
-
* @returns Plain metadata; `
|
|
606
|
+
* @returns Plain metadata; `goalIds: 'all'` is expanded to the current goal ids.
|
|
521
607
|
*/
|
|
522
608
|
describe(audience, context) {
|
|
523
609
|
return [...this.registered.values()]
|
|
524
610
|
.filter((tool) => (!audience || tool.audience === audience) &&
|
|
525
|
-
(
|
|
526
|
-
|
|
611
|
+
(tool.active === undefined ||
|
|
612
|
+
(typeof tool.active === 'boolean'
|
|
613
|
+
? toolActive(tool.active, context)
|
|
614
|
+
: !!context && toolActive(tool.active, context))))
|
|
615
|
+
.map(({ id, kind, description, audience, effect, goalIds }) => ({
|
|
527
616
|
id,
|
|
528
617
|
kind,
|
|
529
618
|
description,
|
|
530
619
|
audience,
|
|
531
620
|
effect,
|
|
532
|
-
|
|
621
|
+
goalIds: goalIds === 'all' ? [...this.options.goalIds()] : [...goalIds],
|
|
533
622
|
}));
|
|
534
623
|
}
|
|
535
624
|
/**
|
|
@@ -552,9 +641,9 @@ export class ToolLifecycleRegistry {
|
|
|
552
641
|
this.tools.seal();
|
|
553
642
|
return this;
|
|
554
643
|
}
|
|
555
|
-
/** Tool that owns the invocation
|
|
644
|
+
/** Tool that owns the invocation id named by the decision, if any. */
|
|
556
645
|
selected(decision) {
|
|
557
|
-
return this.invocations.get(this.options.
|
|
646
|
+
return this.invocations.get(this.options.invocationId(decision));
|
|
558
647
|
}
|
|
559
648
|
}
|
|
560
649
|
/** The lifecycle registry is the public `Tools` Kit entry point. */
|