agentfootprint 7.17.0 → 7.19.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/adapters/hosting/agentcore.js +23 -9
- package/dist/adapters/hosting/agentcore.js.map +1 -1
- package/dist/core/Agent.js +57 -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 +208 -8
- package/dist/core/agent/stages/toolCalls.js.map +1 -1
- package/dist/core/durabilityBarrier.js +68 -0
- package/dist/core/durabilityBarrier.js.map +1 -0
- package/dist/core/pause.js +19 -1
- package/dist/core/pause.js.map +1 -1
- package/dist/esm/adapters/hosting/agentcore.d.ts +4 -4
- package/dist/esm/adapters/hosting/agentcore.js +24 -10
- package/dist/esm/adapters/hosting/agentcore.js.map +1 -1
- package/dist/esm/core/Agent.d.ts +9 -1
- package/dist/esm/core/Agent.js +58 -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 +44 -0
- package/dist/esm/core/agent/stages/toolCalls.js +208 -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/durabilityBarrier.d.ts +61 -0
- package/dist/esm/core/durabilityBarrier.js +63 -0
- package/dist/esm/core/durabilityBarrier.js.map +1 -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/hosting/durability.d.ts +92 -0
- package/dist/esm/hosting/durability.js +174 -0
- package/dist/esm/hosting/durability.js.map +1 -0
- package/dist/esm/hosting/envelope.d.ts +75 -14
- package/dist/esm/hosting/envelope.js +141 -16
- package/dist/esm/hosting/envelope.js.map +1 -1
- package/dist/esm/hosting/errors.d.ts +60 -11
- package/dist/esm/hosting/errors.js +92 -18
- package/dist/esm/hosting/errors.js.map +1 -1
- package/dist/esm/hosting/httpHost.d.ts +17 -1
- package/dist/esm/hosting/httpHost.js +42 -6
- package/dist/esm/hosting/httpHost.js.map +1 -1
- package/dist/esm/hosting/index.d.ts +7 -4
- package/dist/esm/hosting/index.js +6 -3
- package/dist/esm/hosting/index.js.map +1 -1
- package/dist/esm/hosting/nodeHost.d.ts +4 -2
- package/dist/esm/hosting/nodeHost.js +14 -3
- package/dist/esm/hosting/nodeHost.js.map +1 -1
- package/dist/esm/hosting/standingAgent.d.ts +22 -7
- package/dist/esm/hosting/standingAgent.js +144 -32
- package/dist/esm/hosting/standingAgent.js.map +1 -1
- package/dist/esm/hosting/types.d.ts +193 -19
- 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/hosting/durability.js +178 -0
- package/dist/hosting/durability.js.map +1 -0
- package/dist/hosting/envelope.js +146 -18
- package/dist/hosting/envelope.js.map +1 -1
- package/dist/hosting/errors.js +95 -19
- package/dist/hosting/errors.js.map +1 -1
- package/dist/hosting/httpHost.js +42 -6
- package/dist/hosting/httpHost.js.map +1 -1
- package/dist/hosting/index.js +10 -2
- package/dist/hosting/index.js.map +1 -1
- package/dist/hosting/nodeHost.js +14 -3
- package/dist/hosting/nodeHost.js.map +1 -1
- package/dist/hosting/standingAgent.js +142 -30
- package/dist/hosting/standingAgent.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/adapters/hosting/agentcore.d.ts +4 -4
- package/dist/types/adapters/hosting/agentcore.d.ts.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 +44 -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/durabilityBarrier.d.ts +62 -0
- package/dist/types/core/durabilityBarrier.d.ts.map +1 -0
- 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/hosting/durability.d.ts +93 -0
- package/dist/types/hosting/durability.d.ts.map +1 -0
- package/dist/types/hosting/envelope.d.ts +75 -14
- package/dist/types/hosting/envelope.d.ts.map +1 -1
- package/dist/types/hosting/errors.d.ts +60 -11
- package/dist/types/hosting/errors.d.ts.map +1 -1
- package/dist/types/hosting/httpHost.d.ts +17 -1
- package/dist/types/hosting/httpHost.d.ts.map +1 -1
- package/dist/types/hosting/index.d.ts +7 -4
- package/dist/types/hosting/index.d.ts.map +1 -1
- package/dist/types/hosting/nodeHost.d.ts +4 -2
- package/dist/types/hosting/nodeHost.d.ts.map +1 -1
- package/dist/types/hosting/standingAgent.d.ts +22 -7
- package/dist/types/hosting/standingAgent.d.ts.map +1 -1
- package/dist/types/hosting/types.d.ts +193 -19
- package/dist/types/hosting/types.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
|
@@ -20,100 +20,145 @@
|
|
|
20
20
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
21
|
exports.buildSeedStage = void 0;
|
|
22
22
|
const typedEmit_js_1 = require("../../../recorders/core/typedEmit.js");
|
|
23
|
+
const runChain_js_1 = require("../middleware/runChain.js");
|
|
24
|
+
const ledger_js_1 = require("../middleware/ledger.js");
|
|
23
25
|
/**
|
|
24
26
|
* Build the seed stage function for an Agent instance. Captures both
|
|
25
27
|
* the chart-build-time constants and the per-run mutable accessors
|
|
26
28
|
* via the deps object.
|
|
27
29
|
*/
|
|
28
30
|
function buildSeedStage(deps) {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
// (without resumeOnError) start fresh.
|
|
37
|
-
const resumeHistory = deps.consumePendingResumeHistory();
|
|
38
|
-
if (resumeHistory && resumeHistory.length > 0) {
|
|
39
|
-
scope.history = [...resumeHistory];
|
|
40
|
-
}
|
|
41
|
-
else {
|
|
42
|
-
scope.history = [{ role: 'user', content: args.message }];
|
|
43
|
-
}
|
|
44
|
-
// Default identity uses the runId so multi-run isolation works
|
|
45
|
-
// without consumer changes; explicit identity (multi-tenant)
|
|
46
|
-
// overrides via `agent.run({ identity })`.
|
|
47
|
-
scope.runIdentity = args.identity ?? {
|
|
48
|
-
conversationId: deps.getCurrentRunId() ?? 'default',
|
|
31
|
+
const chain = deps.messageMiddleware ?? [];
|
|
32
|
+
// No chain → the same synchronous function this stage has always been.
|
|
33
|
+
// Not an optimisation: an agent without middleware must produce the same
|
|
34
|
+
// stage shape, the same committed keys and the same request bytes as before.
|
|
35
|
+
if (chain.length === 0) {
|
|
36
|
+
return (scope) => {
|
|
37
|
+
seedFrom(scope, scope.$getArgs().message, deps);
|
|
49
38
|
};
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
scope.totalOutputTokens = 0;
|
|
61
|
-
scope.turnStartMs = Date.now();
|
|
62
|
-
scope.systemPromptInjections = [];
|
|
63
|
-
scope.messagesInjections = [];
|
|
64
|
-
scope.toolsInjections = [];
|
|
65
|
-
scope.llmLatestContent = '';
|
|
66
|
-
scope.llmLatestToolCalls = [];
|
|
67
|
-
// v2.14 — initialize thinking blocks. Empty array means "no thinking
|
|
68
|
-
// this iteration"; the NormalizeThinking sub-subflow overwrites
|
|
69
|
-
// this AFTER each CallLLM when a ThinkingHandler is configured.
|
|
70
|
-
scope.thinkingBlocks = [];
|
|
71
|
-
scope.pausedToolCallId = '';
|
|
72
|
-
scope.pausedToolName = '';
|
|
73
|
-
scope.pausedToolStartMs = 0;
|
|
74
|
-
scope.cumTokensInput = 0;
|
|
75
|
-
scope.cumTokensOutput = 0;
|
|
76
|
-
scope.cumEstimatedUsd = 0;
|
|
77
|
-
scope.costBudgetHit = false;
|
|
78
|
-
scope.activeInjections = [];
|
|
79
|
-
scope.activatedInjectionIds = [];
|
|
80
|
-
scope.dynamicToolSchemas = deps.toolSchemas;
|
|
81
|
-
// Cache layer state (v2.6) — initialized to inert defaults.
|
|
82
|
-
// CacheDecision subflow populates `cacheMarkers` per iteration;
|
|
83
|
-
// UpdateSkillHistory + CacheGate consume `cachingDisabled`,
|
|
84
|
-
// `recentHitRate`, `skillHistory`. Empty defaults mean the
|
|
85
|
-
// CacheGate falls through to 'apply-markers' on iter 1 (no
|
|
86
|
-
// history yet → no churn detected; recentHitRate undefined →
|
|
87
|
-
// hit-rate floor doesn't fire).
|
|
88
|
-
scope.cacheMarkers = [];
|
|
89
|
-
scope.cachingDisabled = deps.cachingDisabled;
|
|
90
|
-
scope.recentHitRate = undefined;
|
|
91
|
-
scope.skillHistory = [];
|
|
92
|
-
// Skill-graph cursor — reset per turn so each new user message re-enters the
|
|
93
|
-
// graph through the entry router (cold start). The Injection Engine advances
|
|
94
|
-
// it each iteration; undefined for agents without a skillGraph().
|
|
95
|
-
scope.currentSkillId = undefined;
|
|
96
|
-
// `.configure()` — resolved ONCE here (seed runs exactly once per run)
|
|
97
|
-
// and written to scope, which means the run's commit log records the
|
|
98
|
-
// model and instructions the run actually used. A run that changed its
|
|
99
|
-
// own model without committing that fact would produce a trace that
|
|
100
|
-
// reads as if the built-in default answered.
|
|
101
|
-
//
|
|
102
|
-
// Only what the resolver actually returned is written: an agent with no
|
|
103
|
-
// `.configure()`, or one whose resolver returned `{}`, commits nothing
|
|
104
|
-
// extra and behaves exactly as before.
|
|
105
|
-
if (deps.resolveRunConfig) {
|
|
106
|
-
const resolved = deps.resolveRunConfig(args);
|
|
107
|
-
if (resolved?.model !== undefined)
|
|
108
|
-
scope.resolvedModel = resolved.model;
|
|
109
|
-
if (resolved?.instructions !== undefined)
|
|
110
|
-
scope.resolvedInstructions = resolved.instructions;
|
|
111
|
-
}
|
|
112
|
-
(0, typedEmit_js_1.typedEmit)(scope, 'agentfootprint.agent.turn_start', {
|
|
113
|
-
turnIndex: 0,
|
|
114
|
-
userPrompt: args.message,
|
|
39
|
+
}
|
|
40
|
+
return async (scope) => {
|
|
41
|
+
const args = scope.$getArgs();
|
|
42
|
+
const verdict = await (0, runChain_js_1.runMessageChain)(chain, {
|
|
43
|
+
phase: 'input',
|
|
44
|
+
content: args.message,
|
|
45
|
+
history: [],
|
|
46
|
+
// The input boundary runs before iteration 1 exists.
|
|
47
|
+
iteration: 0,
|
|
48
|
+
...(args.identity && { identity: args.identity }),
|
|
115
49
|
});
|
|
50
|
+
(0, ledger_js_1.recordDecisions)(scope, verdict.decisions);
|
|
51
|
+
if (verdict.kind === 'deny') {
|
|
52
|
+
// Seed the run anyway, with the content as it stood when it was
|
|
53
|
+
// refused, then stop. Committing it costs nothing (a refusal is a fact
|
|
54
|
+
// about a run, and hiding what was refused would make the record
|
|
55
|
+
// useless), and a fully-seeded state means `resumeOnError` and every
|
|
56
|
+
// recorder see the shape they expect rather than a half-built one.
|
|
57
|
+
seedFrom(scope, verdict.content, deps);
|
|
58
|
+
scope.messageDeniedReason = verdict.reason;
|
|
59
|
+
scope.messageDeniedPhase = 'input';
|
|
60
|
+
scope.messageDeniedBy = verdict.middleware;
|
|
61
|
+
// Stops the chart here: no injections, no slots, no LLM call. The
|
|
62
|
+
// boundary turns these flags into a MessageDeniedError.
|
|
63
|
+
scope.$break(`message denied at input: ${verdict.reason}`);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
seedFrom(scope, verdict.content, deps);
|
|
116
67
|
};
|
|
117
68
|
}
|
|
118
69
|
exports.buildSeedStage = buildSeedStage;
|
|
70
|
+
/**
|
|
71
|
+
* Initialise every mutable field of `AgentState` from `message` + the run
|
|
72
|
+
* args. Split out so the message the run proceeds with can come either
|
|
73
|
+
* straight from the caller or from the `'input'` middleware chain — one
|
|
74
|
+
* initialiser, so the two paths cannot drift.
|
|
75
|
+
*/
|
|
76
|
+
function seedFrom(scope, message, deps) {
|
|
77
|
+
const args = scope.$getArgs();
|
|
78
|
+
scope.userMessage = message;
|
|
79
|
+
// If `resumeOnError(...)` set the side channel, restore the
|
|
80
|
+
// checkpointed conversation history. The next iteration sees
|
|
81
|
+
// the prior messages and continues from the failure point.
|
|
82
|
+
// Always clear the field after reading so subsequent runs
|
|
83
|
+
// (without resumeOnError) start fresh.
|
|
84
|
+
const resumeHistory = deps.consumePendingResumeHistory();
|
|
85
|
+
if (resumeHistory && resumeHistory.length > 0) {
|
|
86
|
+
scope.history = [...resumeHistory];
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
scope.history = [{ role: 'user', content: message }];
|
|
90
|
+
}
|
|
91
|
+
// Default identity uses the runId so multi-run isolation works
|
|
92
|
+
// without consumer changes; explicit identity (multi-tenant)
|
|
93
|
+
// overrides via `agent.run({ identity })`.
|
|
94
|
+
scope.runIdentity = args.identity ?? {
|
|
95
|
+
conversationId: deps.getCurrentRunId() ?? 'default',
|
|
96
|
+
};
|
|
97
|
+
scope.newMessages = [];
|
|
98
|
+
scope.turnNumber = 1;
|
|
99
|
+
// Permissive default — explicit cap will land when PricingTable
|
|
100
|
+
// gets a context-window field. Memory pickByBudget treats anything
|
|
101
|
+
// ≥ minimumTokens as "fits", so this just enables the budget path.
|
|
102
|
+
scope.contextTokensRemaining = 32_000;
|
|
103
|
+
scope.iteration = 1;
|
|
104
|
+
scope.maxIterations = deps.maxIterations;
|
|
105
|
+
scope.finalContent = '';
|
|
106
|
+
scope.totalInputTokens = 0;
|
|
107
|
+
scope.totalOutputTokens = 0;
|
|
108
|
+
scope.turnStartMs = Date.now();
|
|
109
|
+
scope.systemPromptInjections = [];
|
|
110
|
+
scope.messagesInjections = [];
|
|
111
|
+
scope.toolsInjections = [];
|
|
112
|
+
scope.llmLatestContent = '';
|
|
113
|
+
scope.llmLatestToolCalls = [];
|
|
114
|
+
// v2.14 — initialize thinking blocks. Empty array means "no thinking
|
|
115
|
+
// this iteration"; the NormalizeThinking sub-subflow overwrites
|
|
116
|
+
// this AFTER each CallLLM when a ThinkingHandler is configured.
|
|
117
|
+
scope.thinkingBlocks = [];
|
|
118
|
+
scope.pausedToolCallId = '';
|
|
119
|
+
scope.pausedToolName = '';
|
|
120
|
+
scope.pausedToolStartMs = 0;
|
|
121
|
+
scope.cumTokensInput = 0;
|
|
122
|
+
scope.cumTokensOutput = 0;
|
|
123
|
+
scope.cumEstimatedUsd = 0;
|
|
124
|
+
scope.costBudgetHit = false;
|
|
125
|
+
scope.activeInjections = [];
|
|
126
|
+
scope.activatedInjectionIds = [];
|
|
127
|
+
scope.dynamicToolSchemas = deps.toolSchemas;
|
|
128
|
+
// Cache layer state (v2.6) — initialized to inert defaults.
|
|
129
|
+
// CacheDecision subflow populates `cacheMarkers` per iteration;
|
|
130
|
+
// UpdateSkillHistory + CacheGate consume `cachingDisabled`,
|
|
131
|
+
// `recentHitRate`, `skillHistory`. Empty defaults mean the
|
|
132
|
+
// CacheGate falls through to 'apply-markers' on iter 1 (no
|
|
133
|
+
// history yet → no churn detected; recentHitRate undefined →
|
|
134
|
+
// hit-rate floor doesn't fire).
|
|
135
|
+
scope.cacheMarkers = [];
|
|
136
|
+
scope.cachingDisabled = deps.cachingDisabled;
|
|
137
|
+
scope.recentHitRate = undefined;
|
|
138
|
+
scope.skillHistory = [];
|
|
139
|
+
// Skill-graph cursor — reset per turn so each new user message re-enters the
|
|
140
|
+
// graph through the entry router (cold start). The Injection Engine advances
|
|
141
|
+
// it each iteration; undefined for agents without a skillGraph().
|
|
142
|
+
scope.currentSkillId = undefined;
|
|
143
|
+
// `.configure()` — resolved ONCE here (seed runs exactly once per run)
|
|
144
|
+
// and written to scope, which means the run's commit log records the
|
|
145
|
+
// model and instructions the run actually used. A run that changed its
|
|
146
|
+
// own model without committing that fact would produce a trace that
|
|
147
|
+
// reads as if the built-in default answered.
|
|
148
|
+
//
|
|
149
|
+
// Only what the resolver actually returned is written: an agent with no
|
|
150
|
+
// `.configure()`, or one whose resolver returned `{}`, commits nothing
|
|
151
|
+
// extra and behaves exactly as before.
|
|
152
|
+
if (deps.resolveRunConfig) {
|
|
153
|
+
const resolved = deps.resolveRunConfig(args);
|
|
154
|
+
if (resolved?.model !== undefined)
|
|
155
|
+
scope.resolvedModel = resolved.model;
|
|
156
|
+
if (resolved?.instructions !== undefined)
|
|
157
|
+
scope.resolvedInstructions = resolved.instructions;
|
|
158
|
+
}
|
|
159
|
+
(0, typedEmit_js_1.typedEmit)(scope, 'agentfootprint.agent.turn_start', {
|
|
160
|
+
turnIndex: 0,
|
|
161
|
+
userPrompt: message,
|
|
162
|
+
});
|
|
163
|
+
}
|
|
119
164
|
//# sourceMappingURL=seed.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"seed.js","sourceRoot":"","sources":["../../../../src/core/agent/stages/seed.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;GAiBG;;;AAIH,uEAAiE;
|
|
1
|
+
{"version":3,"file":"seed.js","sourceRoot":"","sources":["../../../../src/core/agent/stages/seed.ts"],"names":[],"mappings":"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"}
|
|
@@ -21,6 +21,19 @@
|
|
|
21
21
|
* `tool.execute`. Deny → tool not executed; result is a synthetic
|
|
22
22
|
* denial string. Allow / gate_open → execution proceeds.
|
|
23
23
|
*
|
|
24
|
+
* Gate order for one call, and why it is this order:
|
|
25
|
+
*
|
|
26
|
+
* permission → MIDDLEWARE CHAIN → arg validation → check-in →
|
|
27
|
+
* credentials → execute
|
|
28
|
+
*
|
|
29
|
+
* The chain sits after the permission gate so an existing checker still
|
|
30
|
+
* decides first (a denial there means no middleware runs), and before arg
|
|
31
|
+
* validation so validation judges the args that will actually be sent —
|
|
32
|
+
* a middleware that transformed args into something the tool's schema
|
|
33
|
+
* rejects must be caught, not forwarded. A middleware answering `ask`
|
|
34
|
+
* pauses on the SAME wire `checkIn` uses; the human's answer is a
|
|
35
|
+
* decision, not a result, so the chain resumes and the REAL tool runs.
|
|
36
|
+
*
|
|
24
37
|
* `read_skill` is the auto-attached activation tool — when the LLM
|
|
25
38
|
* calls it with a valid Skill id, the next InjectionEngine pass
|
|
26
39
|
* activates that Skill (lifetime: turn).
|
|
@@ -32,6 +45,8 @@ const extractSequence_js_1 = require("../../../security/extractSequence.js");
|
|
|
32
45
|
const types_js_1 = require("../../../identity/types.js");
|
|
33
46
|
const pause_js_1 = require("../../pause.js");
|
|
34
47
|
const checkin_js_1 = require("../../checkin.js");
|
|
48
|
+
const runChain_js_1 = require("../middleware/runChain.js");
|
|
49
|
+
const ledger_js_1 = require("../middleware/ledger.js");
|
|
35
50
|
const toolArgsValidation_js_1 = require("../toolArgsValidation.js");
|
|
36
51
|
const validators_js_1 = require("../validators.js");
|
|
37
52
|
/**
|
|
@@ -136,6 +151,13 @@ function buildToolCallsHandler(deps) {
|
|
|
136
151
|
};
|
|
137
152
|
return {
|
|
138
153
|
execute: async (scope) => {
|
|
154
|
+
// Durable-write barrier — the LAST iteration's state must have landed in
|
|
155
|
+
// the session store before THIS iteration's tools are allowed to run.
|
|
156
|
+
// `undefined` (no composer, or nothing outstanding) means no await, no
|
|
157
|
+
// microtask, no behaviour change whatsoever.
|
|
158
|
+
const durable = deps.awaitDurable?.();
|
|
159
|
+
if (durable)
|
|
160
|
+
await durable;
|
|
139
161
|
// Materialize ONCE — `scope.llmLatestToolCalls` is a live TypedScope
|
|
140
162
|
// deep-Proxy view; spreading yields the raw (plain, structured-clone-
|
|
141
163
|
// safe) elements. This array is embedded into the assistant history
|
|
@@ -205,6 +227,12 @@ function buildToolCallsHandler(deps) {
|
|
|
205
227
|
// identity, and abort signal — enough surface to build sequence-
|
|
206
228
|
// aware policies (forbidden chains, idempotency limits, cost
|
|
207
229
|
// guards) without maintaining parallel state.
|
|
230
|
+
// Args as they will actually be used. The middleware chain below may
|
|
231
|
+
// replace this; everything downstream (validation, the check-in
|
|
232
|
+
// evidence a human approves, credentials, execute, the read_skill
|
|
233
|
+
// gate) reads `callArgs`, never `tc.args`, so there is exactly one
|
|
234
|
+
// answer to "what did this call really run with".
|
|
235
|
+
let callArgs = tc.args;
|
|
208
236
|
let denied = false;
|
|
209
237
|
let haltContext;
|
|
210
238
|
if (permissionChecker) {
|
|
@@ -271,6 +299,52 @@ function buildToolCallsHandler(deps) {
|
|
|
271
299
|
result = `[permission denied: checker error: ${msg}]`;
|
|
272
300
|
}
|
|
273
301
|
}
|
|
302
|
+
// ── The middleware chain ─────────────────────────────────────────
|
|
303
|
+
// Walked only for a call the permission gate let through, so an
|
|
304
|
+
// existing checker keeps deciding first and a denial there costs
|
|
305
|
+
// nothing. A denial from the chain lands as the tool result, exactly
|
|
306
|
+
// like every other refusal in this loop — the model reads it and
|
|
307
|
+
// adapts. An `ask` commits partial state and pauses, on the same wire
|
|
308
|
+
// the check-in gate uses.
|
|
309
|
+
if (!denied && deps.toolMiddleware && deps.toolMiddleware.length > 0) {
|
|
310
|
+
const chain = await (0, runChain_js_1.runToolChain)(deps.toolMiddleware, {
|
|
311
|
+
toolName: tc.name,
|
|
312
|
+
toolCallId: tc.id,
|
|
313
|
+
iteration,
|
|
314
|
+
args: callArgs,
|
|
315
|
+
history: newHistory,
|
|
316
|
+
...(runIdentity && { identity: runIdentity }),
|
|
317
|
+
...(env.signal && { signal: env.signal }),
|
|
318
|
+
});
|
|
319
|
+
(0, ledger_js_1.recordDecisions)(scope, chain.decisions);
|
|
320
|
+
callArgs = chain.args;
|
|
321
|
+
if (chain.kind === 'deny') {
|
|
322
|
+
denied = true;
|
|
323
|
+
result = chain.reason;
|
|
324
|
+
}
|
|
325
|
+
else if (chain.kind === 'ask') {
|
|
326
|
+
// Commit partial state so resume() finds history intact (the
|
|
327
|
+
// pauseHere / check-in path does the same). The TRANSFORMED args
|
|
328
|
+
// ride the checkpoint: a person approves what the chain produced,
|
|
329
|
+
// not what the model originally proposed.
|
|
330
|
+
scope.history = newHistory;
|
|
331
|
+
scope.pausedToolCallId = tc.id;
|
|
332
|
+
scope.pausedToolName = tc.name;
|
|
333
|
+
scope.pausedToolStartMs = startMs;
|
|
334
|
+
scope.pausedAsk = true;
|
|
335
|
+
scope.pausedAskArgs = chain.args;
|
|
336
|
+
scope.pausedAskIndex = chain.index;
|
|
337
|
+
scope.pausedAskMiddleware = chain.middleware;
|
|
338
|
+
// A defined return value triggers the footprintjs pause; this
|
|
339
|
+
// object becomes the checkpoint's pauseData, and detectPause
|
|
340
|
+
// surfaces `pauseData.ask` as `outcome.ask`.
|
|
341
|
+
return {
|
|
342
|
+
toolCallId: tc.id,
|
|
343
|
+
toolName: tc.name,
|
|
344
|
+
ask: { ...chain.payload, middleware: chain.middleware },
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
}
|
|
274
348
|
// Tool-args validation (#9) — AFTER the permission gate (policy must
|
|
275
349
|
// see every attempted call, valid or not) and BEFORE credential
|
|
276
350
|
// resolution (never acquire credentials for a call that won't run).
|
|
@@ -281,7 +355,7 @@ function buildToolCallsHandler(deps) {
|
|
|
281
355
|
// tools (their inputSchema is the contract the LLM was shown).
|
|
282
356
|
let argsRejected = false;
|
|
283
357
|
if (!denied && tool && toolArgValidation !== 'off') {
|
|
284
|
-
const verdict = (0, toolArgsValidation_js_1.validateToolArgs)(
|
|
358
|
+
const verdict = (0, toolArgsValidation_js_1.validateToolArgs)(callArgs, tool.schema.inputSchema);
|
|
285
359
|
if (!verdict.ok) {
|
|
286
360
|
(0, typedEmit_js_1.typedEmit)(scope, 'agentfootprint.validation.args_invalid', {
|
|
287
361
|
toolName: tc.name,
|
|
@@ -319,7 +393,7 @@ function buildToolCallsHandler(deps) {
|
|
|
319
393
|
const historyForEvidence = systemPrompt
|
|
320
394
|
? [{ role: 'system', content: systemPrompt }, ...newHistory]
|
|
321
395
|
: newHistory;
|
|
322
|
-
if (!(0, checkin_js_1.shouldCheckIn)(tool.checkIn,
|
|
396
|
+
if (!(0, checkin_js_1.shouldCheckIn)(tool.checkIn, callArgs, {
|
|
323
397
|
iteration,
|
|
324
398
|
toolCallId: tc.id,
|
|
325
399
|
history: historyForEvidence,
|
|
@@ -331,7 +405,7 @@ function buildToolCallsHandler(deps) {
|
|
|
331
405
|
const intent = scope.llmLatestContent ? String(scope.llmLatestContent) : undefined;
|
|
332
406
|
const evidence = await deps.checkIn.assembler({
|
|
333
407
|
tool: { name: tc.name, description: tool.schema.description },
|
|
334
|
-
args:
|
|
408
|
+
args: callArgs,
|
|
335
409
|
...(intent !== undefined && { intent }),
|
|
336
410
|
iteration,
|
|
337
411
|
history: historyForEvidence,
|
|
@@ -340,7 +414,7 @@ function buildToolCallsHandler(deps) {
|
|
|
340
414
|
});
|
|
341
415
|
const request = {
|
|
342
416
|
tool: tc.name,
|
|
343
|
-
args:
|
|
417
|
+
args: callArgs,
|
|
344
418
|
...(intent !== undefined && { intent }),
|
|
345
419
|
evidence,
|
|
346
420
|
};
|
|
@@ -358,7 +432,7 @@ function buildToolCallsHandler(deps) {
|
|
|
358
432
|
scope.pausedToolName = tc.name;
|
|
359
433
|
scope.pausedToolStartMs = startMs;
|
|
360
434
|
scope.pausedCheckIn = true;
|
|
361
|
-
scope.pausedCheckInArgs =
|
|
435
|
+
scope.pausedCheckInArgs = callArgs;
|
|
362
436
|
// Returning a defined value triggers the footprintjs pause; the
|
|
363
437
|
// returned object becomes the checkpoint's pauseData. detectPause
|
|
364
438
|
// surfaces `pauseData.checkIn` as `outcome.checkIn`.
|
|
@@ -423,7 +497,7 @@ function buildToolCallsHandler(deps) {
|
|
|
423
497
|
try {
|
|
424
498
|
if (!tool)
|
|
425
499
|
throw new Error(`Unknown tool: ${tc.name}`);
|
|
426
|
-
result = await tool.execute(
|
|
500
|
+
result = await tool.execute(callArgs, {
|
|
427
501
|
toolCallId: tc.id,
|
|
428
502
|
iteration,
|
|
429
503
|
...(env.signal && { signal: env.signal }),
|
|
@@ -462,7 +536,7 @@ function buildToolCallsHandler(deps) {
|
|
|
462
536
|
// so plain read_skill agents are byte-for-byte unaffected.
|
|
463
537
|
let skillRejected = false;
|
|
464
538
|
if (deps.allowedSkillIds && tc.name === 'read_skill' && !error && !denied) {
|
|
465
|
-
const reqId =
|
|
539
|
+
const reqId = callArgs.id;
|
|
466
540
|
if (typeof reqId === 'string' && reqId.length > 0) {
|
|
467
541
|
const currentSkillId = scope.currentSkillId;
|
|
468
542
|
const allowed = deps.allowedSkillIds(currentSkillId);
|
|
@@ -508,7 +582,7 @@ function buildToolCallsHandler(deps) {
|
|
|
508
582
|
// NEXT pass activates that Skill (lifetime: turn — stays
|
|
509
583
|
// active until the turn ends).
|
|
510
584
|
if (tc.name === 'read_skill' && !error && !denied && !skillRejected) {
|
|
511
|
-
const requestedId =
|
|
585
|
+
const requestedId = callArgs.id;
|
|
512
586
|
if (typeof requestedId === 'string' && requestedId.length > 0) {
|
|
513
587
|
const current = scope.activatedInjectionIds;
|
|
514
588
|
if (!current.includes(requestedId)) {
|
|
@@ -562,6 +636,12 @@ function buildToolCallsHandler(deps) {
|
|
|
562
636
|
return undefined; // explicit: no pause, flow continues to loopTo
|
|
563
637
|
},
|
|
564
638
|
resume: async (scope, input) => {
|
|
639
|
+
// Same barrier as `execute`, for the same reason: an approved check-in
|
|
640
|
+
// runs the REAL tool here, and that side effect must not go out ahead of
|
|
641
|
+
// the state the last write is still carrying.
|
|
642
|
+
const durable = deps.awaitDurable?.();
|
|
643
|
+
if (durable)
|
|
644
|
+
await durable;
|
|
565
645
|
// Consumer-supplied resume input becomes the paused tool's result.
|
|
566
646
|
// The subflow's pre-pause scope is restored automatically by
|
|
567
647
|
// footprintjs 4.17.0 via `checkpoint.subflowStates`, so
|
|
@@ -570,6 +650,126 @@ function buildToolCallsHandler(deps) {
|
|
|
570
650
|
const toolCallId = scope.pausedToolCallId;
|
|
571
651
|
const toolName = scope.pausedToolName;
|
|
572
652
|
const startMs = scope.pausedToolStartMs;
|
|
653
|
+
// ── Middleware-ask decision path ─────────────────────────────────
|
|
654
|
+
// Discriminated by `scope.pausedAsk`, restored from the checkpoint.
|
|
655
|
+
//
|
|
656
|
+
// The answer is a DECISION, not a result. That is the whole reason the
|
|
657
|
+
// outcome union has no `result` arm: a middleware asks a person whether
|
|
658
|
+
// this call may proceed, and on approval the REAL tool runs — the person
|
|
659
|
+
// never writes the tool's answer, and neither does the middleware.
|
|
660
|
+
//
|
|
661
|
+
// A malformed resume DECLINES, for the same reason the check-in path
|
|
662
|
+
// does: a governed call must never execute because a message was
|
|
663
|
+
// mis-shaped.
|
|
664
|
+
if (scope.pausedAsk === true) {
|
|
665
|
+
const iteration = scope.iteration;
|
|
666
|
+
const args = (scope.pausedAskArgs ?? {});
|
|
667
|
+
const askIndex = (scope.pausedAskIndex ?? 0);
|
|
668
|
+
const askedBy = (scope.pausedAskMiddleware ?? 'middleware');
|
|
669
|
+
const decision = (0, checkin_js_1.isCheckInDecision)(input)
|
|
670
|
+
? input
|
|
671
|
+
: (0, checkin_js_1.checkInDeclined)({ by: 'unknown', note: 'resume input was not a CheckInDecision' });
|
|
672
|
+
let result;
|
|
673
|
+
let error;
|
|
674
|
+
if (!decision.approved) {
|
|
675
|
+
result = decision.note ? `declined by human: ${decision.note}` : 'declined by human';
|
|
676
|
+
(0, ledger_js_1.recordDecisions)(scope, [
|
|
677
|
+
{
|
|
678
|
+
middleware: askedBy,
|
|
679
|
+
at: 'tool',
|
|
680
|
+
toolName,
|
|
681
|
+
toolCallId,
|
|
682
|
+
iteration,
|
|
683
|
+
outcome: 'deny',
|
|
684
|
+
changed: false,
|
|
685
|
+
why: `declined by ${decision.by}${decision.note ? `: ${decision.note}` : ''}`,
|
|
686
|
+
},
|
|
687
|
+
]);
|
|
688
|
+
}
|
|
689
|
+
else {
|
|
690
|
+
(0, ledger_js_1.recordDecisions)(scope, [
|
|
691
|
+
{
|
|
692
|
+
middleware: askedBy,
|
|
693
|
+
at: 'tool',
|
|
694
|
+
toolName,
|
|
695
|
+
toolCallId,
|
|
696
|
+
iteration,
|
|
697
|
+
outcome: 'allow',
|
|
698
|
+
changed: false,
|
|
699
|
+
why: `approved by ${decision.by}${decision.note ? `: ${decision.note}` : ''}`,
|
|
700
|
+
},
|
|
701
|
+
]);
|
|
702
|
+
// Continue the chain from the link AFTER the one that asked. Its
|
|
703
|
+
// decision is already on the checkpoint; re-running it would ask the
|
|
704
|
+
// same question twice and file a duplicate row.
|
|
705
|
+
//
|
|
706
|
+
// `askPolicy: 'refuse'` because footprintjs's `PausableHandler.resume`
|
|
707
|
+
// returns void — a resumed dispatch has no second checkpoint to give.
|
|
708
|
+
// A link further down the chain that also wants a person gets a named,
|
|
709
|
+
// model-visible refusal and the tool does NOT run. That is the same
|
|
710
|
+
// rule already applied to a tool that tries to pause during an
|
|
711
|
+
// approved check-in resume: at most one human question per resume.
|
|
712
|
+
const rest = await (0, runChain_js_1.runToolChain)(deps.toolMiddleware ?? [], {
|
|
713
|
+
toolName,
|
|
714
|
+
toolCallId,
|
|
715
|
+
iteration,
|
|
716
|
+
args,
|
|
717
|
+
history: [...scope.history],
|
|
718
|
+
startIndex: askIndex + 1,
|
|
719
|
+
askPolicy: 'refuse',
|
|
720
|
+
});
|
|
721
|
+
(0, ledger_js_1.recordDecisions)(scope, rest.decisions);
|
|
722
|
+
const tool = lookupTool(toolName);
|
|
723
|
+
if (rest.kind === 'deny') {
|
|
724
|
+
result = rest.reason;
|
|
725
|
+
}
|
|
726
|
+
else if (tool?.checkIn !== undefined) {
|
|
727
|
+
// Same one-question rule, from the other direction: this tool also
|
|
728
|
+
// demands consent, and there is no checkpoint left to ask with.
|
|
729
|
+
// Refusing loudly beats executing a tool whose consent gate we
|
|
730
|
+
// silently skipped.
|
|
731
|
+
error = true;
|
|
732
|
+
result =
|
|
733
|
+
`tool '${toolName}' also declares checkIn, and a resumed dispatch cannot pause ` +
|
|
734
|
+
`again to ask a second time. The call was not executed — approve it through one ` +
|
|
735
|
+
`gate, not both.`;
|
|
736
|
+
}
|
|
737
|
+
else {
|
|
738
|
+
const env = scope.$getEnv();
|
|
739
|
+
const dispatched = await resolveCredentialAndExecute(scope, tool, toolName, rest.args, toolCallId, iteration, env);
|
|
740
|
+
result = dispatched.result;
|
|
741
|
+
error = dispatched.error;
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
const askResultStr = typeof result === 'string' ? result : (0, validators_js_1.safeStringify)(result);
|
|
745
|
+
const askHistory = [
|
|
746
|
+
...scope.history,
|
|
747
|
+
{ role: 'tool', content: askResultStr, toolCallId, toolName },
|
|
748
|
+
];
|
|
749
|
+
scope.history = askHistory;
|
|
750
|
+
scope.lastToolResult = { toolName, result: askResultStr };
|
|
751
|
+
(0, typedEmit_js_1.typedEmit)(scope, 'agentfootprint.stream.tool_end', {
|
|
752
|
+
toolCallId,
|
|
753
|
+
result,
|
|
754
|
+
durationMs: Date.now() - startMs,
|
|
755
|
+
...(error === true && { error: true }),
|
|
756
|
+
});
|
|
757
|
+
(0, typedEmit_js_1.typedEmit)(scope, 'agentfootprint.agent.iteration_end', {
|
|
758
|
+
turnIndex: 0,
|
|
759
|
+
iterIndex: iteration,
|
|
760
|
+
toolCallCount: 1,
|
|
761
|
+
history: askHistory,
|
|
762
|
+
});
|
|
763
|
+
scope.iteration = iteration + 1;
|
|
764
|
+
scope.pausedToolCallId = '';
|
|
765
|
+
scope.pausedToolName = '';
|
|
766
|
+
scope.pausedToolStartMs = 0;
|
|
767
|
+
scope.pausedAsk = false;
|
|
768
|
+
scope.pausedAskArgs = undefined;
|
|
769
|
+
scope.pausedAskIndex = undefined;
|
|
770
|
+
scope.pausedAskMiddleware = undefined;
|
|
771
|
+
return;
|
|
772
|
+
}
|
|
573
773
|
// ── Check-in decision path ───────────────────────────────────────
|
|
574
774
|
// A check-in pause is discriminated by `scope.pausedCheckIn` (restored
|
|
575
775
|
// from the checkpoint). The resume input is a `CheckInDecision`. On
|