@x-code-cli/core 0.1.7 → 0.1.9
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/dist/agent/file-ingest.d.ts +62 -0
- package/dist/agent/file-ingest.d.ts.map +1 -0
- package/dist/agent/file-ingest.js +390 -0
- package/dist/agent/file-ingest.js.map +1 -0
- package/dist/agent/light-compact.d.ts +13 -0
- package/dist/agent/light-compact.d.ts.map +1 -0
- package/dist/agent/light-compact.js +106 -0
- package/dist/agent/light-compact.js.map +1 -0
- package/dist/agent/loop-guard.d.ts +50 -0
- package/dist/agent/loop-guard.d.ts.map +1 -0
- package/dist/agent/loop-guard.js +107 -0
- package/dist/agent/loop-guard.js.map +1 -0
- package/dist/agent/loop-state.d.ts +11 -0
- package/dist/agent/loop-state.d.ts.map +1 -1
- package/dist/agent/loop-state.js +3 -1
- package/dist/agent/loop-state.js.map +1 -1
- package/dist/agent/loop.d.ts +2 -2
- package/dist/agent/loop.d.ts.map +1 -1
- package/dist/agent/loop.js +73 -8
- package/dist/agent/loop.js.map +1 -1
- package/dist/agent/messages.d.ts +5 -2
- package/dist/agent/messages.d.ts.map +1 -1
- package/dist/agent/messages.js.map +1 -1
- package/dist/agent/provider-compat.d.ts +7 -0
- package/dist/agent/provider-compat.d.ts.map +1 -1
- package/dist/agent/provider-compat.js +122 -0
- package/dist/agent/provider-compat.js.map +1 -1
- package/dist/agent/stream-utils.d.ts +7 -0
- package/dist/agent/stream-utils.d.ts.map +1 -1
- package/dist/agent/stream-utils.js.map +1 -1
- package/dist/agent/system-prompt.js +3 -3
- package/dist/agent/system-prompt.js.map +1 -1
- package/dist/agent/tool-execution.d.ts.map +1 -1
- package/dist/agent/tool-execution.js +68 -26
- package/dist/agent/tool-execution.js.map +1 -1
- package/dist/agent/tool-result-sanitize.d.ts +8 -0
- package/dist/agent/tool-result-sanitize.d.ts.map +1 -0
- package/dist/agent/tool-result-sanitize.js +77 -0
- package/dist/agent/tool-result-sanitize.js.map +1 -0
- package/dist/agent/vision-fallback.d.ts +22 -0
- package/dist/agent/vision-fallback.d.ts.map +1 -0
- package/dist/agent/vision-fallback.js +127 -0
- package/dist/agent/vision-fallback.js.map +1 -0
- package/dist/index.d.ts +10 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -1
- package/dist/index.js.map +1 -1
- package/dist/knowledge/session-usage.d.ts +24 -0
- package/dist/knowledge/session-usage.d.ts.map +1 -0
- package/dist/knowledge/session-usage.js +86 -0
- package/dist/knowledge/session-usage.js.map +1 -0
- package/dist/providers/cache-control.d.ts +29 -0
- package/dist/providers/cache-control.d.ts.map +1 -0
- package/dist/providers/cache-control.js +93 -0
- package/dist/providers/cache-control.js.map +1 -0
- package/dist/providers/capabilities.d.ts +15 -0
- package/dist/providers/capabilities.d.ts.map +1 -0
- package/dist/providers/capabilities.js +38 -0
- package/dist/providers/capabilities.js.map +1 -0
- package/dist/tools/index.d.ts +31 -5
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +1 -10
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/read-file.d.ts +29 -1
- package/dist/tools/read-file.d.ts.map +1 -1
- package/dist/tools/read-file.js +103 -10
- package/dist/tools/read-file.js.map +1 -1
- package/dist/tools/shell-provider.d.ts +13 -0
- package/dist/tools/shell-provider.d.ts.map +1 -0
- package/dist/tools/shell-provider.js +74 -0
- package/dist/tools/shell-provider.js.map +1 -0
- package/dist/tools/shell-utils.d.ts +1 -7
- package/dist/tools/shell-utils.d.ts.map +1 -1
- package/dist/tools/shell-utils.js +0 -17
- package/dist/tools/shell-utils.js.map +1 -1
- package/dist/tools/truncate.d.ts +36 -0
- package/dist/tools/truncate.d.ts.map +1 -0
- package/dist/tools/truncate.js +118 -0
- package/dist/tools/truncate.js.map +1 -0
- package/dist/tools/web-search.js +2 -2
- package/dist/tools/web-search.js.map +1 -1
- package/dist/types/index.d.ts +8 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js.map +1 -1
- package/dist/utils/shell-error.d.ts +12 -0
- package/dist/utils/shell-error.d.ts.map +1 -0
- package/dist/utils/shell-error.js +73 -0
- package/dist/utils/shell-error.js.map +1 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +104 -10
- package/dist/utils.js.map +1 -1
- package/package.json +21 -12
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"system-prompt.js","sourceRoot":"","sources":["../../src/agent/system-prompt.ts"],"names":[],"mappings":"AAAA,8CAA8C;AAC9C,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"system-prompt.js","sourceRoot":"","sources":["../../src/agent/system-prompt.ts"],"names":[],"mappings":"AAAA,8CAA8C;AAC9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAA;AAE7D,MAAM,kBAAkB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2BAuFA,CAAA;AAE3B,sFAAsF;AACtF,MAAM,UAAU,iBAAiB,CAAC,OAIjC;IACC,MAAM,aAAa,GAAG,gBAAgB,EAAE,CAAA;IAExC,IAAI,MAAM,GAAG,kBAAkB,CAAC,OAAO,CAAC,eAAe,EAAE,OAAO,CAAC,QAAQ,CAAC;SACvE,OAAO,CAAC,YAAY,EAAE,aAAa,CAAC,IAAI,CAAC;SACzC,OAAO,CAAC,UAAU,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC;SAClC,OAAO,CAAC,YAAY,EAAE,OAAO,EAAE,OAAO,IAAI,SAAS,CAAC;SACpD,OAAO,CAAC,gBAAgB,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAA;IAE/D,IAAI,OAAO,EAAE,gBAAgB,EAAE,CAAC;QAC9B,MAAM,IAAI,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAA;IAC7C,CAAC;IAED,OAAO,MAAM,CAAA;AACf,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-execution.d.ts","sourceRoot":"","sources":["../../src/agent/tool-execution.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"tool-execution.d.ts","sourceRoot":"","sources":["../../src/agent/tool-execution.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAGrE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAsHhD,KAAK,QAAQ,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAAA;AA0HxF,oEAAoE;AACpE,wBAAsB,gBAAgB,CACpC,SAAS,EAAE,QAAQ,EAAE,EACrB,KAAK,EAAE,SAAS,EAChB,OAAO,EAAE,YAAY,EACrB,SAAS,EAAE,cAAc,GACxB,OAAO,CAAC,IAAI,CAAC,CAIf"}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
// @x-code-cli/core — Tool execution & dispatch
|
|
2
|
-
import { execa } from 'execa';
|
|
3
2
|
import fs from 'node:fs/promises';
|
|
4
3
|
import path from 'node:path';
|
|
5
4
|
import { checkPermission } from '../permissions/index.js';
|
|
6
5
|
import { truncateToolResult } from '../tools/index.js';
|
|
7
6
|
import { clearProgressReporter, reportProgress } from '../tools/progress.js';
|
|
8
|
-
import {
|
|
7
|
+
import { getShellProvider } from '../tools/shell-provider.js';
|
|
8
|
+
import { foldShellErrorNoise } from '../utils/shell-error.js';
|
|
9
|
+
import { checkForLoop, recordToolCall } from './loop-guard.js';
|
|
9
10
|
import { toolResultMessage } from './messages.js';
|
|
10
11
|
/** Count occurrences of a substring without creating intermediate arrays. */
|
|
11
12
|
function countOccurrences(content, search) {
|
|
@@ -55,29 +56,7 @@ async function executeWriteTool(toolName, input, toolCallId) {
|
|
|
55
56
|
}
|
|
56
57
|
/** Execute a shell command with streaming. */
|
|
57
58
|
async function executeShell(command, timeout, callbacks, toolCallId) {
|
|
58
|
-
const
|
|
59
|
-
// On Windows, force the console codepage to UTF-8 (65001) at the OS level
|
|
60
|
-
// BEFORE PowerShell starts parsing the command. This ensures even parse errors
|
|
61
|
-
// (e.g. `&&` on PS 5.1) produce UTF-8 output instead of GBK garbled text.
|
|
62
|
-
// We wrap via `cmd.exe /c "chcp 65001 >nul && powershell ..."` because
|
|
63
|
-
// [Console]::OutputEncoding only takes effect after parsing completes.
|
|
64
|
-
let proc;
|
|
65
|
-
if (type === 'powershell') {
|
|
66
|
-
const escapedCommand = command.replace(/"/g, '\\"');
|
|
67
|
-
const psCmd = `chcp 65001 >nul && ${executable} ${args.join(' ')} "${escapedCommand}"`;
|
|
68
|
-
proc = execa('cmd.exe', ['/c', psCmd], {
|
|
69
|
-
timeout,
|
|
70
|
-
reject: false,
|
|
71
|
-
env: { ...process.env, PYTHONIOENCODING: 'utf-8' },
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
else {
|
|
75
|
-
proc = execa(executable, [...args, command], {
|
|
76
|
-
timeout,
|
|
77
|
-
reject: false,
|
|
78
|
-
env: { ...process.env, PYTHONIOENCODING: 'utf-8' },
|
|
79
|
-
});
|
|
80
|
-
}
|
|
59
|
+
const proc = getShellProvider().spawn(command, { timeout });
|
|
81
60
|
reportProgress(toolCallId, 'Running command...');
|
|
82
61
|
const onChunk = (chunk) => {
|
|
83
62
|
const s = chunk.toString();
|
|
@@ -95,7 +74,16 @@ async function executeShell(command, timeout, callbacks, toolCallId) {
|
|
|
95
74
|
proc.stdout?.on('data', onChunk);
|
|
96
75
|
proc.stderr?.on('data', onChunk);
|
|
97
76
|
const result = await proc;
|
|
98
|
-
|
|
77
|
+
// Fold PowerShell/cmd multi-line error blocks to a single line before they
|
|
78
|
+
// reach the model. A misquoted command on Windows emits 5–10 lines per
|
|
79
|
+
// attempt; across a loop of failed retries those stacks accumulate faster
|
|
80
|
+
// than the actual diagnostic signal. execa's stdout/stderr are typed as
|
|
81
|
+
// `string | unknown[] | Uint8Array` — we spawn with default string mode, so
|
|
82
|
+
// a cast is safe, but keep a defensive fallback for non-string just in case.
|
|
83
|
+
const toStr = (v) => (typeof v === 'string' ? v : '');
|
|
84
|
+
const stdout = foldShellErrorNoise(toStr(result.stdout));
|
|
85
|
+
const stderr = foldShellErrorNoise(toStr(result.stderr));
|
|
86
|
+
const output = [stdout, stderr].filter(Boolean).join('\n').trim();
|
|
99
87
|
if (result.exitCode !== 0) {
|
|
100
88
|
const text = output ? `${output}\nExit code ${result.exitCode}` : `Exit code ${result.exitCode}`;
|
|
101
89
|
return { output: text, isError: true };
|
|
@@ -112,10 +100,18 @@ function pushToolResult(state, callbacks, toolCallId, toolName, output, isError
|
|
|
112
100
|
clearProgressReporter(toolCallId);
|
|
113
101
|
callbacks.onToolResult(toolCallId, output, isError);
|
|
114
102
|
}
|
|
103
|
+
/** Tools whose execution is driven by the AI SDK (they have an `execute` on
|
|
104
|
+
* the tool definition). By the time we see them in `processToolCalls`, the
|
|
105
|
+
* tool has already run and its result is already in `state.messages`. We
|
|
106
|
+
* can't pre-block these — only record for loop detection and annotate. */
|
|
107
|
+
const AUTO_EXECUTED_TOOLS = new Set(['readFile', 'glob', 'grep', 'listDir', 'webFetch', 'webSearch', 'saveKnowledge']);
|
|
115
108
|
/** Handle a single tool call. Returns when the call has been fully dispatched. */
|
|
116
109
|
async function handleToolCall(tc, state, options, callbacks) {
|
|
117
110
|
const { toolName, input, toolCallId } = tc;
|
|
118
111
|
// ── askUser tool ──
|
|
112
|
+
// Skip the loop guard for askUser — the model asking the user the same
|
|
113
|
+
// clarifying question twice is almost always intentional (e.g. the user
|
|
114
|
+
// answered ambiguously) and blocking it would silently break the UX.
|
|
119
115
|
if (toolName === 'askUser') {
|
|
120
116
|
const question = input.question;
|
|
121
117
|
const optionsList = input.options;
|
|
@@ -123,6 +119,52 @@ async function handleToolCall(tc, state, options, callbacks) {
|
|
|
123
119
|
pushToolResult(state, callbacks, toolCallId, toolName, `User answered: ${answer}`);
|
|
124
120
|
return;
|
|
125
121
|
}
|
|
122
|
+
// ── Doom-loop detection ──
|
|
123
|
+
// For manual tools we pre-block. For auto-executed tools the call has
|
|
124
|
+
// already run (result landed in state.messages via collectTurnResponse);
|
|
125
|
+
// we still record the hash and, on soft-block, push a supplemental notice
|
|
126
|
+
// so the next turn sees a clear stop signal. On hard-block, we additionally
|
|
127
|
+
// prompt the user before returning.
|
|
128
|
+
const isAutoExecuted = AUTO_EXECUTED_TOOLS.has(toolName);
|
|
129
|
+
const loopCheck = checkForLoop(state, toolName, input, toolCallId);
|
|
130
|
+
if (loopCheck.kind !== 'ok') {
|
|
131
|
+
recordToolCall(state, toolName, input);
|
|
132
|
+
if (isAutoExecuted) {
|
|
133
|
+
// The tool result already exists in state.messages. Append a follow-up
|
|
134
|
+
// user-role notice so the model's next step has explicit context that
|
|
135
|
+
// this path is spinning — without this nudge, some models keep trying.
|
|
136
|
+
state.messages.push({
|
|
137
|
+
role: 'user',
|
|
138
|
+
content: `[loop-guard] ${loopCheck.message}`,
|
|
139
|
+
});
|
|
140
|
+
callbacks.onToolResult(toolCallId, `[loop-guard] ${loopCheck.message}`, true);
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
// Manual tool — short-circuit by synthesising the result. The tool body
|
|
144
|
+
// never runs; no side effects, no permission prompt.
|
|
145
|
+
pushToolResult(state, callbacks, toolCallId, toolName, `[loop-guard] ${loopCheck.message}`, true);
|
|
146
|
+
}
|
|
147
|
+
if (loopCheck.kind === 'hard-block') {
|
|
148
|
+
const answer = await callbacks
|
|
149
|
+
.onAskUser(`The model keeps calling ${toolName} with identical arguments. How do you want to proceed?`, [
|
|
150
|
+
{ label: 'Pause', description: 'Pause the turn — you can type a new instruction.' },
|
|
151
|
+
{ label: 'Continue', description: 'Let the model keep trying; the loop guard stays armed.' },
|
|
152
|
+
])
|
|
153
|
+
.catch(() => 'Pause');
|
|
154
|
+
if (answer.toLowerCase().startsWith('pause')) {
|
|
155
|
+
// Clear the recent-calls window so the guard doesn't immediately
|
|
156
|
+
// re-trigger on the next turn if the model legitimately retries
|
|
157
|
+
// once with the same args under the user's guidance.
|
|
158
|
+
state.recentToolCalls = [];
|
|
159
|
+
state.messages.push({
|
|
160
|
+
role: 'user',
|
|
161
|
+
content: '[loop-guard] User paused the loop. Wait for further instructions rather than calling more tools.',
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
recordToolCall(state, toolName, input);
|
|
126
168
|
// ── Permission check for write tools and shell ──
|
|
127
169
|
if (toolName === 'writeFile' || toolName === 'edit' || toolName === 'shell') {
|
|
128
170
|
const approved = await checkPermission({ toolCallId, toolName, input }, options.trustMode, callbacks.onAskPermission);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-execution.js","sourceRoot":"","sources":["../../src/agent/tool-execution.ts"],"names":[],"mappings":"AAAA,+CAA+C;AAC/C,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"tool-execution.js","sourceRoot":"","sources":["../../src/agent/tool-execution.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ModelMessage } from 'ai';
|
|
2
|
+
/**
|
|
3
|
+
* Walk `messages` in place and truncate any oversized tool-result parts. Only
|
|
4
|
+
* mutates the `output.value` field; the rest of the message structure is
|
|
5
|
+
* preserved exactly as the provider returned it.
|
|
6
|
+
*/
|
|
7
|
+
export declare function truncateToolResultsInMessages(messages: ModelMessage[]): void;
|
|
8
|
+
//# sourceMappingURL=tool-result-sanitize.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-result-sanitize.d.ts","sourceRoot":"","sources":["../../src/agent/tool-result-sanitize.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,IAAI,CAAA;AAgCtC;;;;GAIG;AACH,wBAAgB,6BAA6B,CAAC,QAAQ,EAAE,YAAY,EAAE,GAAG,IAAI,CAmC5E"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// @x-code-cli/core — Truncate tool-result parts inside ModelMessage arrays
|
|
2
|
+
//
|
|
3
|
+
// AI SDK auto-executed tools (readFile / grep / glob / listDir / webFetch /
|
|
4
|
+
// webSearch) return their results inside `response.messages` as tool-result
|
|
5
|
+
// parts. The manual tool path in `tool-execution.ts` runs every output
|
|
6
|
+
// through `truncateToolResult`, but auto-executed results bypass that path
|
|
7
|
+
// and land in `state.messages` at full size. This module walks the messages
|
|
8
|
+
// produced by a completed stream and applies the same per-tool truncation
|
|
9
|
+
// policy in-place before they persist into the conversation state.
|
|
10
|
+
//
|
|
11
|
+
// Policy is per-tool:
|
|
12
|
+
// - shell / edit / writeFile: manual path already truncated
|
|
13
|
+
// - readFile: head-tail (preserve file start + file end)
|
|
14
|
+
// - grep / glob / listDir: head-only (lexical order is meaningful; the tail
|
|
15
|
+
// carries no additional signal once the head is representative)
|
|
16
|
+
// - webFetch: head-tail (pages often have navigation cruft at top + bottom,
|
|
17
|
+
// but the meaningful content is usually the middle. head-tail still beats
|
|
18
|
+
// head-only because it preserves the final anchors)
|
|
19
|
+
// - default: head-tail
|
|
20
|
+
import { truncateToolResult } from '../tools/truncate.js';
|
|
21
|
+
const PER_TOOL_POLICY = {
|
|
22
|
+
readFile: { direction: 'head-tail' },
|
|
23
|
+
grep: { direction: 'head', maxLines: 500 },
|
|
24
|
+
glob: { direction: 'head', maxLines: 500 },
|
|
25
|
+
listDir: { direction: 'head', maxLines: 500 },
|
|
26
|
+
webFetch: { direction: 'head-tail' },
|
|
27
|
+
webSearch: { direction: 'head-tail' },
|
|
28
|
+
shell: { direction: 'head' },
|
|
29
|
+
};
|
|
30
|
+
function policyFor(toolName) {
|
|
31
|
+
if (!toolName)
|
|
32
|
+
return { direction: 'head-tail' };
|
|
33
|
+
return PER_TOOL_POLICY[toolName] ?? { direction: 'head-tail' };
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Walk `messages` in place and truncate any oversized tool-result parts. Only
|
|
37
|
+
* mutates the `output.value` field; the rest of the message structure is
|
|
38
|
+
* preserved exactly as the provider returned it.
|
|
39
|
+
*/
|
|
40
|
+
export function truncateToolResultsInMessages(messages) {
|
|
41
|
+
for (const msg of messages) {
|
|
42
|
+
if (msg.role !== 'tool')
|
|
43
|
+
continue;
|
|
44
|
+
if (!Array.isArray(msg.content))
|
|
45
|
+
continue;
|
|
46
|
+
for (const part of msg.content) {
|
|
47
|
+
if (part?.type !== 'tool-result')
|
|
48
|
+
continue;
|
|
49
|
+
const output = part.output;
|
|
50
|
+
if (!output)
|
|
51
|
+
continue;
|
|
52
|
+
// Text output: `{ type: 'text', value: string }`
|
|
53
|
+
if (output.type === 'text' && typeof output.value === 'string') {
|
|
54
|
+
const truncated = truncateToolResult(output.value, policyFor(part.toolName));
|
|
55
|
+
if (truncated.length !== output.value.length) {
|
|
56
|
+
output.value = truncated;
|
|
57
|
+
}
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
// Content output: `{ type: 'content', value: Array<{ type: string, text?: string, ... }> }`
|
|
61
|
+
// Only the text entries are mutable — image-data / file-data / file-url
|
|
62
|
+
// are binary payloads that the provider-compat layer handles elsewhere.
|
|
63
|
+
if (output.type === 'content' && Array.isArray(output.value)) {
|
|
64
|
+
const entries = output.value;
|
|
65
|
+
for (const entry of entries) {
|
|
66
|
+
if (entry?.type === 'text' && typeof entry.text === 'string') {
|
|
67
|
+
const truncated = truncateToolResult(entry.text, policyFor(part.toolName));
|
|
68
|
+
if (truncated.length !== entry.text.length) {
|
|
69
|
+
entry.text = truncated;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=tool-result-sanitize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-result-sanitize.js","sourceRoot":"","sources":["../../src/agent/tool-result-sanitize.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface VisionProvider {
|
|
2
|
+
/** Provider id, e.g. "google" / "zhipu". */
|
|
3
|
+
provider: string;
|
|
4
|
+
/** Full <provider>:<model> id passed to the AI SDK registry. */
|
|
5
|
+
modelId: string;
|
|
6
|
+
/** Short label for UI notices ("Gemini 2.5 Flash"). */
|
|
7
|
+
label: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Pick the best available vision sub-agent given the keys the user has
|
|
11
|
+
* configured. Returns null if no vision-capable provider has a key —
|
|
12
|
+
* caller should fall back to local OCR.
|
|
13
|
+
*/
|
|
14
|
+
export declare function pickVisionProvider(): VisionProvider | null;
|
|
15
|
+
/**
|
|
16
|
+
* Generate a textual description of an image via the chosen sub-agent.
|
|
17
|
+
* The prompt asks for both verbatim text AND visual elements (layout,
|
|
18
|
+
* colors, components) — OCR alone misses the latter, so we want the
|
|
19
|
+
* caption to subsume what OCR would have produced.
|
|
20
|
+
*/
|
|
21
|
+
export declare function captionImage(filePath: string, sub: VisionProvider): Promise<string>;
|
|
22
|
+
//# sourceMappingURL=vision-fallback.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vision-fallback.d.ts","sourceRoot":"","sources":["../../src/agent/vision-fallback.ts"],"names":[],"mappings":"AAwBA,MAAM,WAAW,cAAc;IAC7B,4CAA4C;IAC5C,QAAQ,EAAE,MAAM,CAAA;IAChB,gEAAgE;IAChE,OAAO,EAAE,MAAM,CAAA;IACf,uDAAuD;IACvD,KAAK,EAAE,MAAM,CAAA;CACd;AA0BD;;;;GAIG;AACH,wBAAgB,kBAAkB,IAAI,cAAc,GAAG,IAAI,CAS1D;AAuBD;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAyCzF"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
// @x-code-cli/core — Vision sub-agent for text-only providers
|
|
2
|
+
//
|
|
3
|
+
// When the user attaches an image but the active model can't natively see
|
|
4
|
+
// images (DeepSeek today, custom by default), automatically borrow any
|
|
5
|
+
// other configured provider that DOES have a vision model and use it as
|
|
6
|
+
// a caption sub-agent. The caption is injected as a TextPart into the
|
|
7
|
+
// user message so the main model sees a description without ever
|
|
8
|
+
// receiving the binary.
|
|
9
|
+
//
|
|
10
|
+
// Why this exists: DeepSeek users were stuck with local tesseract OCR,
|
|
11
|
+
// which is fine for code screenshots but useless for UI mockups, diagrams,
|
|
12
|
+
// or photos. Most users who set up DeepSeek also have a key for at least
|
|
13
|
+
// one free-tier provider (Gemini or GLM-4V-Flash); detecting that and
|
|
14
|
+
// reusing it removes the need for a manual /model switch every time the
|
|
15
|
+
// user pastes a screenshot.
|
|
16
|
+
import fs from 'node:fs/promises';
|
|
17
|
+
import path from 'node:path';
|
|
18
|
+
import { generateText } from 'ai';
|
|
19
|
+
import { getAvailableProviders } from '../config/index.js';
|
|
20
|
+
import { createModelRegistry } from '../providers/registry.js';
|
|
21
|
+
import { debugLog } from '../utils.js';
|
|
22
|
+
/** Vision-capable model id + display label per provider. Models picked to
|
|
23
|
+
* favor cheap / free-tier offerings — the goal is a quick caption, not
|
|
24
|
+
* deep analysis. */
|
|
25
|
+
const VISION_MODELS = {
|
|
26
|
+
google: { modelId: 'google:gemini-2.5-flash', label: 'Gemini 2.5 Flash' },
|
|
27
|
+
zhipu: { modelId: 'zhipu:glm-4v-flash', label: 'GLM-4V Flash' },
|
|
28
|
+
alibaba: { modelId: 'alibaba:qwen-vl-plus', label: 'Qwen-VL Plus' },
|
|
29
|
+
openai: { modelId: 'openai:gpt-4o-mini', label: 'GPT-4o Mini' },
|
|
30
|
+
anthropic: { modelId: 'anthropic:claude-haiku-4-5', label: 'Claude Haiku 4.5' },
|
|
31
|
+
moonshotai: {
|
|
32
|
+
modelId: 'moonshotai:moonshot-v1-32k-vision-preview',
|
|
33
|
+
label: 'Moonshot Vision Preview',
|
|
34
|
+
},
|
|
35
|
+
xai: { modelId: 'xai:grok-2-vision-1212', label: 'Grok 2 Vision' },
|
|
36
|
+
};
|
|
37
|
+
/** Order in which we try providers when picking a vision sub-agent.
|
|
38
|
+
* Free tiers and cheap-per-image models go first; heavier flagships
|
|
39
|
+
* last. Gemini 2.5 Flash leads because its free tier is the most
|
|
40
|
+
* generous (1500/day) and the model is also the strongest at the
|
|
41
|
+
* free price point. GLM-4V-Flash is second because it's truly free
|
|
42
|
+
* and reachable from China without a proxy. */
|
|
43
|
+
const VISION_PRIORITY = ['google', 'zhipu', 'alibaba', 'openai', 'anthropic', 'moonshotai', 'xai'];
|
|
44
|
+
/**
|
|
45
|
+
* Pick the best available vision sub-agent given the keys the user has
|
|
46
|
+
* configured. Returns null if no vision-capable provider has a key —
|
|
47
|
+
* caller should fall back to local OCR.
|
|
48
|
+
*/
|
|
49
|
+
export function pickVisionProvider() {
|
|
50
|
+
const available = new Set(getAvailableProviders());
|
|
51
|
+
for (const provider of VISION_PRIORITY) {
|
|
52
|
+
if (!available.has(provider))
|
|
53
|
+
continue;
|
|
54
|
+
const model = VISION_MODELS[provider];
|
|
55
|
+
if (!model)
|
|
56
|
+
continue;
|
|
57
|
+
return { provider, modelId: model.modelId, label: model.label };
|
|
58
|
+
}
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
/** Same media-type mapping as file-ingest.ts uses for ImagePart hints. */
|
|
62
|
+
function mediaTypeFor(filePath) {
|
|
63
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
64
|
+
if (ext === '.jpg' || ext === '.jpeg')
|
|
65
|
+
return 'image/jpeg';
|
|
66
|
+
if (ext === '.webp')
|
|
67
|
+
return 'image/webp';
|
|
68
|
+
if (ext === '.gif')
|
|
69
|
+
return 'image/gif';
|
|
70
|
+
if (ext === '.bmp')
|
|
71
|
+
return 'image/bmp';
|
|
72
|
+
return 'image/png';
|
|
73
|
+
}
|
|
74
|
+
/** In-memory cache so re-attaching the same image (or the same image across
|
|
75
|
+
* multiple submits in one session) doesn't re-burn tokens on the sub-agent.
|
|
76
|
+
* Keyed by `${providerId}:${file size}:${first-64-bytes-base64}` — same
|
|
77
|
+
* cheap collision-resistant key strategy provider-compat.ts uses for OCR. */
|
|
78
|
+
const captionCache = new Map();
|
|
79
|
+
async function cacheKey(filePath, providerModelId) {
|
|
80
|
+
const buffer = await fs.readFile(filePath);
|
|
81
|
+
return `${providerModelId}:${buffer.length}:${buffer.subarray(0, 64).toString('base64')}`;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Generate a textual description of an image via the chosen sub-agent.
|
|
85
|
+
* The prompt asks for both verbatim text AND visual elements (layout,
|
|
86
|
+
* colors, components) — OCR alone misses the latter, so we want the
|
|
87
|
+
* caption to subsume what OCR would have produced.
|
|
88
|
+
*/
|
|
89
|
+
export async function captionImage(filePath, sub) {
|
|
90
|
+
const key = await cacheKey(filePath, sub.modelId);
|
|
91
|
+
const cached = captionCache.get(key);
|
|
92
|
+
if (cached != null) {
|
|
93
|
+
debugLog('vision-fallback.cache-hit', `${sub.modelId} ${path.basename(filePath)}`);
|
|
94
|
+
return cached;
|
|
95
|
+
}
|
|
96
|
+
const buffer = await fs.readFile(filePath);
|
|
97
|
+
const registry = createModelRegistry();
|
|
98
|
+
// The registry's languageModel() type is `${string}:${string}` but our
|
|
99
|
+
// VISION_MODELS entries are typed as plain string. Cast at the boundary —
|
|
100
|
+
// we control both ends and every entry is of the form "provider:model".
|
|
101
|
+
const model = registry.languageModel(sub.modelId);
|
|
102
|
+
debugLog('vision-fallback.caption', `${sub.modelId} ${path.basename(filePath)} ${buffer.length}B`);
|
|
103
|
+
const { text } = await generateText({
|
|
104
|
+
model,
|
|
105
|
+
messages: [
|
|
106
|
+
{
|
|
107
|
+
role: 'user',
|
|
108
|
+
content: [
|
|
109
|
+
{
|
|
110
|
+
type: 'text',
|
|
111
|
+
text: 'Describe this image in detail so a text-only AI can act on it. ' +
|
|
112
|
+
'Include: (1) any visible text transcribed verbatim, ' +
|
|
113
|
+
'(2) UI elements, layout, and visual hierarchy, ' +
|
|
114
|
+
'(3) colors, icons, shapes, and other visual details, ' +
|
|
115
|
+
'(4) inferred purpose or context. ' +
|
|
116
|
+
'Be thorough and specific. Output plain text only — no markdown formatting.',
|
|
117
|
+
},
|
|
118
|
+
{ type: 'image', image: buffer, mediaType: mediaTypeFor(filePath) },
|
|
119
|
+
],
|
|
120
|
+
},
|
|
121
|
+
],
|
|
122
|
+
});
|
|
123
|
+
const caption = text.trim();
|
|
124
|
+
captionCache.set(key, caption);
|
|
125
|
+
return caption;
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=vision-fallback.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vision-fallback.js","sourceRoot":"","sources":["../../src/agent/vision-fallback.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -8,12 +8,21 @@ export { agentLoop, saveSession, compressMessages } from './agent/loop.js';
|
|
|
8
8
|
export type { LoopState } from './agent/loop.js';
|
|
9
9
|
export { buildSystemPrompt } from './agent/system-prompt.js';
|
|
10
10
|
export { classifyApiError } from './agent/api-errors.js';
|
|
11
|
+
export { buildUserContent, extractFileReferences, ingestFile, classifyFile } from './agent/file-ingest.js';
|
|
12
|
+
export type { FileKind, FileReference, IngestedPart } from './agent/file-ingest.js';
|
|
13
|
+
export { captionImage, pickVisionProvider } from './agent/vision-fallback.js';
|
|
14
|
+
export type { VisionProvider } from './agent/vision-fallback.js';
|
|
15
|
+
export { capabilitiesOf, providerOf } from './providers/capabilities.js';
|
|
16
|
+
export type { ProviderCapabilities } from './providers/capabilities.js';
|
|
11
17
|
export { toolRegistry, truncateToolResult } from './tools/index.js';
|
|
12
|
-
export {
|
|
18
|
+
export { getShellProvider } from './tools/shell-provider.js';
|
|
19
|
+
export type { ShellProvider, ShellType } from './tools/shell-provider.js';
|
|
13
20
|
export { checkPermission, getPermissionLevel } from './permissions/index.js';
|
|
14
21
|
export { GLOBAL_XCODE_DIR, XCODE_DIR, debugLog } from './utils.js';
|
|
15
22
|
export { buildKnowledgeContext } from './knowledge/loader.js';
|
|
16
23
|
export { getAutoMemory, initMemories } from './knowledge/auto-memory.js';
|
|
17
24
|
export { loadLatestSession, saveSessionSummary, formatSessionForPrompt } from './knowledge/session.js';
|
|
25
|
+
export { loadLatestUsageSnapshot, listSessionUsageSnapshots } from './knowledge/session-usage.js';
|
|
26
|
+
export type { SessionUsageSnapshot } from './knowledge/session-usage.js';
|
|
18
27
|
export { initProject } from './knowledge/init.js';
|
|
19
28
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,YAAY,EACV,eAAe,EACf,UAAU,EACV,cAAc,EACd,eAAe,EACf,cAAc,EACd,YAAY,EACZ,iBAAiB,EACjB,aAAa,EACb,cAAc,EACd,YAAY,EACZ,aAAa,GACd,MAAM,kBAAkB,CAAA;AAEzB,OAAO,EAAE,aAAa,EAAE,wBAAwB,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAC9G,YAAY,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAGrD,OAAO,EAAE,cAAc,EAAE,qBAAqB,EAAE,aAAa,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AACxH,YAAY,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAGnD,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAA;AAG7D,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AAC1E,YAAY,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAChD,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAA;AAC5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,YAAY,EACV,eAAe,EACf,UAAU,EACV,cAAc,EACd,eAAe,EACf,cAAc,EACd,YAAY,EACZ,iBAAiB,EACjB,aAAa,EACb,cAAc,EACd,YAAY,EACZ,aAAa,GACd,MAAM,kBAAkB,CAAA;AAEzB,OAAO,EAAE,aAAa,EAAE,wBAAwB,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAC9G,YAAY,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAGrD,OAAO,EAAE,cAAc,EAAE,qBAAqB,EAAE,aAAa,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AACxH,YAAY,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAGnD,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAA;AAG7D,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AAC1E,YAAY,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAChD,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAA;AAC5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AACxD,OAAO,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAA;AAC1G,YAAY,EAAE,QAAQ,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAA;AACnF,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAA;AAC7E,YAAY,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAA;AAGhE,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAA;AACxE,YAAY,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAA;AAGvE,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AACnE,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAA;AAC5D,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAA;AAGzE,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAA;AAG5E,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAGlE,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAA;AAC7D,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAA;AACxE,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAA;AACtG,OAAO,EAAE,uBAAuB,EAAE,yBAAyB,EAAE,MAAM,8BAA8B,CAAA;AACjG,YAAY,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAA;AACxE,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -8,9 +8,13 @@ export { createModelRegistry } from './providers/registry.js';
|
|
|
8
8
|
export { agentLoop, saveSession, compressMessages } from './agent/loop.js';
|
|
9
9
|
export { buildSystemPrompt } from './agent/system-prompt.js';
|
|
10
10
|
export { classifyApiError } from './agent/api-errors.js';
|
|
11
|
+
export { buildUserContent, extractFileReferences, ingestFile, classifyFile } from './agent/file-ingest.js';
|
|
12
|
+
export { captionImage, pickVisionProvider } from './agent/vision-fallback.js';
|
|
13
|
+
// Provider capabilities
|
|
14
|
+
export { capabilitiesOf, providerOf } from './providers/capabilities.js';
|
|
11
15
|
// Tools
|
|
12
16
|
export { toolRegistry, truncateToolResult } from './tools/index.js';
|
|
13
|
-
export {
|
|
17
|
+
export { getShellProvider } from './tools/shell-provider.js';
|
|
14
18
|
// Permissions
|
|
15
19
|
export { checkPermission, getPermissionLevel } from './permissions/index.js';
|
|
16
20
|
// Utils
|
|
@@ -19,5 +23,6 @@ export { GLOBAL_XCODE_DIR, XCODE_DIR, debugLog } from './utils.js';
|
|
|
19
23
|
export { buildKnowledgeContext } from './knowledge/loader.js';
|
|
20
24
|
export { getAutoMemory, initMemories } from './knowledge/auto-memory.js';
|
|
21
25
|
export { loadLatestSession, saveSessionSummary, formatSessionForPrompt } from './knowledge/session.js';
|
|
26
|
+
export { loadLatestUsageSnapshot, listSessionUsageSnapshots } from './knowledge/session-usage.js';
|
|
22
27
|
export { initProject } from './knowledge/init.js';
|
|
23
28
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,wCAAwC;AAiBxC,OAAO,EAAE,aAAa,EAAE,wBAAwB,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAG9G,SAAS;AACT,OAAO,EAAE,cAAc,EAAE,qBAAqB,EAAE,aAAa,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAGxH,oBAAoB;AACpB,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAA;AAE7D,QAAQ;AACR,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AAE1E,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAA;AAC5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,wCAAwC;AAiBxC,OAAO,EAAE,aAAa,EAAE,wBAAwB,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAG9G,SAAS;AACT,OAAO,EAAE,cAAc,EAAE,qBAAqB,EAAE,aAAa,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAGxH,oBAAoB;AACpB,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAA;AAE7D,QAAQ;AACR,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AAE1E,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAA;AAC5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AACxD,OAAO,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAA;AAE1G,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAA;AAG7E,wBAAwB;AACxB,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAA;AAGxE,QAAQ;AACR,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AACnE,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAA;AAG5D,cAAc;AACd,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAA;AAE5E,QAAQ;AACR,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAElE,YAAY;AACZ,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAA;AAC7D,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAA;AACxE,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAA;AACtG,OAAO,EAAE,uBAAuB,EAAE,yBAAyB,EAAE,MAAM,8BAA8B,CAAA;AAEjG,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { LoopState } from '../agent/loop-state.js';
|
|
2
|
+
import type { TokenUsage } from '../types/index.js';
|
|
3
|
+
export interface SessionUsageSnapshot {
|
|
4
|
+
id: string;
|
|
5
|
+
modelId: string;
|
|
6
|
+
startedAt: string;
|
|
7
|
+
updatedAt: string;
|
|
8
|
+
turnCount: number;
|
|
9
|
+
usage: TokenUsage;
|
|
10
|
+
}
|
|
11
|
+
/** Write the current usage state to disk. Fire-and-forget from the loop —
|
|
12
|
+
* callers should `void` this; failures are swallowed so a transient FS error
|
|
13
|
+
* never aborts the agent turn. */
|
|
14
|
+
export declare function persistUsageSnapshot(state: LoopState, modelId: string): Promise<void>;
|
|
15
|
+
/** Read the most recent usage snapshot for the current project (cwd). Returns
|
|
16
|
+
* null when no session has run here yet. /usage prefers in-memory state for
|
|
17
|
+
* the live session and only hits this on a fresh process. */
|
|
18
|
+
export declare function loadLatestUsageSnapshot(): Promise<SessionUsageSnapshot | null>;
|
|
19
|
+
/** Enumerate every per-session usage snapshot in this project, newest first.
|
|
20
|
+
* Used by `/usage history` — purely project-local, never traverses other
|
|
21
|
+
* cwds. Skips `latest.usage.json` since it's a duplicate pointer. Silently
|
|
22
|
+
* drops malformed JSON files instead of failing the whole listing. */
|
|
23
|
+
export declare function listSessionUsageSnapshots(): Promise<SessionUsageSnapshot[]>;
|
|
24
|
+
//# sourceMappingURL=session-usage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-usage.d.ts","sourceRoot":"","sources":["../../src/knowledge/session-usage.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAA;AACvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAGnD,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,MAAM,CAAA;IACV,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,UAAU,CAAA;CAClB;AAgBD;;mCAEmC;AACnC,wBAAsB,oBAAoB,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAmB3F;AAED;;8DAE8D;AAC9D,wBAAsB,uBAAuB,IAAI,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAOpF;AAED;;;uEAGuE;AACvE,wBAAsB,yBAAyB,IAAI,OAAO,CAAC,oBAAoB,EAAE,CAAC,CAqBjF"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// @x-code-cli/core — Per-session token usage persistence.
|
|
2
|
+
//
|
|
3
|
+
// Writes a small JSON snapshot to .x-code/sessions/{sessionId}.usage.json on
|
|
4
|
+
// every assistant turn so that /usage works after a restart and so the user
|
|
5
|
+
// can grep/diff usage across sessions in the project. Scope is intentionally
|
|
6
|
+
// per-cwd (project-local) — there's no cross-project aggregation.
|
|
7
|
+
//
|
|
8
|
+
// Kept separate from session.ts (which writes the LLM-generated SessionSummary
|
|
9
|
+
// at exit/compact) because the two write at different cadences and would
|
|
10
|
+
// clobber each other if they shared a file.
|
|
11
|
+
import fs from 'node:fs/promises';
|
|
12
|
+
import path from 'node:path';
|
|
13
|
+
import { XCODE_DIR } from '../utils.js';
|
|
14
|
+
const SESSIONS_DIR = `${XCODE_DIR}/sessions`;
|
|
15
|
+
function getSessionsDir() {
|
|
16
|
+
return path.join(process.cwd(), SESSIONS_DIR);
|
|
17
|
+
}
|
|
18
|
+
function getUsagePath(sessionId) {
|
|
19
|
+
return path.join(getSessionsDir(), `${sessionId}.usage.json`);
|
|
20
|
+
}
|
|
21
|
+
function getLatestUsagePath() {
|
|
22
|
+
return path.join(getSessionsDir(), 'latest.usage.json');
|
|
23
|
+
}
|
|
24
|
+
/** Write the current usage state to disk. Fire-and-forget from the loop —
|
|
25
|
+
* callers should `void` this; failures are swallowed so a transient FS error
|
|
26
|
+
* never aborts the agent turn. */
|
|
27
|
+
export async function persistUsageSnapshot(state, modelId) {
|
|
28
|
+
const snapshot = {
|
|
29
|
+
id: state.sessionId,
|
|
30
|
+
modelId,
|
|
31
|
+
startedAt: state.startedAt,
|
|
32
|
+
updatedAt: new Date().toISOString(),
|
|
33
|
+
turnCount: state.turnCount,
|
|
34
|
+
usage: { ...state.tokenUsage },
|
|
35
|
+
};
|
|
36
|
+
try {
|
|
37
|
+
await fs.mkdir(getSessionsDir(), { recursive: true });
|
|
38
|
+
const json = JSON.stringify(snapshot, null, 2);
|
|
39
|
+
await Promise.all([
|
|
40
|
+
fs.writeFile(getUsagePath(state.sessionId), json, 'utf-8'),
|
|
41
|
+
fs.writeFile(getLatestUsagePath(), json, 'utf-8'),
|
|
42
|
+
]);
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
// Persistence is best-effort; never block the agent loop on FS errors.
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/** Read the most recent usage snapshot for the current project (cwd). Returns
|
|
49
|
+
* null when no session has run here yet. /usage prefers in-memory state for
|
|
50
|
+
* the live session and only hits this on a fresh process. */
|
|
51
|
+
export async function loadLatestUsageSnapshot() {
|
|
52
|
+
try {
|
|
53
|
+
const raw = await fs.readFile(getLatestUsagePath(), 'utf-8');
|
|
54
|
+
return JSON.parse(raw);
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/** Enumerate every per-session usage snapshot in this project, newest first.
|
|
61
|
+
* Used by `/usage history` — purely project-local, never traverses other
|
|
62
|
+
* cwds. Skips `latest.usage.json` since it's a duplicate pointer. Silently
|
|
63
|
+
* drops malformed JSON files instead of failing the whole listing. */
|
|
64
|
+
export async function listSessionUsageSnapshots() {
|
|
65
|
+
let entries;
|
|
66
|
+
try {
|
|
67
|
+
entries = await fs.readdir(getSessionsDir());
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return [];
|
|
71
|
+
}
|
|
72
|
+
const usageFiles = entries.filter((f) => f.endsWith('.usage.json') && f !== 'latest.usage.json');
|
|
73
|
+
const snapshots = await Promise.all(usageFiles.map(async (f) => {
|
|
74
|
+
try {
|
|
75
|
+
const raw = await fs.readFile(path.join(getSessionsDir(), f), 'utf-8');
|
|
76
|
+
return JSON.parse(raw);
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
}));
|
|
82
|
+
return snapshots
|
|
83
|
+
.filter((s) => s !== null)
|
|
84
|
+
.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=session-usage.js.map
|