agentfootprint 7.17.0 → 7.18.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/AGENTS.md +1 -1
- package/CLAUDE.md +2 -2
- package/ai-instructions/claude-code/SKILL.md +1 -1
- package/dist/core/Agent.js +51 -2
- package/dist/core/Agent.js.map +1 -1
- package/dist/core/RunnerBase.js +16 -1
- package/dist/core/RunnerBase.js.map +1 -1
- package/dist/core/agent/AgentBuilder.js +117 -1
- package/dist/core/agent/AgentBuilder.js.map +1 -1
- package/dist/core/agent/middleware/errors.js +54 -0
- package/dist/core/agent/middleware/errors.js.map +1 -0
- package/dist/core/agent/middleware/index.js +23 -0
- package/dist/core/agent/middleware/index.js.map +1 -0
- package/dist/core/agent/middleware/ledger.js +61 -0
- package/dist/core/agent/middleware/ledger.js.map +1 -0
- package/dist/core/agent/middleware/outcomes.js +66 -0
- package/dist/core/agent/middleware/outcomes.js.map +1 -0
- package/dist/core/agent/middleware/runChain.js +144 -0
- package/dist/core/agent/middleware/runChain.js.map +1 -0
- package/dist/core/agent/middleware/types.js +41 -0
- package/dist/core/agent/middleware/types.js.map +1 -0
- package/dist/core/agent/stages/prepareFinal.js +10 -0
- package/dist/core/agent/stages/prepareFinal.js.map +1 -1
- package/dist/core/agent/stages/route.js +62 -1
- package/dist/core/agent/stages/route.js.map +1 -1
- package/dist/core/agent/stages/seed.js +130 -85
- package/dist/core/agent/stages/seed.js.map +1 -1
- package/dist/core/agent/stages/toolCalls.js +195 -8
- package/dist/core/agent/stages/toolCalls.js.map +1 -1
- package/dist/core/pause.js +19 -1
- package/dist/core/pause.js.map +1 -1
- package/dist/esm/core/Agent.d.ts +9 -1
- package/dist/esm/core/Agent.js +52 -3
- package/dist/esm/core/Agent.js.map +1 -1
- package/dist/esm/core/RunnerBase.js +16 -1
- package/dist/esm/core/RunnerBase.js.map +1 -1
- package/dist/esm/core/agent/AgentBuilder.d.ts +97 -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/buildAgentChart.d.ts +2 -2
- package/dist/esm/core/agent/middleware/errors.d.ts +51 -0
- package/dist/esm/core/agent/middleware/errors.js +50 -0
- package/dist/esm/core/agent/middleware/errors.js.map +1 -0
- package/dist/esm/core/agent/middleware/index.d.ts +16 -0
- package/dist/esm/core/agent/middleware/index.js +16 -0
- package/dist/esm/core/agent/middleware/index.js.map +1 -0
- package/dist/esm/core/agent/middleware/ledger.d.ts +40 -0
- package/dist/esm/core/agent/middleware/ledger.js +57 -0
- package/dist/esm/core/agent/middleware/ledger.js.map +1 -0
- package/dist/esm/core/agent/middleware/outcomes.d.ts +45 -0
- package/dist/esm/core/agent/middleware/outcomes.js +60 -0
- package/dist/esm/core/agent/middleware/outcomes.js.map +1 -0
- package/dist/esm/core/agent/middleware/runChain.d.ts +104 -0
- package/dist/esm/core/agent/middleware/runChain.js +139 -0
- package/dist/esm/core/agent/middleware/runChain.js.map +1 -0
- package/dist/esm/core/agent/middleware/types.d.ts +191 -0
- package/dist/esm/core/agent/middleware/types.js +40 -0
- package/dist/esm/core/agent/middleware/types.js.map +1 -0
- package/dist/esm/core/agent/stages/prepareFinal.d.ts +10 -0
- package/dist/esm/core/agent/stages/prepareFinal.js +10 -0
- package/dist/esm/core/agent/stages/prepareFinal.js.map +1 -1
- package/dist/esm/core/agent/stages/route.d.ts +29 -0
- package/dist/esm/core/agent/stages/route.js +60 -0
- package/dist/esm/core/agent/stages/route.js.map +1 -1
- package/dist/esm/core/agent/stages/seed.d.ts +16 -1
- package/dist/esm/core/agent/stages/seed.js +130 -85
- package/dist/esm/core/agent/stages/seed.js.map +1 -1
- package/dist/esm/core/agent/stages/toolCalls.d.ts +23 -0
- package/dist/esm/core/agent/stages/toolCalls.js +195 -8
- package/dist/esm/core/agent/stages/toolCalls.js.map +1 -1
- package/dist/esm/core/agent/types.d.ts +18 -0
- package/dist/esm/core/pause.d.ts +42 -0
- package/dist/esm/core/pause.js +17 -0
- package/dist/esm/core/pause.js.map +1 -1
- package/dist/esm/events/dispatcher.d.ts +1 -1
- package/dist/esm/events/dispatcher.js.map +1 -1
- package/dist/esm/events/payloads.d.ts +26 -0
- package/dist/esm/events/registry.d.ts +5 -1
- package/dist/esm/events/registry.js +4 -0
- package/dist/esm/events/registry.js.map +1 -1
- package/dist/esm/index.d.ts +2 -1
- package/dist/esm/index.js +13 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/lib/mcp/mcpServe.js +39 -6
- package/dist/esm/lib/mcp/mcpServe.js.map +1 -1
- package/dist/esm/lib/mcp/types.d.ts +16 -0
- package/dist/events/dispatcher.js.map +1 -1
- package/dist/events/registry.js +4 -0
- package/dist/events/registry.js.map +1 -1
- package/dist/index.js +31 -14
- package/dist/index.js.map +1 -1
- package/dist/lib/mcp/mcpServe.js +39 -6
- package/dist/lib/mcp/mcpServe.js.map +1 -1
- package/dist/types/core/Agent.d.ts +9 -1
- package/dist/types/core/Agent.d.ts.map +1 -1
- package/dist/types/core/RunnerBase.d.ts.map +1 -1
- package/dist/types/core/agent/AgentBuilder.d.ts +97 -0
- package/dist/types/core/agent/AgentBuilder.d.ts.map +1 -1
- package/dist/types/core/agent/buildAgentChart.d.ts +2 -2
- package/dist/types/core/agent/buildAgentChart.d.ts.map +1 -1
- package/dist/types/core/agent/middleware/errors.d.ts +52 -0
- package/dist/types/core/agent/middleware/errors.d.ts.map +1 -0
- package/dist/types/core/agent/middleware/index.d.ts +17 -0
- package/dist/types/core/agent/middleware/index.d.ts.map +1 -0
- package/dist/types/core/agent/middleware/ledger.d.ts +41 -0
- package/dist/types/core/agent/middleware/ledger.d.ts.map +1 -0
- package/dist/types/core/agent/middleware/outcomes.d.ts +46 -0
- package/dist/types/core/agent/middleware/outcomes.d.ts.map +1 -0
- package/dist/types/core/agent/middleware/runChain.d.ts +105 -0
- package/dist/types/core/agent/middleware/runChain.d.ts.map +1 -0
- package/dist/types/core/agent/middleware/types.d.ts +192 -0
- package/dist/types/core/agent/middleware/types.d.ts.map +1 -0
- package/dist/types/core/agent/stages/prepareFinal.d.ts +10 -0
- package/dist/types/core/agent/stages/prepareFinal.d.ts.map +1 -1
- package/dist/types/core/agent/stages/route.d.ts +29 -0
- package/dist/types/core/agent/stages/route.d.ts.map +1 -1
- package/dist/types/core/agent/stages/seed.d.ts +16 -1
- package/dist/types/core/agent/stages/seed.d.ts.map +1 -1
- package/dist/types/core/agent/stages/toolCalls.d.ts +23 -0
- package/dist/types/core/agent/stages/toolCalls.d.ts.map +1 -1
- package/dist/types/core/agent/types.d.ts +18 -0
- package/dist/types/core/agent/types.d.ts.map +1 -1
- package/dist/types/core/pause.d.ts +42 -0
- package/dist/types/core/pause.d.ts.map +1 -1
- package/dist/types/events/dispatcher.d.ts +1 -1
- package/dist/types/events/dispatcher.d.ts.map +1 -1
- package/dist/types/events/payloads.d.ts +26 -0
- package/dist/types/events/payloads.d.ts.map +1 -1
- package/dist/types/events/registry.d.ts +5 -1
- package/dist/types/events/registry.d.ts.map +1 -1
- package/dist/types/index.d.ts +2 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/lib/mcp/mcpServe.d.ts.map +1 -1
- package/dist/types/lib/mcp/types.d.ts +16 -0
- package/dist/types/lib/mcp/types.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* middleware — PUBLIC barrel. The governance chain family.
|
|
3
|
+
*
|
|
4
|
+
* Two chains, one vocabulary:
|
|
5
|
+
*
|
|
6
|
+
* `.toolMiddleware(...)` wraps every tool dispatch
|
|
7
|
+
* `.messageMiddleware(...)` wraps the message boundary — the input
|
|
8
|
+
* before the model sees it, the output before
|
|
9
|
+
* the caller receives it
|
|
10
|
+
*
|
|
11
|
+
* Three verbs: `allow`, `deny`, `ask`. There is no fourth, and in
|
|
12
|
+
* particular there is no way to return a result — see `types.ts`.
|
|
13
|
+
*/
|
|
14
|
+
export { allow, ask, deny } from './outcomes.js';
|
|
15
|
+
export { MessageDeniedError, type MessageDeniedContext } from './errors.js';
|
|
16
|
+
export type { AllowOutcome, AskOutcome, AskPayload, DenyOutcome, MessageMiddleware, MessageMiddlewareContext, MessageOutcome, MiddlewareDecision, ToolMiddleware, ToolMiddlewareContext, ToolOutcome, } from './types.js';
|
|
17
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/core/agent/middleware/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,kBAAkB,EAAE,KAAK,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAC5E,YAAY,EACV,YAAY,EACZ,UAAU,EACV,UAAU,EACV,WAAW,EACX,iBAAiB,EACjB,wBAAwB,EACxB,cAAc,EACd,kBAAkB,EAClB,cAAc,EACd,qBAAqB,EACrB,WAAW,GACZ,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* middleware/ledger — INTERNAL. Where a chain's decisions become record.
|
|
3
|
+
*
|
|
4
|
+
* Pattern: one writer for one committed key.
|
|
5
|
+
* Role: core/ layer. `runChain` decides; this file is the only thing
|
|
6
|
+
* that commits what it decided and emits the matching event, so
|
|
7
|
+
* the three call sites cannot record the same fact three
|
|
8
|
+
* different ways.
|
|
9
|
+
* Emits: `agentfootprint.middleware.decision`, one per row.
|
|
10
|
+
*
|
|
11
|
+
* ## Why the original value is committed too
|
|
12
|
+
*
|
|
13
|
+
* A transform makes the trace and the wire disagree — the model saw one
|
|
14
|
+
* string, the run remembers another. Committing the pair, with the
|
|
15
|
+
* middleware's name and its `why` beside it, is what turns that
|
|
16
|
+
* disagreement from a lie into a record. Every slice taken afterwards can
|
|
17
|
+
* then find the moment the value changed and who changed it.
|
|
18
|
+
*
|
|
19
|
+
* This is not a redaction layer, and it is not trying to be one. For the
|
|
20
|
+
* `'input'` phase in particular, the ledger row is the ONLY copy of the
|
|
21
|
+
* pre-scrub text anywhere in the run — the seed stage commits the
|
|
22
|
+
* transformed message and nothing else ever holds the original. If your
|
|
23
|
+
* threat model says the original must not survive in the commit log,
|
|
24
|
+
* configure footprintjs redaction over this key; it scrubs `before` /
|
|
25
|
+
* `after` at write time and the row itself survives, so the run still says
|
|
26
|
+
* a scrub happened and who did it.
|
|
27
|
+
*/
|
|
28
|
+
import type { MiddlewareDecision } from './types.js';
|
|
29
|
+
/** The scope surface this file needs. Structurally a `TypedScope<AgentState>`. */
|
|
30
|
+
export interface LedgerScope {
|
|
31
|
+
middlewareDecisions?: readonly MiddlewareDecision[];
|
|
32
|
+
$emit(name: string, payload?: unknown): void;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Append decision rows to the run's ledger and emit one event per row.
|
|
36
|
+
*
|
|
37
|
+
* No-op on an empty list, so an agent with no middleware never writes the
|
|
38
|
+
* key at all and its commit log is the one it always had.
|
|
39
|
+
*/
|
|
40
|
+
export declare function recordDecisions(scope: LedgerScope, rows: readonly MiddlewareDecision[]): void;
|
|
41
|
+
//# sourceMappingURL=ledger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ledger.d.ts","sourceRoot":"","sources":["../../../../../src/core/agent/middleware/ledger.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAGH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAErD,kFAAkF;AAClF,MAAM,WAAW,WAAW;IAC1B,mBAAmB,CAAC,EAAE,SAAS,kBAAkB,EAAE,CAAC;IACpD,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;CAC9C;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,SAAS,kBAAkB,EAAE,GAAG,IAAI,CAoB7F"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* middleware/outcomes — PUBLIC. The three verbs a middleware speaks.
|
|
3
|
+
*
|
|
4
|
+
* Pattern: Smart constructors over a closed union.
|
|
5
|
+
* Role: core/ layer. `allow` / `deny` / `ask` are the entire vocabulary
|
|
6
|
+
* of both chains, which is why they are short: they are written
|
|
7
|
+
* inside every middleware body, several times, and a governance
|
|
8
|
+
* rule reads better as `deny('writes to prod need a ticket')` than
|
|
9
|
+
* as a namespaced ceremony.
|
|
10
|
+
* Emits: N/A.
|
|
11
|
+
*
|
|
12
|
+
* The `allow` overloads carry a rule: you can pass the value through
|
|
13
|
+
* untouched with no explanation, but you cannot CHANGE it without one. A
|
|
14
|
+
* transform is the only thing here that makes the trace and the wire
|
|
15
|
+
* disagree, so it is the only thing required to say why.
|
|
16
|
+
*/
|
|
17
|
+
import type { AllowOutcome, AskOutcome, AskPayload, DenyOutcome } from './types.js';
|
|
18
|
+
/** Pass the value through untouched. */
|
|
19
|
+
export declare function allow(): AllowOutcome<never>;
|
|
20
|
+
/**
|
|
21
|
+
* Replace the value and say why.
|
|
22
|
+
*
|
|
23
|
+
* The `why` is mandatory and lands in the ledger next to the before/after
|
|
24
|
+
* pair, so a run that was scrubbed can be read back as a run that was
|
|
25
|
+
* scrubbed rather than as a run whose input was always that way.
|
|
26
|
+
*/
|
|
27
|
+
export declare function allow<T>(value: T, why: string): AllowOutcome<T>;
|
|
28
|
+
/**
|
|
29
|
+
* Refuse the call.
|
|
30
|
+
*
|
|
31
|
+
* For a tool the reason reaches the model verbatim, as the tool's result,
|
|
32
|
+
* and the loop continues — the agent gets to adapt. For a message it
|
|
33
|
+
* surfaces as a `MessageDeniedError`.
|
|
34
|
+
*/
|
|
35
|
+
export declare function deny(reason: string): DenyOutcome;
|
|
36
|
+
/**
|
|
37
|
+
* Suspend the run and put the question to a person. Tool dispatch only —
|
|
38
|
+
* `MessageOutcome` has no `ask` arm, so this cannot be returned from a
|
|
39
|
+
* message middleware.
|
|
40
|
+
*
|
|
41
|
+
* The answer is a decision, not a result: approve and the chain continues
|
|
42
|
+
* and the REAL tool runs; decline and it becomes a denial the model reads.
|
|
43
|
+
* A middleware never gets to write the answer itself.
|
|
44
|
+
*/
|
|
45
|
+
export declare function ask(payload: AskPayload): AskOutcome;
|
|
46
|
+
//# sourceMappingURL=outcomes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"outcomes.d.ts","sourceRoot":"","sources":["../../../../../src/core/agent/middleware/outcomes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEpF,wCAAwC;AACxC,wBAAgB,KAAK,IAAI,YAAY,CAAC,KAAK,CAAC,CAAC;AAC7C;;;;;;GAMG;AACH,wBAAgB,KAAK,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC;AAajE;;;;;;GAMG;AACH,wBAAgB,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,CAQhD;AAED;;;;;;;;GAQG;AACH,wBAAgB,GAAG,CAAC,OAAO,EAAE,UAAU,GAAG,UAAU,CAanD"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* middleware/runChain — INTERNAL. The one place a chain is walked.
|
|
3
|
+
*
|
|
4
|
+
* Pattern: Chain of Responsibility driver.
|
|
5
|
+
* Role: core/ layer. Both stages (tool dispatch, message boundary) and
|
|
6
|
+
* the MCP server call in here, so the ordering law, the
|
|
7
|
+
* throw-is-a-denial law and the ledger row shape are written once
|
|
8
|
+
* and cannot drift between the three callers.
|
|
9
|
+
* Emits: N/A — it RETURNS rows; the caller commits and emits them, so a
|
|
10
|
+
* middleware still never touches scope.
|
|
11
|
+
*
|
|
12
|
+
* ## The rules this file implements
|
|
13
|
+
*
|
|
14
|
+
* **Order is declaration order.** The chain is walked front to back, and
|
|
15
|
+
* each link is handed the previous link's output — so a scrubber placed
|
|
16
|
+
* first is a scrubber every later rule sees the effect of.
|
|
17
|
+
*
|
|
18
|
+
* **The first non-allow answer wins.** A deny or an ask stops the walk;
|
|
19
|
+
* the links behind it do not run. There is no "second opinion" pass,
|
|
20
|
+
* because a refusal that a later middleware could overturn is not a
|
|
21
|
+
* refusal.
|
|
22
|
+
*
|
|
23
|
+
* **A middleware that throws is a denial, never a pass.** The thrown
|
|
24
|
+
* message becomes the reason. This is the same deny-by-default the
|
|
25
|
+
* permission gate has always applied to a checker that throws: a
|
|
26
|
+
* governance layer whose failure mode is "allow" is not a governance
|
|
27
|
+
* layer.
|
|
28
|
+
*/
|
|
29
|
+
import type { LLMMessage } from '../../../adapters/types.js';
|
|
30
|
+
import type { MemoryIdentity } from '../../../memory/identity/types.js';
|
|
31
|
+
import type { AskPayload, MessageMiddleware, MiddlewareDecision, ToolMiddleware } from './types.js';
|
|
32
|
+
/** Tool args, as they travel the chain. */
|
|
33
|
+
export type ToolArgs = Readonly<Record<string, unknown>>;
|
|
34
|
+
/**
|
|
35
|
+
* What to do when a middleware answers `ask` somewhere that cannot pause.
|
|
36
|
+
*
|
|
37
|
+
* `'pause'` — the caller will checkpoint (the normal dispatch path).
|
|
38
|
+
* `'refuse'` — the ask is converted into a deny naming the middleware. Used
|
|
39
|
+
* on the resume path (footprintjs's `PausableHandler.resume` returns void,
|
|
40
|
+
* so a resumed dispatch cannot suspend a second time) and by `mcpServe`
|
|
41
|
+
* (MCP is request/response — there is no pause to carry the ask).
|
|
42
|
+
*/
|
|
43
|
+
export type AskPolicy = 'pause' | 'refuse';
|
|
44
|
+
export type ToolChainResult = {
|
|
45
|
+
readonly kind: 'allow';
|
|
46
|
+
readonly args: ToolArgs;
|
|
47
|
+
readonly decisions: MiddlewareDecision[];
|
|
48
|
+
} | {
|
|
49
|
+
readonly kind: 'deny';
|
|
50
|
+
readonly reason: string;
|
|
51
|
+
readonly middleware: string;
|
|
52
|
+
readonly args: ToolArgs;
|
|
53
|
+
readonly decisions: MiddlewareDecision[];
|
|
54
|
+
} | {
|
|
55
|
+
readonly kind: 'ask';
|
|
56
|
+
readonly payload: AskPayload;
|
|
57
|
+
readonly middleware: string;
|
|
58
|
+
/** Index of the link that asked — resume continues from `index + 1`. */
|
|
59
|
+
readonly index: number;
|
|
60
|
+
/** Args as transformed up to the ask. These ride the checkpoint. */
|
|
61
|
+
readonly args: ToolArgs;
|
|
62
|
+
readonly decisions: MiddlewareDecision[];
|
|
63
|
+
};
|
|
64
|
+
export interface ToolChainInput {
|
|
65
|
+
readonly toolName: string;
|
|
66
|
+
readonly toolCallId: string;
|
|
67
|
+
readonly iteration: number;
|
|
68
|
+
readonly args: ToolArgs;
|
|
69
|
+
readonly history: readonly LLMMessage[];
|
|
70
|
+
readonly identity?: MemoryIdentity;
|
|
71
|
+
readonly signal?: AbortSignal;
|
|
72
|
+
/**
|
|
73
|
+
* Where to start walking. `0` for a fresh dispatch; `n` on resume, to
|
|
74
|
+
* continue the chain AFTER the link that asked without re-running the
|
|
75
|
+
* links whose decisions the checkpoint already carries.
|
|
76
|
+
*/
|
|
77
|
+
readonly startIndex?: number;
|
|
78
|
+
readonly askPolicy?: AskPolicy;
|
|
79
|
+
}
|
|
80
|
+
/** Walk the tool chain. Never throws — a throwing link becomes a denial. */
|
|
81
|
+
export declare function runToolChain(chain: readonly ToolMiddleware[], input: ToolChainInput): Promise<ToolChainResult>;
|
|
82
|
+
export type MessageChainResult = {
|
|
83
|
+
readonly kind: 'allow';
|
|
84
|
+
readonly content: string;
|
|
85
|
+
readonly decisions: MiddlewareDecision[];
|
|
86
|
+
} | {
|
|
87
|
+
readonly kind: 'deny';
|
|
88
|
+
readonly reason: string;
|
|
89
|
+
readonly middleware: string;
|
|
90
|
+
/** Content as transformed by the links BEFORE the refusal. */
|
|
91
|
+
readonly content: string;
|
|
92
|
+
readonly decisions: MiddlewareDecision[];
|
|
93
|
+
};
|
|
94
|
+
export interface MessageChainInput {
|
|
95
|
+
readonly phase: 'input' | 'output';
|
|
96
|
+
readonly content: string;
|
|
97
|
+
readonly history: readonly LLMMessage[];
|
|
98
|
+
/** ReAct iteration for the ledger row. `0` at `'input'` (before iter 1). */
|
|
99
|
+
readonly iteration: number;
|
|
100
|
+
readonly identity?: MemoryIdentity;
|
|
101
|
+
readonly signal?: AbortSignal;
|
|
102
|
+
}
|
|
103
|
+
/** Walk the message chain. Never throws — a throwing link becomes a denial. */
|
|
104
|
+
export declare function runMessageChain(chain: readonly MessageMiddleware[], input: MessageChainInput): Promise<MessageChainResult>;
|
|
105
|
+
//# sourceMappingURL=runChain.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runChain.d.ts","sourceRoot":"","sources":["../../../../../src/core/agent/middleware/runChain.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* middleware/types — PUBLIC. The shape of a governance chain.
|
|
3
|
+
*
|
|
4
|
+
* Pattern: Chain of Responsibility (GoF), with the one dangerous answer
|
|
5
|
+
* removed at the type level.
|
|
6
|
+
* Role: core/ layer. Two chains, one vocabulary:
|
|
7
|
+
*
|
|
8
|
+
* `toolMiddleware` wraps every tool dispatch
|
|
9
|
+
* `messageMiddleware` wraps the message boundary — the input
|
|
10
|
+
* before the model sees it, the output
|
|
11
|
+
* before the caller receives it
|
|
12
|
+
*
|
|
13
|
+
* Emits: N/A. A middleware is a pure decision. It never touches scope,
|
|
14
|
+
* never emits and never writes — the stage does all of that, so
|
|
15
|
+
* "record everything that happened" lives in ONE place rather
|
|
16
|
+
* than being re-implemented per middleware.
|
|
17
|
+
*
|
|
18
|
+
* ## The law that shapes these types
|
|
19
|
+
*
|
|
20
|
+
* **A middleware cannot answer for the tool.** The outcome union has three
|
|
21
|
+
* arms — allow, deny, ask — and no `result` arm. There is no spelling of
|
|
22
|
+
* "here is what the tool would have returned". Whatever the chain decides,
|
|
23
|
+
* the answer the model finally reads is the real tool's output or a
|
|
24
|
+
* refusal. That is not a convention a reviewer has to enforce; it is the
|
|
25
|
+
* absence of a field.
|
|
26
|
+
*
|
|
27
|
+
* **A transform declares itself.** `allow(value, why)` requires a reason
|
|
28
|
+
* whenever it changes something, and the stage commits BOTH the original
|
|
29
|
+
* and the transformed value into the run's ledger. A prompt scrubbed by a
|
|
30
|
+
* middleware that hid its own scrubbing would poison every slice taken
|
|
31
|
+
* afterwards: the trace would show text nobody ever sent.
|
|
32
|
+
*
|
|
33
|
+
* **`ask` exists only where a pause exists.** `ToolOutcome` has it because
|
|
34
|
+
* tool dispatch runs inside a footprintjs pausable stage — the same door
|
|
35
|
+
* `checkIn` and `askHuman` already go through. `MessageOutcome` does not,
|
|
36
|
+
* because the message boundary is a plain stage, and inventing a second
|
|
37
|
+
* pause to give it one would be a worse answer than not offering it.
|
|
38
|
+
*/
|
|
39
|
+
import type { LLMMessage } from '../../../adapters/types.js';
|
|
40
|
+
import type { MemoryIdentity } from '../../../memory/identity/types.js';
|
|
41
|
+
/**
|
|
42
|
+
* Let the call through — optionally with a replacement for what the chain
|
|
43
|
+
* carries forward.
|
|
44
|
+
*
|
|
45
|
+
* `allow()` passes the value along untouched. `allow(value, why)` replaces
|
|
46
|
+
* it and says why; the `why` is not decoration, it is the row the ledger
|
|
47
|
+
* shows a person asking "who changed this, and what did it look like
|
|
48
|
+
* before?".
|
|
49
|
+
*/
|
|
50
|
+
export interface AllowOutcome<T> {
|
|
51
|
+
readonly kind: 'allow';
|
|
52
|
+
/** The replacement value. Absent = pass through unchanged. */
|
|
53
|
+
readonly value?: T;
|
|
54
|
+
/** Why the value changed. Present whenever `value` is. */
|
|
55
|
+
readonly why?: string;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Refuse the call. For a tool, `reason` reaches the model verbatim as the
|
|
59
|
+
* tool result and the run continues — a denial is data the agent can adapt
|
|
60
|
+
* to, not a crash. For a message, `reason` surfaces as a
|
|
61
|
+
* `MessageDeniedError` at the API boundary.
|
|
62
|
+
*/
|
|
63
|
+
export interface DenyOutcome {
|
|
64
|
+
readonly kind: 'deny';
|
|
65
|
+
readonly reason: string;
|
|
66
|
+
}
|
|
67
|
+
/** Suspend the run and put the question to a person. Tool dispatch only. */
|
|
68
|
+
export interface AskOutcome {
|
|
69
|
+
readonly kind: 'ask';
|
|
70
|
+
readonly payload: AskPayload;
|
|
71
|
+
}
|
|
72
|
+
/** What a person is being asked. Carried verbatim to the checkpoint. */
|
|
73
|
+
export interface AskPayload {
|
|
74
|
+
/** The question, in your own words. Shown to whoever answers. */
|
|
75
|
+
readonly question: string;
|
|
76
|
+
/** Anything else the answering UI should render. Never interpreted here. */
|
|
77
|
+
readonly detail?: unknown;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Everything a tool middleware may answer. Closed, and every arm has a
|
|
81
|
+
* home in this codebase: allow rides the normal dispatch, deny rides the
|
|
82
|
+
* synthetic tool result every other gate already uses, ask rides the
|
|
83
|
+
* pausable-stage checkpoint that `checkIn` rides.
|
|
84
|
+
*/
|
|
85
|
+
export type ToolOutcome = AllowOutcome<Readonly<Record<string, unknown>>> | DenyOutcome | AskOutcome;
|
|
86
|
+
/** Everything a message middleware may answer. No `ask` — see the header. */
|
|
87
|
+
export type MessageOutcome = AllowOutcome<string> | DenyOutcome;
|
|
88
|
+
/** The call a tool middleware is deciding about. */
|
|
89
|
+
export interface ToolMiddlewareContext {
|
|
90
|
+
readonly toolName: string;
|
|
91
|
+
/** Matches `stream.tool_start.toolCallId` for this dispatch. */
|
|
92
|
+
readonly toolCallId: string;
|
|
93
|
+
/** ReAct iteration this call belongs to. */
|
|
94
|
+
readonly iteration: number;
|
|
95
|
+
/**
|
|
96
|
+
* The args as THIS middleware sees them — every earlier transform in the
|
|
97
|
+
* chain already applied. The first middleware sees what the model asked
|
|
98
|
+
* for; the last sees what the tool is about to receive.
|
|
99
|
+
*/
|
|
100
|
+
readonly args: Readonly<Record<string, unknown>>;
|
|
101
|
+
/** Conversation so far, including the assistant turn that made this call. */
|
|
102
|
+
readonly history: readonly LLMMessage[];
|
|
103
|
+
/** Multi-tenant run identity, when the run carried one. */
|
|
104
|
+
readonly identity?: MemoryIdentity;
|
|
105
|
+
/** Abort signal from `run({ env: { signal } })`. */
|
|
106
|
+
readonly signal?: AbortSignal;
|
|
107
|
+
}
|
|
108
|
+
/** The message a message middleware is deciding about. */
|
|
109
|
+
export interface MessageMiddlewareContext {
|
|
110
|
+
/**
|
|
111
|
+
* `'input'` runs at the very top of the run, BEFORE the user's message is
|
|
112
|
+
* committed — so the window strategies, the injections, the slots, the
|
|
113
|
+
* request bytes and every later slice all see the transformed text and
|
|
114
|
+
* agree with each other. `'output'` runs where the final answer is
|
|
115
|
+
* captured, so the record and the caller receive the same string.
|
|
116
|
+
*/
|
|
117
|
+
readonly phase: 'input' | 'output';
|
|
118
|
+
/** The content as THIS middleware sees it — earlier transforms applied. */
|
|
119
|
+
readonly content: string;
|
|
120
|
+
/** Conversation so far. Empty at `'input'`. */
|
|
121
|
+
readonly history: readonly LLMMessage[];
|
|
122
|
+
readonly identity?: MemoryIdentity;
|
|
123
|
+
readonly signal?: AbortSignal;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* One link in the tool-dispatch chain.
|
|
127
|
+
*
|
|
128
|
+
* @example
|
|
129
|
+
* ```ts
|
|
130
|
+
* const noProdWrites: ToolMiddleware = {
|
|
131
|
+
* name: 'no-prod-writes',
|
|
132
|
+
* onToolCall: (call) =>
|
|
133
|
+
* call.args.env === 'prod' ? deny('writes to prod need a change ticket') : allow(),
|
|
134
|
+
* };
|
|
135
|
+
* ```
|
|
136
|
+
*/
|
|
137
|
+
export interface ToolMiddleware {
|
|
138
|
+
/** Identifies this middleware in every ledger row and event it produces. */
|
|
139
|
+
readonly name: string;
|
|
140
|
+
onToolCall(call: ToolMiddlewareContext): ToolOutcome | Promise<ToolOutcome>;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* One link in the message chain. Runs at both phases unless it decides
|
|
144
|
+
* otherwise by reading `msg.phase`.
|
|
145
|
+
*
|
|
146
|
+
* @example
|
|
147
|
+
* ```ts
|
|
148
|
+
* const scrubSSNs: MessageMiddleware = {
|
|
149
|
+
* name: 'scrub-ssns',
|
|
150
|
+
* onMessage: (msg) => {
|
|
151
|
+
* const clean = msg.content.replace(/\b\d{3}-\d{2}-\d{4}\b/g, '[ssn]');
|
|
152
|
+
* return clean === msg.content ? allow() : allow(clean, 'masked a US SSN');
|
|
153
|
+
* },
|
|
154
|
+
* };
|
|
155
|
+
* ```
|
|
156
|
+
*/
|
|
157
|
+
export interface MessageMiddleware {
|
|
158
|
+
readonly name: string;
|
|
159
|
+
onMessage(msg: MessageMiddlewareContext): MessageOutcome | Promise<MessageOutcome>;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* One row per middleware decision, committed to `scope.middlewareDecisions`.
|
|
163
|
+
*
|
|
164
|
+
* Every decision files a row, including the pass-throughs. A chain that
|
|
165
|
+
* only recorded its refusals would leave you unable to tell "the middleware
|
|
166
|
+
* looked and was fine with it" apart from "the middleware never ran" — and
|
|
167
|
+
* those are different facts about a run.
|
|
168
|
+
*/
|
|
169
|
+
export interface MiddlewareDecision {
|
|
170
|
+
/** The middleware's `name`. */
|
|
171
|
+
readonly middleware: string;
|
|
172
|
+
/** Which chain this row came from. */
|
|
173
|
+
readonly at: 'tool' | 'message';
|
|
174
|
+
/** Message chain only. */
|
|
175
|
+
readonly phase?: 'input' | 'output';
|
|
176
|
+
/** Tool chain only. */
|
|
177
|
+
readonly toolName?: string;
|
|
178
|
+
/** Tool chain only. */
|
|
179
|
+
readonly toolCallId?: string;
|
|
180
|
+
/** ReAct iteration. `0` for the `'input'` phase, which runs before iter 1. */
|
|
181
|
+
readonly iteration: number;
|
|
182
|
+
readonly outcome: 'allow' | 'deny' | 'ask';
|
|
183
|
+
/** True when this row changed the value the chain carries forward. */
|
|
184
|
+
readonly changed: boolean;
|
|
185
|
+
/** The transform's `why`, the denial's `reason`, or the ask's `question`. */
|
|
186
|
+
readonly why?: string;
|
|
187
|
+
/** The value before this middleware. Present only when `changed`. */
|
|
188
|
+
readonly before?: unknown;
|
|
189
|
+
/** The value after this middleware. Present only when `changed`. */
|
|
190
|
+
readonly after?: unknown;
|
|
191
|
+
}
|
|
192
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../src/core/agent/middleware/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -13,6 +13,16 @@
|
|
|
13
13
|
*
|
|
14
14
|
* Pure function — no closure over Agent class state. Imported and
|
|
15
15
|
* passed directly to `flowChart(...)` in buildAgentChart.
|
|
16
|
+
*
|
|
17
|
+
* NOT the seam for `messageMiddleware`'s `'output'` half, deliberately.
|
|
18
|
+
* This stage runs inside the Final BRANCH subflow, whose state does not
|
|
19
|
+
* merge back into the run (a branch mount hands its outputMapper the
|
|
20
|
+
* branch's RESULT, not its scope). Rows filed here would land in an
|
|
21
|
+
* isolated commit log and never reach `snapshot.sharedState`, splitting
|
|
22
|
+
* one ledger across two places. The chain runs one stage earlier instead
|
|
23
|
+
* — in the Route decider, in the main chart — and rewrites
|
|
24
|
+
* `llmLatestContent`, which is the value this stage copies. See
|
|
25
|
+
* `stages/route.ts`.
|
|
16
26
|
*/
|
|
17
27
|
import type { TypedScope } from 'footprintjs';
|
|
18
28
|
import type { AgentState } from '../types.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prepareFinal.d.ts","sourceRoot":"","sources":["../../../../../src/core/agent/stages/prepareFinal.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"prepareFinal.d.ts","sourceRoot":"","sources":["../../../../../src/core/agent/stages/prepareFinal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,eAAO,MAAM,iBAAiB,UAAW,WAAW,UAAU,CAAC,KAAG,IAkCjE,CAAC"}
|
|
@@ -14,6 +14,35 @@
|
|
|
14
14
|
*/
|
|
15
15
|
import type { TypedScope } from 'footprintjs';
|
|
16
16
|
import type { AgentState } from '../types.js';
|
|
17
|
+
import type { MessageMiddleware } from '../middleware/types.js';
|
|
17
18
|
export type RouteBranch = 'tool-calls' | 'final';
|
|
18
19
|
export declare const routeDeciderStage: (scope: TypedScope<AgentState>) => RouteBranch;
|
|
20
|
+
/**
|
|
21
|
+
* Build the Route decider, optionally carrying the `'output'` half of the
|
|
22
|
+
* message chain.
|
|
23
|
+
*
|
|
24
|
+
* ## Why the chain runs HERE and not in PrepareFinal
|
|
25
|
+
*
|
|
26
|
+
* PrepareFinal is where `finalContent` is set, which makes it look like the
|
|
27
|
+
* output seam. It is not, for one structural reason: it lives inside the Final
|
|
28
|
+
* BRANCH subflow, and a branch mount hands its `outputMapper` the branch's
|
|
29
|
+
* RESULT, not its scope — so nothing that stage writes reaches the run's
|
|
30
|
+
* shared state. Ledger rows filed there would sit in an isolated commit log,
|
|
31
|
+
* and one ledger would be split across two places a reader has to know about.
|
|
32
|
+
*
|
|
33
|
+
* The decider is one stage earlier, in the main chart, and it is the first
|
|
34
|
+
* moment the run KNOWS this turn is the final answer. Its writes commit before
|
|
35
|
+
* the branch resolves (footprintjs commits a decider stage ahead of matching
|
|
36
|
+
* the branch), and the value it rewrites — `llmLatestContent` — is exactly
|
|
37
|
+
* what PrepareFinal copies into `finalContent`. So the caller, the committed
|
|
38
|
+
* record, the `newMessages` the memory writes persist, and the ledger row all
|
|
39
|
+
* carry the same string. One seam, one answer.
|
|
40
|
+
*
|
|
41
|
+
* The `'tool-calls'` path is untouched: a turn that is still calling tools has
|
|
42
|
+
* not produced an answer, and running an output rule over a half-finished turn
|
|
43
|
+
* would be running it over something that is not the output.
|
|
44
|
+
*
|
|
45
|
+
* Empty chain → the exact synchronous decider this file has always exported.
|
|
46
|
+
*/
|
|
47
|
+
export declare function buildRouteDeciderStage(messageMiddleware?: readonly MessageMiddleware[]): (scope: TypedScope<AgentState>) => RouteBranch | Promise<RouteBranch>;
|
|
19
48
|
//# sourceMappingURL=route.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../../src/core/agent/stages/route.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,MAAM,MAAM,WAAW,GAAG,YAAY,GAAG,OAAO,CAAC;AAEjD,eAAO,MAAM,iBAAiB,UAAW,WAAW,UAAU,CAAC,KAAG,WAmBjE,CAAC"}
|
|
1
|
+
{"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../../src/core/agent/stages/route.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAIhE,MAAM,MAAM,WAAW,GAAG,YAAY,GAAG,OAAO,CAAC;AAEjD,eAAO,MAAM,iBAAiB,UAAW,WAAW,UAAU,CAAC,KAAG,WAmBjE,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,sBAAsB,CACpC,iBAAiB,CAAC,EAAE,SAAS,iBAAiB,EAAE,GAC/C,CAAC,KAAK,EAAE,UAAU,CAAC,UAAU,CAAC,KAAK,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC,CA6BvE"}
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
import type { TypedScope } from 'footprintjs';
|
|
20
20
|
import type { LLMMessage, LLMToolSchema } from '../../../adapters/types.js';
|
|
21
21
|
import type { AgentInput, AgentState, RunConfig } from '../types.js';
|
|
22
|
+
import type { MessageMiddleware } from '../middleware/types.js';
|
|
22
23
|
export interface SeedStageDeps {
|
|
23
24
|
/** Resolved `clampIterations(opts.maxIterations ?? 10)`. Frozen at
|
|
24
25
|
* chart-build time. */
|
|
@@ -53,11 +54,25 @@ export interface SeedStageDeps {
|
|
|
53
54
|
* written, so the commit log is byte-identical to earlier releases.
|
|
54
55
|
*/
|
|
55
56
|
readonly resolveRunConfig?: (input: AgentInput) => RunConfig | undefined;
|
|
57
|
+
/**
|
|
58
|
+
* The message chain (`.messageMiddleware(...)`), walked here at the
|
|
59
|
+
* `'input'` phase — BEFORE `userMessage` and `history` are written.
|
|
60
|
+
*
|
|
61
|
+
* This is the only placement that keeps the run honest. Everything
|
|
62
|
+
* downstream reads `scope.history`: the window strategies, the injection
|
|
63
|
+
* engine, all three slots, the request that goes on the wire, and every
|
|
64
|
+
* slice taken afterwards. Transform later than this and those components
|
|
65
|
+
* disagree about what the user actually said — the trace would show one
|
|
66
|
+
* message and the model would have answered another.
|
|
67
|
+
*
|
|
68
|
+
* Empty / undefined → seed stays the synchronous stage it always was.
|
|
69
|
+
*/
|
|
70
|
+
readonly messageMiddleware?: readonly MessageMiddleware[];
|
|
56
71
|
}
|
|
57
72
|
/**
|
|
58
73
|
* Build the seed stage function for an Agent instance. Captures both
|
|
59
74
|
* the chart-build-time constants and the per-run mutable accessors
|
|
60
75
|
* via the deps object.
|
|
61
76
|
*/
|
|
62
|
-
export declare function buildSeedStage(deps: SeedStageDeps): (scope: TypedScope<AgentState>) => void
|
|
77
|
+
export declare function buildSeedStage(deps: SeedStageDeps): (scope: TypedScope<AgentState>) => void | Promise<void>;
|
|
63
78
|
//# sourceMappingURL=seed.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"seed.d.ts","sourceRoot":"","sources":["../../../../../src/core/agent/stages/seed.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAE5E,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"seed.d.ts","sourceRoot":"","sources":["../../../../../src/core/agent/stages/seed.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAE5E,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACrE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAIhE,MAAM,WAAW,aAAa;IAC5B;4BACwB;IACxB,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,0EAA0E;IAC1E,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC;IAClC;;iEAE6D;IAC7D,QAAQ,CAAC,WAAW,EAAE,SAAS,aAAa,EAAE,CAAC;IAC/C;;;;;;OAMG;IACH,QAAQ,CAAC,2BAA2B,EAAE,MAAM,SAAS,UAAU,EAAE,GAAG,SAAS,CAAC;IAC9E;;;;;OAKG;IACH,QAAQ,CAAC,eAAe,EAAE,MAAM,MAAM,GAAG,SAAS,CAAC;IACnD;;;;;;;OAOG;IACH,QAAQ,CAAC,gBAAgB,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,SAAS,GAAG,SAAS,CAAC;IACzE;;;;;;;;;;;;OAYG;IACH,QAAQ,CAAC,iBAAiB,CAAC,EAAE,SAAS,iBAAiB,EAAE,CAAC;CAC3D;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,aAAa,GAClB,CAAC,KAAK,EAAE,UAAU,CAAC,UAAU,CAAC,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAsCzD"}
|
|
@@ -20,6 +20,19 @@
|
|
|
20
20
|
* `tool.execute`. Deny → tool not executed; result is a synthetic
|
|
21
21
|
* denial string. Allow / gate_open → execution proceeds.
|
|
22
22
|
*
|
|
23
|
+
* Gate order for one call, and why it is this order:
|
|
24
|
+
*
|
|
25
|
+
* permission → MIDDLEWARE CHAIN → arg validation → check-in →
|
|
26
|
+
* credentials → execute
|
|
27
|
+
*
|
|
28
|
+
* The chain sits after the permission gate so an existing checker still
|
|
29
|
+
* decides first (a denial there means no middleware runs), and before arg
|
|
30
|
+
* validation so validation judges the args that will actually be sent —
|
|
31
|
+
* a middleware that transformed args into something the tool's schema
|
|
32
|
+
* rejects must be caught, not forwarded. A middleware answering `ask`
|
|
33
|
+
* pauses on the SAME wire `checkIn` uses; the human's answer is a
|
|
34
|
+
* decision, not a result, so the chain resumes and the REAL tool runs.
|
|
35
|
+
*
|
|
23
36
|
* `read_skill` is the auto-attached activation tool — when the LLM
|
|
24
37
|
* calls it with a valid Skill id, the next InjectionEngine pass
|
|
25
38
|
* activates that Skill (lifetime: turn).
|
|
@@ -31,6 +44,7 @@ import type { CredentialProvider } from '../../../identity/types.js';
|
|
|
31
44
|
import { type ResolvedCheckInConfig } from '../../checkin.js';
|
|
32
45
|
import type { ProviderToolCache } from '../../slots/buildToolsSlot.js';
|
|
33
46
|
import type { Tool } from '../../tools.js';
|
|
47
|
+
import type { ToolMiddleware } from '../middleware/types.js';
|
|
34
48
|
import { type ToolArgValidationMode } from '../toolArgsValidation.js';
|
|
35
49
|
import type { AgentState } from '../types.js';
|
|
36
50
|
export interface ToolCallsHandlerDeps {
|
|
@@ -78,6 +92,15 @@ export interface ToolCallsHandlerDeps {
|
|
|
78
92
|
* Agent (no check-in). The gate fires ONLY for tools that declared `checkIn`.
|
|
79
93
|
*/
|
|
80
94
|
readonly checkIn?: ResolvedCheckInConfig;
|
|
95
|
+
/**
|
|
96
|
+
* The tool-dispatch governance chain (`.toolMiddleware(...)`), in
|
|
97
|
+
* declaration order. Walked AFTER the permission gate — so an existing
|
|
98
|
+
* `PermissionChecker` still decides first and a denial there means no
|
|
99
|
+
* middleware runs at all — and BEFORE arg validation, so validation judges
|
|
100
|
+
* the args that will actually be sent rather than the ones the model
|
|
101
|
+
* proposed. Empty / undefined → not walked, no ledger key, no events.
|
|
102
|
+
*/
|
|
103
|
+
readonly toolMiddleware?: readonly ToolMiddleware[];
|
|
81
104
|
}
|
|
82
105
|
/**
|
|
83
106
|
* Build the pausable tool-call handler for the agent's chart.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toolCalls.d.ts","sourceRoot":"","sources":["../../../../../src/core/agent/stages/toolCalls.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"toolCalls.d.ts","sourceRoot":"","sources":["../../../../../src/core/agent/stages/toolCalls.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC/D,OAAO,KAAK,EAAc,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAIhF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAC;AACrE,OAAO,KAAK,EAAc,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAGjF,OAAO,EAIL,KAAK,qBAAqB,EAG3B,MAAM,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AACvE,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAE3C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAG7D,OAAO,EAGL,KAAK,qBAAqB,EAC3B,MAAM,0BAA0B,CAAC;AAElC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,MAAM,WAAW,oBAAoB;IACnC;;4DAEwD;IACxD,QAAQ,CAAC,cAAc,EAAE,WAAW,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IACnD;;qDAEiD;IACjD,QAAQ,CAAC,oBAAoB,CAAC,EAAE,YAAY,CAAC;IAC7C;;;;;OAKG;IACH,QAAQ,CAAC,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IAC/C;;4DAEwD;IACxD,QAAQ,CAAC,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IAC/C;;kEAE8D;IAC9D,QAAQ,CAAC,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;IACjD;;;8EAG0E;IAC1E,QAAQ,CAAC,iBAAiB,CAAC,EAAE,qBAAqB,CAAC;IACnD;;;;;;OAMG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,CAAC,cAAc,CAAC,EAAE,MAAM,KAAK,SAAS,MAAM,EAAE,CAAC;IAC1E;;;;;;OAMG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,qBAAqB,CAAC;IACzC;;;;;;;OAOG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,SAAS,cAAc,EAAE,CAAC;CACrD;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,oBAAoB,GACzB,eAAe,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,CA60BzC"}
|
|
@@ -19,6 +19,7 @@ import type { ToolArgValidationMode } from './toolArgsValidation.js';
|
|
|
19
19
|
import type { ThinkingBlock } from '../../thinking/types.js';
|
|
20
20
|
import type { ReliabilityScope } from '../../reliability/types.js';
|
|
21
21
|
import type { WindowRecord } from './window/types.js';
|
|
22
|
+
import type { MiddlewareDecision } from './middleware/types.js';
|
|
22
23
|
/**
|
|
23
24
|
* Dials for the deferred observer queue (RFC-001) — only meaningful with
|
|
24
25
|
* `observerDelivery: 'deferred'` (passing them without it throws at
|
|
@@ -347,6 +348,23 @@ export interface AgentState {
|
|
|
347
348
|
pausedToolStartMs: number;
|
|
348
349
|
pausedCheckIn?: boolean;
|
|
349
350
|
pausedCheckInArgs?: Readonly<Record<string, unknown>>;
|
|
351
|
+
pausedAsk?: boolean;
|
|
352
|
+
pausedAskArgs?: Readonly<Record<string, unknown>>;
|
|
353
|
+
pausedAskIndex?: number;
|
|
354
|
+
pausedAskMiddleware?: string;
|
|
355
|
+
/** One row per middleware decision, in the order they were decided —
|
|
356
|
+
* including the pass-throughs, because "the rule looked and was fine with
|
|
357
|
+
* it" and "the rule never ran" are different facts about a run. A transform
|
|
358
|
+
* files both the value it received and the value it produced, so a slice
|
|
359
|
+
* taken later can find the moment the text changed and who changed it.
|
|
360
|
+
* Written only by an agent that configured a chain; absent otherwise. */
|
|
361
|
+
middlewareDecisions?: readonly MiddlewareDecision[];
|
|
362
|
+
/** Set by the seed / prepare-final stage when a `messageMiddleware` returned
|
|
363
|
+
* `deny`. Read at the API boundary, where it becomes a `MessageDeniedError`.
|
|
364
|
+
* Never carries the refused content — see MessageDeniedError. */
|
|
365
|
+
messageDeniedReason?: string;
|
|
366
|
+
messageDeniedPhase?: 'input' | 'output';
|
|
367
|
+
messageDeniedBy?: string;
|
|
350
368
|
cumTokensInput: number;
|
|
351
369
|
cumTokensOutput: number;
|
|
352
370
|
cumEstimatedUsd: number;
|