@jangts/agent-core 0.3.6 → 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 +20 -0
- package/README.md +5 -5
- 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 +4 -2
- package/development/docs/api/README.md +2 -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/reference.en.md +508 -410
- package/development/docs/api/reference.md +508 -410
- package/development/docs/api/support.en.md +55 -9
- package/development/docs/api/support.md +53 -7
- package/development/docs/api/types.en.md +35 -1
- package/development/docs/api/types.md +43 -0
- 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 +4 -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/model.js +56 -12
- package/dist/cjs/capabilities/tool-conditions.js +65 -0
- package/dist/cjs/capabilities/tool-runtime.js +10 -3
- package/dist/cjs/context/audit.js +5 -8
- package/dist/cjs/context/index.js +3 -4
- package/dist/cjs/index.js +21 -17
- package/dist/cjs/kits/answering/tasks.js +5 -5
- 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/presentation/formats.js +110 -0
- package/dist/cjs/kits/presentation/index.js +10 -5
- package/dist/cjs/kits/reflection.js +2 -2
- package/dist/cjs/kits/text-blocks.js +101 -0
- package/dist/cjs/kits/tools.js +246 -158
- 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 +14 -0
- 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/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/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/esm/capabilities/index.d.ts +5 -10
- package/dist/esm/capabilities/index.js +5 -9
- 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 +10 -3
- package/dist/esm/capabilities/tools.d.ts +3 -3
- 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/index.d.ts +19 -13
- package/dist/esm/index.js +18 -15
- package/dist/esm/kits/answering/tasks.d.ts +6 -6
- package/dist/esm/kits/answering/tasks.js +5 -5
- 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/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/reflection.js +3 -3
- 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 +246 -158
- 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.js +14 -0
- 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 +1 -1
- package/dist/esm/structs/capabilities/model.d.ts +42 -11
- package/dist/esm/structs/capabilities/tools.d.ts +9 -4
- package/dist/esm/structs/common.d.ts +1 -1
- package/dist/esm/structs/index.d.ts +7 -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/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 +7 -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/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/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/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/cjs/structs/kits/{intents → domains}/catalog.js +0 -0
- /package/dist/cjs/structs/kits/{intents → domains}/registry.js +0 -0
- /package/dist/cjs/structs/kits/{intents → domains}/scopes.js +0 -0
- /package/dist/esm/structs/kits/{intents → domains}/catalog.js +0 -0
- /package/dist/esm/structs/kits/{intents → domains}/registry.js +0 -0
- /package/dist/esm/structs/kits/{intents → domains}/scopes.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);
|
|
@@ -114,17 +159,31 @@ export class ToolLifecycleRegistry {
|
|
|
114
159
|
* hooks must return `void` synchronously, while the remaining handlers are checked for
|
|
115
160
|
* accidental thenables only.
|
|
116
161
|
*
|
|
117
|
-
* @param input - Tool definition with identity, audience, effect and its
|
|
162
|
+
* @param input - Tool definition with identity, audience, effect and its hooks.
|
|
118
163
|
* @returns This registry, for chaining.
|
|
119
164
|
* @throws A configured registration error for a sealed registry, invalid identity,
|
|
120
|
-
* description, audience, effect,
|
|
121
|
-
* 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.
|
|
122
168
|
*/
|
|
123
169
|
register(input) {
|
|
124
|
-
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;
|
|
125
171
|
const parseInput = inputParser?.bind(input), parseOutput = outputParser?.bind(input);
|
|
126
|
-
const available = availability
|
|
127
|
-
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);
|
|
128
187
|
const tool = {
|
|
129
188
|
...registrationData(input, [
|
|
130
189
|
'id',
|
|
@@ -132,148 +191,148 @@ export class ToolLifecycleRegistry {
|
|
|
132
191
|
'description',
|
|
133
192
|
'audience',
|
|
134
193
|
'effect',
|
|
135
|
-
'
|
|
136
|
-
'
|
|
137
|
-
'
|
|
194
|
+
'goalIds',
|
|
195
|
+
'delegate',
|
|
196
|
+
'lifecycle',
|
|
138
197
|
'parseInput',
|
|
139
198
|
'parseOutput',
|
|
140
199
|
'execute',
|
|
141
200
|
'debugSteps',
|
|
142
|
-
'
|
|
201
|
+
'active',
|
|
143
202
|
]),
|
|
203
|
+
...modelTool,
|
|
144
204
|
id,
|
|
145
205
|
kind,
|
|
146
206
|
description,
|
|
147
207
|
audience,
|
|
148
208
|
effect,
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
209
|
+
delegate,
|
|
210
|
+
lifecycle,
|
|
211
|
+
goalIds: frozenGoalIds,
|
|
152
212
|
parseInput,
|
|
153
213
|
parseOutput,
|
|
154
214
|
debugSteps: debugSteps?.bind(input),
|
|
155
215
|
execute: execute?.bind(input),
|
|
156
|
-
|
|
157
|
-
? (context) => booleanResult(available(context), 'Tool availability must be synchronous', 'Tool availability must return boolean')
|
|
158
|
-
: undefined,
|
|
216
|
+
active: available,
|
|
159
217
|
};
|
|
160
|
-
const bindMethods = (value, keys, data = {}) => Object.freeze({
|
|
161
|
-
...registrationData(value, [...keys, ...Reflect.ownKeys(data)]),
|
|
162
|
-
...data,
|
|
163
|
-
...Object.fromEntries(keys.map((key) => {
|
|
164
|
-
const handler = value[key];
|
|
165
|
-
return [
|
|
166
|
-
key,
|
|
167
|
-
typeof handler === 'function'
|
|
168
|
-
? (...args) => (['authorize', 'inspect', 'finalize'].includes(String(key))
|
|
169
|
-
? assertion
|
|
170
|
-
: synchronous)(handler.apply(value, args), `Tool lifecycle ${String(key)} must finish synchronously with the declared result`)
|
|
171
|
-
: handler,
|
|
172
|
-
];
|
|
173
|
-
})),
|
|
174
|
-
});
|
|
175
218
|
const registered = Object.freeze({
|
|
176
219
|
...tool,
|
|
177
|
-
...(
|
|
178
|
-
? {
|
|
179
|
-
invocation: bindMethods(invocation, ['call', 'inspect', 'authorize', 'resume', 'complete'], { step: invocation.step, protocol: invocation.protocol }),
|
|
180
|
-
}
|
|
181
|
-
: {}),
|
|
182
|
-
...(contribution
|
|
183
|
-
? {
|
|
184
|
-
contribution: bindMethods(contribution, [
|
|
185
|
-
'composition',
|
|
186
|
-
'inspect',
|
|
187
|
-
'recoveryContext',
|
|
188
|
-
'projectContext',
|
|
189
|
-
'visibleText',
|
|
190
|
-
'reviewDraft',
|
|
191
|
-
'finalize',
|
|
192
|
-
'missingInteractionAllowed',
|
|
193
|
-
'rules',
|
|
194
|
-
'modelFields',
|
|
195
|
-
'supportsIntent',
|
|
196
|
-
]),
|
|
197
|
-
}
|
|
198
|
-
: {}),
|
|
220
|
+
...(lifecycle ? { lifecycle: bindLifecycle(lifecycle) } : {}),
|
|
199
221
|
});
|
|
200
|
-
return this.install(input, registered);
|
|
222
|
+
return this.install(input, registered, true);
|
|
201
223
|
}
|
|
202
|
-
|
|
203
|
-
|
|
224
|
+
/** Validate one declaration in the fixed order documented by `register`. */
|
|
225
|
+
assertRegistration(input, handlers) {
|
|
204
226
|
const policy = this.options.registration;
|
|
205
227
|
const identityPredicate = policy.isValidId;
|
|
206
228
|
const isValidId = () => booleanResult(identityPredicate(input), 'Tool identity predicates must be synchronous', 'Tool identity predicates must return boolean');
|
|
207
229
|
const invalid = policy.invalidTool ?? 'Invalid registered tool';
|
|
208
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;
|
|
209
239
|
breakWhile([
|
|
240
|
+
{ occur: () => this.sealed, error: 'Tool registry is sealed', tags: ['sealed'] },
|
|
210
241
|
{
|
|
211
|
-
occur: [() => !policy.kinds.includes(
|
|
242
|
+
occur: [() => !policy.kinds.includes(input.kind), () => !isValidId()],
|
|
212
243
|
error: invalid,
|
|
213
244
|
tags: ['identity'],
|
|
214
245
|
},
|
|
215
|
-
{
|
|
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
|
+
},
|
|
216
258
|
{
|
|
217
259
|
occur: [
|
|
218
|
-
() => !policy.audiences.includes(
|
|
219
|
-
() => !policy.effects.includes(
|
|
260
|
+
() => !policy.audiences.includes(input.audience),
|
|
261
|
+
() => !policy.effects.includes(input.effect),
|
|
220
262
|
],
|
|
221
263
|
error: invalid,
|
|
222
264
|
tags: ['execution-contract'],
|
|
223
265
|
},
|
|
224
266
|
{
|
|
225
267
|
occur: [
|
|
226
|
-
() =>
|
|
227
|
-
() =>
|
|
228
|
-
() =>
|
|
229
|
-
|
|
230
|
-
() =>
|
|
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,
|
|
231
273
|
],
|
|
232
274
|
error: invalid,
|
|
233
|
-
tags: ['
|
|
275
|
+
tags: ['goals'],
|
|
234
276
|
},
|
|
235
277
|
{
|
|
236
|
-
occur: () => ![
|
|
278
|
+
occur: () => ![parseInput, parseOutput, execute].every((handler) => typeof handler === 'function'),
|
|
237
279
|
error: invalid,
|
|
238
280
|
tags: ['handlers'],
|
|
239
281
|
},
|
|
240
282
|
{
|
|
241
|
-
occur: () => this.tools.has(
|
|
242
|
-
error: () => new Error(`Duplicate registered tool: ${
|
|
283
|
+
occur: () => this.tools.has(input.id),
|
|
284
|
+
error: () => new Error(`Duplicate registered tool: ${input.id}`),
|
|
243
285
|
tags: ['duplicate-name'],
|
|
244
286
|
},
|
|
245
287
|
{
|
|
246
|
-
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,
|
|
247
294
|
error: invalidInvocation,
|
|
248
|
-
tags: ['
|
|
295
|
+
tags: ['lifecycle', 'identity'],
|
|
249
296
|
},
|
|
250
297
|
{
|
|
251
|
-
occur:
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
298
|
+
occur: () => ownsInvocation && typeof lifecycle.call !== 'function',
|
|
299
|
+
error: invalidInvocation,
|
|
300
|
+
tags: ['lifecycle', 'contract'],
|
|
301
|
+
},
|
|
302
|
+
{
|
|
303
|
+
occur: () => ownsInvocation && typeof lifecycle.protocol?.keys !== 'function',
|
|
255
304
|
error: invalidInvocation,
|
|
256
|
-
tags: ['
|
|
305
|
+
tags: ['lifecycle', 'contract'],
|
|
257
306
|
},
|
|
258
307
|
{
|
|
259
|
-
occur: () =>
|
|
308
|
+
occur: () => ownsInvocation &&
|
|
260
309
|
policy.protocolVersion !== undefined &&
|
|
261
|
-
|
|
262
|
-
|
|
310
|
+
lifecycle.protocol?.version !== undefined &&
|
|
311
|
+
lifecycle.protocol.version !== policy.protocolVersion,
|
|
263
312
|
error: invalidInvocation,
|
|
264
|
-
tags: ['
|
|
313
|
+
tags: ['lifecycle', 'version'],
|
|
265
314
|
},
|
|
266
315
|
{
|
|
267
|
-
occur: () =>
|
|
316
|
+
occur: () => !!lifecycle?.id && this.invocations.has(lifecycle.id),
|
|
268
317
|
error: invalidInvocation,
|
|
269
|
-
tags: ['
|
|
318
|
+
tags: ['lifecycle', 'duplicate-id'],
|
|
270
319
|
},
|
|
271
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
|
+
});
|
|
272
329
|
this.tools.register(tool);
|
|
273
330
|
this.registered.set(tool.id, tool);
|
|
274
331
|
this.sources.set(tool.id, input);
|
|
275
|
-
if (tool.
|
|
276
|
-
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);
|
|
277
336
|
return this;
|
|
278
337
|
}
|
|
279
338
|
/** Register every tool in iteration order. */
|
|
@@ -325,6 +384,10 @@ export class ToolLifecycleRegistry {
|
|
|
325
384
|
values() {
|
|
326
385
|
return this.registered.values();
|
|
327
386
|
}
|
|
387
|
+
/** Whether a delegate id is claimed by a registered tool. */
|
|
388
|
+
hasDelegate(id) {
|
|
389
|
+
return this.delegateIndex.has(id);
|
|
390
|
+
}
|
|
328
391
|
/**
|
|
329
392
|
* Install the single normalization handler.
|
|
330
393
|
*
|
|
@@ -360,134 +423,142 @@ export class ToolLifecycleRegistry {
|
|
|
360
423
|
return synchronous(this.finalizer.call(receiver, receiver, decision, context), 'Tool finalization must be synchronous');
|
|
361
424
|
}
|
|
362
425
|
/**
|
|
363
|
-
*
|
|
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.
|
|
364
430
|
*
|
|
365
431
|
* Diagnostics are swapped to the invocation's own sink for the duration of the call and
|
|
366
432
|
* restored afterwards, so nested tool calls report under the innermost scope.
|
|
367
433
|
*
|
|
368
|
-
* @param
|
|
434
|
+
* @param next - Decision next carrying the claimed delegate id in `type`.
|
|
369
435
|
* @param context - Execution context forwarded to Core's invocation executor.
|
|
370
|
-
* @throws A configured error when the
|
|
436
|
+
* @throws A configured error when the delegate is unknown or the tool is unavailable.
|
|
371
437
|
*/
|
|
372
|
-
call(
|
|
373
|
-
const tool = this.invocations.get(
|
|
438
|
+
call(next, context) {
|
|
439
|
+
const tool = this.delegateIndex.get(next.type) ?? this.invocations.get(next.type);
|
|
374
440
|
breakWhile([
|
|
375
441
|
{
|
|
376
|
-
occur: () => !tool?.
|
|
377
|
-
error: () => this.options.
|
|
378
|
-
new Error(`Tool
|
|
442
|
+
occur: () => !tool?.lifecycle,
|
|
443
|
+
error: () => this.options.unavailableInvocation?.(next.type) ??
|
|
444
|
+
new Error(`Tool invocation is not registered: ${next.type}`),
|
|
379
445
|
},
|
|
380
446
|
{
|
|
381
|
-
occur: () => tool.
|
|
447
|
+
occur: () => !toolActive(tool.active, context),
|
|
382
448
|
error: () => this.options.forbidden?.() ?? new Error('Tool is not available'),
|
|
383
449
|
},
|
|
384
450
|
]);
|
|
385
451
|
executeInvocation({
|
|
386
452
|
id: tool.id,
|
|
387
|
-
call: (
|
|
453
|
+
call: (input, executionContext, execution) => {
|
|
388
454
|
const previous = this.activeDiagnostics;
|
|
389
455
|
this.activeDiagnostics = execution.diagnostics;
|
|
390
456
|
try {
|
|
391
|
-
tool.
|
|
457
|
+
tool.lifecycle.call(input, executionContext, execution);
|
|
392
458
|
}
|
|
393
459
|
finally {
|
|
394
460
|
this.activeDiagnostics = previous;
|
|
395
461
|
}
|
|
396
462
|
},
|
|
397
|
-
},
|
|
463
|
+
}, next, context, this.invocationPorts);
|
|
398
464
|
}
|
|
399
|
-
/** Output protocols plus one protocol per registered invocation
|
|
465
|
+
/** Output protocols plus one protocol per registered invocation. */
|
|
400
466
|
protocols() {
|
|
401
|
-
return new ProtocolRegistry([
|
|
402
|
-
...this.options.outputs(),
|
|
403
|
-
...[...this.invocations].map(([step, tool]) => [step, tool.invocation.protocol]),
|
|
404
|
-
]);
|
|
467
|
+
return new ProtocolRegistry([...this.options.outputs(), ...this.invocationProtocols()]);
|
|
405
468
|
}
|
|
406
469
|
/**
|
|
407
|
-
* Protocols exposed to the model: output protocols plus
|
|
470
|
+
* Protocols exposed to the model: output protocols plus invocations whose audience is `model` and
|
|
408
471
|
* which are currently available in `context`.
|
|
409
472
|
*/
|
|
410
473
|
modelProtocols(context) {
|
|
411
474
|
return new ProtocolRegistry([
|
|
412
475
|
...(this.options.modelOutputs?.(context) ?? this.options.outputs()),
|
|
413
|
-
...
|
|
414
|
-
.filter(([, tool]) => tool.audience === 'model' && (!tool.available || tool.available(context)))
|
|
415
|
-
.map(([step, tool]) => [step, tool.invocation.protocol]),
|
|
476
|
+
...this.invocationProtocols((tool) => tool.audience === 'model' && toolActive(tool.active, context)),
|
|
416
477
|
]);
|
|
417
478
|
}
|
|
418
|
-
/** Whether the protocol registered for this
|
|
419
|
-
isTerminal(
|
|
420
|
-
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]);
|
|
421
488
|
}
|
|
422
|
-
/**
|
|
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
|
+
*/
|
|
423
495
|
assertDecision(decision, context) {
|
|
424
496
|
for (const tool of this.registered.values())
|
|
425
|
-
tool.
|
|
426
|
-
this.selected(decision)?.invocation?.inspect?.(decision, context);
|
|
497
|
+
tool.lifecycle?.inspect?.(decision, context);
|
|
427
498
|
}
|
|
428
499
|
/** Run the selected invocation's authorization hook, if any. */
|
|
429
500
|
authorize(decision, context) {
|
|
430
|
-
this.selected(decision)?.
|
|
501
|
+
this.selected(decision)?.lifecycle?.authorize?.(decision, context);
|
|
431
502
|
}
|
|
432
503
|
/** Whether the selected invocation should resume rather than start fresh; defaults to false. */
|
|
433
504
|
resume(decision, context) {
|
|
434
|
-
const
|
|
435
|
-
return
|
|
436
|
-
? booleanResult(
|
|
505
|
+
const lifecycle = this.selected(decision)?.lifecycle;
|
|
506
|
+
return lifecycle?.resume
|
|
507
|
+
? booleanResult(lifecycle.resume(decision, context), 'Tool resume must return boolean synchronously')
|
|
437
508
|
: false;
|
|
438
509
|
}
|
|
439
510
|
/** Let the selected invocation rewrite the decision on completion, if it declares a hook. */
|
|
440
511
|
complete(decision, context) {
|
|
441
|
-
return this.selected(decision)?.
|
|
512
|
+
return this.selected(decision)?.lifecycle?.complete?.(decision, context);
|
|
442
513
|
}
|
|
443
|
-
/** Fold every
|
|
514
|
+
/** Fold every tool's context projection over the value, in registration order. */
|
|
444
515
|
projectContext(value) {
|
|
445
|
-
return [...this.registered.values()].reduce((current, tool) => tool.
|
|
516
|
+
return [...this.registered.values()].reduce((current, tool) => tool.lifecycle?.projectContext?.(current) ?? current, value);
|
|
446
517
|
}
|
|
447
|
-
/** Fold every
|
|
518
|
+
/** Fold every tool's visible-text transform over the text. */
|
|
448
519
|
visibleText(text, context) {
|
|
449
|
-
return [...this.registered.values()].reduce((value, tool) => tool.
|
|
520
|
+
return [...this.registered.values()].reduce((value, tool) => tool.lifecycle?.visibleText?.(value, context) ?? value, text);
|
|
450
521
|
}
|
|
451
|
-
/** Fold every
|
|
522
|
+
/** Fold every tool's review transform over the draft. */
|
|
452
523
|
reviewDraft(draft, context) {
|
|
453
|
-
return [...this.registered.values()].reduce((value, tool) => tool.
|
|
524
|
+
return [...this.registered.values()].reduce((value, tool) => tool.lifecycle?.reviewDraft?.(value, context) ?? value, draft);
|
|
454
525
|
}
|
|
455
526
|
/** Collected recovery context values; `undefined` results are omitted. */
|
|
456
527
|
recoveryContext(context) {
|
|
457
528
|
return [...this.registered.values()].flatMap((tool) => {
|
|
458
|
-
const value = tool.
|
|
529
|
+
const value = tool.lifecycle?.recoveryContext?.(context);
|
|
459
530
|
return value === undefined ? [] : [value];
|
|
460
531
|
});
|
|
461
532
|
}
|
|
462
533
|
/**
|
|
463
534
|
* The single output template that owns this delivery.
|
|
464
535
|
*
|
|
465
|
-
* @throws An error when more than one
|
|
466
|
-
*
|
|
536
|
+
* @throws An error when more than one tool claims composition, because delivery would
|
|
537
|
+
* otherwise depend on registration order.
|
|
467
538
|
*/
|
|
468
539
|
composition(context) {
|
|
469
540
|
const values = [...this.registered.values()].flatMap((tool) => {
|
|
470
|
-
const value = tool.
|
|
541
|
+
const value = tool.lifecycle?.composition?.(context);
|
|
471
542
|
return value ? [value] : [];
|
|
472
543
|
});
|
|
473
544
|
breakWhile(values.length > 1, 'More than one output template owns this delivery');
|
|
474
545
|
return values[0];
|
|
475
546
|
}
|
|
476
|
-
/** Run every
|
|
547
|
+
/** Run every tool's finalize hook for the given phase. */
|
|
477
548
|
finalize(decision, context, phase) {
|
|
478
549
|
for (const tool of this.registered.values())
|
|
479
|
-
tool.
|
|
550
|
+
tool.lifecycle?.finalize?.(decision, context, phase);
|
|
480
551
|
}
|
|
481
|
-
/** Whether any
|
|
552
|
+
/** Whether any tool permits the interaction to be missing. */
|
|
482
553
|
missingInteractionAllowed(decision, context) {
|
|
483
|
-
return [...this.registered.values()].some((tool) => tool.
|
|
484
|
-
? 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')
|
|
485
556
|
: false);
|
|
486
557
|
}
|
|
487
558
|
/** Task rules ordered by their declared `order`, joined into one prompt block. */
|
|
488
559
|
taskRules(context) {
|
|
489
560
|
return [...this.registered.values()]
|
|
490
|
-
.flatMap((tool) => tool.
|
|
561
|
+
.flatMap((tool) => tool.lifecycle?.rules?.(context) ?? [])
|
|
491
562
|
.sort((left, right) => left.order - right.order)
|
|
492
563
|
.map((rule) => rule.text)
|
|
493
564
|
.filter(Boolean)
|
|
@@ -496,7 +567,7 @@ export class ToolLifecycleRegistry {
|
|
|
496
567
|
/** Model-context fragments for one placement, ordered by each field's `order`. */
|
|
497
568
|
contextFields(context, placement) {
|
|
498
569
|
return [...this.registered.values()]
|
|
499
|
-
.flatMap((tool) => (tool.
|
|
570
|
+
.flatMap((tool) => (tool.lifecycle?.modelFields?.(context, placement) ?? [])
|
|
500
571
|
.filter((field) => field.placement === placement)
|
|
501
572
|
.map((field) => ({ tool, field })))
|
|
502
573
|
.sort((left, right) => left.field.order - right.field.order)
|
|
@@ -504,11 +575,25 @@ export class ToolLifecycleRegistry {
|
|
|
504
575
|
}
|
|
505
576
|
/** Merged model-context values for one placement; later fragments win per key. */
|
|
506
577
|
modelFields(context, placement) {
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
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);
|
|
512
597
|
}
|
|
513
598
|
/**
|
|
514
599
|
* Describe registered tools, optionally limited by audience and availability.
|
|
@@ -518,19 +603,22 @@ export class ToolLifecycleRegistry {
|
|
|
518
603
|
*
|
|
519
604
|
* @param audience - Audience to keep; omit to include every audience.
|
|
520
605
|
* @param context - Context for availability checks.
|
|
521
|
-
* @returns Plain metadata; `
|
|
606
|
+
* @returns Plain metadata; `goalIds: 'all'` is expanded to the current goal ids.
|
|
522
607
|
*/
|
|
523
608
|
describe(audience, context) {
|
|
524
609
|
return [...this.registered.values()]
|
|
525
610
|
.filter((tool) => (!audience || tool.audience === audience) &&
|
|
526
|
-
(
|
|
527
|
-
|
|
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 }) => ({
|
|
528
616
|
id,
|
|
529
617
|
kind,
|
|
530
618
|
description,
|
|
531
619
|
audience,
|
|
532
620
|
effect,
|
|
533
|
-
|
|
621
|
+
goalIds: goalIds === 'all' ? [...this.options.goalIds()] : [...goalIds],
|
|
534
622
|
}));
|
|
535
623
|
}
|
|
536
624
|
/**
|
|
@@ -553,9 +641,9 @@ export class ToolLifecycleRegistry {
|
|
|
553
641
|
this.tools.seal();
|
|
554
642
|
return this;
|
|
555
643
|
}
|
|
556
|
-
/** Tool that owns the invocation
|
|
644
|
+
/** Tool that owns the invocation id named by the decision, if any. */
|
|
557
645
|
selected(decision) {
|
|
558
|
-
return this.invocations.get(this.options.
|
|
646
|
+
return this.invocations.get(this.options.invocationId(decision));
|
|
559
647
|
}
|
|
560
648
|
}
|
|
561
649
|
/** The lifecycle registry is the public `Tools` Kit entry point. */
|