@provos/ironcurtain 0.1.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/LICENSE +202 -0
- package/README.md +311 -0
- package/dist/agent/index.d.ts +10 -0
- package/dist/agent/index.js +71 -0
- package/dist/agent/index.js.map +1 -0
- package/dist/agent/prompts.d.ts +5 -0
- package/dist/agent/prompts.js +26 -0
- package/dist/agent/prompts.js.map +1 -0
- package/dist/agent/tools.d.ts +13 -0
- package/dist/agent/tools.js +51 -0
- package/dist/agent/tools.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +78 -0
- package/dist/cli.js.map +1 -0
- package/dist/config/constitution.md +16 -0
- package/dist/config/generated/compiled-policy.json +236 -0
- package/dist/config/generated/test-scenarios.json +765 -0
- package/dist/config/generated/tool-annotations.json +955 -0
- package/dist/config/index.d.ts +25 -0
- package/dist/config/index.js +151 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/mcp-servers.json +22 -0
- package/dist/config/model-provider.d.ts +49 -0
- package/dist/config/model-provider.js +78 -0
- package/dist/config/model-provider.js.map +1 -0
- package/dist/config/paths.d.ts +59 -0
- package/dist/config/paths.js +96 -0
- package/dist/config/paths.js.map +1 -0
- package/dist/config/types.d.ts +89 -0
- package/dist/config/types.js +2 -0
- package/dist/config/types.js.map +1 -0
- package/dist/config/user-config.d.ts +93 -0
- package/dist/config/user-config.js +309 -0
- package/dist/config/user-config.js.map +1 -0
- package/dist/hash.d.ts +17 -0
- package/dist/hash.js +34 -0
- package/dist/hash.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +61 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +11 -0
- package/dist/logger.js +93 -0
- package/dist/logger.js.map +1 -0
- package/dist/pipeline/annotate.d.ts +9 -0
- package/dist/pipeline/annotate.js +136 -0
- package/dist/pipeline/annotate.js.map +1 -0
- package/dist/pipeline/compile.d.ts +23 -0
- package/dist/pipeline/compile.js +386 -0
- package/dist/pipeline/compile.js.map +1 -0
- package/dist/pipeline/constitution-compiler.d.ts +22 -0
- package/dist/pipeline/constitution-compiler.js +197 -0
- package/dist/pipeline/constitution-compiler.js.map +1 -0
- package/dist/pipeline/generate-with-repair.d.ts +22 -0
- package/dist/pipeline/generate-with-repair.js +64 -0
- package/dist/pipeline/generate-with-repair.js.map +1 -0
- package/dist/pipeline/handwritten-scenarios.d.ts +9 -0
- package/dist/pipeline/handwritten-scenarios.js +321 -0
- package/dist/pipeline/handwritten-scenarios.js.map +1 -0
- package/dist/pipeline/llm-logger.d.ts +42 -0
- package/dist/pipeline/llm-logger.js +78 -0
- package/dist/pipeline/llm-logger.js.map +1 -0
- package/dist/pipeline/pipeline-shared.d.ts +47 -0
- package/dist/pipeline/pipeline-shared.js +145 -0
- package/dist/pipeline/pipeline-shared.js.map +1 -0
- package/dist/pipeline/policy-verifier.d.ts +46 -0
- package/dist/pipeline/policy-verifier.js +277 -0
- package/dist/pipeline/policy-verifier.js.map +1 -0
- package/dist/pipeline/scenario-generator.d.ts +11 -0
- package/dist/pipeline/scenario-generator.js +128 -0
- package/dist/pipeline/scenario-generator.js.map +1 -0
- package/dist/pipeline/tool-annotator.d.ts +24 -0
- package/dist/pipeline/tool-annotator.js +201 -0
- package/dist/pipeline/tool-annotator.js.map +1 -0
- package/dist/pipeline/types.d.ts +122 -0
- package/dist/pipeline/types.js +10 -0
- package/dist/pipeline/types.js.map +1 -0
- package/dist/sandbox/index.d.ts +39 -0
- package/dist/sandbox/index.js +178 -0
- package/dist/sandbox/index.js.map +1 -0
- package/dist/session/agent-session.d.ts +83 -0
- package/dist/session/agent-session.js +382 -0
- package/dist/session/agent-session.js.map +1 -0
- package/dist/session/cli-transport.d.ts +61 -0
- package/dist/session/cli-transport.js +320 -0
- package/dist/session/cli-transport.js.map +1 -0
- package/dist/session/errors.d.ts +19 -0
- package/dist/session/errors.js +33 -0
- package/dist/session/errors.js.map +1 -0
- package/dist/session/index.d.ts +29 -0
- package/dist/session/index.js +104 -0
- package/dist/session/index.js.map +1 -0
- package/dist/session/message-compactor.d.ts +32 -0
- package/dist/session/message-compactor.js +81 -0
- package/dist/session/message-compactor.js.map +1 -0
- package/dist/session/prompts.d.ts +5 -0
- package/dist/session/prompts.js +62 -0
- package/dist/session/prompts.js.map +1 -0
- package/dist/session/resource-budget-tracker.d.ts +124 -0
- package/dist/session/resource-budget-tracker.js +327 -0
- package/dist/session/resource-budget-tracker.js.map +1 -0
- package/dist/session/step-loop-detector.d.ts +63 -0
- package/dist/session/step-loop-detector.js +136 -0
- package/dist/session/step-loop-detector.js.map +1 -0
- package/dist/session/transport.d.ts +24 -0
- package/dist/session/transport.js +2 -0
- package/dist/session/transport.js.map +1 -0
- package/dist/session/truncate-result.d.ts +35 -0
- package/dist/session/truncate-result.js +71 -0
- package/dist/session/truncate-result.js.map +1 -0
- package/dist/session/types.d.ts +220 -0
- package/dist/session/types.js +6 -0
- package/dist/session/types.js.map +1 -0
- package/dist/trusted-process/audit-log.d.ts +7 -0
- package/dist/trusted-process/audit-log.js +21 -0
- package/dist/trusted-process/audit-log.js.map +1 -0
- package/dist/trusted-process/call-circuit-breaker.d.ts +33 -0
- package/dist/trusted-process/call-circuit-breaker.js +61 -0
- package/dist/trusted-process/call-circuit-breaker.js.map +1 -0
- package/dist/trusted-process/escalation.d.ts +7 -0
- package/dist/trusted-process/escalation.js +38 -0
- package/dist/trusted-process/escalation.js.map +1 -0
- package/dist/trusted-process/index.d.ts +32 -0
- package/dist/trusted-process/index.js +151 -0
- package/dist/trusted-process/index.js.map +1 -0
- package/dist/trusted-process/mcp-client-manager.d.ts +25 -0
- package/dist/trusted-process/mcp-client-manager.js +90 -0
- package/dist/trusted-process/mcp-client-manager.js.map +1 -0
- package/dist/trusted-process/mcp-proxy-server.d.ts +24 -0
- package/dist/trusted-process/mcp-proxy-server.js +451 -0
- package/dist/trusted-process/mcp-proxy-server.js.map +1 -0
- package/dist/trusted-process/path-utils.d.ts +50 -0
- package/dist/trusted-process/path-utils.js +158 -0
- package/dist/trusted-process/path-utils.js.map +1 -0
- package/dist/trusted-process/policy-engine.d.ts +88 -0
- package/dist/trusted-process/policy-engine.js +523 -0
- package/dist/trusted-process/policy-engine.js.map +1 -0
- package/dist/trusted-process/policy-roots.d.ts +50 -0
- package/dist/trusted-process/policy-roots.js +67 -0
- package/dist/trusted-process/policy-roots.js.map +1 -0
- package/dist/trusted-process/policy-types.d.ts +6 -0
- package/dist/trusted-process/policy-types.js +2 -0
- package/dist/trusted-process/policy-types.js.map +1 -0
- package/dist/trusted-process/sandbox-integration.d.ts +92 -0
- package/dist/trusted-process/sandbox-integration.js +184 -0
- package/dist/trusted-process/sandbox-integration.js.map +1 -0
- package/dist/types/argument-roles.d.ts +112 -0
- package/dist/types/argument-roles.js +344 -0
- package/dist/types/argument-roles.js.map +1 -0
- package/dist/types/audit.d.ts +18 -0
- package/dist/types/audit.js +2 -0
- package/dist/types/audit.js.map +1 -0
- package/dist/types/mcp.d.ts +20 -0
- package/dist/types/mcp.js +2 -0
- package/dist/types/mcp.js.map +1 -0
- package/package.json +83 -0
- package/src/config/constitution.md +16 -0
- package/src/config/generated/compiled-policy.json +236 -0
- package/src/config/generated/test-scenarios.json +765 -0
- package/src/config/generated/tool-annotations.json +955 -0
- package/src/config/mcp-servers.json +22 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MessageCompactor -- summarizes older conversation messages to keep
|
|
3
|
+
* context within bounds during multi-turn sessions.
|
|
4
|
+
*
|
|
5
|
+
* Standalone and unit-testable. No dependency on AgentSession.
|
|
6
|
+
*/
|
|
7
|
+
import { type ModelMessage } from 'ai';
|
|
8
|
+
import type { ResolvedAutoCompactConfig, ResolvedUserConfig } from '../config/user-config.js';
|
|
9
|
+
export interface CompactionResult {
|
|
10
|
+
readonly compacted: true;
|
|
11
|
+
readonly originalMessageCount: number;
|
|
12
|
+
readonly newMessageCount: number;
|
|
13
|
+
readonly summaryPreview: string;
|
|
14
|
+
}
|
|
15
|
+
export declare class MessageCompactor {
|
|
16
|
+
private readonly config;
|
|
17
|
+
private summaryModel;
|
|
18
|
+
private lastInputTokens;
|
|
19
|
+
constructor(config: ResolvedAutoCompactConfig);
|
|
20
|
+
/** Called after each turn with the last step's input token count. */
|
|
21
|
+
recordInputTokens(tokens: number): void;
|
|
22
|
+
/** Returns true if compaction should run before the next turn. */
|
|
23
|
+
shouldCompact(): boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Compacts the message array in-place. Returns null if nothing to compact.
|
|
26
|
+
*
|
|
27
|
+
* Splits messages into toSummarize (older) and toKeep (recent).
|
|
28
|
+
* Sends toSummarize to a summarization model and replaces the array
|
|
29
|
+
* with [summaryMessage, ...toKeep].
|
|
30
|
+
*/
|
|
31
|
+
compact(messages: ModelMessage[], userConfig: ResolvedUserConfig): Promise<CompactionResult | null>;
|
|
32
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MessageCompactor -- summarizes older conversation messages to keep
|
|
3
|
+
* context within bounds during multi-turn sessions.
|
|
4
|
+
*
|
|
5
|
+
* Standalone and unit-testable. No dependency on AgentSession.
|
|
6
|
+
*/
|
|
7
|
+
import { generateText } from 'ai';
|
|
8
|
+
import { createLanguageModel } from '../config/model-provider.js';
|
|
9
|
+
const SUMMARIZER_SYSTEM_PROMPT = `You are summarizing a conversation between a user and an AI coding agent.
|
|
10
|
+
Provide a detailed but concise summary that preserves all context needed to continue the conversation naturally.
|
|
11
|
+
|
|
12
|
+
Include:
|
|
13
|
+
- What the user asked for and what has been accomplished
|
|
14
|
+
- Files created, modified, or being worked on (with paths)
|
|
15
|
+
- Current state: what's done, in progress, and planned next
|
|
16
|
+
- Key decisions, constraints, or requirements discussed
|
|
17
|
+
- Errors encountered and how they were resolved
|
|
18
|
+
|
|
19
|
+
Your summary will replace the older conversation history. Be comprehensive but concise.`;
|
|
20
|
+
/** Minimum messages in the toSummarize partition to bother compacting. */
|
|
21
|
+
const MIN_MESSAGES_TO_COMPACT = 4;
|
|
22
|
+
export class MessageCompactor {
|
|
23
|
+
config;
|
|
24
|
+
summaryModel = null;
|
|
25
|
+
lastInputTokens = 0;
|
|
26
|
+
constructor(config) {
|
|
27
|
+
this.config = config;
|
|
28
|
+
}
|
|
29
|
+
/** Called after each turn with the last step's input token count. */
|
|
30
|
+
recordInputTokens(tokens) {
|
|
31
|
+
this.lastInputTokens = tokens;
|
|
32
|
+
}
|
|
33
|
+
/** Returns true if compaction should run before the next turn. */
|
|
34
|
+
shouldCompact() {
|
|
35
|
+
if (!this.config.enabled)
|
|
36
|
+
return false;
|
|
37
|
+
return this.lastInputTokens > this.config.thresholdTokens;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Compacts the message array in-place. Returns null if nothing to compact.
|
|
41
|
+
*
|
|
42
|
+
* Splits messages into toSummarize (older) and toKeep (recent).
|
|
43
|
+
* Sends toSummarize to a summarization model and replaces the array
|
|
44
|
+
* with [summaryMessage, ...toKeep].
|
|
45
|
+
*/
|
|
46
|
+
async compact(messages, userConfig) {
|
|
47
|
+
const keepCount = Math.min(this.config.keepRecentMessages, messages.length);
|
|
48
|
+
const splitIndex = messages.length - keepCount;
|
|
49
|
+
if (splitIndex < MIN_MESSAGES_TO_COMPACT)
|
|
50
|
+
return null;
|
|
51
|
+
const toSummarize = messages.slice(0, splitIndex);
|
|
52
|
+
const toKeep = messages.slice(splitIndex);
|
|
53
|
+
if (!this.summaryModel) {
|
|
54
|
+
this.summaryModel = await createLanguageModel(this.config.summaryModelId, userConfig);
|
|
55
|
+
}
|
|
56
|
+
const result = await generateText({
|
|
57
|
+
model: this.summaryModel,
|
|
58
|
+
system: SUMMARIZER_SYSTEM_PROMPT,
|
|
59
|
+
messages: [
|
|
60
|
+
...toSummarize,
|
|
61
|
+
{ role: 'user', content: 'Summarize the conversation above.' },
|
|
62
|
+
],
|
|
63
|
+
});
|
|
64
|
+
const summary = result.text;
|
|
65
|
+
const summaryMessage = {
|
|
66
|
+
role: 'user',
|
|
67
|
+
content: `[Conversation summary]\n\n${summary}`,
|
|
68
|
+
};
|
|
69
|
+
const originalMessageCount = messages.length;
|
|
70
|
+
messages.length = 0;
|
|
71
|
+
messages.push(summaryMessage, ...toKeep);
|
|
72
|
+
this.lastInputTokens = 0;
|
|
73
|
+
return {
|
|
74
|
+
compacted: true,
|
|
75
|
+
originalMessageCount,
|
|
76
|
+
newMessageCount: messages.length,
|
|
77
|
+
summaryPreview: summary.substring(0, 200),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=message-compactor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message-compactor.js","sourceRoot":"","sources":["../../src/session/message-compactor.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,YAAY,EAAyC,MAAM,IAAI,CAAC;AACzE,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAGlE,MAAM,wBAAwB,GAAG;;;;;;;;;;wFAUuD,CAAC;AAEzF,0EAA0E;AAC1E,MAAM,uBAAuB,GAAG,CAAC,CAAC;AASlC,MAAM,OAAO,gBAAgB;IACV,MAAM,CAA4B;IAC3C,YAAY,GAAyB,IAAI,CAAC;IAC1C,eAAe,GAAG,CAAC,CAAC;IAE5B,YAAY,MAAiC;QAC3C,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED,qEAAqE;IACrE,iBAAiB,CAAC,MAAc;QAC9B,IAAI,CAAC,eAAe,GAAG,MAAM,CAAC;IAChC,CAAC;IAED,kEAAkE;IAClE,aAAa;QACX,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO;YAAE,OAAO,KAAK,CAAC;QACvC,OAAO,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC;IAC5D,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,OAAO,CACX,QAAwB,EACxB,UAA8B;QAE9B,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,kBAAkB,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;QAC5E,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,GAAG,SAAS,CAAC;QAE/C,IAAI,UAAU,GAAG,uBAAuB;YAAE,OAAO,IAAI,CAAC;QAEtD,MAAM,WAAW,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;QAClD,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QAE1C,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC;YACvB,IAAI,CAAC,YAAY,GAAG,MAAM,mBAAmB,CAC3C,IAAI,CAAC,MAAM,CAAC,cAAc,EAC1B,UAAU,CACX,CAAC;QACJ,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC;YAChC,KAAK,EAAE,IAAI,CAAC,YAAY;YACxB,MAAM,EAAE,wBAAwB;YAChC,QAAQ,EAAE;gBACR,GAAG,WAAW;gBACd,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,mCAAmC,EAAE;aAC/D;SACF,CAAC,CAAC;QAEH,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC;QAC5B,MAAM,cAAc,GAAiB;YACnC,IAAI,EAAE,MAAM;YACZ,OAAO,EAAE,6BAA6B,OAAO,EAAE;SAChD,CAAC;QAEF,MAAM,oBAAoB,GAAG,QAAQ,CAAC,MAAM,CAAC;QAC7C,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;QACpB,QAAQ,CAAC,IAAI,CAAC,cAAc,EAAE,GAAG,MAAM,CAAC,CAAC;QAEzC,IAAI,CAAC,eAAe,GAAG,CAAC,CAAC;QAEzB,OAAO;YACL,SAAS,EAAE,IAAI;YACf,oBAAoB;YACpB,eAAe,EAAE,QAAQ,CAAC,MAAM;YAChC,cAAc,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC,EAAE,GAAG,CAAC;SAC1C,CAAC;IACJ,CAAC;CACF"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Builds the system prompt for the agent. Shared between
|
|
3
|
+
* the legacy runAgent() function and the new AgentSession.
|
|
4
|
+
*/
|
|
5
|
+
export function buildSystemPrompt(toolCatalog, allowedDirectory) {
|
|
6
|
+
const sandboxInfo = allowedDirectory
|
|
7
|
+
? `\nYour sandbox directory is: ${allowedDirectory}\nAll file operations within this directory are automatically allowed.\n`
|
|
8
|
+
: '';
|
|
9
|
+
return `You are a helpful assistant. You complete tasks by writing TypeScript code that executes in a secure sandbox.
|
|
10
|
+
|
|
11
|
+
Every tool call in your code goes through a security policy engine. If a call is denied, do NOT retry it -- explain the denial to the user.
|
|
12
|
+
${sandboxInfo}
|
|
13
|
+
## Code Mode rules
|
|
14
|
+
|
|
15
|
+
- Tools are synchronous — do NOT use \`await\`.
|
|
16
|
+
- Use \`return\` to send a value back to the conversation.
|
|
17
|
+
- Example: \`const result = filesystem.filesystem_list_directory({ path: "/tmp" });\`
|
|
18
|
+
|
|
19
|
+
## Efficient code execution
|
|
20
|
+
|
|
21
|
+
Each execute_code call adds its result to the conversation history, increasing token usage for every subsequent step. Minimize the number of execute_code calls by batching work.
|
|
22
|
+
|
|
23
|
+
- When processing multiple items (files, directories, etc.), write a SINGLE code block with a loop. Do NOT make separate execute_code calls for each item.
|
|
24
|
+
- Collect a list first, then process all items in the same code block.
|
|
25
|
+
- Return a concise summary (e.g. "Moved 12 files") instead of per-item details.
|
|
26
|
+
|
|
27
|
+
BAD — 81 separate execute_code calls:
|
|
28
|
+
Step 1: get_file_info for file1.txt
|
|
29
|
+
Step 2: move_file for file1.txt
|
|
30
|
+
Step 3: get_file_info for file2.txt
|
|
31
|
+
...
|
|
32
|
+
|
|
33
|
+
GOOD — one execute_code call with a loop:
|
|
34
|
+
const dir = filesystem.filesystem_list_directory({ path: "/data" });
|
|
35
|
+
let count = 0;
|
|
36
|
+
for (const entry of dir.entries) {
|
|
37
|
+
if (entry.name.endsWith('.txt')) {
|
|
38
|
+
filesystem.filesystem_move_file({ source: \`/data/\${entry.name}\`, destination: \`/archive/\${entry.name}\` });
|
|
39
|
+
count++;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return \`Moved \${count} .txt files to /archive\`;
|
|
43
|
+
|
|
44
|
+
## Context management
|
|
45
|
+
|
|
46
|
+
Large tool results are automatically truncated. To avoid losing information:
|
|
47
|
+
|
|
48
|
+
- Before reading files, use list_directory to survey what exists. Assess which files are relevant to the task.
|
|
49
|
+
- Do NOT read all files in a directory at once. Read a few at a time, summarize findings, then continue if needed.
|
|
50
|
+
- For large files, use the head and tail parameters on read_text_file to read specific portions.
|
|
51
|
+
Example: filesystem.filesystem_read_text_file({ path: "large.log", tail: 50 })
|
|
52
|
+
- If a result contains [... truncated N bytes ...], use targeted reads to access the specific portion you need.
|
|
53
|
+
- When you need information from multiple files, read them in a single execute_code call using a loop, not in separate calls.
|
|
54
|
+
|
|
55
|
+
## Available tools
|
|
56
|
+
|
|
57
|
+
${toolCatalog}
|
|
58
|
+
|
|
59
|
+
To get the full TypeScript interface for any tool (parameter types, optional params), call \`__getToolInterface('tool.name')\` inside your code.
|
|
60
|
+
`;
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=prompts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompts.js","sourceRoot":"","sources":["../../src/session/prompts.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAC/B,WAAmB,EACnB,gBAAyB;IAEzB,MAAM,WAAW,GAAG,gBAAgB;QAClC,CAAC,CAAC,gCAAgC,gBAAgB,0EAA0E;QAC5H,CAAC,CAAC,EAAE,CAAC;IAEP,OAAO;;;EAGP,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA6CX,WAAW;;;CAGZ,CAAC;AACF,CAAC"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ResourceBudgetTracker -- enforces configurable resource limits per turn.
|
|
3
|
+
*
|
|
4
|
+
* All four budget dimensions (tokens, steps, wall-clock time, cost) are
|
|
5
|
+
* per-turn limits that reset when the agent returns control. Cumulative
|
|
6
|
+
* session totals are tracked separately for display purposes.
|
|
7
|
+
*
|
|
8
|
+
* Turn lifecycle: call startTurn() before each agent turn, endTurn() after.
|
|
9
|
+
* Idle time between turns does not count against the wall-clock budget.
|
|
10
|
+
*
|
|
11
|
+
* Provides:
|
|
12
|
+
* - isExhausted() pre-check for belt-and-suspenders enforcement
|
|
13
|
+
* - recordStep() for accumulation after each AI SDK step
|
|
14
|
+
* - createStopCondition() for AI SDK v6 stopWhen integration
|
|
15
|
+
* - getRemainingWallClockMs() for AbortSignal timeout
|
|
16
|
+
* - getActiveWarnings() for surfacing threshold warnings
|
|
17
|
+
*/
|
|
18
|
+
import type { ResolvedResourceBudgetConfig } from '../config/user-config.js';
|
|
19
|
+
import type { LanguageModelUsage, ToolSet, StopCondition } from 'ai';
|
|
20
|
+
export type BudgetDimension = 'tokens' | 'steps' | 'wall_clock' | 'cost';
|
|
21
|
+
/** Cumulative session totals across all turns. */
|
|
22
|
+
export interface CumulativeBudgetSnapshot {
|
|
23
|
+
readonly totalInputTokens: number;
|
|
24
|
+
readonly totalOutputTokens: number;
|
|
25
|
+
readonly totalTokens: number;
|
|
26
|
+
readonly stepCount: number;
|
|
27
|
+
readonly activeSeconds: number;
|
|
28
|
+
readonly estimatedCostUsd: number;
|
|
29
|
+
}
|
|
30
|
+
export interface BudgetSnapshot {
|
|
31
|
+
readonly totalInputTokens: number;
|
|
32
|
+
readonly totalOutputTokens: number;
|
|
33
|
+
readonly totalTokens: number;
|
|
34
|
+
readonly stepCount: number;
|
|
35
|
+
readonly elapsedSeconds: number;
|
|
36
|
+
readonly estimatedCostUsd: number;
|
|
37
|
+
readonly cumulative: CumulativeBudgetSnapshot;
|
|
38
|
+
}
|
|
39
|
+
export interface BudgetExhaustedVerdict {
|
|
40
|
+
readonly exhausted: true;
|
|
41
|
+
readonly dimension: BudgetDimension;
|
|
42
|
+
readonly message: string;
|
|
43
|
+
readonly snapshot: BudgetSnapshot;
|
|
44
|
+
}
|
|
45
|
+
export interface BudgetWarningVerdict {
|
|
46
|
+
readonly warning: true;
|
|
47
|
+
readonly dimension: BudgetDimension;
|
|
48
|
+
readonly message: string;
|
|
49
|
+
readonly percentUsed: number;
|
|
50
|
+
readonly snapshot: BudgetSnapshot;
|
|
51
|
+
}
|
|
52
|
+
export type BudgetVerdict = {
|
|
53
|
+
readonly ok: true;
|
|
54
|
+
} | BudgetExhaustedVerdict;
|
|
55
|
+
export declare class ResourceBudgetTracker {
|
|
56
|
+
private readonly config;
|
|
57
|
+
private readonly pricing;
|
|
58
|
+
private turnStartMs;
|
|
59
|
+
private turnInputTokens;
|
|
60
|
+
private turnOutputTokens;
|
|
61
|
+
private turnCacheReadTokens;
|
|
62
|
+
private turnStepCount;
|
|
63
|
+
private cumulativeInputTokens;
|
|
64
|
+
private cumulativeOutputTokens;
|
|
65
|
+
private cumulativeCacheReadTokens;
|
|
66
|
+
private cumulativeStepCount;
|
|
67
|
+
private cumulativeActiveMs;
|
|
68
|
+
/** Once a dimension is exhausted within a turn, the verdict is latched. */
|
|
69
|
+
private exhaustedVerdict;
|
|
70
|
+
/** Track which dimensions have already fired a warning (emit each once per turn). */
|
|
71
|
+
private warnedDimensions;
|
|
72
|
+
/** Pending warnings accumulated by recordStep(), drained by getActiveWarnings(). */
|
|
73
|
+
private pendingWarnings;
|
|
74
|
+
constructor(config: ResolvedResourceBudgetConfig, modelId: string);
|
|
75
|
+
/**
|
|
76
|
+
* Begin a new turn. Resets per-turn accumulators and starts the clock.
|
|
77
|
+
* If a turn is already active, defensively ends it first.
|
|
78
|
+
*/
|
|
79
|
+
startTurn(): void;
|
|
80
|
+
/**
|
|
81
|
+
* End the current turn. Rolls per-turn values into cumulative totals.
|
|
82
|
+
* No-op if no turn is active.
|
|
83
|
+
*/
|
|
84
|
+
endTurn(): void;
|
|
85
|
+
/**
|
|
86
|
+
* Pre-check: returns a verdict if any budget dimension is exhausted.
|
|
87
|
+
* Also checks wall-clock time (which advances even between steps).
|
|
88
|
+
*/
|
|
89
|
+
isExhausted(): BudgetExhaustedVerdict | null;
|
|
90
|
+
/**
|
|
91
|
+
* Record token usage from a completed step. Accumulates per-turn totals
|
|
92
|
+
* and evaluates all budget dimensions. Returns exhausted verdict or ok.
|
|
93
|
+
*/
|
|
94
|
+
recordStep(usage: LanguageModelUsage): BudgetVerdict;
|
|
95
|
+
/** Returns a read-only snapshot of current per-turn budget consumption. */
|
|
96
|
+
getSnapshot(): BudgetSnapshot;
|
|
97
|
+
/**
|
|
98
|
+
* Returns warnings that have been generated since the last call.
|
|
99
|
+
* Each dimension warns at most once per turn.
|
|
100
|
+
*/
|
|
101
|
+
getActiveWarnings(): BudgetWarningVerdict[];
|
|
102
|
+
/**
|
|
103
|
+
* Creates a StopCondition for AI SDK v6 `stopWhen`.
|
|
104
|
+
* The closure tracks how many steps it has already processed
|
|
105
|
+
* so it only calls recordStep() for new steps (the steps array
|
|
106
|
+
* passed to the callback is cumulative).
|
|
107
|
+
*/
|
|
108
|
+
createStopCondition(): StopCondition<ToolSet>;
|
|
109
|
+
/**
|
|
110
|
+
* Returns remaining wall-clock time in milliseconds for the current turn,
|
|
111
|
+
* or null if wall-clock budget is disabled.
|
|
112
|
+
*/
|
|
113
|
+
getRemainingWallClockMs(): number | null;
|
|
114
|
+
private resetTurnCounters;
|
|
115
|
+
private getTurnElapsedMs;
|
|
116
|
+
private getTurnElapsedSeconds;
|
|
117
|
+
private estimateCost;
|
|
118
|
+
/** Returns cumulative snapshot including the current in-progress turn. */
|
|
119
|
+
private getCumulativeSnapshot;
|
|
120
|
+
private evaluate;
|
|
121
|
+
private checkExhaustion;
|
|
122
|
+
private checkWarnings;
|
|
123
|
+
private maybeWarn;
|
|
124
|
+
}
|
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ResourceBudgetTracker -- enforces configurable resource limits per turn.
|
|
3
|
+
*
|
|
4
|
+
* All four budget dimensions (tokens, steps, wall-clock time, cost) are
|
|
5
|
+
* per-turn limits that reset when the agent returns control. Cumulative
|
|
6
|
+
* session totals are tracked separately for display purposes.
|
|
7
|
+
*
|
|
8
|
+
* Turn lifecycle: call startTurn() before each agent turn, endTurn() after.
|
|
9
|
+
* Idle time between turns does not count against the wall-clock budget.
|
|
10
|
+
*
|
|
11
|
+
* Provides:
|
|
12
|
+
* - isExhausted() pre-check for belt-and-suspenders enforcement
|
|
13
|
+
* - recordStep() for accumulation after each AI SDK step
|
|
14
|
+
* - createStopCondition() for AI SDK v6 stopWhen integration
|
|
15
|
+
* - getRemainingWallClockMs() for AbortSignal timeout
|
|
16
|
+
* - getActiveWarnings() for surfacing threshold warnings
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Static pricing table keyed by model name substring (first match wins).
|
|
20
|
+
* Approximate by design — prevents $50 surprises, not audit-grade billing.
|
|
21
|
+
*
|
|
22
|
+
* ORDERING MATTERS: more specific matches must come before broader ones
|
|
23
|
+
* (e.g. 'claude-opus-4-5' before 'claude-opus', 'gpt-4.1-nano' before 'gpt-4.1').
|
|
24
|
+
*
|
|
25
|
+
* Last updated: 2026-02-19
|
|
26
|
+
* Sources: platform.claude.com, openai.com/api/pricing, ai.google.dev/gemini-api/docs/pricing
|
|
27
|
+
*/
|
|
28
|
+
const MODEL_PRICING = [
|
|
29
|
+
// --- Anthropic ---
|
|
30
|
+
// Opus 4.5/4.6 ($5/$25/$0.50)
|
|
31
|
+
{ match: 'claude-opus-4-5', pricing: { inputPerMillion: 5, outputPerMillion: 25, cacheReadPerMillion: 0.50 } },
|
|
32
|
+
{ match: 'claude-opus-4-6', pricing: { inputPerMillion: 5, outputPerMillion: 25, cacheReadPerMillion: 0.50 } },
|
|
33
|
+
// Opus 4/4.1 ($15/$75/$1.50) — also catches unversioned 'claude-opus'
|
|
34
|
+
{ match: 'claude-opus', pricing: { inputPerMillion: 15, outputPerMillion: 75, cacheReadPerMillion: 1.50 } },
|
|
35
|
+
// Sonnet 4/4.5/4.6 (same pricing: $3/$15/$0.30)
|
|
36
|
+
{ match: 'claude-sonnet', pricing: { inputPerMillion: 3, outputPerMillion: 15, cacheReadPerMillion: 0.30 } },
|
|
37
|
+
// Haiku 4.5 ($1/$5/$0.10)
|
|
38
|
+
{ match: 'claude-haiku-4-5', pricing: { inputPerMillion: 1, outputPerMillion: 5, cacheReadPerMillion: 0.10 } },
|
|
39
|
+
// Haiku 3.5 ($0.80/$4/$0.08) — also catches unversioned 'claude-haiku'
|
|
40
|
+
{ match: 'claude-haiku', pricing: { inputPerMillion: 0.80, outputPerMillion: 4, cacheReadPerMillion: 0.08 } },
|
|
41
|
+
// --- OpenAI ---
|
|
42
|
+
// GPT-5.2 Pro ($21/$168)
|
|
43
|
+
{ match: 'gpt-5.2-pro', pricing: { inputPerMillion: 21, outputPerMillion: 168, cacheReadPerMillion: 21 } },
|
|
44
|
+
// GPT-5.2 ($1.75/$14/$0.175)
|
|
45
|
+
{ match: 'gpt-5.2', pricing: { inputPerMillion: 1.75, outputPerMillion: 14, cacheReadPerMillion: 0.175 } },
|
|
46
|
+
// GPT-5 mini ($0.25/$2/$0.025)
|
|
47
|
+
{ match: 'gpt-5-mini', pricing: { inputPerMillion: 0.25, outputPerMillion: 2, cacheReadPerMillion: 0.025 } },
|
|
48
|
+
// GPT-4.1 nano ($0.20/$0.80/$0.05)
|
|
49
|
+
{ match: 'gpt-4.1-nano', pricing: { inputPerMillion: 0.20, outputPerMillion: 0.80, cacheReadPerMillion: 0.05 } },
|
|
50
|
+
// GPT-4.1 mini ($0.80/$3.20/$0.20)
|
|
51
|
+
{ match: 'gpt-4.1-mini', pricing: { inputPerMillion: 0.80, outputPerMillion: 3.20, cacheReadPerMillion: 0.20 } },
|
|
52
|
+
// GPT-4.1 ($3/$12/$0.75)
|
|
53
|
+
{ match: 'gpt-4.1', pricing: { inputPerMillion: 3, outputPerMillion: 12, cacheReadPerMillion: 0.75 } },
|
|
54
|
+
// o4-mini ($4/$16/$1)
|
|
55
|
+
{ match: 'o4-mini', pricing: { inputPerMillion: 4, outputPerMillion: 16, cacheReadPerMillion: 1 } },
|
|
56
|
+
// GPT-4o mini ($0.15/$0.60/$0.075)
|
|
57
|
+
{ match: 'gpt-4o-mini', pricing: { inputPerMillion: 0.15, outputPerMillion: 0.60, cacheReadPerMillion: 0.075 } },
|
|
58
|
+
// GPT-4o ($2.50/$10/$1.25)
|
|
59
|
+
{ match: 'gpt-4o', pricing: { inputPerMillion: 2.50, outputPerMillion: 10, cacheReadPerMillion: 1.25 } },
|
|
60
|
+
// --- Google Gemini ---
|
|
61
|
+
// Gemini 3.1 Pro ($2/$12)
|
|
62
|
+
{ match: 'gemini-3.1-pro', pricing: { inputPerMillion: 2, outputPerMillion: 12, cacheReadPerMillion: 0.20 } },
|
|
63
|
+
// Gemini 3 Flash ($0.50/$3)
|
|
64
|
+
{ match: 'gemini-3-flash', pricing: { inputPerMillion: 0.50, outputPerMillion: 3, cacheReadPerMillion: 0.05 } },
|
|
65
|
+
// Gemini 3 Pro ($2/$12)
|
|
66
|
+
{ match: 'gemini-3-pro', pricing: { inputPerMillion: 2, outputPerMillion: 12, cacheReadPerMillion: 0.20 } },
|
|
67
|
+
// Gemini 2.5 Pro ($1.25/$10/$0.125)
|
|
68
|
+
{ match: 'gemini-2.5-pro', pricing: { inputPerMillion: 1.25, outputPerMillion: 10, cacheReadPerMillion: 0.125 } },
|
|
69
|
+
// Gemini 2.5 Flash-Lite ($0.10/$0.40)
|
|
70
|
+
{ match: 'gemini-2.5-flash-lite', pricing: { inputPerMillion: 0.10, outputPerMillion: 0.40, cacheReadPerMillion: 0.01 } },
|
|
71
|
+
// Gemini 2.5 Flash ($0.30/$2.50/$0.03)
|
|
72
|
+
{ match: 'gemini-2.5-flash', pricing: { inputPerMillion: 0.30, outputPerMillion: 2.50, cacheReadPerMillion: 0.03 } },
|
|
73
|
+
// Gemini 2.0 Flash-Lite ($0.075/$0.30)
|
|
74
|
+
{ match: 'gemini-2.0-flash-lite', pricing: { inputPerMillion: 0.075, outputPerMillion: 0.30, cacheReadPerMillion: 0.0075 } },
|
|
75
|
+
// Gemini 2.0 Flash ($0.10/$0.40/$0.01)
|
|
76
|
+
{ match: 'gemini-2.0-flash', pricing: { inputPerMillion: 0.10, outputPerMillion: 0.40, cacheReadPerMillion: 0.01 } },
|
|
77
|
+
// Gemini 1.5 Pro ($1.25/$5/$0.125)
|
|
78
|
+
{ match: 'gemini-1.5-pro', pricing: { inputPerMillion: 1.25, outputPerMillion: 5, cacheReadPerMillion: 0.125 } },
|
|
79
|
+
];
|
|
80
|
+
const FALLBACK_PRICING = { inputPerMillion: 3, outputPerMillion: 15, cacheReadPerMillion: 0.30 };
|
|
81
|
+
function resolvePricing(modelId) {
|
|
82
|
+
const lower = modelId.toLowerCase();
|
|
83
|
+
for (const entry of MODEL_PRICING) {
|
|
84
|
+
if (lower.includes(entry.match))
|
|
85
|
+
return entry.pricing;
|
|
86
|
+
}
|
|
87
|
+
return FALLBACK_PRICING;
|
|
88
|
+
}
|
|
89
|
+
export class ResourceBudgetTracker {
|
|
90
|
+
config;
|
|
91
|
+
pricing;
|
|
92
|
+
// --- Per-turn state (reset on each startTurn) ---
|
|
93
|
+
turnStartMs = null;
|
|
94
|
+
turnInputTokens = 0;
|
|
95
|
+
turnOutputTokens = 0;
|
|
96
|
+
turnCacheReadTokens = 0;
|
|
97
|
+
turnStepCount = 0;
|
|
98
|
+
// --- Cumulative session state (rolled up on each endTurn) ---
|
|
99
|
+
cumulativeInputTokens = 0;
|
|
100
|
+
cumulativeOutputTokens = 0;
|
|
101
|
+
cumulativeCacheReadTokens = 0;
|
|
102
|
+
cumulativeStepCount = 0;
|
|
103
|
+
cumulativeActiveMs = 0;
|
|
104
|
+
/** Once a dimension is exhausted within a turn, the verdict is latched. */
|
|
105
|
+
exhaustedVerdict = null;
|
|
106
|
+
/** Track which dimensions have already fired a warning (emit each once per turn). */
|
|
107
|
+
warnedDimensions = new Set();
|
|
108
|
+
/** Pending warnings accumulated by recordStep(), drained by getActiveWarnings(). */
|
|
109
|
+
pendingWarnings = [];
|
|
110
|
+
constructor(config, modelId) {
|
|
111
|
+
this.config = config;
|
|
112
|
+
this.pricing = resolvePricing(modelId);
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Begin a new turn. Resets per-turn accumulators and starts the clock.
|
|
116
|
+
* If a turn is already active, defensively ends it first.
|
|
117
|
+
*/
|
|
118
|
+
startTurn() {
|
|
119
|
+
if (this.turnStartMs !== null) {
|
|
120
|
+
this.endTurn();
|
|
121
|
+
}
|
|
122
|
+
this.resetTurnCounters();
|
|
123
|
+
this.exhaustedVerdict = null;
|
|
124
|
+
this.warnedDimensions.clear();
|
|
125
|
+
this.pendingWarnings = [];
|
|
126
|
+
this.turnStartMs = Date.now();
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* End the current turn. Rolls per-turn values into cumulative totals.
|
|
130
|
+
* No-op if no turn is active.
|
|
131
|
+
*/
|
|
132
|
+
endTurn() {
|
|
133
|
+
if (this.turnStartMs === null)
|
|
134
|
+
return;
|
|
135
|
+
this.cumulativeActiveMs += Date.now() - this.turnStartMs;
|
|
136
|
+
this.cumulativeInputTokens += this.turnInputTokens;
|
|
137
|
+
this.cumulativeOutputTokens += this.turnOutputTokens;
|
|
138
|
+
this.cumulativeCacheReadTokens += this.turnCacheReadTokens;
|
|
139
|
+
this.cumulativeStepCount += this.turnStepCount;
|
|
140
|
+
this.turnStartMs = null;
|
|
141
|
+
this.resetTurnCounters();
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Pre-check: returns a verdict if any budget dimension is exhausted.
|
|
145
|
+
* Also checks wall-clock time (which advances even between steps).
|
|
146
|
+
*/
|
|
147
|
+
isExhausted() {
|
|
148
|
+
if (this.exhaustedVerdict)
|
|
149
|
+
return this.exhaustedVerdict;
|
|
150
|
+
const exhausted = this.checkExhaustion(this.getSnapshot());
|
|
151
|
+
if (exhausted)
|
|
152
|
+
this.exhaustedVerdict = exhausted;
|
|
153
|
+
return exhausted;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Record token usage from a completed step. Accumulates per-turn totals
|
|
157
|
+
* and evaluates all budget dimensions. Returns exhausted verdict or ok.
|
|
158
|
+
*/
|
|
159
|
+
recordStep(usage) {
|
|
160
|
+
if (this.exhaustedVerdict)
|
|
161
|
+
return this.exhaustedVerdict;
|
|
162
|
+
this.turnInputTokens += usage.inputTokens ?? 0;
|
|
163
|
+
this.turnOutputTokens += usage.outputTokens ?? 0;
|
|
164
|
+
this.turnCacheReadTokens += usage.inputTokenDetails?.cacheReadTokens ?? 0;
|
|
165
|
+
this.turnStepCount++;
|
|
166
|
+
return this.evaluate();
|
|
167
|
+
}
|
|
168
|
+
/** Returns a read-only snapshot of current per-turn budget consumption. */
|
|
169
|
+
getSnapshot() {
|
|
170
|
+
return {
|
|
171
|
+
totalInputTokens: this.turnInputTokens,
|
|
172
|
+
totalOutputTokens: this.turnOutputTokens,
|
|
173
|
+
totalTokens: this.turnInputTokens + this.turnOutputTokens,
|
|
174
|
+
stepCount: this.turnStepCount,
|
|
175
|
+
elapsedSeconds: this.getTurnElapsedSeconds(),
|
|
176
|
+
estimatedCostUsd: this.estimateCost(this.turnInputTokens, this.turnOutputTokens, this.turnCacheReadTokens),
|
|
177
|
+
cumulative: this.getCumulativeSnapshot(),
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Returns warnings that have been generated since the last call.
|
|
182
|
+
* Each dimension warns at most once per turn.
|
|
183
|
+
*/
|
|
184
|
+
getActiveWarnings() {
|
|
185
|
+
return this.pendingWarnings.splice(0);
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Creates a StopCondition for AI SDK v6 `stopWhen`.
|
|
189
|
+
* The closure tracks how many steps it has already processed
|
|
190
|
+
* so it only calls recordStep() for new steps (the steps array
|
|
191
|
+
* passed to the callback is cumulative).
|
|
192
|
+
*/
|
|
193
|
+
createStopCondition() {
|
|
194
|
+
let processedCount = 0;
|
|
195
|
+
return ({ steps }) => {
|
|
196
|
+
// Process only new steps since last invocation
|
|
197
|
+
for (let i = processedCount; i < steps.length; i++) {
|
|
198
|
+
const verdict = this.recordStep(steps[i].usage);
|
|
199
|
+
if ('exhausted' in verdict)
|
|
200
|
+
return true;
|
|
201
|
+
}
|
|
202
|
+
processedCount = steps.length;
|
|
203
|
+
// Also check wall clock (advances between steps)
|
|
204
|
+
return this.isExhausted() !== null;
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Returns remaining wall-clock time in milliseconds for the current turn,
|
|
209
|
+
* or null if wall-clock budget is disabled.
|
|
210
|
+
*/
|
|
211
|
+
getRemainingWallClockMs() {
|
|
212
|
+
if (this.config.maxSessionSeconds === null)
|
|
213
|
+
return null;
|
|
214
|
+
const elapsedMs = this.getTurnElapsedMs();
|
|
215
|
+
const limitMs = this.config.maxSessionSeconds * 1000;
|
|
216
|
+
return Math.max(0, limitMs - elapsedMs);
|
|
217
|
+
}
|
|
218
|
+
// --- Private helpers ---
|
|
219
|
+
resetTurnCounters() {
|
|
220
|
+
this.turnInputTokens = 0;
|
|
221
|
+
this.turnOutputTokens = 0;
|
|
222
|
+
this.turnCacheReadTokens = 0;
|
|
223
|
+
this.turnStepCount = 0;
|
|
224
|
+
}
|
|
225
|
+
getTurnElapsedMs() {
|
|
226
|
+
if (this.turnStartMs === null)
|
|
227
|
+
return 0;
|
|
228
|
+
return Date.now() - this.turnStartMs;
|
|
229
|
+
}
|
|
230
|
+
getTurnElapsedSeconds() {
|
|
231
|
+
return this.getTurnElapsedMs() / 1000;
|
|
232
|
+
}
|
|
233
|
+
estimateCost(inputTokens, outputTokens, cacheReadTokens) {
|
|
234
|
+
const nonCachedInput = inputTokens - cacheReadTokens;
|
|
235
|
+
return ((nonCachedInput / 1_000_000) * this.pricing.inputPerMillion +
|
|
236
|
+
(cacheReadTokens / 1_000_000) * this.pricing.cacheReadPerMillion +
|
|
237
|
+
(outputTokens / 1_000_000) * this.pricing.outputPerMillion);
|
|
238
|
+
}
|
|
239
|
+
/** Returns cumulative snapshot including the current in-progress turn. */
|
|
240
|
+
getCumulativeSnapshot() {
|
|
241
|
+
const totalInput = this.cumulativeInputTokens + this.turnInputTokens;
|
|
242
|
+
const totalOutput = this.cumulativeOutputTokens + this.turnOutputTokens;
|
|
243
|
+
const totalCacheRead = this.cumulativeCacheReadTokens + this.turnCacheReadTokens;
|
|
244
|
+
const totalActiveMs = this.cumulativeActiveMs + this.getTurnElapsedMs();
|
|
245
|
+
return {
|
|
246
|
+
totalInputTokens: totalInput,
|
|
247
|
+
totalOutputTokens: totalOutput,
|
|
248
|
+
totalTokens: totalInput + totalOutput,
|
|
249
|
+
stepCount: this.cumulativeStepCount + this.turnStepCount,
|
|
250
|
+
activeSeconds: totalActiveMs / 1000,
|
|
251
|
+
estimatedCostUsd: this.estimateCost(totalInput, totalOutput, totalCacheRead),
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
evaluate() {
|
|
255
|
+
const snapshot = this.getSnapshot();
|
|
256
|
+
// Check exhaustion for each dimension
|
|
257
|
+
const exhausted = this.checkExhaustion(snapshot);
|
|
258
|
+
if (exhausted) {
|
|
259
|
+
this.exhaustedVerdict = exhausted;
|
|
260
|
+
return exhausted;
|
|
261
|
+
}
|
|
262
|
+
// Check warnings (only fires once per dimension per turn)
|
|
263
|
+
this.checkWarnings(snapshot);
|
|
264
|
+
return { ok: true };
|
|
265
|
+
}
|
|
266
|
+
checkExhaustion(snapshot) {
|
|
267
|
+
const { maxTotalTokens, maxSteps, maxSessionSeconds, maxEstimatedCostUsd } = this.config;
|
|
268
|
+
if (maxTotalTokens !== null && snapshot.totalTokens >= maxTotalTokens) {
|
|
269
|
+
return {
|
|
270
|
+
exhausted: true,
|
|
271
|
+
dimension: 'tokens',
|
|
272
|
+
message: `Token budget exhausted: ${snapshot.totalTokens.toLocaleString()} / ${maxTotalTokens.toLocaleString()} tokens used`,
|
|
273
|
+
snapshot,
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
if (maxSteps !== null && snapshot.stepCount >= maxSteps) {
|
|
277
|
+
return {
|
|
278
|
+
exhausted: true,
|
|
279
|
+
dimension: 'steps',
|
|
280
|
+
message: `Step budget exhausted: ${snapshot.stepCount} / ${maxSteps} steps used`,
|
|
281
|
+
snapshot,
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
if (maxSessionSeconds !== null && snapshot.elapsedSeconds >= maxSessionSeconds) {
|
|
285
|
+
return {
|
|
286
|
+
exhausted: true,
|
|
287
|
+
dimension: 'wall_clock',
|
|
288
|
+
message: `Time budget exhausted: ${Math.round(snapshot.elapsedSeconds)}s / ${maxSessionSeconds}s elapsed`,
|
|
289
|
+
snapshot,
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
if (maxEstimatedCostUsd !== null && snapshot.estimatedCostUsd >= maxEstimatedCostUsd) {
|
|
293
|
+
return {
|
|
294
|
+
exhausted: true,
|
|
295
|
+
dimension: 'cost',
|
|
296
|
+
message: `Cost budget exhausted: $${snapshot.estimatedCostUsd.toFixed(2)} / $${maxEstimatedCostUsd.toFixed(2)} estimated`,
|
|
297
|
+
snapshot,
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
return null;
|
|
301
|
+
}
|
|
302
|
+
checkWarnings(snapshot) {
|
|
303
|
+
this.maybeWarn('tokens', this.config.maxTotalTokens, snapshot.totalTokens, snapshot, (pct) => `Token usage at ${pct}%: ${snapshot.totalTokens.toLocaleString()} / ${this.config.maxTotalTokens.toLocaleString()}`);
|
|
304
|
+
this.maybeWarn('steps', this.config.maxSteps, snapshot.stepCount, snapshot, (pct) => `Step usage at ${pct}%: ${snapshot.stepCount} / ${this.config.maxSteps}`);
|
|
305
|
+
this.maybeWarn('wall_clock', this.config.maxSessionSeconds, snapshot.elapsedSeconds, snapshot, (pct) => `Time usage at ${pct}%: ${Math.round(snapshot.elapsedSeconds)}s / ${this.config.maxSessionSeconds}s`);
|
|
306
|
+
this.maybeWarn('cost', this.config.maxEstimatedCostUsd, snapshot.estimatedCostUsd, snapshot, (pct) => `Cost at ${pct}%: $${snapshot.estimatedCostUsd.toFixed(2)} / $${this.config.maxEstimatedCostUsd.toFixed(2)}`);
|
|
307
|
+
}
|
|
308
|
+
maybeWarn(dimension, limit, current, snapshot, messageFn) {
|
|
309
|
+
if (limit === null)
|
|
310
|
+
return;
|
|
311
|
+
if (this.warnedDimensions.has(dimension))
|
|
312
|
+
return;
|
|
313
|
+
const ratio = current / limit;
|
|
314
|
+
if (ratio >= this.config.warnThresholdPercent / 100 && ratio < 1) {
|
|
315
|
+
const percentUsed = Math.round(ratio * 100);
|
|
316
|
+
this.warnedDimensions.add(dimension);
|
|
317
|
+
this.pendingWarnings.push({
|
|
318
|
+
warning: true,
|
|
319
|
+
dimension,
|
|
320
|
+
message: messageFn(percentUsed),
|
|
321
|
+
percentUsed,
|
|
322
|
+
snapshot,
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
//# sourceMappingURL=resource-budget-tracker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resource-budget-tracker.js","sourceRoot":"","sources":["../../src/session/resource-budget-tracker.ts"],"names":[],"mappings":"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"}
|