agentfootprint 2.11.0 → 2.11.2
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 +2 -1
- package/dist/core/Agent.js +89 -1341
- package/dist/core/Agent.js.map +1 -1
- package/dist/core/agent/AgentBuilder.js +489 -0
- package/dist/core/agent/AgentBuilder.js.map +1 -0
- package/dist/core/agent/buildAgentChart.js +227 -0
- package/dist/core/agent/buildAgentChart.js.map +1 -0
- package/dist/core/agent/buildToolRegistry.js +115 -0
- package/dist/core/agent/buildToolRegistry.js.map +1 -0
- package/dist/core/agent/stages/breakFinal.js +28 -0
- package/dist/core/agent/stages/breakFinal.js.map +1 -0
- package/dist/core/agent/stages/callLLM.js +129 -0
- package/dist/core/agent/stages/callLLM.js.map +1 -0
- package/dist/core/agent/stages/iterationStart.js +24 -0
- package/dist/core/agent/stages/iterationStart.js.map +1 -0
- package/dist/core/agent/stages/prepareFinal.js +45 -0
- package/dist/core/agent/stages/prepareFinal.js.map +1 -0
- package/dist/core/agent/stages/route.js +36 -0
- package/dist/core/agent/stages/route.js.map +1 -0
- package/dist/core/agent/stages/seed.js +95 -0
- package/dist/core/agent/stages/seed.js.map +1 -0
- package/dist/core/agent/stages/toolCalls.js +250 -0
- package/dist/core/agent/stages/toolCalls.js.map +1 -0
- package/dist/core/agent/types.js +12 -0
- package/dist/core/agent/types.js.map +1 -0
- package/dist/core/agent/validators.js +131 -0
- package/dist/core/agent/validators.js.map +1 -0
- package/dist/esm/core/Agent.js +87 -1338
- package/dist/esm/core/Agent.js.map +1 -1
- package/dist/esm/core/agent/AgentBuilder.js +485 -0
- package/dist/esm/core/agent/AgentBuilder.js.map +1 -0
- package/dist/esm/core/agent/buildAgentChart.js +223 -0
- package/dist/esm/core/agent/buildAgentChart.js.map +1 -0
- package/dist/esm/core/agent/buildToolRegistry.js +111 -0
- package/dist/esm/core/agent/buildToolRegistry.js.map +1 -0
- package/dist/esm/core/agent/stages/breakFinal.js +24 -0
- package/dist/esm/core/agent/stages/breakFinal.js.map +1 -0
- package/dist/esm/core/agent/stages/callLLM.js +125 -0
- package/dist/esm/core/agent/stages/callLLM.js.map +1 -0
- package/dist/esm/core/agent/stages/iterationStart.js +20 -0
- package/dist/esm/core/agent/stages/iterationStart.js.map +1 -0
- package/dist/esm/core/agent/stages/prepareFinal.js +41 -0
- package/dist/esm/core/agent/stages/prepareFinal.js.map +1 -0
- package/dist/esm/core/agent/stages/route.js +32 -0
- package/dist/esm/core/agent/stages/route.js.map +1 -0
- package/dist/esm/core/agent/stages/seed.js +91 -0
- package/dist/esm/core/agent/stages/seed.js.map +1 -0
- package/dist/esm/core/agent/stages/toolCalls.js +246 -0
- package/dist/esm/core/agent/stages/toolCalls.js.map +1 -0
- package/dist/esm/core/agent/types.js +11 -0
- package/dist/esm/core/agent/types.js.map +1 -0
- package/dist/esm/core/agent/validators.js +124 -0
- package/dist/esm/core/agent/validators.js.map +1 -0
- package/dist/esm/reliability/CircuitBreaker.js +156 -0
- package/dist/esm/reliability/CircuitBreaker.js.map +1 -0
- package/dist/esm/reliability/buildReliabilityGateChart.js +359 -0
- package/dist/esm/reliability/buildReliabilityGateChart.js.map +1 -0
- package/dist/esm/reliability/classifyError.js +56 -0
- package/dist/esm/reliability/classifyError.js.map +1 -0
- package/dist/esm/reliability/index.js +36 -0
- package/dist/esm/reliability/index.js.map +1 -0
- package/dist/esm/reliability/types.js +44 -0
- package/dist/esm/reliability/types.js.map +1 -0
- package/dist/reliability/CircuitBreaker.js +165 -0
- package/dist/reliability/CircuitBreaker.js.map +1 -0
- package/dist/reliability/buildReliabilityGateChart.js +363 -0
- package/dist/reliability/buildReliabilityGateChart.js.map +1 -0
- package/dist/reliability/classifyError.js +60 -0
- package/dist/reliability/classifyError.js.map +1 -0
- package/dist/reliability/index.js +42 -0
- package/dist/reliability/index.js.map +1 -0
- package/dist/reliability/types.js +48 -0
- package/dist/reliability/types.js.map +1 -0
- package/dist/types/core/Agent.d.ts +7 -400
- package/dist/types/core/Agent.d.ts.map +1 -1
- package/dist/types/core/agent/AgentBuilder.d.ts +348 -0
- package/dist/types/core/agent/AgentBuilder.d.ts.map +1 -0
- package/dist/types/core/agent/buildAgentChart.d.ts +74 -0
- package/dist/types/core/agent/buildAgentChart.d.ts.map +1 -0
- package/dist/types/core/agent/buildToolRegistry.d.ts +62 -0
- package/dist/types/core/agent/buildToolRegistry.d.ts.map +1 -0
- package/dist/types/core/agent/stages/breakFinal.d.ts +23 -0
- package/dist/types/core/agent/stages/breakFinal.d.ts.map +1 -0
- package/dist/types/core/agent/stages/callLLM.d.ts +54 -0
- package/dist/types/core/agent/stages/callLLM.d.ts.map +1 -0
- package/dist/types/core/agent/stages/iterationStart.d.ts +16 -0
- package/dist/types/core/agent/stages/iterationStart.d.ts.map +1 -0
- package/dist/types/core/agent/stages/prepareFinal.d.ts +20 -0
- package/dist/types/core/agent/stages/prepareFinal.d.ts.map +1 -0
- package/dist/types/core/agent/stages/route.d.ts +19 -0
- package/dist/types/core/agent/stages/route.d.ts.map +1 -0
- package/dist/types/core/agent/stages/seed.d.ts +54 -0
- package/dist/types/core/agent/stages/seed.d.ts.map +1 -0
- package/dist/types/core/agent/stages/toolCalls.d.ts +50 -0
- package/dist/types/core/agent/stages/toolCalls.d.ts.map +1 -0
- package/dist/types/core/agent/types.d.ts +154 -0
- package/dist/types/core/agent/types.d.ts.map +1 -0
- package/dist/types/core/agent/validators.d.ts +48 -0
- package/dist/types/core/agent/validators.d.ts.map +1 -0
- package/dist/types/reliability/CircuitBreaker.d.ts +76 -0
- package/dist/types/reliability/CircuitBreaker.d.ts.map +1 -0
- package/dist/types/reliability/buildReliabilityGateChart.d.ts +54 -0
- package/dist/types/reliability/buildReliabilityGateChart.d.ts.map +1 -0
- package/dist/types/reliability/classifyError.d.ts +29 -0
- package/dist/types/reliability/classifyError.d.ts.map +1 -0
- package/dist/types/reliability/index.d.ts +34 -0
- package/dist/types/reliability/index.d.ts.map +1 -0
- package/dist/types/reliability/types.d.ts +256 -0
- package/dist/types/reliability/types.d.ts.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* seed — initial stage of the agent's chart. Initializes every mutable
|
|
3
|
+
* field of `AgentState` from the consumer's input.
|
|
4
|
+
*
|
|
5
|
+
* Runs once per `agent.run({ input })`. The chart is built once at
|
|
6
|
+
* Agent construction, so seed has access to BOTH:
|
|
7
|
+
*
|
|
8
|
+
* • CHART-BUILD-TIME constants (maxIterations, cachingDisabled,
|
|
9
|
+
* toolSchemas) — passed as direct values to the factory.
|
|
10
|
+
* • PER-RUN MUTABLE state (pendingResumeHistory from
|
|
11
|
+
* resumeOnError(), currentRunContext.runId set per run) —
|
|
12
|
+
* passed as accessor closures over the Agent instance, since
|
|
13
|
+
* these change between consecutive `agent.run()` invocations.
|
|
14
|
+
*
|
|
15
|
+
* The accessor pattern keeps `seed` decoupled from the Agent class
|
|
16
|
+
* while preserving the per-run mutability the resume + identity
|
|
17
|
+
* features need.
|
|
18
|
+
*/
|
|
19
|
+
import { typedEmit } from '../../../recorders/core/typedEmit.js';
|
|
20
|
+
/**
|
|
21
|
+
* Build the seed stage function for an Agent instance. Captures both
|
|
22
|
+
* the chart-build-time constants and the per-run mutable accessors
|
|
23
|
+
* via the deps object.
|
|
24
|
+
*/
|
|
25
|
+
export function buildSeedStage(deps) {
|
|
26
|
+
return (scope) => {
|
|
27
|
+
const args = scope.$getArgs();
|
|
28
|
+
scope.userMessage = args.message;
|
|
29
|
+
// If `resumeOnError(...)` set the side channel, restore the
|
|
30
|
+
// checkpointed conversation history. The next iteration sees
|
|
31
|
+
// the prior messages and continues from the failure point.
|
|
32
|
+
// Always clear the field after reading so subsequent runs
|
|
33
|
+
// (without resumeOnError) start fresh.
|
|
34
|
+
const resumeHistory = deps.consumePendingResumeHistory();
|
|
35
|
+
if (resumeHistory && resumeHistory.length > 0) {
|
|
36
|
+
scope.history = [...resumeHistory];
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
scope.history = [{ role: 'user', content: args.message }];
|
|
40
|
+
}
|
|
41
|
+
// Default identity uses the runId so multi-run isolation works
|
|
42
|
+
// without consumer changes; explicit identity (multi-tenant)
|
|
43
|
+
// overrides via `agent.run({ identity })`.
|
|
44
|
+
scope.runIdentity = args.identity ?? {
|
|
45
|
+
conversationId: deps.getCurrentRunId() ?? 'default',
|
|
46
|
+
};
|
|
47
|
+
scope.newMessages = [];
|
|
48
|
+
scope.turnNumber = 1;
|
|
49
|
+
// Permissive default — explicit cap will land when PricingTable
|
|
50
|
+
// gets a context-window field. Memory pickByBudget treats anything
|
|
51
|
+
// ≥ minimumTokens as "fits", so this just enables the budget path.
|
|
52
|
+
scope.contextTokensRemaining = 32_000;
|
|
53
|
+
scope.iteration = 1;
|
|
54
|
+
scope.maxIterations = deps.maxIterations;
|
|
55
|
+
scope.finalContent = '';
|
|
56
|
+
scope.totalInputTokens = 0;
|
|
57
|
+
scope.totalOutputTokens = 0;
|
|
58
|
+
scope.turnStartMs = Date.now();
|
|
59
|
+
scope.systemPromptInjections = [];
|
|
60
|
+
scope.messagesInjections = [];
|
|
61
|
+
scope.toolsInjections = [];
|
|
62
|
+
scope.llmLatestContent = '';
|
|
63
|
+
scope.llmLatestToolCalls = [];
|
|
64
|
+
scope.pausedToolCallId = '';
|
|
65
|
+
scope.pausedToolName = '';
|
|
66
|
+
scope.pausedToolStartMs = 0;
|
|
67
|
+
scope.cumTokensInput = 0;
|
|
68
|
+
scope.cumTokensOutput = 0;
|
|
69
|
+
scope.cumEstimatedUsd = 0;
|
|
70
|
+
scope.costBudgetHit = false;
|
|
71
|
+
scope.activeInjections = [];
|
|
72
|
+
scope.activatedInjectionIds = [];
|
|
73
|
+
scope.dynamicToolSchemas = deps.toolSchemas;
|
|
74
|
+
// Cache layer state (v2.6) — initialized to inert defaults.
|
|
75
|
+
// CacheDecision subflow populates `cacheMarkers` per iteration;
|
|
76
|
+
// UpdateSkillHistory + CacheGate consume `cachingDisabled`,
|
|
77
|
+
// `recentHitRate`, `skillHistory`. Empty defaults mean the
|
|
78
|
+
// CacheGate falls through to 'apply-markers' on iter 1 (no
|
|
79
|
+
// history yet → no churn detected; recentHitRate undefined →
|
|
80
|
+
// hit-rate floor doesn't fire).
|
|
81
|
+
scope.cacheMarkers = [];
|
|
82
|
+
scope.cachingDisabled = deps.cachingDisabled;
|
|
83
|
+
scope.recentHitRate = undefined;
|
|
84
|
+
scope.skillHistory = [];
|
|
85
|
+
typedEmit(scope, 'agentfootprint.agent.turn_start', {
|
|
86
|
+
turnIndex: 0,
|
|
87
|
+
userPrompt: args.message,
|
|
88
|
+
});
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=seed.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seed.js","sourceRoot":"","sources":["../../../../../src/core/agent/stages/seed.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* toolCalls — pausable handler for executing the LLM-requested tool
|
|
3
|
+
* calls in the agent's ReAct loop.
|
|
4
|
+
*
|
|
5
|
+
* • `execute` iterates `scope.llmLatestToolCalls`, dispatches each
|
|
6
|
+
* tool, appends results to scope.history, and increments
|
|
7
|
+
* `scope.iteration`. If a tool throws `PauseRequest` (via
|
|
8
|
+
* `pauseHere()`), commits partial state and returns the pause
|
|
9
|
+
* payload so footprintjs captures a checkpoint.
|
|
10
|
+
* • `resume` runs after the consumer supplies the human's answer.
|
|
11
|
+
* Treats that answer as the paused tool's result, appends to
|
|
12
|
+
* history, then continues the ReAct iteration loop.
|
|
13
|
+
*
|
|
14
|
+
* Dispatch resolution order:
|
|
15
|
+
* 1. Static registry built at chart-build time (registryByName).
|
|
16
|
+
* 2. External `ToolProvider.list(ctx).find(...)` if a `.toolProvider()`
|
|
17
|
+
* was wired and the tool isn't in the static registry.
|
|
18
|
+
*
|
|
19
|
+
* Permission gate (when `permissionChecker` is configured) runs BEFORE
|
|
20
|
+
* `tool.execute`. Deny → tool not executed; result is a synthetic
|
|
21
|
+
* denial string. Allow / gate_open → execution proceeds.
|
|
22
|
+
*
|
|
23
|
+
* `read_skill` is the auto-attached activation tool — when the LLM
|
|
24
|
+
* calls it with a valid Skill id, the next InjectionEngine pass
|
|
25
|
+
* activates that Skill (lifetime: turn).
|
|
26
|
+
*/
|
|
27
|
+
import { typedEmit } from '../../../recorders/core/typedEmit.js';
|
|
28
|
+
import { isPauseRequest } from '../../pause.js';
|
|
29
|
+
import { safeStringify } from '../validators.js';
|
|
30
|
+
/**
|
|
31
|
+
* Build the pausable tool-call handler for the agent's chart.
|
|
32
|
+
*/
|
|
33
|
+
export function buildToolCallsHandler(deps) {
|
|
34
|
+
const { registryByName, externalToolProvider, permissionChecker } = deps;
|
|
35
|
+
return {
|
|
36
|
+
execute: async (scope) => {
|
|
37
|
+
const toolCalls = scope.llmLatestToolCalls;
|
|
38
|
+
const iteration = scope.iteration;
|
|
39
|
+
const newHistory = [...scope.history];
|
|
40
|
+
// ALWAYS push the assistant turn when there are tool calls — even
|
|
41
|
+
// if the content was empty — so providers (Anthropic, OpenAI) can
|
|
42
|
+
// round-trip the tool_use blocks via `LLMMessage.toolCalls`.
|
|
43
|
+
// Without this, the next iteration's request lacks the assistant
|
|
44
|
+
// turn that initiated the tool call, and the API rejects the
|
|
45
|
+
// following tool_result with "preceding tool_use missing".
|
|
46
|
+
if (scope.llmLatestContent || toolCalls.length > 0) {
|
|
47
|
+
newHistory.push({
|
|
48
|
+
role: 'assistant',
|
|
49
|
+
content: scope.llmLatestContent ?? '',
|
|
50
|
+
...(toolCalls.length > 0 && { toolCalls }),
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
// Resolve a tool by name, consulting the external ToolProvider
|
|
54
|
+
// if one was wired via `.toolProvider()` and the static
|
|
55
|
+
// registry doesn't carry the tool. The provider sees the same
|
|
56
|
+
// ctx the Tools slot used, so dispatch + visibility stay
|
|
57
|
+
// consistent within the iteration.
|
|
58
|
+
const lookupTool = (toolName) => {
|
|
59
|
+
const fromRegistry = registryByName.get(toolName);
|
|
60
|
+
if (fromRegistry)
|
|
61
|
+
return fromRegistry;
|
|
62
|
+
if (!externalToolProvider)
|
|
63
|
+
return undefined;
|
|
64
|
+
const activatedIds = scope.activatedInjectionIds ?? [];
|
|
65
|
+
const identity = scope.runIdentity;
|
|
66
|
+
const ctx = {
|
|
67
|
+
iteration: scope.iteration,
|
|
68
|
+
...(activatedIds.length > 0 && {
|
|
69
|
+
activeSkillId: activatedIds[activatedIds.length - 1],
|
|
70
|
+
}),
|
|
71
|
+
...(identity && { identity }),
|
|
72
|
+
};
|
|
73
|
+
const visible = externalToolProvider.list(ctx);
|
|
74
|
+
return visible.find((t) => t.schema.name === toolName);
|
|
75
|
+
};
|
|
76
|
+
for (const tc of toolCalls) {
|
|
77
|
+
const tool = lookupTool(tc.name);
|
|
78
|
+
typedEmit(scope, 'agentfootprint.stream.tool_start', {
|
|
79
|
+
toolName: tc.name,
|
|
80
|
+
toolCallId: tc.id,
|
|
81
|
+
args: tc.args,
|
|
82
|
+
...(toolCalls.length > 1 && { parallelCount: toolCalls.length }),
|
|
83
|
+
});
|
|
84
|
+
const startMs = Date.now();
|
|
85
|
+
let result;
|
|
86
|
+
let error;
|
|
87
|
+
// Permission gate — when a checker is configured, evaluate BEFORE
|
|
88
|
+
// executing the tool. Emits `permission.check` with the decision.
|
|
89
|
+
// On 'deny', the tool is not executed and its result is a
|
|
90
|
+
// synthetic denial string; on 'allow'/'gate_open', execution
|
|
91
|
+
// proceeds normally (the gate is informational — the consumer's
|
|
92
|
+
// checker is responsible for any gate-open side effects).
|
|
93
|
+
let denied = false;
|
|
94
|
+
if (permissionChecker) {
|
|
95
|
+
try {
|
|
96
|
+
const decision = await permissionChecker.check({
|
|
97
|
+
capability: 'tool_call',
|
|
98
|
+
actor: 'agent',
|
|
99
|
+
target: tc.name,
|
|
100
|
+
context: tc.args,
|
|
101
|
+
});
|
|
102
|
+
typedEmit(scope, 'agentfootprint.permission.check', {
|
|
103
|
+
capability: 'tool_call',
|
|
104
|
+
actor: 'agent',
|
|
105
|
+
target: tc.name,
|
|
106
|
+
result: decision.result,
|
|
107
|
+
...(decision.policyRuleId !== undefined && { policyRuleId: decision.policyRuleId }),
|
|
108
|
+
...(decision.rationale !== undefined && { rationale: decision.rationale }),
|
|
109
|
+
});
|
|
110
|
+
if (decision.result === 'deny') {
|
|
111
|
+
denied = true;
|
|
112
|
+
result = `[permission denied: ${decision.rationale ?? 'policy'}]`;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
catch (permErr) {
|
|
116
|
+
// A checker that throws is treated as deny-by-default. The
|
|
117
|
+
// denial message records the thrown error so consumers can
|
|
118
|
+
// debug policy-adapter failures without losing the run.
|
|
119
|
+
denied = true;
|
|
120
|
+
const msg = permErr instanceof Error ? permErr.message : String(permErr);
|
|
121
|
+
typedEmit(scope, 'agentfootprint.permission.check', {
|
|
122
|
+
capability: 'tool_call',
|
|
123
|
+
actor: 'agent',
|
|
124
|
+
target: tc.name,
|
|
125
|
+
result: 'deny',
|
|
126
|
+
rationale: `permission-checker threw: ${msg}`,
|
|
127
|
+
});
|
|
128
|
+
result = `[permission denied: checker error: ${msg}]`;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
if (!denied) {
|
|
132
|
+
try {
|
|
133
|
+
if (!tool)
|
|
134
|
+
throw new Error(`Unknown tool: ${tc.name}`);
|
|
135
|
+
result = await tool.execute(tc.args, {
|
|
136
|
+
toolCallId: tc.id,
|
|
137
|
+
iteration,
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
catch (err) {
|
|
141
|
+
if (isPauseRequest(err)) {
|
|
142
|
+
// Commit partial state so resume() can find history intact.
|
|
143
|
+
scope.history = newHistory;
|
|
144
|
+
scope.pausedToolCallId = tc.id;
|
|
145
|
+
scope.pausedToolName = tc.name;
|
|
146
|
+
scope.pausedToolStartMs = startMs;
|
|
147
|
+
// Returning a defined value triggers footprintjs pause —
|
|
148
|
+
// the returned object becomes the checkpoint's pauseData.
|
|
149
|
+
return {
|
|
150
|
+
toolCallId: tc.id,
|
|
151
|
+
toolName: tc.name,
|
|
152
|
+
...(typeof err.data === 'object' && err.data !== null
|
|
153
|
+
? err.data
|
|
154
|
+
: { data: err.data }),
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
error = true;
|
|
158
|
+
result = err instanceof Error ? err.message : String(err);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
const durationMs = Date.now() - startMs;
|
|
162
|
+
typedEmit(scope, 'agentfootprint.stream.tool_end', {
|
|
163
|
+
toolCallId: tc.id,
|
|
164
|
+
result,
|
|
165
|
+
durationMs,
|
|
166
|
+
...(error === true && { error: true }),
|
|
167
|
+
});
|
|
168
|
+
const resultStr = typeof result === 'string' ? result : safeStringify(result);
|
|
169
|
+
newHistory.push({
|
|
170
|
+
role: 'tool',
|
|
171
|
+
content: resultStr,
|
|
172
|
+
toolCallId: tc.id,
|
|
173
|
+
toolName: tc.name,
|
|
174
|
+
});
|
|
175
|
+
// ── Dynamic ReAct wiring ───────────────────────────────
|
|
176
|
+
//
|
|
177
|
+
// (1) `lastToolResult` drives `on-tool-return` Injection
|
|
178
|
+
// triggers — the InjectionEngine's NEXT pass will see
|
|
179
|
+
// this and activate any matching Instructions.
|
|
180
|
+
scope.lastToolResult = { toolName: tc.name, result: resultStr };
|
|
181
|
+
// (2) `read_skill` is the auto-attached activation tool.
|
|
182
|
+
// When the LLM calls it with a valid Skill id, append
|
|
183
|
+
// to `activatedInjectionIds` so the InjectionEngine's
|
|
184
|
+
// NEXT pass activates that Skill (lifetime: turn — stays
|
|
185
|
+
// active until the turn ends).
|
|
186
|
+
if (tc.name === 'read_skill' && !error && !denied) {
|
|
187
|
+
const requestedId = tc.args.id;
|
|
188
|
+
if (typeof requestedId === 'string' && requestedId.length > 0) {
|
|
189
|
+
const current = scope.activatedInjectionIds;
|
|
190
|
+
if (!current.includes(requestedId)) {
|
|
191
|
+
scope.activatedInjectionIds = [...current, requestedId];
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
scope.history = newHistory;
|
|
197
|
+
typedEmit(scope, 'agentfootprint.agent.iteration_end', {
|
|
198
|
+
turnIndex: 0,
|
|
199
|
+
iterIndex: iteration,
|
|
200
|
+
toolCallCount: toolCalls.length,
|
|
201
|
+
history: scope.history,
|
|
202
|
+
});
|
|
203
|
+
scope.iteration = iteration + 1;
|
|
204
|
+
return undefined; // explicit: no pause, flow continues to loopTo
|
|
205
|
+
},
|
|
206
|
+
resume: (scope, input) => {
|
|
207
|
+
// Consumer-supplied resume input becomes the paused tool's result.
|
|
208
|
+
// The subflow's pre-pause scope is restored automatically by
|
|
209
|
+
// footprintjs 4.17.0 via `checkpoint.subflowStates`, so
|
|
210
|
+
// `scope.history` and `scope.pausedToolCallId` read back cleanly
|
|
211
|
+
// across same-executor AND cross-executor resume.
|
|
212
|
+
const toolCallId = scope.pausedToolCallId;
|
|
213
|
+
const toolName = scope.pausedToolName;
|
|
214
|
+
const startMs = scope.pausedToolStartMs;
|
|
215
|
+
const resultStr = typeof input === 'string' ? input : safeStringify(input);
|
|
216
|
+
const newHistory = [
|
|
217
|
+
...scope.history,
|
|
218
|
+
{
|
|
219
|
+
role: 'tool',
|
|
220
|
+
content: resultStr,
|
|
221
|
+
toolCallId,
|
|
222
|
+
toolName,
|
|
223
|
+
},
|
|
224
|
+
];
|
|
225
|
+
scope.history = newHistory;
|
|
226
|
+
typedEmit(scope, 'agentfootprint.stream.tool_end', {
|
|
227
|
+
toolCallId,
|
|
228
|
+
result: input,
|
|
229
|
+
durationMs: Date.now() - startMs,
|
|
230
|
+
});
|
|
231
|
+
const iteration = scope.iteration;
|
|
232
|
+
typedEmit(scope, 'agentfootprint.agent.iteration_end', {
|
|
233
|
+
turnIndex: 0,
|
|
234
|
+
iterIndex: iteration,
|
|
235
|
+
toolCallCount: 1,
|
|
236
|
+
history: scope.history,
|
|
237
|
+
});
|
|
238
|
+
scope.iteration = iteration + 1;
|
|
239
|
+
// Clear pause checkpoint fields.
|
|
240
|
+
scope.pausedToolCallId = '';
|
|
241
|
+
scope.pausedToolName = '';
|
|
242
|
+
scope.pausedToolStartMs = 0;
|
|
243
|
+
},
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
//# sourceMappingURL=toolCalls.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toolCalls.js","sourceRoot":"","sources":["../../../../../src/core/agent/stages/toolCalls.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAKH,OAAO,EAAE,SAAS,EAAE,MAAM,sCAAsC,CAAC;AAEjE,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAEhD,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAkBjD;;GAEG;AACH,MAAM,UAAU,qBAAqB,CACnC,IAA0B;IAE1B,MAAM,EAAE,cAAc,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,GAAG,IAAI,CAAC;IAEzE,OAAO;QACL,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;YACvB,MAAM,SAAS,GAAG,KAAK,CAAC,kBAIrB,CAAC;YACJ,MAAM,SAAS,GAAG,KAAK,CAAC,SAAmB,CAAC;YAC5C,MAAM,UAAU,GAAiB,CAAC,GAAI,KAAK,CAAC,OAAiC,CAAC,CAAC;YAC/E,kEAAkE;YAClE,kEAAkE;YAClE,6DAA6D;YAC7D,iEAAiE;YACjE,6DAA6D;YAC7D,2DAA2D;YAC3D,IAAI,KAAK,CAAC,gBAAgB,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACnD,UAAU,CAAC,IAAI,CAAC;oBACd,IAAI,EAAE,WAA0B;oBAChC,OAAO,EAAE,KAAK,CAAC,gBAAgB,IAAI,EAAE;oBACrC,GAAG,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,SAAS,EAAE,CAAC;iBAC3C,CAAC,CAAC;YACL,CAAC;YACD,+DAA+D;YAC/D,wDAAwD;YACxD,8DAA8D;YAC9D,yDAAyD;YACzD,mCAAmC;YACnC,MAAM,UAAU,GAAG,CAAC,QAAgB,EAAoB,EAAE;gBACxD,MAAM,YAAY,GAAG,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;gBAClD,IAAI,YAAY;oBAAE,OAAO,YAAY,CAAC;gBACtC,IAAI,CAAC,oBAAoB;oBAAE,OAAO,SAAS,CAAC;gBAC5C,MAAM,YAAY,GAAI,KAAK,CAAC,qBAAuD,IAAI,EAAE,CAAC;gBAC1F,MAAM,QAAQ,GAAG,KAAK,CAAC,WAEV,CAAC;gBACd,MAAM,GAAG,GAAwB;oBAC/B,SAAS,EAAE,KAAK,CAAC,SAAmB;oBACpC,GAAG,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,IAAI;wBAC7B,aAAa,EAAE,YAAY,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,CAAC;qBACrD,CAAC;oBACF,GAAG,CAAC,QAAQ,IAAI,EAAE,QAAQ,EAAE,CAAC;iBAC9B,CAAC;gBACF,MAAM,OAAO,GAAG,oBAAoB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBAC/C,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC;YACzD,CAAC,CAAC;YAEF,KAAK,MAAM,EAAE,IAAI,SAAS,EAAE,CAAC;gBAC3B,MAAM,IAAI,GAAG,UAAU,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC;gBACjC,SAAS,CAAC,KAAK,EAAE,kCAAkC,EAAE;oBACnD,QAAQ,EAAE,EAAE,CAAC,IAAI;oBACjB,UAAU,EAAE,EAAE,CAAC,EAAE;oBACjB,IAAI,EAAE,EAAE,CAAC,IAAI;oBACb,GAAG,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,aAAa,EAAE,SAAS,CAAC,MAAM,EAAE,CAAC;iBACjE,CAAC,CAAC;gBACH,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;gBAC3B,IAAI,MAAe,CAAC;gBACpB,IAAI,KAA0B,CAAC;gBAC/B,kEAAkE;gBAClE,kEAAkE;gBAClE,0DAA0D;gBAC1D,6DAA6D;gBAC7D,gEAAgE;gBAChE,0DAA0D;gBAC1D,IAAI,MAAM,GAAG,KAAK,CAAC;gBACnB,IAAI,iBAAiB,EAAE,CAAC;oBACtB,IAAI,CAAC;wBACH,MAAM,QAAQ,GAAG,MAAM,iBAAiB,CAAC,KAAK,CAAC;4BAC7C,UAAU,EAAE,WAAW;4BACvB,KAAK,EAAE,OAAO;4BACd,MAAM,EAAE,EAAE,CAAC,IAAI;4BACf,OAAO,EAAE,EAAE,CAAC,IAAI;yBACjB,CAAC,CAAC;wBACH,SAAS,CAAC,KAAK,EAAE,iCAAiC,EAAE;4BAClD,UAAU,EAAE,WAAW;4BACvB,KAAK,EAAE,OAAO;4BACd,MAAM,EAAE,EAAE,CAAC,IAAI;4BACf,MAAM,EAAE,QAAQ,CAAC,MAAM;4BACvB,GAAG,CAAC,QAAQ,CAAC,YAAY,KAAK,SAAS,IAAI,EAAE,YAAY,EAAE,QAAQ,CAAC,YAAY,EAAE,CAAC;4BACnF,GAAG,CAAC,QAAQ,CAAC,SAAS,KAAK,SAAS,IAAI,EAAE,SAAS,EAAE,QAAQ,CAAC,SAAS,EAAE,CAAC;yBAC3E,CAAC,CAAC;wBACH,IAAI,QAAQ,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;4BAC/B,MAAM,GAAG,IAAI,CAAC;4BACd,MAAM,GAAG,uBAAuB,QAAQ,CAAC,SAAS,IAAI,QAAQ,GAAG,CAAC;wBACpE,CAAC;oBACH,CAAC;oBAAC,OAAO,OAAO,EAAE,CAAC;wBACjB,2DAA2D;wBAC3D,2DAA2D;wBAC3D,wDAAwD;wBACxD,MAAM,GAAG,IAAI,CAAC;wBACd,MAAM,GAAG,GAAG,OAAO,YAAY,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;wBACzE,SAAS,CAAC,KAAK,EAAE,iCAAiC,EAAE;4BAClD,UAAU,EAAE,WAAW;4BACvB,KAAK,EAAE,OAAO;4BACd,MAAM,EAAE,EAAE,CAAC,IAAI;4BACf,MAAM,EAAE,MAAM;4BACd,SAAS,EAAE,6BAA6B,GAAG,EAAE;yBAC9C,CAAC,CAAC;wBACH,MAAM,GAAG,sCAAsC,GAAG,GAAG,CAAC;oBACxD,CAAC;gBACH,CAAC;gBACD,IAAI,CAAC,MAAM,EAAE,CAAC;oBACZ,IAAI,CAAC;wBACH,IAAI,CAAC,IAAI;4BAAE,MAAM,IAAI,KAAK,CAAC,iBAAiB,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;wBACvD,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE;4BACnC,UAAU,EAAE,EAAE,CAAC,EAAE;4BACjB,SAAS;yBACV,CAAC,CAAC;oBACL,CAAC;oBAAC,OAAO,GAAG,EAAE,CAAC;wBACb,IAAI,cAAc,CAAC,GAAG,CAAC,EAAE,CAAC;4BACxB,4DAA4D;4BAC5D,KAAK,CAAC,OAAO,GAAG,UAAU,CAAC;4BAC3B,KAAK,CAAC,gBAAgB,GAAG,EAAE,CAAC,EAAE,CAAC;4BAC/B,KAAK,CAAC,cAAc,GAAG,EAAE,CAAC,IAAI,CAAC;4BAC/B,KAAK,CAAC,iBAAiB,GAAG,OAAO,CAAC;4BAClC,yDAAyD;4BACzD,0DAA0D;4BAC1D,OAAO;gCACL,UAAU,EAAE,EAAE,CAAC,EAAE;gCACjB,QAAQ,EAAE,EAAE,CAAC,IAAI;gCACjB,GAAG,CAAC,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ,IAAI,GAAG,CAAC,IAAI,KAAK,IAAI;oCACnD,CAAC,CAAE,GAAG,CAAC,IAAgC;oCACvC,CAAC,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC;6BACxB,CAAC;wBACJ,CAAC;wBACD,KAAK,GAAG,IAAI,CAAC;wBACb,MAAM,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;oBAC5D,CAAC;gBACH,CAAC;gBACD,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC;gBACxC,SAAS,CAAC,KAAK,EAAE,gCAAgC,EAAE;oBACjD,UAAU,EAAE,EAAE,CAAC,EAAE;oBACjB,MAAM;oBACN,UAAU;oBACV,GAAG,CAAC,KAAK,KAAK,IAAI,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;iBACvC,CAAC,CAAC;gBACH,MAAM,SAAS,GAAG,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;gBAC9E,UAAU,CAAC,IAAI,CAAC;oBACd,IAAI,EAAE,MAAM;oBACZ,OAAO,EAAE,SAAS;oBAClB,UAAU,EAAE,EAAE,CAAC,EAAE;oBACjB,QAAQ,EAAE,EAAE,CAAC,IAAI;iBAClB,CAAC,CAAC;gBAEH,0DAA0D;gBAC1D,EAAE;gBACF,yDAAyD;gBACzD,0DAA0D;gBAC1D,mDAAmD;gBACnD,KAAK,CAAC,cAAc,GAAG,EAAE,QAAQ,EAAE,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;gBAEhE,yDAAyD;gBACzD,0DAA0D;gBAC1D,0DAA0D;gBAC1D,6DAA6D;gBAC7D,mCAAmC;gBACnC,IAAI,EAAE,CAAC,IAAI,KAAK,YAAY,IAAI,CAAC,KAAK,IAAI,CAAC,MAAM,EAAE,CAAC;oBAClD,MAAM,WAAW,GAAI,EAAE,CAAC,IAAyB,CAAC,EAAE,CAAC;oBACrD,IAAI,OAAO,WAAW,KAAK,QAAQ,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;wBAC9D,MAAM,OAAO,GAAG,KAAK,CAAC,qBAA0C,CAAC;wBACjE,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;4BACnC,KAAK,CAAC,qBAAqB,GAAG,CAAC,GAAG,OAAO,EAAE,WAAW,CAAC,CAAC;wBAC1D,CAAC;oBACH,CAAC;gBACH,CAAC;YACH,CAAC;YACD,KAAK,CAAC,OAAO,GAAG,UAAU,CAAC;YAE3B,SAAS,CAAC,KAAK,EAAE,oCAAoC,EAAE;gBACrD,SAAS,EAAE,CAAC;gBACZ,SAAS,EAAE,SAAS;gBACpB,aAAa,EAAE,SAAS,CAAC,MAAM;gBAC/B,OAAO,EAAE,KAAK,CAAC,OAAO;aACvB,CAAC,CAAC;YACH,KAAK,CAAC,SAAS,GAAG,SAAS,GAAG,CAAC,CAAC;YAChC,OAAO,SAAS,CAAC,CAAC,+CAA+C;QACnE,CAAC;QACD,MAAM,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;YACvB,mEAAmE;YACnE,6DAA6D;YAC7D,wDAAwD;YACxD,iEAAiE;YACjE,kDAAkD;YAClD,MAAM,UAAU,GAAG,KAAK,CAAC,gBAA0B,CAAC;YACpD,MAAM,QAAQ,GAAG,KAAK,CAAC,cAAwB,CAAC;YAChD,MAAM,OAAO,GAAG,KAAK,CAAC,iBAA2B,CAAC;YAClD,MAAM,SAAS,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;YAC3E,MAAM,UAAU,GAAiB;gBAC/B,GAAI,KAAK,CAAC,OAAiC;gBAC3C;oBACE,IAAI,EAAE,MAAM;oBACZ,OAAO,EAAE,SAAS;oBAClB,UAAU;oBACV,QAAQ;iBACT;aACF,CAAC;YACF,KAAK,CAAC,OAAO,GAAG,UAAU,CAAC;YAE3B,SAAS,CAAC,KAAK,EAAE,gCAAgC,EAAE;gBACjD,UAAU;gBACV,MAAM,EAAE,KAAK;gBACb,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO;aACjC,CAAC,CAAC;YACH,MAAM,SAAS,GAAG,KAAK,CAAC,SAAmB,CAAC;YAC5C,SAAS,CAAC,KAAK,EAAE,oCAAoC,EAAE;gBACrD,SAAS,EAAE,CAAC;gBACZ,SAAS,EAAE,SAAS;gBACpB,aAAa,EAAE,CAAC;gBAChB,OAAO,EAAE,KAAK,CAAC,OAAO;aACvB,CAAC,CAAC;YACH,KAAK,CAAC,SAAS,GAAG,SAAS,GAAG,CAAC,CAAC;YAChC,iCAAiC;YACjC,KAAK,CAAC,gBAAgB,GAAG,EAAE,CAAC;YAC5B,KAAK,CAAC,cAAc,GAAG,EAAE,CAAC;YAC1B,KAAK,CAAC,iBAAiB,GAAG,CAAC,CAAC;QAC9B,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent type definitions — both PUBLIC types (AgentOptions, AgentInput,
|
|
3
|
+
* AgentOutput) consumed by `Agent.create({...}).run({...})` callers AND
|
|
4
|
+
* the INTERNAL `AgentState` shape used by stage functions.
|
|
5
|
+
*
|
|
6
|
+
* These were originally inline in `core/Agent.ts`; extracted here as
|
|
7
|
+
* part of the v2.11.1 decomposition. `core/Agent.ts` re-exports them
|
|
8
|
+
* for back-compat (the 28+ existing import sites continue to work).
|
|
9
|
+
*/
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/core/agent/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent validators — pure helper functions extracted from Agent.ts.
|
|
3
|
+
*
|
|
4
|
+
* These run at Agent construction time (eagerly, so misconfiguration
|
|
5
|
+
* fails fast at `.build()`) and during stage execution (safeStringify
|
|
6
|
+
* for tool-result formatting).
|
|
7
|
+
*
|
|
8
|
+
* Pure functions, no class state — extracted for readability and
|
|
9
|
+
* isolated testability. The Agent class imports + invokes these in
|
|
10
|
+
* its constructor and stage handlers.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Validate that every memory definition has a unique id. Each memory
|
|
14
|
+
* writes to its own scope key (`memoryInjection_${id}`); duplicates
|
|
15
|
+
* silently overwrite, leading to data loss that's hard to debug.
|
|
16
|
+
*
|
|
17
|
+
* Throws on collision so `Agent.build()` fails fast at construction.
|
|
18
|
+
*/
|
|
19
|
+
export function validateMemoryIdUniqueness(memories) {
|
|
20
|
+
const seen = new Set();
|
|
21
|
+
for (const m of memories) {
|
|
22
|
+
if (seen.has(m.id)) {
|
|
23
|
+
throw new Error(`Agent: duplicate memory id '${m.id}'. Each memory needs a unique id to keep ` +
|
|
24
|
+
'its scope key (`memoryInjection_${id}`) collision-free.');
|
|
25
|
+
}
|
|
26
|
+
seen.add(m.id);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Clamp `maxIterations` to a safe range. The lower bound (1) prevents
|
|
31
|
+
* a 0-iteration agent (no LLM calls = no work); the upper bound (50)
|
|
32
|
+
* prevents runaway loops in misconfigured agents.
|
|
33
|
+
*/
|
|
34
|
+
export function clampIterations(n) {
|
|
35
|
+
if (!Number.isInteger(n) || n < 1)
|
|
36
|
+
return 1;
|
|
37
|
+
if (n > 50)
|
|
38
|
+
return 50;
|
|
39
|
+
return n;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Validate tool-name uniqueness across `.tool()`-registered tools +
|
|
43
|
+
* every Skill's `inject.tools[]`. The LLM dispatches by `tool.schema.name`
|
|
44
|
+
* (the wire format), so any collision silently shadows execution.
|
|
45
|
+
*
|
|
46
|
+
* Called eagerly in the Agent constructor so `Agent.build()` throws
|
|
47
|
+
* immediately, not on first `run()`.
|
|
48
|
+
*
|
|
49
|
+
* `read_skill` is reserved when ≥1 Skill is registered — collisions
|
|
50
|
+
* with consumer tools throw.
|
|
51
|
+
*/
|
|
52
|
+
export function validateToolNameUniqueness(registry, injections) {
|
|
53
|
+
// Static registry: unique within itself. The Agent.tool() builder
|
|
54
|
+
// method already throws on per-call duplicates; this is the
|
|
55
|
+
// belt-and-suspenders check at build time.
|
|
56
|
+
const staticNames = new Set();
|
|
57
|
+
for (const entry of registry) {
|
|
58
|
+
if (staticNames.has(entry.name)) {
|
|
59
|
+
throw new Error(`Agent: duplicate tool name '${entry.name}' in .tool() registry. ` +
|
|
60
|
+
`Tool names must be unique within the static registry.`);
|
|
61
|
+
}
|
|
62
|
+
staticNames.add(entry.name);
|
|
63
|
+
}
|
|
64
|
+
// `read_skill` is reserved when any Skill is registered. Collisions
|
|
65
|
+
// with consumer-supplied tools break the auto-attach path.
|
|
66
|
+
const skills = injections.filter((i) => i.flavor === 'skill');
|
|
67
|
+
if (skills.length > 0 && staticNames.has('read_skill')) {
|
|
68
|
+
throw new Error(`Agent: tool name 'read_skill' is reserved when ≥1 Skill is registered. ` +
|
|
69
|
+
`Rename your custom 'read_skill' tool or unregister it.`);
|
|
70
|
+
}
|
|
71
|
+
// Per-skill check: a skill's `inject.tools` array must be internally
|
|
72
|
+
// unique (no duplicate names within the same skill — that's a
|
|
73
|
+
// skill authoring bug). Across skills, sharing a Tool reference is
|
|
74
|
+
// EXPECTED and supported — common tools (e.g., a `flogi_lookup`
|
|
75
|
+
// used by multiple investigation skills) appear in multiple skills'
|
|
76
|
+
// tool arrays. Only one skill is active at a time (or, when several
|
|
77
|
+
// are active, deduped by name + reference at runtime). Sharing the
|
|
78
|
+
// same Tool object across skills is the supported pattern; sharing
|
|
79
|
+
// a Tool NAME with a DIFFERENT execute function is the actual bug —
|
|
80
|
+
// we detect that here too.
|
|
81
|
+
const seenByName = new Map();
|
|
82
|
+
for (const skill of skills) {
|
|
83
|
+
const intraSkill = new Set();
|
|
84
|
+
for (const tool of skill.inject.tools ?? []) {
|
|
85
|
+
const name = tool.schema.name;
|
|
86
|
+
if (intraSkill.has(name)) {
|
|
87
|
+
throw new Error(`Agent: skill '${skill.id}' lists tool '${name}' more than once in its ` +
|
|
88
|
+
`inject.tools array. Each skill's tools must be unique within itself.`);
|
|
89
|
+
}
|
|
90
|
+
intraSkill.add(name);
|
|
91
|
+
// Skill tools collide with the static .tool() registry → ambiguous dispatch
|
|
92
|
+
if (staticNames.has(name)) {
|
|
93
|
+
throw new Error(`Agent: skill '${skill.id}' tool '${name}' collides with the static .tool() ` +
|
|
94
|
+
`registry. Either rename the skill's tool or remove the static registration.`);
|
|
95
|
+
}
|
|
96
|
+
// Same name across skills with DIFFERENT Tool objects = ambiguous when
|
|
97
|
+
// both skills active. Same name + SAME Tool reference = supported sharing.
|
|
98
|
+
const prior = seenByName.get(name);
|
|
99
|
+
if (prior && prior !== tool) {
|
|
100
|
+
throw new Error(`Agent: tool name '${name}' is declared by multiple skills with different ` +
|
|
101
|
+
`Tool implementations. Skills MAY share the SAME Tool reference across ` +
|
|
102
|
+
`their inject.tools arrays (deduped at dispatch); they may NOT register ` +
|
|
103
|
+
`different functions under the same name (ambiguous dispatch).`);
|
|
104
|
+
}
|
|
105
|
+
seenByName.set(name, tool);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* JSON.stringify with circular-ref protection. Tool results are untrusted —
|
|
111
|
+
* a hostile/buggy tool returning a cyclic object must not crash the run.
|
|
112
|
+
* Falls back to '[unstringifiable: <reason>]' so the LLM still sees that
|
|
113
|
+
* the tool ran and produced something unusable.
|
|
114
|
+
*/
|
|
115
|
+
export function safeStringify(value) {
|
|
116
|
+
try {
|
|
117
|
+
return JSON.stringify(value);
|
|
118
|
+
}
|
|
119
|
+
catch (err) {
|
|
120
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
121
|
+
return `[unstringifiable: ${reason}]`;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=validators.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validators.js","sourceRoot":"","sources":["../../../../src/core/agent/validators.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CircuitBreaker — pure state-machine functions for the Nygard breaker
|
|
3
|
+
* pattern.
|
|
4
|
+
*
|
|
5
|
+
* Refactored from a class-with-instance-state to PURE FUNCTIONS that
|
|
6
|
+
* take a state record and return a new one. Reasons:
|
|
7
|
+
*
|
|
8
|
+
* 1. **No hidden runtime state.** Breaker state lives in scope where
|
|
9
|
+
* it's visible to commitLog, narrative, and rules — the footprintjs
|
|
10
|
+
* "everything in scope" principle. The closure used to be the
|
|
11
|
+
* source of truth and scope held only a projection; now scope IS
|
|
12
|
+
* the source of truth.
|
|
13
|
+
*
|
|
14
|
+
* 2. **Round-trippable across gate invocations.** Because state is a
|
|
15
|
+
* plain record, gate's outputMapper writes it back to agent scope;
|
|
16
|
+
* agent scope persists across the ReAct loop's many LLM-call gate
|
|
17
|
+
* invocations; gate's inputMapper reads it back in for the next
|
|
18
|
+
* call. Per-process persistence comes from the agent scope, not
|
|
19
|
+
* from a closure that hides between runs.
|
|
20
|
+
*
|
|
21
|
+
* 3. **Distributable later.** A future v2.12 `BreakerStateStore`
|
|
22
|
+
* adapter (Redis/DynamoDB) just needs to serialize/deserialize the
|
|
23
|
+
* state record. No class instances to reconstruct.
|
|
24
|
+
*
|
|
25
|
+
* 4. **Testable in isolation.** Pure functions; no instance setup.
|
|
26
|
+
*
|
|
27
|
+
* Pattern: Nygard *Release It!* — three states (CLOSED → OPEN →
|
|
28
|
+
* HALF-OPEN) with cooldown and probe-success thresholds.
|
|
29
|
+
*/
|
|
30
|
+
/**
|
|
31
|
+
* Thrown by `assertAdmit()` when the breaker is OPEN and the cooldown
|
|
32
|
+
* window has not elapsed. The reliability gate stage catches this,
|
|
33
|
+
* classifies via `classifyError` → `'circuit-open'`, and lets the
|
|
34
|
+
* post-decide rules route on it.
|
|
35
|
+
*/
|
|
36
|
+
export class CircuitOpenError extends Error {
|
|
37
|
+
code = 'ERR_CIRCUIT_OPEN';
|
|
38
|
+
cause;
|
|
39
|
+
retryAfter;
|
|
40
|
+
constructor(providerName, lastErrorMessage, retryAfter) {
|
|
41
|
+
super(`[${providerName}] circuit breaker is OPEN — failing fast (next probe at ${new Date(retryAfter).toISOString()}). Underlying error: ${lastErrorMessage ?? 'unknown'}`);
|
|
42
|
+
this.name = 'CircuitOpenError';
|
|
43
|
+
this.cause = lastErrorMessage;
|
|
44
|
+
this.retryAfter = retryAfter;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
// ─── Defaults ────────────────────────────────────────────────────────
|
|
48
|
+
const DEFAULT_FAILURE_THRESHOLD = 5;
|
|
49
|
+
const DEFAULT_COOLDOWN_MS = 30_000;
|
|
50
|
+
const DEFAULT_HALF_OPEN_SUCCESS_THRESHOLD = 2;
|
|
51
|
+
function defaultShouldCount(error) {
|
|
52
|
+
// User cancellations don't indicate vendor health
|
|
53
|
+
const e = error;
|
|
54
|
+
if (e?.name === 'AbortError')
|
|
55
|
+
return false;
|
|
56
|
+
if (e?.code === 'ABORT_ERR')
|
|
57
|
+
return false;
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
// ─── Constructors ────────────────────────────────────────────────────
|
|
61
|
+
/** Initial state for a freshly-CLOSED breaker. */
|
|
62
|
+
export function initialBreakerState() {
|
|
63
|
+
return {
|
|
64
|
+
state: 'closed',
|
|
65
|
+
consecutiveFailures: 0,
|
|
66
|
+
consecutiveSuccesses: 0,
|
|
67
|
+
openedAt: 0,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
// ─── Pure transitions ────────────────────────────────────────────────
|
|
71
|
+
/**
|
|
72
|
+
* Decide whether to admit a call. Returns the (possibly-updated) state
|
|
73
|
+
* AND whether to admit. If OPEN and cooldown elapsed, transitions to
|
|
74
|
+
* HALF-OPEN and admits. Pure: caller must use the returned state.
|
|
75
|
+
*
|
|
76
|
+
* Usage in the gate stage:
|
|
77
|
+
* ```ts
|
|
78
|
+
* const { admitted, nextState } = admitCall(scope.breakerStates[name], config);
|
|
79
|
+
* scope.breakerStates[name] = nextState;
|
|
80
|
+
* if (!admitted) throw new CircuitOpenError(name, nextState.lastErrorMessage, ...);
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
83
|
+
export function admitCall(state, config) {
|
|
84
|
+
const cooldownMs = config?.cooldownMs ?? DEFAULT_COOLDOWN_MS;
|
|
85
|
+
if (state.state === 'closed' || state.state === 'half-open') {
|
|
86
|
+
return { admitted: true, nextState: state };
|
|
87
|
+
}
|
|
88
|
+
// OPEN — check cooldown
|
|
89
|
+
if (Date.now() - state.openedAt >= cooldownMs) {
|
|
90
|
+
return {
|
|
91
|
+
admitted: true,
|
|
92
|
+
nextState: { ...state, state: 'half-open', consecutiveSuccesses: 0 },
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
return { admitted: false, nextState: state };
|
|
96
|
+
}
|
|
97
|
+
/** Record a successful call. Returns the (possibly-updated) state. */
|
|
98
|
+
export function recordSuccess(state, config) {
|
|
99
|
+
const halfOpenSuccessThreshold = config?.halfOpenSuccessThreshold ?? DEFAULT_HALF_OPEN_SUCCESS_THRESHOLD;
|
|
100
|
+
if (state.state === 'half-open') {
|
|
101
|
+
const consecutiveSuccesses = state.consecutiveSuccesses + 1;
|
|
102
|
+
if (consecutiveSuccesses >= halfOpenSuccessThreshold) {
|
|
103
|
+
// Probe successes met threshold → fully CLOSE
|
|
104
|
+
return {
|
|
105
|
+
state: 'closed',
|
|
106
|
+
consecutiveFailures: 0,
|
|
107
|
+
consecutiveSuccesses: 0,
|
|
108
|
+
openedAt: 0,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
return { ...state, consecutiveSuccesses };
|
|
112
|
+
}
|
|
113
|
+
if (state.state === 'closed') {
|
|
114
|
+
// Reset failure counter on success
|
|
115
|
+
return state.consecutiveFailures === 0 ? state : { ...state, consecutiveFailures: 0 };
|
|
116
|
+
}
|
|
117
|
+
return state;
|
|
118
|
+
}
|
|
119
|
+
/** Record a failed call. Returns the (possibly-updated) state. */
|
|
120
|
+
export function recordFailure(state, err, config) {
|
|
121
|
+
const shouldCount = config?.shouldCount ?? defaultShouldCount;
|
|
122
|
+
if (!shouldCount(err))
|
|
123
|
+
return state;
|
|
124
|
+
const failureThreshold = config?.failureThreshold ?? DEFAULT_FAILURE_THRESHOLD;
|
|
125
|
+
const lastErrorMessage = err?.message ?? String(err);
|
|
126
|
+
if (state.state === 'half-open') {
|
|
127
|
+
// Probe failed → re-OPEN
|
|
128
|
+
return {
|
|
129
|
+
state: 'open',
|
|
130
|
+
consecutiveFailures: state.consecutiveFailures,
|
|
131
|
+
consecutiveSuccesses: 0,
|
|
132
|
+
openedAt: Date.now(),
|
|
133
|
+
lastErrorMessage,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
if (state.state === 'closed') {
|
|
137
|
+
const consecutiveFailures = state.consecutiveFailures + 1;
|
|
138
|
+
if (consecutiveFailures >= failureThreshold) {
|
|
139
|
+
return {
|
|
140
|
+
state: 'open',
|
|
141
|
+
consecutiveFailures,
|
|
142
|
+
consecutiveSuccesses: 0,
|
|
143
|
+
openedAt: Date.now(),
|
|
144
|
+
lastErrorMessage,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
return { ...state, consecutiveFailures, lastErrorMessage };
|
|
148
|
+
}
|
|
149
|
+
// OPEN: leave state unchanged (admitCall handles cooldown)
|
|
150
|
+
return state;
|
|
151
|
+
}
|
|
152
|
+
/** Compute the next probe time given a state + config. */
|
|
153
|
+
export function nextProbeTime(state, config) {
|
|
154
|
+
return state.openedAt + (config?.cooldownMs ?? DEFAULT_COOLDOWN_MS);
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=CircuitBreaker.js.map
|