agentfootprint 7.22.1 → 7.24.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/README.md +34 -0
- package/ai-instructions/claude-code/SKILL.md +2 -1
- package/dist/core/agent/AgentBuilder.js +117 -1
- package/dist/core/agent/AgentBuilder.js.map +1 -1
- package/dist/core/agent/act.js +143 -0
- package/dist/core/agent/act.js.map +1 -0
- package/dist/core/agent/middleware/index.js +5 -0
- package/dist/core/agent/middleware/index.js.map +1 -1
- package/dist/core/agent/middleware/ledger.js +7 -0
- package/dist/core/agent/middleware/ledger.js.map +1 -1
- package/dist/core/agent/middleware/outcomes.js +9 -2
- package/dist/core/agent/middleware/outcomes.js.map +1 -1
- package/dist/core/agent/middleware/runChain.js +101 -3
- package/dist/core/agent/middleware/runChain.js.map +1 -1
- package/dist/core/agent/middleware/types.js +30 -0
- package/dist/core/agent/middleware/types.js.map +1 -1
- package/dist/core/agent/moments.js +53 -0
- package/dist/core/agent/moments.js.map +1 -0
- package/dist/core/agent/stages/toolCalls.js +139 -8
- package/dist/core/agent/stages/toolCalls.js.map +1 -1
- package/dist/core/tools.js.map +1 -1
- package/dist/esm/core/agent/AgentBuilder.d.ts +71 -0
- package/dist/esm/core/agent/AgentBuilder.js +117 -1
- package/dist/esm/core/agent/AgentBuilder.js.map +1 -1
- package/dist/esm/core/agent/act.d.ts +69 -0
- package/dist/esm/core/agent/act.js +139 -0
- package/dist/esm/core/agent/act.js.map +1 -0
- package/dist/esm/core/agent/middleware/index.d.ts +6 -1
- package/dist/esm/core/agent/middleware/index.js +5 -0
- package/dist/esm/core/agent/middleware/index.js.map +1 -1
- package/dist/esm/core/agent/middleware/ledger.d.ts +6 -0
- package/dist/esm/core/agent/middleware/ledger.js +7 -0
- package/dist/esm/core/agent/middleware/ledger.js.map +1 -1
- package/dist/esm/core/agent/middleware/outcomes.d.ts +15 -0
- package/dist/esm/core/agent/middleware/outcomes.js +9 -2
- package/dist/esm/core/agent/middleware/outcomes.js.map +1 -1
- package/dist/esm/core/agent/middleware/runChain.d.ts +49 -0
- package/dist/esm/core/agent/middleware/runChain.js +99 -2
- package/dist/esm/core/agent/middleware/runChain.js.map +1 -1
- package/dist/esm/core/agent/middleware/types.d.ts +144 -10
- package/dist/esm/core/agent/middleware/types.js +30 -0
- package/dist/esm/core/agent/middleware/types.js.map +1 -1
- package/dist/esm/core/agent/moments.d.ts +55 -0
- package/dist/esm/core/agent/moments.js +49 -0
- package/dist/esm/core/agent/moments.js.map +1 -0
- package/dist/esm/core/agent/stages/toolCalls.d.ts +6 -1
- package/dist/esm/core/agent/stages/toolCalls.js +140 -9
- package/dist/esm/core/agent/stages/toolCalls.js.map +1 -1
- package/dist/esm/core/tools.d.ts +18 -0
- package/dist/esm/core/tools.js.map +1 -1
- package/dist/esm/events/payloads.d.ts +4 -0
- package/dist/esm/index.d.ts +3 -1
- package/dist/esm/index.js +7 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/lib/mcp/index.d.ts +1 -1
- package/dist/esm/lib/mcp/mcpClient.js +104 -11
- package/dist/esm/lib/mcp/mcpClient.js.map +1 -1
- package/dist/esm/lib/mcp/mcpServe.js +31 -1
- package/dist/esm/lib/mcp/mcpServe.js.map +1 -1
- package/dist/esm/lib/mcp/mockMcpClient.js +4 -0
- package/dist/esm/lib/mcp/mockMcpClient.js.map +1 -1
- package/dist/esm/lib/mcp/types.d.ts +71 -7
- package/dist/esm/tool-providers/index.d.ts +1 -1
- package/dist/index.js +12 -2
- package/dist/index.js.map +1 -1
- package/dist/lib/mcp/mcpClient.js +104 -11
- package/dist/lib/mcp/mcpClient.js.map +1 -1
- package/dist/lib/mcp/mcpServe.js +30 -0
- package/dist/lib/mcp/mcpServe.js.map +1 -1
- package/dist/lib/mcp/mockMcpClient.js +4 -0
- package/dist/lib/mcp/mockMcpClient.js.map +1 -1
- package/dist/types/core/agent/AgentBuilder.d.ts +71 -0
- package/dist/types/core/agent/AgentBuilder.d.ts.map +1 -1
- package/dist/types/core/agent/act.d.ts +70 -0
- package/dist/types/core/agent/act.d.ts.map +1 -0
- package/dist/types/core/agent/middleware/index.d.ts +6 -1
- package/dist/types/core/agent/middleware/index.d.ts.map +1 -1
- package/dist/types/core/agent/middleware/ledger.d.ts +6 -0
- package/dist/types/core/agent/middleware/ledger.d.ts.map +1 -1
- package/dist/types/core/agent/middleware/outcomes.d.ts +15 -0
- package/dist/types/core/agent/middleware/outcomes.d.ts.map +1 -1
- package/dist/types/core/agent/middleware/runChain.d.ts +49 -0
- package/dist/types/core/agent/middleware/runChain.d.ts.map +1 -1
- package/dist/types/core/agent/middleware/types.d.ts +144 -10
- package/dist/types/core/agent/middleware/types.d.ts.map +1 -1
- package/dist/types/core/agent/moments.d.ts +56 -0
- package/dist/types/core/agent/moments.d.ts.map +1 -0
- package/dist/types/core/agent/stages/toolCalls.d.ts +6 -1
- package/dist/types/core/agent/stages/toolCalls.d.ts.map +1 -1
- package/dist/types/core/tools.d.ts +18 -0
- package/dist/types/core/tools.d.ts.map +1 -1
- package/dist/types/events/payloads.d.ts +4 -0
- package/dist/types/events/payloads.d.ts.map +1 -1
- package/dist/types/index.d.ts +3 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/lib/mcp/index.d.ts +1 -1
- package/dist/types/lib/mcp/index.d.ts.map +1 -1
- package/dist/types/lib/mcp/mcpClient.d.ts.map +1 -1
- package/dist/types/lib/mcp/mcpServe.d.ts.map +1 -1
- package/dist/types/lib/mcp/types.d.ts +71 -7
- package/dist/types/lib/mcp/types.d.ts.map +1 -1
- package/dist/types/tool-providers/index.d.ts +1 -1
- package/dist/types/tool-providers/index.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -26,9 +26,19 @@
|
|
|
26
26
|
* permission gate has always applied to a checker that throws: a
|
|
27
27
|
* governance layer whose failure mode is "allow" is not a governance
|
|
28
28
|
* layer.
|
|
29
|
+
*
|
|
30
|
+
* **The result comes back through the onion.** `runToolAfterChain` walks the
|
|
31
|
+
* SAME list in reverse, so the first-declared link gets the first word about
|
|
32
|
+
* the call and the last word about the result. An outer rule is the one that
|
|
33
|
+
* sees what every inner rule already did — which is the only order in which
|
|
34
|
+
* "wrap" means anything.
|
|
35
|
+
*
|
|
36
|
+
* **A link that has no hook for a moment files no row.** It did not decide;
|
|
37
|
+
* saying it allowed would be inventing a decision, and the ledger's whole
|
|
38
|
+
* value is that a row means somebody looked.
|
|
29
39
|
*/
|
|
30
40
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
31
|
-
exports.runMessageChain = exports.runToolChain = void 0;
|
|
41
|
+
exports.runMessageChain = exports.runToolAfterChain = exports.runToolChain = void 0;
|
|
32
42
|
/** Walk the tool chain. Never throws — a throwing link becomes a denial. */
|
|
33
43
|
async function runToolChain(chain, input) {
|
|
34
44
|
const decisions = [];
|
|
@@ -36,8 +46,13 @@ async function runToolChain(chain, input) {
|
|
|
36
46
|
let args = input.args;
|
|
37
47
|
for (let i = input.startIndex ?? 0; i < chain.length; i++) {
|
|
38
48
|
const mw = chain[i];
|
|
49
|
+
// A result-only link takes no part in dispatch — and files no row here,
|
|
50
|
+
// because it did not decide anything about this call.
|
|
51
|
+
if (typeof mw.onToolCall !== 'function')
|
|
52
|
+
continue;
|
|
39
53
|
const row = (extra) => ({
|
|
40
54
|
middleware: mw.name,
|
|
55
|
+
moment: 'before-tool',
|
|
41
56
|
at: 'tool',
|
|
42
57
|
toolName: input.toolName,
|
|
43
58
|
toolCallId: input.toolCallId,
|
|
@@ -50,6 +65,10 @@ async function runToolChain(chain, input) {
|
|
|
50
65
|
try {
|
|
51
66
|
outcome = await mw.onToolCall({
|
|
52
67
|
toolName: input.toolName,
|
|
68
|
+
// Present only when the tool HAS a source. An `undefined` key is not
|
|
69
|
+
// the same fact as an absent one to a middleware that spells its rule
|
|
70
|
+
// `'toolSource' in call`, and "the agent's own" is an absence.
|
|
71
|
+
...(input.toolSource !== undefined && { toolSource: input.toolSource }),
|
|
53
72
|
toolCallId: input.toolCallId,
|
|
54
73
|
iteration: input.iteration,
|
|
55
74
|
args,
|
|
@@ -90,12 +109,90 @@ async function runToolChain(chain, input) {
|
|
|
90
109
|
decisions.push(row({ changed: true, ...(outcome.why && { why: outcome.why }), before, after: args }));
|
|
91
110
|
}
|
|
92
111
|
else {
|
|
93
|
-
|
|
112
|
+
// `allow(undefined, why)` — nothing moved, and the link said why it was
|
|
113
|
+
// comfortable. The reason belongs on the row of the call it permitted.
|
|
114
|
+
decisions.push(row(outcome.why ? { why: outcome.why } : {}));
|
|
94
115
|
}
|
|
95
116
|
}
|
|
96
117
|
return { kind: 'allow', args, decisions };
|
|
97
118
|
}
|
|
98
119
|
exports.runToolChain = runToolChain;
|
|
120
|
+
/**
|
|
121
|
+
* Walk the chain BACKWARDS over a finished call. Never throws — a throwing
|
|
122
|
+
* link becomes a denial, which at this moment means the model reads the
|
|
123
|
+
* reason instead of the result.
|
|
124
|
+
*
|
|
125
|
+
* Only called for a call that actually executed. A call the chain denied, or
|
|
126
|
+
* one still waiting on a person, has no result to decide about, and asking a
|
|
127
|
+
* rule about a result that does not exist would be the same fabrication the
|
|
128
|
+
* outcome union exists to prevent.
|
|
129
|
+
*/
|
|
130
|
+
async function runToolAfterChain(chain, input) {
|
|
131
|
+
const decisions = [];
|
|
132
|
+
const real = input.result;
|
|
133
|
+
let result = real;
|
|
134
|
+
// Reverse: first-declared saw the call first, so it sees the result last.
|
|
135
|
+
for (let i = chain.length - 1; i >= 0; i--) {
|
|
136
|
+
const mw = chain[i];
|
|
137
|
+
const hook = mw.onToolResult;
|
|
138
|
+
if (typeof hook !== 'function')
|
|
139
|
+
continue;
|
|
140
|
+
const row = (extra) => ({
|
|
141
|
+
middleware: mw.name,
|
|
142
|
+
moment: 'after-tool',
|
|
143
|
+
at: 'tool',
|
|
144
|
+
toolName: input.toolName,
|
|
145
|
+
toolCallId: input.toolCallId,
|
|
146
|
+
iteration: input.iteration,
|
|
147
|
+
outcome: 'allow',
|
|
148
|
+
changed: false,
|
|
149
|
+
...extra,
|
|
150
|
+
});
|
|
151
|
+
let outcome;
|
|
152
|
+
try {
|
|
153
|
+
outcome = await hook.call(mw, {
|
|
154
|
+
toolName: input.toolName,
|
|
155
|
+
...(input.toolSource !== undefined && { toolSource: input.toolSource }),
|
|
156
|
+
toolCallId: input.toolCallId,
|
|
157
|
+
iteration: input.iteration,
|
|
158
|
+
args: input.args,
|
|
159
|
+
result,
|
|
160
|
+
...(input.error === true && { error: true }),
|
|
161
|
+
history: input.history,
|
|
162
|
+
...(input.identity && { identity: input.identity }),
|
|
163
|
+
...(input.signal && { signal: input.signal }),
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
catch (err) {
|
|
167
|
+
const reason = `middleware '${mw.name}' threw: ${err instanceof Error ? err.message : String(err)}`;
|
|
168
|
+
decisions.push(row({ outcome: 'deny', changed: true, why: reason, before: result, after: reason }));
|
|
169
|
+
return { kind: 'deny', reason, middleware: mw.name, result: real, decisions };
|
|
170
|
+
}
|
|
171
|
+
if (outcome.kind === 'deny') {
|
|
172
|
+
// `changed: true` on purpose: unlike a refusal before dispatch, this one
|
|
173
|
+
// DOES replace something. The real result rides `before` — often the
|
|
174
|
+
// only copy of it in the run.
|
|
175
|
+
decisions.push(row({
|
|
176
|
+
outcome: 'deny',
|
|
177
|
+
changed: true,
|
|
178
|
+
why: outcome.reason,
|
|
179
|
+
before: result,
|
|
180
|
+
after: outcome.reason,
|
|
181
|
+
}));
|
|
182
|
+
return { kind: 'deny', reason: outcome.reason, middleware: mw.name, result: real, decisions };
|
|
183
|
+
}
|
|
184
|
+
if (outcome.value !== undefined) {
|
|
185
|
+
const before = result;
|
|
186
|
+
result = outcome.value;
|
|
187
|
+
decisions.push(row({ changed: true, ...(outcome.why && { why: outcome.why }), before, after: result }));
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
decisions.push(row(outcome.why ? { why: outcome.why } : {}));
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return { kind: 'allow', result, decisions };
|
|
194
|
+
}
|
|
195
|
+
exports.runToolAfterChain = runToolAfterChain;
|
|
99
196
|
/** Walk the message chain. Never throws — a throwing link becomes a denial. */
|
|
100
197
|
async function runMessageChain(chain, input) {
|
|
101
198
|
const decisions = [];
|
|
@@ -103,6 +200,7 @@ async function runMessageChain(chain, input) {
|
|
|
103
200
|
for (const mw of chain) {
|
|
104
201
|
const row = (extra) => ({
|
|
105
202
|
middleware: mw.name,
|
|
203
|
+
moment: input.phase,
|
|
106
204
|
at: 'message',
|
|
107
205
|
phase: input.phase,
|
|
108
206
|
iteration: input.iteration,
|
|
@@ -135,7 +233,7 @@ async function runMessageChain(chain, input) {
|
|
|
135
233
|
decisions.push(row({ changed: true, ...(outcome.why && { why: outcome.why }), before, after: content }));
|
|
136
234
|
}
|
|
137
235
|
else {
|
|
138
|
-
decisions.push(row({}));
|
|
236
|
+
decisions.push(row(outcome.why ? { why: outcome.why } : {}));
|
|
139
237
|
}
|
|
140
238
|
}
|
|
141
239
|
return { kind: 'allow', content, decisions };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runChain.js","sourceRoot":"","sources":["../../../../src/core/agent/middleware/runChain.ts"],"names":[],"mappings":";AAAA
|
|
1
|
+
{"version":3,"file":"runChain.js","sourceRoot":"","sources":["../../../../src/core/agent/middleware/runChain.ts"],"names":[],"mappings":"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"}
|
|
@@ -36,6 +36,36 @@
|
|
|
36
36
|
* `checkIn` and `askHuman` already go through. `MessageOutcome` does not,
|
|
37
37
|
* because the message boundary is a plain stage, and inventing a second
|
|
38
38
|
* pause to give it one would be a worse answer than not offering it.
|
|
39
|
+
* `ToolResultOutcome` does not either — see below.
|
|
40
|
+
*
|
|
41
|
+
* ## The two moments of a tool call
|
|
42
|
+
*
|
|
43
|
+
* A tool middleware may speak twice about one call:
|
|
44
|
+
*
|
|
45
|
+
* `onToolCall` BEFORE dispatch — about the call. Three verbs.
|
|
46
|
+
* `onToolResult` AFTER the tool ran, before its result enters the history
|
|
47
|
+
* or reaches the model — about the result. Two verbs.
|
|
48
|
+
*
|
|
49
|
+
* Each hook is named for what it RECEIVES; the moments they sit at are named
|
|
50
|
+
* `'before-tool'` and `'after-tool'` (and `.act()`'s keys after those).
|
|
51
|
+
*
|
|
52
|
+
* Either hook alone is a whole middleware; a link with only `onToolResult`
|
|
53
|
+
* does not take part in dispatch at all. The chain walks the two moments in **onion
|
|
54
|
+
* order**: the first-declared link sees the CALL first and the RESULT last —
|
|
55
|
+
* first word going in, last word coming out — which is the order that lets an
|
|
56
|
+
* outer rule inspect what every inner rule already did.
|
|
57
|
+
*
|
|
58
|
+
* **There is no `ask` at the after-tool moment.** Not because the machinery is missing —
|
|
59
|
+
* the dispatch loop pauses perfectly well — but because the tool has ALREADY
|
|
60
|
+
* RUN. A person woken to answer a question about a side effect that already
|
|
61
|
+
* happened cannot prevent it; the honest verbs there are "let the result
|
|
62
|
+
* through" and "do not let the model see it", and both are available now. The
|
|
63
|
+
* business cases this was measured against — authorize before, hide from the
|
|
64
|
+
* model, annotate the result, attach a fact through a context trigger — none
|
|
65
|
+
* of them needed a person at that moment. This is a refusal, not an oversight,
|
|
66
|
+
* and it is written here so evidence can promote it later: bring a case that
|
|
67
|
+
* genuinely needs a human at the after-tool moment and the arm can be added, on the
|
|
68
|
+
* pause machinery that already exists.
|
|
39
69
|
*/
|
|
40
70
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
41
71
|
//# sourceMappingURL=types.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/core/agent/middleware/types.ts"],"names":[],"mappings":";AAAA
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/core/agent/middleware/types.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmEG"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* moments — PUBLIC. The five places in the loop where a rule may speak.
|
|
4
|
+
*
|
|
5
|
+
* Pattern: Closed enumeration + a mechanical name mapping, with the mapping
|
|
6
|
+
* pinned by the compiler rather than by memory.
|
|
7
|
+
* Role: core/ layer. `LoopMoment` is the vocabulary `.act()` is keyed on
|
|
8
|
+
* and the vocabulary every ledger row now stamps itself with, so a
|
|
9
|
+
* person asking "where in the loop did this happen?" and a person
|
|
10
|
+
* asking "where in the loop can I intervene?" are reading the same
|
|
11
|
+
* five words.
|
|
12
|
+
* Emits: N/A (types + one pure function).
|
|
13
|
+
*
|
|
14
|
+
* ## Why the list is here and not inside `.act()`
|
|
15
|
+
*
|
|
16
|
+
* A bundle whose keys ARE the moments is only honest while the two sets
|
|
17
|
+
* agree. Keep the list of moments in the options type and the sixth moment
|
|
18
|
+
* ships with no key and nothing notices; keep it here, derive the key names
|
|
19
|
+
* from it, and the day a moment is added the build fails inside `act.ts`
|
|
20
|
+
* naming the key nobody wrote. That is the same trick `conformSource` plays
|
|
21
|
+
* on the outside world, turned inward on ourselves.
|
|
22
|
+
*
|
|
23
|
+
* The mapping between the two spellings is mechanical — kebab in the record
|
|
24
|
+
* (`'before-tool'`, which reads as a phase), camel in the bundle
|
|
25
|
+
* (`beforeTool`, which reads as a property) — and `actKeyFor` is its runtime
|
|
26
|
+
* twin, so the validator that decides which keys `.act()` accepts is
|
|
27
|
+
* DERIVED from this list rather than typed out beside it.
|
|
28
|
+
*/
|
|
29
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
30
|
+
exports.actKeyFor = exports.LOOP_MOMENTS = void 0;
|
|
31
|
+
/**
|
|
32
|
+
* Every moment of one agent turn where a rule may act, in the order the loop
|
|
33
|
+
* reaches them.
|
|
34
|
+
*
|
|
35
|
+
* ```
|
|
36
|
+
* input → [ window → … LLM … → before-tool → tool → after-tool ]* → output
|
|
37
|
+
* ```
|
|
38
|
+
*
|
|
39
|
+
* `watch` attends all five and more; `act` attends exactly these. The
|
|
40
|
+
* difference is what a rule may DO there — an observer reports, a rule
|
|
41
|
+
* changes what happens next.
|
|
42
|
+
*/
|
|
43
|
+
exports.LOOP_MOMENTS = ['input', 'before-tool', 'after-tool', 'window', 'output'];
|
|
44
|
+
/**
|
|
45
|
+
* The runtime twin of {@link ActKey}. `.act()`'s accepted key set is built
|
|
46
|
+
* from this over `LOOP_MOMENTS`, so an unknown key is refused by a rule that
|
|
47
|
+
* cannot fall behind the list it is derived from.
|
|
48
|
+
*/
|
|
49
|
+
function actKeyFor(moment) {
|
|
50
|
+
return moment.replace(/-(.)/g, (_match, c) => c.toUpperCase());
|
|
51
|
+
}
|
|
52
|
+
exports.actKeyFor = actKeyFor;
|
|
53
|
+
//# sourceMappingURL=moments.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"moments.js","sourceRoot":"","sources":["../../../src/core/agent/moments.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;;;AAEH;;;;;;;;;;;GAWG;AACU,QAAA,YAAY,GAAG,CAAC,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,QAAQ,EAAE,QAAQ,CAAU,CAAC;AAehG;;;;GAIG;AACH,SAAgB,SAAS,CAAC,MAAkB;IAC1C,OAAO,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,MAAM,EAAE,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAW,CAAC;AACnF,CAAC;AAFD,8BAEC"}
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
* Gate order for one call, and why it is this order:
|
|
25
25
|
*
|
|
26
26
|
* permission → MIDDLEWARE CHAIN → arg validation → check-in →
|
|
27
|
-
* credentials → execute
|
|
27
|
+
* credentials → execute → THE CHAIN AGAIN, BACKWARDS (`onToolResult`)
|
|
28
28
|
*
|
|
29
29
|
* The chain sits after the permission gate so an existing checker still
|
|
30
30
|
* decides first (a denial there means no middleware runs), and before arg
|
|
@@ -34,6 +34,11 @@
|
|
|
34
34
|
* pauses on the SAME wire `checkIn` uses; the human's answer is a
|
|
35
35
|
* decision, not a result, so the chain resumes and the REAL tool runs.
|
|
36
36
|
*
|
|
37
|
+
* The last step is the same chain walked BACKWARDS over the result, before it
|
|
38
|
+
* becomes history — so the first-declared rule has the first word about the
|
|
39
|
+
* call and the last word about the answer. It runs only for a call that
|
|
40
|
+
* executed: everything the gates above refuse has no result to decide about.
|
|
41
|
+
*
|
|
37
42
|
* `read_skill` is the auto-attached activation tool — when the LLM
|
|
38
43
|
* calls it with a valid Skill id, the next InjectionEngine pass
|
|
39
44
|
* activates that Skill (lifetime: turn).
|
|
@@ -73,6 +78,45 @@ function buildToolCallsHandler(deps) {
|
|
|
73
78
|
const cached = providerToolCache?.current ?? [];
|
|
74
79
|
return cached.find((t) => t.schema.name === toolName);
|
|
75
80
|
};
|
|
81
|
+
/**
|
|
82
|
+
* The after-tool moment: the chain's last word, on a call that RAN.
|
|
83
|
+
*
|
|
84
|
+
* Called from all three dispatch sites (the loop, an approved ask, an
|
|
85
|
+
* approved check-in) and from nowhere else — every one of them has just
|
|
86
|
+
* executed a tool, which is the entire precondition. A call the chain
|
|
87
|
+
* denied, a call whose args were rejected, a call whose credential never
|
|
88
|
+
* issued and a call still waiting on a person have no result, and asking a
|
|
89
|
+
* rule about a result that does not exist would be the same fabrication the
|
|
90
|
+
* outcome union removes.
|
|
91
|
+
*
|
|
92
|
+
* Returns what the MODEL reads. The real result stays with the caller for
|
|
93
|
+
* `stream.tool_end`, so an event stream keeps reporting what the tool
|
|
94
|
+
* returned while the history carries what the rules allowed through — and
|
|
95
|
+
* the ledger row beside them says which is which.
|
|
96
|
+
*/
|
|
97
|
+
const afterMoment = async (scope, call) => {
|
|
98
|
+
const chain = deps.toolMiddleware ?? [];
|
|
99
|
+
// No `onToolResult` hook anywhere in the chain → no walk, no rows, no await
|
|
100
|
+
// beyond the ones this dispatch already made. An agent whose middleware
|
|
101
|
+
// only governs calls is byte-identical to one built before this moment
|
|
102
|
+
// existed.
|
|
103
|
+
if (!chain.some((mw) => typeof mw.onToolResult === 'function'))
|
|
104
|
+
return call.result;
|
|
105
|
+
const verdict = await (0, runChain_js_1.runToolAfterChain)(chain, {
|
|
106
|
+
toolName: call.toolName,
|
|
107
|
+
...(call.tool?.source !== undefined && { toolSource: call.tool.source }),
|
|
108
|
+
toolCallId: call.toolCallId,
|
|
109
|
+
iteration: call.iteration,
|
|
110
|
+
args: call.args,
|
|
111
|
+
result: call.result,
|
|
112
|
+
...(call.error === true && { error: true }),
|
|
113
|
+
history: call.history,
|
|
114
|
+
...(call.identity && { identity: call.identity }),
|
|
115
|
+
...(call.signal && { signal: call.signal }),
|
|
116
|
+
});
|
|
117
|
+
(0, ledger_js_1.recordDecisions)(scope, verdict.decisions);
|
|
118
|
+
return verdict.kind === 'deny' ? verdict.reason : verdict.result;
|
|
119
|
+
};
|
|
76
120
|
// Resolve a tool's declared credential (declare-and-push) and execute it,
|
|
77
121
|
// emitting the same credential.* events as the main loop. Used by the
|
|
78
122
|
// check-in RESUME path when a human APPROVES — the tool never ran at pause
|
|
@@ -137,16 +181,24 @@ function buildToolCallsHandler(deps) {
|
|
|
137
181
|
hasCredentials,
|
|
138
182
|
...(resolvedCredential && { credential: resolvedCredential }),
|
|
139
183
|
});
|
|
140
|
-
return { result };
|
|
184
|
+
return { result, executed: true };
|
|
141
185
|
}
|
|
142
186
|
catch (err) {
|
|
143
187
|
if ((0, pause_js_1.isPauseRequest)(err)) {
|
|
144
188
|
return {
|
|
145
189
|
result: `tool '${toolName}' requested a pause while resuming an approved check-in, which is not supported`,
|
|
146
190
|
error: true,
|
|
191
|
+
// It ran — a tool that asked to pause had already started work.
|
|
192
|
+
executed: true,
|
|
147
193
|
};
|
|
148
194
|
}
|
|
149
|
-
|
|
195
|
+
// A tool that threw still RAN, and may have done half its work; the
|
|
196
|
+
// after-tool moment exists precisely for the rules that care about that.
|
|
197
|
+
return {
|
|
198
|
+
result: err instanceof Error ? err.message : String(err),
|
|
199
|
+
error: true,
|
|
200
|
+
executed: true,
|
|
201
|
+
};
|
|
150
202
|
}
|
|
151
203
|
};
|
|
152
204
|
return {
|
|
@@ -234,6 +286,8 @@ function buildToolCallsHandler(deps) {
|
|
|
234
286
|
// answer to "what did this call really run with".
|
|
235
287
|
let callArgs = tc.args;
|
|
236
288
|
let denied = false;
|
|
289
|
+
/** True once `tool.execute` has been entered — see `afterMoment`. */
|
|
290
|
+
let executed = false;
|
|
237
291
|
let haltContext;
|
|
238
292
|
if (permissionChecker) {
|
|
239
293
|
try {
|
|
@@ -309,6 +363,9 @@ function buildToolCallsHandler(deps) {
|
|
|
309
363
|
if (!denied && deps.toolMiddleware && deps.toolMiddleware.length > 0) {
|
|
310
364
|
const chain = await (0, runChain_js_1.runToolChain)(deps.toolMiddleware, {
|
|
311
365
|
toolName: tc.name,
|
|
366
|
+
// Provenance from the tool that is about to run, so a policy can
|
|
367
|
+
// scope to the server that served it. Absent for our own tools.
|
|
368
|
+
...(tool?.source !== undefined && { toolSource: tool.source }),
|
|
312
369
|
toolCallId: tc.id,
|
|
313
370
|
iteration,
|
|
314
371
|
args: callArgs,
|
|
@@ -497,6 +554,10 @@ function buildToolCallsHandler(deps) {
|
|
|
497
554
|
try {
|
|
498
555
|
if (!tool)
|
|
499
556
|
throw new Error(`Unknown tool: ${tc.name}`);
|
|
557
|
+
// Set BEFORE the await: a tool that throws has still run, and a
|
|
558
|
+
// tool that does not exist has not. This flag is the entire
|
|
559
|
+
// precondition of the after-tool moment below.
|
|
560
|
+
executed = true;
|
|
500
561
|
result = await tool.execute(callArgs, {
|
|
501
562
|
toolCallId: tc.id,
|
|
502
563
|
iteration,
|
|
@@ -556,6 +617,24 @@ function buildToolCallsHandler(deps) {
|
|
|
556
617
|
}
|
|
557
618
|
}
|
|
558
619
|
}
|
|
620
|
+
// ── The after-tool moment ────────────────────────────────────────
|
|
621
|
+
// Last thing before the result becomes history, and only for a call
|
|
622
|
+
// that ran. `modelResult` is what the model reads; `result` stays the
|
|
623
|
+
// truth about the tool and is what `stream.tool_end` reports.
|
|
624
|
+
const modelResult = executed
|
|
625
|
+
? await afterMoment(scope, {
|
|
626
|
+
...(tool && { tool }),
|
|
627
|
+
toolName: tc.name,
|
|
628
|
+
toolCallId: tc.id,
|
|
629
|
+
iteration,
|
|
630
|
+
args: callArgs,
|
|
631
|
+
result,
|
|
632
|
+
...(error === true && { error: true }),
|
|
633
|
+
history: newHistory,
|
|
634
|
+
...(runIdentity && { identity: runIdentity }),
|
|
635
|
+
...(env.signal && { signal: env.signal }),
|
|
636
|
+
})
|
|
637
|
+
: result;
|
|
559
638
|
const durationMs = Date.now() - startMs;
|
|
560
639
|
(0, typedEmit_js_1.typedEmit)(scope, 'agentfootprint.stream.tool_end', {
|
|
561
640
|
toolCallId: tc.id,
|
|
@@ -563,7 +642,7 @@ function buildToolCallsHandler(deps) {
|
|
|
563
642
|
durationMs,
|
|
564
643
|
...(error === true && { error: true }),
|
|
565
644
|
});
|
|
566
|
-
const resultStr = typeof
|
|
645
|
+
const resultStr = typeof modelResult === 'string' ? modelResult : (0, validators_js_1.safeStringify)(modelResult);
|
|
567
646
|
newHistory.push({
|
|
568
647
|
role: 'tool',
|
|
569
648
|
content: resultStr,
|
|
@@ -670,12 +749,16 @@ function buildToolCallsHandler(deps) {
|
|
|
670
749
|
? input
|
|
671
750
|
: (0, checkin_js_1.checkInDeclined)({ by: 'unknown', note: 'resume input was not a CheckInDecision' });
|
|
672
751
|
let result;
|
|
752
|
+
/** What the model reads. Differs from `result` only when a rule at the
|
|
753
|
+
* after-tool moment transformed or withheld it. */
|
|
754
|
+
let modelResult;
|
|
673
755
|
let error;
|
|
674
756
|
if (!decision.approved) {
|
|
675
757
|
result = decision.note ? `declined by human: ${decision.note}` : 'declined by human';
|
|
676
758
|
(0, ledger_js_1.recordDecisions)(scope, [
|
|
677
759
|
{
|
|
678
760
|
middleware: askedBy,
|
|
761
|
+
moment: 'before-tool',
|
|
679
762
|
at: 'tool',
|
|
680
763
|
toolName,
|
|
681
764
|
toolCallId,
|
|
@@ -690,6 +773,7 @@ function buildToolCallsHandler(deps) {
|
|
|
690
773
|
(0, ledger_js_1.recordDecisions)(scope, [
|
|
691
774
|
{
|
|
692
775
|
middleware: askedBy,
|
|
776
|
+
moment: 'before-tool',
|
|
693
777
|
at: 'tool',
|
|
694
778
|
toolName,
|
|
695
779
|
toolCallId,
|
|
@@ -709,8 +793,14 @@ function buildToolCallsHandler(deps) {
|
|
|
709
793
|
// model-visible refusal and the tool does NOT run. That is the same
|
|
710
794
|
// rule already applied to a tool that tries to pause during an
|
|
711
795
|
// approved check-in resume: at most one human question per resume.
|
|
796
|
+
// Resolved BEFORE the chain runs, not after, because the links that
|
|
797
|
+
// continue here must see the same `toolSource` the links before the
|
|
798
|
+
// ask saw. A chain that changed its mind about where a tool came from
|
|
799
|
+
// halfway through one dispatch would be worse than not knowing.
|
|
800
|
+
const tool = lookupTool(toolName);
|
|
712
801
|
const rest = await (0, runChain_js_1.runToolChain)(deps.toolMiddleware ?? [], {
|
|
713
802
|
toolName,
|
|
803
|
+
...(tool?.source !== undefined && { toolSource: tool.source }),
|
|
714
804
|
toolCallId,
|
|
715
805
|
iteration,
|
|
716
806
|
args,
|
|
@@ -719,7 +809,6 @@ function buildToolCallsHandler(deps) {
|
|
|
719
809
|
askPolicy: 'refuse',
|
|
720
810
|
});
|
|
721
811
|
(0, ledger_js_1.recordDecisions)(scope, rest.decisions);
|
|
722
|
-
const tool = lookupTool(toolName);
|
|
723
812
|
if (rest.kind === 'deny') {
|
|
724
813
|
result = rest.reason;
|
|
725
814
|
}
|
|
@@ -739,9 +828,30 @@ function buildToolCallsHandler(deps) {
|
|
|
739
828
|
const dispatched = await resolveCredentialAndExecute(scope, tool, toolName, rest.args, toolCallId, iteration, env);
|
|
740
829
|
result = dispatched.result;
|
|
741
830
|
error = dispatched.error;
|
|
831
|
+
// The tool ran on this side of the pause, so the chain gets its
|
|
832
|
+
// last word here too — a rule about results cannot be skipped by
|
|
833
|
+
// routing a call through a human.
|
|
834
|
+
if (dispatched.executed === true) {
|
|
835
|
+
modelResult = await afterMoment(scope, {
|
|
836
|
+
...(tool && { tool }),
|
|
837
|
+
toolName,
|
|
838
|
+
toolCallId,
|
|
839
|
+
iteration,
|
|
840
|
+
args: rest.args,
|
|
841
|
+
result,
|
|
842
|
+
...(error === true && { error: true }),
|
|
843
|
+
history: [...scope.history],
|
|
844
|
+
...(scope.runIdentity && { identity: scope.runIdentity }),
|
|
845
|
+
...(env.signal && { signal: env.signal }),
|
|
846
|
+
});
|
|
847
|
+
}
|
|
742
848
|
}
|
|
743
849
|
}
|
|
744
|
-
|
|
850
|
+
// `modelResult` is only set where the after-tool moment ran; everywhere
|
|
851
|
+
// else the two are the same value.
|
|
852
|
+
if (modelResult === undefined)
|
|
853
|
+
modelResult = result;
|
|
854
|
+
const askResultStr = typeof modelResult === 'string' ? modelResult : (0, validators_js_1.safeStringify)(modelResult);
|
|
745
855
|
const askHistory = [
|
|
746
856
|
...scope.history,
|
|
747
857
|
{ role: 'tool', content: askResultStr, toolCallId, toolName },
|
|
@@ -795,17 +905,38 @@ function buildToolCallsHandler(deps) {
|
|
|
795
905
|
...(decision.note !== undefined && { note: decision.note }),
|
|
796
906
|
});
|
|
797
907
|
let result;
|
|
908
|
+
/** What the model reads — see the ask path above. */
|
|
909
|
+
let modelResult;
|
|
798
910
|
let error;
|
|
799
911
|
if (decision.approved) {
|
|
800
912
|
const env = scope.$getEnv();
|
|
801
|
-
const
|
|
913
|
+
const tool = lookupTool(toolName);
|
|
914
|
+
const dispatched = await resolveCredentialAndExecute(scope, tool, toolName, args, toolCallId, iteration, env);
|
|
802
915
|
result = dispatched.result;
|
|
803
916
|
error = dispatched.error;
|
|
917
|
+
// Consent moved the execution to this side of the pause; the rules
|
|
918
|
+
// about results move with it.
|
|
919
|
+
if (dispatched.executed === true) {
|
|
920
|
+
modelResult = await afterMoment(scope, {
|
|
921
|
+
...(tool && { tool }),
|
|
922
|
+
toolName,
|
|
923
|
+
toolCallId,
|
|
924
|
+
iteration,
|
|
925
|
+
args,
|
|
926
|
+
result,
|
|
927
|
+
...(error === true && { error: true }),
|
|
928
|
+
history: [...scope.history],
|
|
929
|
+
...(scope.runIdentity && { identity: scope.runIdentity }),
|
|
930
|
+
...(env.signal && { signal: env.signal }),
|
|
931
|
+
});
|
|
932
|
+
}
|
|
804
933
|
}
|
|
805
934
|
else {
|
|
806
935
|
result = decision.note ? `declined by human: ${decision.note}` : 'declined by human';
|
|
807
936
|
}
|
|
808
|
-
|
|
937
|
+
if (modelResult === undefined)
|
|
938
|
+
modelResult = result;
|
|
939
|
+
const decisionResultStr = typeof modelResult === 'string' ? modelResult : (0, validators_js_1.safeStringify)(modelResult);
|
|
809
940
|
const decisionHistory = [
|
|
810
941
|
...scope.history,
|
|
811
942
|
{ role: 'tool', content: decisionResultStr, toolCallId, toolName },
|