@glbgpt/cli 0.1.0-test.5 → 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/README.md +79 -6
- package/dist/src/auth/authClient.d.ts +21 -2
- package/dist/src/auth/authClient.js +62 -2
- package/dist/src/auth/authClient.js.map +1 -1
- package/dist/src/auth/loginFlow.js +5 -2
- package/dist/src/auth/loginFlow.js.map +1 -1
- package/dist/src/auth/loopbackServer.d.ts +2 -1
- package/dist/src/auth/loopbackServer.js +6 -3
- package/dist/src/auth/loopbackServer.js.map +1 -1
- package/dist/src/commands/accountCommand.js +10 -4
- package/dist/src/commands/accountCommand.js.map +1 -1
- package/dist/src/commands/configCommand.d.ts +5 -0
- package/dist/src/commands/configCommand.js +15 -2
- package/dist/src/commands/configCommand.js.map +1 -1
- package/dist/src/commands/execCommand.d.ts +6 -0
- package/dist/src/commands/execCommand.js +61 -9
- package/dist/src/commands/execCommand.js.map +1 -1
- package/dist/src/commands/loginCommand.js +9 -6
- package/dist/src/commands/loginCommand.js.map +1 -1
- package/dist/src/commands/mediaCommand.js +3 -2
- package/dist/src/commands/mediaCommand.js.map +1 -1
- package/dist/src/commands/skillCommand.d.ts +30 -0
- package/dist/src/commands/skillCommand.js +160 -0
- package/dist/src/commands/skillCommand.js.map +1 -0
- package/dist/src/config/configFile.d.ts +12 -0
- package/dist/src/config/configFile.js +12 -0
- package/dist/src/config/configFile.js.map +1 -1
- package/dist/src/config/env.d.ts +18 -0
- package/dist/src/config/env.js +57 -7
- package/dist/src/config/env.js.map +1 -1
- package/dist/src/core/catalog.d.ts +8 -1
- package/dist/src/core/catalog.js +13 -2
- package/dist/src/core/catalog.js.map +1 -1
- package/dist/src/core/contextPrune.d.ts +13 -0
- package/dist/src/core/contextPrune.js +61 -0
- package/dist/src/core/contextPrune.js.map +1 -0
- package/dist/src/core/httpChatBackend.js +22 -5
- package/dist/src/core/httpChatBackend.js.map +1 -1
- package/dist/src/core/runChat.d.ts +9 -5
- package/dist/src/core/runChat.js +10 -7
- package/dist/src/core/runChat.js.map +1 -1
- package/dist/src/core/session.d.ts +11 -0
- package/dist/src/core/session.js +20 -1
- package/dist/src/core/session.js.map +1 -1
- package/dist/src/core/toolRegistry.d.ts +14 -3
- package/dist/src/core/toolRegistry.js +24 -15
- package/dist/src/core/toolRegistry.js.map +1 -1
- package/dist/src/core/turnLoop.d.ts +4 -0
- package/dist/src/core/turnLoop.js +54 -13
- package/dist/src/core/turnLoop.js.map +1 -1
- package/dist/src/core/types.d.ts +6 -0
- package/dist/src/core/updateCheck.d.ts +46 -0
- package/dist/src/core/updateCheck.js +139 -0
- package/dist/src/core/updateCheck.js.map +1 -0
- package/dist/src/exec/media.d.ts +32 -0
- package/dist/src/exec/media.js +70 -0
- package/dist/src/exec/media.js.map +1 -0
- package/dist/src/exec/output.d.ts +16 -0
- package/dist/src/exec/output.js +41 -0
- package/dist/src/exec/output.js.map +1 -1
- package/dist/src/fs/globTool.d.ts +8 -0
- package/dist/src/fs/globTool.js +117 -0
- package/dist/src/fs/globTool.js.map +1 -0
- package/dist/src/fs/grepTool.d.ts +4 -0
- package/dist/src/fs/grepTool.js +146 -0
- package/dist/src/fs/grepTool.js.map +1 -0
- package/dist/src/fs/index.d.ts +8 -0
- package/dist/src/fs/index.js +13 -0
- package/dist/src/fs/index.js.map +1 -0
- package/dist/src/fs/limits.d.ts +34 -0
- package/dist/src/fs/limits.js +74 -0
- package/dist/src/fs/limits.js.map +1 -0
- package/dist/src/fs/listDirTool.d.ts +2 -0
- package/dist/src/fs/listDirTool.js +68 -0
- package/dist/src/fs/listDirTool.js.map +1 -0
- package/dist/src/fs/readFileTool.d.ts +2 -0
- package/dist/src/fs/readFileTool.js +102 -0
- package/dist/src/fs/readFileTool.js.map +1 -0
- package/dist/src/fs/walk.d.ts +11 -0
- package/dist/src/fs/walk.js +38 -0
- package/dist/src/fs/walk.js.map +1 -0
- package/dist/src/fs/workspaceGuard.d.ts +27 -0
- package/dist/src/fs/workspaceGuard.js +96 -0
- package/dist/src/fs/workspaceGuard.js.map +1 -0
- package/dist/src/index.js +47 -3
- package/dist/src/index.js.map +1 -1
- package/dist/src/mcp/mediaTools.d.ts +63 -0
- package/dist/src/mcp/mediaTools.js +385 -0
- package/dist/src/mcp/mediaTools.js.map +1 -0
- package/dist/src/mcp/server.d.ts +6 -2
- package/dist/src/mcp/server.js +19 -9
- package/dist/src/mcp/server.js.map +1 -1
- package/dist/src/media/aiApiClient.d.ts +5 -0
- package/dist/src/media/aiApiClient.js +12 -1
- package/dist/src/media/aiApiClient.js.map +1 -1
- package/dist/src/media/budget.d.ts +42 -0
- package/dist/src/media/budget.js +95 -0
- package/dist/src/media/budget.js.map +1 -0
- package/dist/src/media/executor.d.ts +9 -0
- package/dist/src/media/executor.js +1 -1
- package/dist/src/media/executor.js.map +1 -1
- package/dist/src/media/generateImageTool.d.ts +8 -2
- package/dist/src/media/generateImageTool.js +51 -3
- package/dist/src/media/generateImageTool.js.map +1 -1
- package/dist/src/media/promptSanitizer.d.ts +11 -0
- package/dist/src/media/promptSanitizer.js +33 -0
- package/dist/src/media/promptSanitizer.js.map +1 -0
- package/dist/src/media/taskManager.d.ts +8 -1
- package/dist/src/media/taskManager.js +1 -1
- package/dist/src/media/taskManager.js.map +1 -1
- package/dist/src/tui/App.js +30 -21
- package/dist/src/tui/App.js.map +1 -1
- package/dist/src/tui/ParamEditor.d.ts +11 -2
- package/dist/src/tui/ParamEditor.js +17 -6
- package/dist/src/tui/ParamEditor.js.map +1 -1
- package/dist/src/tui/atPopup.d.ts +8 -0
- package/dist/src/tui/atPopup.js +1 -1
- package/dist/src/tui/atPopup.js.map +1 -1
- package/dist/src/tui/chatController.d.ts +58 -2
- package/dist/src/tui/chatController.js +231 -40
- package/dist/src/tui/chatController.js.map +1 -1
- package/dist/src/tui/models.d.ts +6 -0
- package/dist/src/tui/models.js +14 -6
- package/dist/src/tui/models.js.map +1 -1
- package/dist/src/tui/run.js +50 -16
- package/dist/src/tui/run.js.map +1 -1
- package/dist/src/util/brokenPipe.d.ts +10 -0
- package/dist/src/util/brokenPipe.js +19 -0
- package/dist/src/util/brokenPipe.js.map +1 -0
- package/package.json +3 -2
- package/skills/globalgpt/SKILL.md +94 -0
- package/skills/globalgpt/references/troubleshooting.md +34 -0
- package/skills/globalgpt-coding/SKILL.md +65 -0
- package/skills/globalgpt-coding/references/troubleshooting.md +34 -0
- package/skills/globalgpt-image/SKILL.md +109 -0
- package/skills/globalgpt-image/references/recipes.md +52 -0
- package/skills/globalgpt-image/references/troubleshooting.md +49 -0
- package/skills/globalgpt-video/SKILL.md +109 -0
- package/skills/globalgpt-video/references/recipes.md +50 -0
- package/skills/globalgpt-video/references/troubleshooting.md +49 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { FS_TOOL_NAMES } from '../fs/index.js';
|
|
2
|
+
import { PRUNE_KEEP_RECENT, PRUNE_MIN_CONTENT_BYTES, PRUNE_PLACEHOLDER } from '../fs/limits.js';
|
|
3
|
+
/**
|
|
4
|
+
* Microcompact selection and projection (spec: 2026-07-19-context-pruning-design.md).
|
|
5
|
+
*
|
|
6
|
+
* Both functions are pure. Selection runs only at trigger events (context over
|
|
7
|
+
* threshold); the returned ids join the session's persisted decision set, which
|
|
8
|
+
* only ever grows — so between triggers the projection is byte-stable and the
|
|
9
|
+
* upstream prompt cache keeps hitting.
|
|
10
|
+
*/
|
|
11
|
+
/** True for a tool result we may clear: successful fs-tool output big enough to matter. */
|
|
12
|
+
function isPrunable(m) {
|
|
13
|
+
if (m.role !== 'tool' || typeof m.content !== 'string' || !m.tool_call_id)
|
|
14
|
+
return false;
|
|
15
|
+
if (!m.name || !FS_TOOL_NAMES.has(m.name))
|
|
16
|
+
return false; // media results carry task_id anchors
|
|
17
|
+
if (Buffer.byteLength(m.content, 'utf8') <= PRUNE_MIN_CONTENT_BYTES)
|
|
18
|
+
return false;
|
|
19
|
+
try {
|
|
20
|
+
const parsed = JSON.parse(m.content);
|
|
21
|
+
if (typeof parsed !== 'object' || parsed === null)
|
|
22
|
+
return false;
|
|
23
|
+
if ('error' in parsed)
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return false; // unparseable content: leave it alone
|
|
28
|
+
}
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* The ids to clear at this trigger: every not-yet-pruned prunable fs result
|
|
33
|
+
* except the most recent PRUNE_KEEP_RECENT (the model is likely still using those).
|
|
34
|
+
*/
|
|
35
|
+
export function selectPrunable(messages, alreadyPruned) {
|
|
36
|
+
const candidates = [];
|
|
37
|
+
for (const m of messages) {
|
|
38
|
+
// Cheap O(1) set check FIRST: already-pruned messages keep their full raw
|
|
39
|
+
// content forever, and re-running isPrunable's JSON.parse over them on every
|
|
40
|
+
// trigger would grow unboundedly — the very cost this feature exists to cut.
|
|
41
|
+
if (m.role !== 'tool' || !m.tool_call_id || alreadyPruned.has(m.tool_call_id))
|
|
42
|
+
continue;
|
|
43
|
+
if (isPrunable(m))
|
|
44
|
+
candidates.push(m.tool_call_id);
|
|
45
|
+
}
|
|
46
|
+
return candidates.slice(0, Math.max(0, candidates.length - PRUNE_KEEP_RECENT));
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* The request-time projection: pruned tool results carry the placeholder instead
|
|
50
|
+
* of their content. The assistant tool_call (with its arguments) stays intact, so
|
|
51
|
+
* the model still sees WHAT was read and can re-run the tool. Returns the input
|
|
52
|
+
* array unchanged when the set is empty (zero-cost fast path).
|
|
53
|
+
*/
|
|
54
|
+
export function applyPruning(messages, prunedIds) {
|
|
55
|
+
if (prunedIds.size === 0)
|
|
56
|
+
return messages;
|
|
57
|
+
return messages.map((m) => m.role === 'tool' && m.tool_call_id && prunedIds.has(m.tool_call_id)
|
|
58
|
+
? { ...m, content: PRUNE_PLACEHOLDER }
|
|
59
|
+
: m);
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=contextPrune.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contextPrune.js","sourceRoot":"","sources":["../../../src/core/contextPrune.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAEhG;;;;;;;GAOG;AAEH,2FAA2F;AAC3F,SAAS,UAAU,CAAC,CAAc;IAChC,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,KAAK,QAAQ,IAAI,CAAC,CAAC,CAAC,YAAY;QAAE,OAAO,KAAK,CAAC;IACxF,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC,CAAC,sCAAsC;IAC/F,IAAI,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,EAAE,MAAM,CAAC,IAAI,uBAAuB;QAAE,OAAO,KAAK,CAAC;IAClF,IAAI,CAAC;QACH,MAAM,MAAM,GAAY,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;QAC9C,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI;YAAE,OAAO,KAAK,CAAC;QAChE,IAAI,OAAO,IAAI,MAAM;YAAE,OAAO,KAAK,CAAC;IACtC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC,CAAC,sCAAsC;IACtD,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAC5B,QAAuB,EACvB,aAAkC;IAElC,MAAM,UAAU,GAAa,EAAE,CAAC;IAChC,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,0EAA0E;QAC1E,6EAA6E;QAC7E,6EAA6E;QAC7E,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,CAAC,YAAY,IAAI,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,YAAY,CAAC;YAAE,SAAS;QACxF,IAAI,UAAU,CAAC,CAAC,CAAC;YAAE,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC;IACrD,CAAC;IACD,OAAO,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,UAAU,CAAC,MAAM,GAAG,iBAAiB,CAAC,CAAC,CAAC;AACjF,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAC1B,QAAuB,EACvB,SAA8B;IAE9B,IAAI,SAAS,CAAC,IAAI,KAAK,CAAC;QAAE,OAAO,QAAQ,CAAC;IAC1C,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CACxB,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,YAAY,IAAI,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,YAAY,CAAC;QAClE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,OAAO,EAAE,iBAAiB,EAAE;QACtC,CAAC,CAAC,CAAC,CACN,CAAC;AACJ,CAAC"}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { withRetry, isTransientNetworkError, describeNetworkError, hostOf } from '../util/retry.js';
|
|
2
|
+
import { describePaymentRequired } from '../media/errors.js';
|
|
3
|
+
import { PaymentRequiredError } from '../media/aiApiClient.js';
|
|
2
4
|
/** Transient gateway/infra statuses worth retrying before the stream starts. */
|
|
3
5
|
const TRANSIENT_STATUS = new Set([502, 503, 504]);
|
|
4
6
|
/**
|
|
@@ -75,7 +77,7 @@ export class HttpChatBackend {
|
|
|
75
77
|
catch {
|
|
76
78
|
/* non-JSON */
|
|
77
79
|
}
|
|
78
|
-
throw businessError(body.code, body.message || text || `status ${res.status}
|
|
80
|
+
throw businessError(body.code, body.message || text || `status ${res.status}`, body.data);
|
|
79
81
|
}
|
|
80
82
|
if (!res.body) {
|
|
81
83
|
throw new Error('Chat response has no body');
|
|
@@ -99,11 +101,24 @@ export class HttpChatBackend {
|
|
|
99
101
|
}
|
|
100
102
|
}
|
|
101
103
|
/** Map a gateway business-error payload to an actionable Error. */
|
|
102
|
-
function businessError(code, message) {
|
|
104
|
+
function businessError(code, message, data) {
|
|
103
105
|
if (code === 401)
|
|
104
106
|
return new Error('Your session has expired. Run `glbgpt login` again.');
|
|
105
|
-
if (code === 402)
|
|
106
|
-
|
|
107
|
+
if (code === 402) {
|
|
108
|
+
// Same subCode → copy mapping the media path uses, so "out of credits" reads
|
|
109
|
+
// identically whether it hit chat or generation (and never as a raw backend string
|
|
110
|
+
// when the subCode is a known one).
|
|
111
|
+
return new Error(describePaymentRequired(new PaymentRequiredError(message, data?.subCode)));
|
|
112
|
+
}
|
|
113
|
+
if (code === 429) {
|
|
114
|
+
// Server-side runaway-guard fuse. A POLICY rejection: display it, NEVER auto-retry
|
|
115
|
+
// (auto-retrying would cancel the throttle) — transient infra errors keep their own
|
|
116
|
+
// retry path, this is not one of them.
|
|
117
|
+
const wait = typeof data?.retryAfterSeconds === 'number' && data.retryAfterSeconds > 0
|
|
118
|
+
? `wait ${data.retryAfterSeconds}s`
|
|
119
|
+
: 'wait a moment';
|
|
120
|
+
return new Error(`Too many requests — ${wait} and try again.`);
|
|
121
|
+
}
|
|
107
122
|
return new Error(`Chat request failed: ${message}`);
|
|
108
123
|
}
|
|
109
124
|
/**
|
|
@@ -127,7 +142,9 @@ function parseSseLine(line) {
|
|
|
127
142
|
return null;
|
|
128
143
|
}
|
|
129
144
|
if (parsed.choices === undefined && parsed.usage === undefined && typeof parsed.code === 'number') {
|
|
130
|
-
|
|
145
|
+
// In-stream rejections carry the same {code, message, data} shape as pre-stream
|
|
146
|
+
// ones — same mapping, or a 402/429 here would leak the raw backend string.
|
|
147
|
+
throw businessError(parsed.code, parsed.message ?? `code ${parsed.code}`, parsed.data);
|
|
131
148
|
}
|
|
132
149
|
return parsed;
|
|
133
150
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"httpChatBackend.js","sourceRoot":"","sources":["../../../src/core/httpChatBackend.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,uBAAuB,EAAE,oBAAoB,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"httpChatBackend.js","sourceRoot":"","sources":["../../../src/core/httpChatBackend.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ToolRegistry } from './toolRegistry.js';
|
|
1
2
|
import type { ChatBackend, ContentPart, EngineEvent } from './types.js';
|
|
2
3
|
/** One chat-only turn's result. `structured` is set when a response schema was requested. */
|
|
3
4
|
export interface ChatTurnResult {
|
|
@@ -16,13 +17,16 @@ export interface ChatTurnParams {
|
|
|
16
17
|
system?: string;
|
|
17
18
|
/** OpenAI `response_format` value (json_schema) for structured output. */
|
|
18
19
|
responseFormat?: unknown;
|
|
20
|
+
/** Client-side tools available to this turn. Defaults to an empty registry (no tools). */
|
|
21
|
+
registry?: ToolRegistry;
|
|
19
22
|
}
|
|
20
23
|
/**
|
|
21
|
-
* Run a single chat
|
|
22
|
-
* output (when a schema was requested), and
|
|
24
|
+
* Run a single chat turn (client tools optional via `params.registry`; defaults to none)
|
|
25
|
+
* and return the final message, parsed structured output (when a schema was requested), and
|
|
26
|
+
* token usage.
|
|
23
27
|
*
|
|
24
|
-
* Shared by `exec` and the MCP server so both behave identically. `onEvent` streams deltas/usage
|
|
25
|
-
* live (exec's --jsonl uses it); callers that only want the final result omit it.
|
|
26
|
-
* backend/turn error or when structured output does not parse.
|
|
28
|
+
* Shared by `exec` and the MCP server so both behave identically. `onEvent` streams deltas/usage/
|
|
29
|
+
* tool events live (exec's --jsonl uses it); callers that only want the final result omit it.
|
|
30
|
+
* Throws on a backend/turn error or when structured output does not parse.
|
|
27
31
|
*/
|
|
28
32
|
export declare function runChatTurn(params: ChatTurnParams, onEvent?: (event: EngineEvent) => void): Promise<ChatTurnResult>;
|
package/dist/src/core/runChat.js
CHANGED
|
@@ -3,18 +3,19 @@ import { TurnLoop } from './turnLoop.js';
|
|
|
3
3
|
import { ToolRegistry } from './toolRegistry.js';
|
|
4
4
|
import { newId } from './id.js';
|
|
5
5
|
/**
|
|
6
|
-
* Run a single chat
|
|
7
|
-
* output (when a schema was requested), and
|
|
6
|
+
* Run a single chat turn (client tools optional via `params.registry`; defaults to none)
|
|
7
|
+
* and return the final message, parsed structured output (when a schema was requested), and
|
|
8
|
+
* token usage.
|
|
8
9
|
*
|
|
9
|
-
* Shared by `exec` and the MCP server so both behave identically. `onEvent` streams deltas/usage
|
|
10
|
-
* live (exec's --jsonl uses it); callers that only want the final result omit it.
|
|
11
|
-
* backend/turn error or when structured output does not parse.
|
|
10
|
+
* Shared by `exec` and the MCP server so both behave identically. `onEvent` streams deltas/usage/
|
|
11
|
+
* tool events live (exec's --jsonl uses it); callers that only want the final result omit it.
|
|
12
|
+
* Throws on a backend/turn error or when structured output does not parse.
|
|
12
13
|
*/
|
|
13
14
|
export async function runChatTurn(params, onEvent) {
|
|
14
15
|
const session = new Session({ id: newId(), model: params.model });
|
|
15
16
|
if (params.system?.trim())
|
|
16
17
|
session.addSystemMessage(params.system.trim());
|
|
17
|
-
const turnLoop = new TurnLoop(params.backend, new ToolRegistry());
|
|
18
|
+
const turnLoop = new TurnLoop(params.backend, params.registry ?? new ToolRegistry());
|
|
18
19
|
let content = '';
|
|
19
20
|
let usage = { prompt_tokens: 0, completion_tokens: 0 };
|
|
20
21
|
let engineError = null;
|
|
@@ -29,7 +30,9 @@ export async function runChatTurn(params, onEvent) {
|
|
|
29
30
|
case 'error':
|
|
30
31
|
engineError = event.message;
|
|
31
32
|
break;
|
|
32
|
-
//
|
|
33
|
+
// tool_call_begin/end DO fire when params.registry carries tools (exec's fs tools);
|
|
34
|
+
// the switch above forwards every event via onEvent below, so callers see them too.
|
|
35
|
+
// turn_complete is implicit in runTurn resolving.
|
|
33
36
|
}
|
|
34
37
|
onEvent?.(event);
|
|
35
38
|
}, { responseFormat: params.responseFormat });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runChat.js","sourceRoot":"","sources":["../../../src/core/runChat.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"runChat.js","sourceRoot":"","sources":["../../../src/core/runChat.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAsBhC;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,MAAsB,EACtB,OAAsC;IAEtC,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;IAClE,IAAI,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE;QAAE,OAAO,CAAC,gBAAgB,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;IAC1E,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,QAAQ,IAAI,IAAI,YAAY,EAAE,CAAC,CAAC;IAErF,IAAI,OAAO,GAAG,EAAE,CAAC;IACjB,IAAI,KAAK,GAAG,EAAE,aAAa,EAAE,CAAC,EAAE,iBAAiB,EAAE,CAAC,EAAE,CAAC;IACvD,IAAI,WAAW,GAAkB,IAAI,CAAC;IAEtC,MAAM,QAAQ,CAAC,OAAO,CACpB,OAAO,EACP,MAAM,CAAC,MAAM,EACb,CAAC,KAAK,EAAE,EAAE;QACR,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;YACnB,KAAK,iBAAiB;gBACpB,OAAO,IAAI,KAAK,CAAC,IAAI,CAAC;gBACtB,MAAM;YACR,KAAK,OAAO;gBACV,KAAK,GAAG,EAAE,aAAa,EAAE,KAAK,CAAC,YAAY,EAAE,iBAAiB,EAAE,KAAK,CAAC,gBAAgB,EAAE,CAAC;gBACzF,MAAM;YACR,KAAK,OAAO;gBACV,WAAW,GAAG,KAAK,CAAC,OAAO,CAAC;gBAC5B,MAAM;YACR,oFAAoF;YACpF,oFAAoF;YACpF,kDAAkD;QACpD,CAAC;QACD,OAAO,EAAE,CAAC,KAAK,CAAC,CAAC;IACnB,CAAC,EACD,EAAE,cAAc,EAAE,MAAM,CAAC,cAAc,EAAE,CAC1C,CAAC;IAEF,IAAI,WAAW;QAAE,MAAM,IAAI,KAAK,CAAC,WAAW,CAAC,CAAC;IAE9C,IAAI,UAAU,GAAmB,IAAI,CAAC;IACtC,IAAI,MAAM,CAAC,cAAc,KAAK,SAAS,EAAE,CAAC;QACxC,IAAI,CAAC;YACH,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACnC,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,IAAI,KAAK,CAAC,kEAAkE,CAAC,CAAC;QACtF,CAAC;IACH,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC;AACxC,CAAC"}
|
|
@@ -6,6 +6,8 @@ export interface SessionSnapshot {
|
|
|
6
6
|
createdAt: number;
|
|
7
7
|
updatedAt: number;
|
|
8
8
|
messages: ChatMessage[];
|
|
9
|
+
/** Context-pruning decision set (absent in pre-pruning snapshots → empty). */
|
|
10
|
+
prunedToolCallIds?: string[];
|
|
9
11
|
}
|
|
10
12
|
/**
|
|
11
13
|
* A single conversation the CLI owns end to end.
|
|
@@ -19,12 +21,21 @@ export declare class Session {
|
|
|
19
21
|
readonly createdAt: number;
|
|
20
22
|
model: string;
|
|
21
23
|
messages: ChatMessage[];
|
|
24
|
+
/**
|
|
25
|
+
* Context-pruning decisions (tool_call ids whose results are cleared in the
|
|
26
|
+
* REQUEST view). Grows only at trigger events; raw `messages` are never edited,
|
|
27
|
+
* so policy changes and bugs stay recoverable. Persisted in the snapshot.
|
|
28
|
+
*/
|
|
29
|
+
prunedToolCallIds: Set<string>;
|
|
22
30
|
constructor(params: {
|
|
23
31
|
id: string;
|
|
24
32
|
model: string;
|
|
25
33
|
createdAt?: number;
|
|
26
34
|
messages?: ChatMessage[];
|
|
35
|
+
prunedToolCallIds?: string[];
|
|
27
36
|
});
|
|
37
|
+
/** The messages to SEND: the pruning projection over the intact raw history. */
|
|
38
|
+
requestMessages(): ChatMessage[];
|
|
28
39
|
/** Prepend a system message (must precede the first user message to steer the model). */
|
|
29
40
|
addSystemMessage(text: string): void;
|
|
30
41
|
/**
|
package/dist/src/core/session.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { applyPruning } from './contextPrune.js';
|
|
1
2
|
/**
|
|
2
3
|
* A single conversation the CLI owns end to end.
|
|
3
4
|
*
|
|
@@ -10,11 +11,22 @@ export class Session {
|
|
|
10
11
|
createdAt;
|
|
11
12
|
model;
|
|
12
13
|
messages;
|
|
14
|
+
/**
|
|
15
|
+
* Context-pruning decisions (tool_call ids whose results are cleared in the
|
|
16
|
+
* REQUEST view). Grows only at trigger events; raw `messages` are never edited,
|
|
17
|
+
* so policy changes and bugs stay recoverable. Persisted in the snapshot.
|
|
18
|
+
*/
|
|
19
|
+
prunedToolCallIds;
|
|
13
20
|
constructor(params) {
|
|
14
21
|
this.id = params.id;
|
|
15
22
|
this.model = params.model;
|
|
16
23
|
this.createdAt = params.createdAt ?? nowMs();
|
|
17
24
|
this.messages = params.messages ?? [];
|
|
25
|
+
this.prunedToolCallIds = new Set(params.prunedToolCallIds ?? []);
|
|
26
|
+
}
|
|
27
|
+
/** The messages to SEND: the pruning projection over the intact raw history. */
|
|
28
|
+
requestMessages() {
|
|
29
|
+
return applyPruning(this.messages, this.prunedToolCallIds);
|
|
18
30
|
}
|
|
19
31
|
/** Prepend a system message (must precede the first user message to steer the model). */
|
|
20
32
|
addSystemMessage(text) {
|
|
@@ -67,10 +79,17 @@ export class Session {
|
|
|
67
79
|
createdAt: this.createdAt,
|
|
68
80
|
updatedAt: nowMs(),
|
|
69
81
|
messages: this.messages,
|
|
82
|
+
...(this.prunedToolCallIds.size > 0 ? { prunedToolCallIds: [...this.prunedToolCallIds] } : {}),
|
|
70
83
|
};
|
|
71
84
|
}
|
|
72
85
|
static fromSnapshot(s) {
|
|
73
|
-
return new Session({
|
|
86
|
+
return new Session({
|
|
87
|
+
id: s.id,
|
|
88
|
+
model: s.model,
|
|
89
|
+
createdAt: s.createdAt,
|
|
90
|
+
messages: s.messages,
|
|
91
|
+
prunedToolCallIds: s.prunedToolCallIds,
|
|
92
|
+
});
|
|
74
93
|
}
|
|
75
94
|
}
|
|
76
95
|
function nowMs() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.js","sourceRoot":"","sources":["../../../src/core/session.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../../../src/core/session.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,10 +7,21 @@ import type { ClientTool, OpenAITool, ToolResult } from './types.js';
|
|
|
7
7
|
*/
|
|
8
8
|
export declare class ToolRegistry {
|
|
9
9
|
private readonly tools;
|
|
10
|
-
|
|
10
|
+
/** `gated: true` marks a tool that is only offered to models with catalog tool_use. */
|
|
11
|
+
register(tool: ClientTool, opts?: {
|
|
12
|
+
gated?: boolean;
|
|
13
|
+
}): this;
|
|
11
14
|
has(name: string): boolean;
|
|
12
|
-
/**
|
|
13
|
-
|
|
15
|
+
/**
|
|
16
|
+
* Parallel-safety marker for TurnLoop: true for read-only tools AND for unknown
|
|
17
|
+
* names (dispatch answers those with an instant error result — no side effects).
|
|
18
|
+
*/
|
|
19
|
+
isReadOnly(name: string): boolean;
|
|
20
|
+
/**
|
|
21
|
+
* OpenAI `tools` array for the chat request. `includeGated=false` omits gated tools
|
|
22
|
+
* (model lacks the catalog tool_use capability). Undefined when nothing remains.
|
|
23
|
+
*/
|
|
24
|
+
toOpenAITools(includeGated?: boolean): OpenAITool[] | undefined;
|
|
14
25
|
/**
|
|
15
26
|
* Dispatch a tool call. `rawArgs` is the JSON-encoded arguments string the model produced.
|
|
16
27
|
* Never throws: parse/execution failures are returned as an error result so the model can react.
|
|
@@ -6,33 +6,42 @@
|
|
|
6
6
|
*/
|
|
7
7
|
export class ToolRegistry {
|
|
8
8
|
tools = new Map();
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
/** `gated: true` marks a tool that is only offered to models with catalog tool_use. */
|
|
10
|
+
register(tool, opts = {}) {
|
|
11
|
+
this.tools.set(tool.name, { tool, gated: opts.gated === true });
|
|
11
12
|
return this;
|
|
12
13
|
}
|
|
13
14
|
has(name) {
|
|
14
15
|
return this.tools.has(name);
|
|
15
16
|
}
|
|
16
|
-
/**
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Parallel-safety marker for TurnLoop: true for read-only tools AND for unknown
|
|
19
|
+
* names (dispatch answers those with an instant error result — no side effects).
|
|
20
|
+
*/
|
|
21
|
+
isReadOnly(name) {
|
|
22
|
+
const entry = this.tools.get(name);
|
|
23
|
+
return entry ? entry.tool.readOnly === true : true;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* OpenAI `tools` array for the chat request. `includeGated=false` omits gated tools
|
|
27
|
+
* (model lacks the catalog tool_use capability). Undefined when nothing remains.
|
|
28
|
+
*/
|
|
29
|
+
toOpenAITools(includeGated = true) {
|
|
30
|
+
const list = [...this.tools.values()]
|
|
31
|
+
.filter((e) => includeGated || !e.gated)
|
|
32
|
+
.map(({ tool: t }) => ({
|
|
21
33
|
type: 'function',
|
|
22
|
-
function: {
|
|
23
|
-
name: t.name,
|
|
24
|
-
description: t.description,
|
|
25
|
-
parameters: t.parameters,
|
|
26
|
-
},
|
|
34
|
+
function: { name: t.name, description: t.description, parameters: t.parameters },
|
|
27
35
|
}));
|
|
36
|
+
return list.length === 0 ? undefined : list;
|
|
28
37
|
}
|
|
29
38
|
/**
|
|
30
39
|
* Dispatch a tool call. `rawArgs` is the JSON-encoded arguments string the model produced.
|
|
31
40
|
* Never throws: parse/execution failures are returned as an error result so the model can react.
|
|
32
41
|
*/
|
|
33
42
|
async dispatch(name, rawArgs) {
|
|
34
|
-
const
|
|
35
|
-
if (!
|
|
43
|
+
const entry = this.tools.get(name);
|
|
44
|
+
if (!entry) {
|
|
36
45
|
return { error: `Unknown tool: ${name}` };
|
|
37
46
|
}
|
|
38
47
|
let args;
|
|
@@ -43,7 +52,7 @@ export class ToolRegistry {
|
|
|
43
52
|
return { error: `Invalid tool arguments (not valid JSON): ${rawArgs}` };
|
|
44
53
|
}
|
|
45
54
|
try {
|
|
46
|
-
return await tool.execute(args);
|
|
55
|
+
return await entry.tool.execute(args);
|
|
47
56
|
}
|
|
48
57
|
catch (err) {
|
|
49
58
|
return { error: err instanceof Error ? err.message : String(err) };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toolRegistry.js","sourceRoot":"","sources":["../../../src/core/toolRegistry.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,MAAM,OAAO,YAAY;IACN,KAAK,GAAG,IAAI,GAAG,
|
|
1
|
+
{"version":3,"file":"toolRegistry.js","sourceRoot":"","sources":["../../../src/core/toolRegistry.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,MAAM,OAAO,YAAY;IACN,KAAK,GAAG,IAAI,GAAG,EAAgD,CAAC;IAEjF,uFAAuF;IACvF,QAAQ,CAAC,IAAgB,EAAE,OAA4B,EAAE;QACvD,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,KAAK,IAAI,EAAE,CAAC,CAAC;QAChE,OAAO,IAAI,CAAC;IACd,CAAC;IAED,GAAG,CAAC,IAAY;QACd,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC9B,CAAC;IAED;;;OAGG;IACH,UAAU,CAAC,IAAY;QACrB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACnC,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;IACrD,CAAC;IAED;;;OAGG;IACH,aAAa,CAAC,YAAY,GAAG,IAAI;QAC/B,MAAM,IAAI,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;aAClC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,YAAY,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC;aACvC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;YACrB,IAAI,EAAE,UAAmB;YACzB,QAAQ,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC,WAAW,EAAE,UAAU,EAAE,CAAC,CAAC,UAAU,EAAE;SACjF,CAAC,CAAC,CAAC;QACN,OAAO,IAAI,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC;IAC9C,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,QAAQ,CAAC,IAAY,EAAE,OAAe;QAC1C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACnC,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,EAAE,KAAK,EAAE,iBAAiB,IAAI,EAAE,EAAE,CAAC;QAC5C,CAAC;QACD,IAAI,IAA6B,CAAC;QAClC,IAAI,CAAC;YACH,IAAI,GAAG,OAAO,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9D,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,KAAK,EAAE,4CAA4C,OAAO,EAAE,EAAE,CAAC;QAC1E,CAAC;QACD,IAAI,CAAC;YACH,OAAO,MAAM,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACxC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,EAAE,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;QACrE,CAAC;IACH,CAAC;CACF"}
|
|
@@ -7,6 +7,10 @@ export interface TurnOptions {
|
|
|
7
7
|
responseFormat?: unknown;
|
|
8
8
|
/** Abort signal for user interruption (Esc in the TUI). */
|
|
9
9
|
signal?: AbortSignal;
|
|
10
|
+
/** Offer gated (catalog tool_use) tools to the model. Default true. */
|
|
11
|
+
includeGatedTools?: boolean;
|
|
12
|
+
/** Hard cap on tool calls dispatched in this turn. Default MAX_TOOL_CALLS_PER_TURN. */
|
|
13
|
+
maxToolCalls?: number;
|
|
10
14
|
}
|
|
11
15
|
/**
|
|
12
16
|
* Drives one user turn to completion, following the OpenAI tool-calling protocol:
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { BUDGET_EXHAUSTED_MESSAGE, MAX_TOOL_CALLS_PER_TURN } from '../fs/limits.js';
|
|
2
|
+
/** Pathological backstop only — the tool budget below governs normal turns. */
|
|
3
|
+
const MAX_ROUNDS = 24;
|
|
3
4
|
/**
|
|
4
5
|
* Drives one user turn to completion, following the OpenAI tool-calling protocol:
|
|
5
6
|
*
|
|
@@ -23,12 +24,14 @@ export class TurnLoop {
|
|
|
23
24
|
// trace. Otherwise a request that fails (e.g. an oversized image payload) stays in history
|
|
24
25
|
// and is re-sent every subsequent turn — poisoning the session so even "hello" keeps failing.
|
|
25
26
|
const checkpoint = session.messageCount();
|
|
27
|
+
const budget = opts.maxToolCalls ?? MAX_TOOL_CALLS_PER_TURN;
|
|
28
|
+
let toolCallsUsed = 0;
|
|
26
29
|
session.addUserMessage(userContent);
|
|
27
30
|
for (let round = 0; round < MAX_ROUNDS; round++) {
|
|
28
31
|
let content;
|
|
29
32
|
let toolCalls;
|
|
30
33
|
try {
|
|
31
|
-
({ content, toolCalls } = await this.streamOneCall(session, emit, opts));
|
|
34
|
+
({ content, toolCalls } = await this.streamOneCall(session, emit, opts, toolCallsUsed >= budget));
|
|
32
35
|
}
|
|
33
36
|
catch (err) {
|
|
34
37
|
// User interruption (Esc): keep whatever partial text already streamed so the
|
|
@@ -57,15 +60,51 @@ export class TurnLoop {
|
|
|
57
60
|
emit({ type: 'turn_complete' });
|
|
58
61
|
return;
|
|
59
62
|
}
|
|
60
|
-
// Execute
|
|
63
|
+
// Execute the requested tools and feed results back into the conversation.
|
|
61
64
|
// Tool execution itself is never aborted mid-flight (a paid submit must not be torn),
|
|
62
65
|
// but an interrupt takes effect before the next model call.
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
66
|
+
//
|
|
67
|
+
// A round whose calls are ALL read-only runs them concurrently (no side effects,
|
|
68
|
+
// cannot conflict); any non-read-only tool (paid media with its confirmation
|
|
69
|
+
// dialog) keeps the round strictly sequential. Budget slots are assigned by call
|
|
70
|
+
// order up front so the outcome is deterministic either way.
|
|
71
|
+
const allowed = toolCalls.map((_, i) => toolCallsUsed + i < budget);
|
|
72
|
+
const parallel = toolCalls.length > 1 && toolCalls.every((c) => this.registry.isReadOnly(c.function.name));
|
|
73
|
+
if (parallel) {
|
|
74
|
+
for (const call of toolCalls) {
|
|
75
|
+
emit({
|
|
76
|
+
type: 'tool_call_begin',
|
|
77
|
+
id: call.id,
|
|
78
|
+
name: call.function.name,
|
|
79
|
+
args: safeParseArgs(call.function.arguments),
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
const results = await Promise.all(toolCalls.map(async (call, i) => {
|
|
83
|
+
const result = allowed[i]
|
|
84
|
+
? await this.registry.dispatch(call.function.name, call.function.arguments)
|
|
85
|
+
: { error: BUDGET_EXHAUSTED_MESSAGE };
|
|
86
|
+
// Ends may arrive out of completion order; the TUI matches rows by call id.
|
|
87
|
+
emit({ type: 'tool_call_end', id: call.id, name: call.function.name, result });
|
|
88
|
+
return result;
|
|
89
|
+
}));
|
|
90
|
+
// Tool messages must follow the tool_calls order the model emitted.
|
|
91
|
+
toolCalls.forEach((call, i) => session.addToolMessage(call.id, call.function.name, results[i]));
|
|
92
|
+
toolCallsUsed = Math.min(budget, toolCallsUsed + toolCalls.length);
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
for (const call of toolCalls) {
|
|
96
|
+
const args = safeParseArgs(call.function.arguments);
|
|
97
|
+
emit({ type: 'tool_call_begin', id: call.id, name: call.function.name, args });
|
|
98
|
+
// Past the budget, don't dispatch: feed the model a budget error instead, so it
|
|
99
|
+
// wraps up with what it has (a normal answer, not a hard stop).
|
|
100
|
+
const result = toolCallsUsed >= budget
|
|
101
|
+
? { error: BUDGET_EXHAUSTED_MESSAGE }
|
|
102
|
+
: await this.registry.dispatch(call.function.name, call.function.arguments);
|
|
103
|
+
if (toolCallsUsed < budget)
|
|
104
|
+
toolCallsUsed += 1;
|
|
105
|
+
emit({ type: 'tool_call_end', id: call.id, name: call.function.name, result });
|
|
106
|
+
session.addToolMessage(call.id, call.function.name, result);
|
|
107
|
+
}
|
|
69
108
|
}
|
|
70
109
|
if (opts.signal?.aborted) {
|
|
71
110
|
emit({ type: 'turn_interrupted' });
|
|
@@ -77,12 +116,14 @@ export class TurnLoop {
|
|
|
77
116
|
emit({ type: 'error', message: `Turn exceeded ${MAX_ROUNDS} tool-calling rounds` });
|
|
78
117
|
}
|
|
79
118
|
/** Stream a single model call, emitting text deltas and accumulating tool_calls. */
|
|
80
|
-
async streamOneCall(session, emit, opts) {
|
|
119
|
+
async streamOneCall(session, emit, opts, forceNoTools) {
|
|
81
120
|
const req = {
|
|
82
121
|
model: session.model,
|
|
83
|
-
|
|
84
|
-
|
|
122
|
+
// The pruning projection, not the raw history (raw stays intact on disk).
|
|
123
|
+
messages: session.requestMessages(),
|
|
124
|
+
tools: this.registry.toOpenAITools(opts.includeGatedTools ?? true),
|
|
85
125
|
stream: true,
|
|
126
|
+
...(forceNoTools ? { tool_choice: 'none' } : {}),
|
|
86
127
|
...(opts.responseFormat !== undefined ? { response_format: opts.responseFormat } : {}),
|
|
87
128
|
};
|
|
88
129
|
let content = '';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"turnLoop.js","sourceRoot":"","sources":["../../../src/core/turnLoop.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"turnLoop.js","sourceRoot":"","sources":["../../../src/core/turnLoop.ts"],"names":[],"mappings":"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"}
|
package/dist/src/core/types.d.ts
CHANGED
|
@@ -98,6 +98,12 @@ export interface ClientTool {
|
|
|
98
98
|
name: string;
|
|
99
99
|
description: string;
|
|
100
100
|
parameters: Record<string, unknown>;
|
|
101
|
+
/**
|
|
102
|
+
* True for tools with no side effects (fs reads). A round whose calls are ALL
|
|
103
|
+
* read-only executes them concurrently; any other tool (paid media with its
|
|
104
|
+
* confirmation dialog) keeps the round strictly sequential.
|
|
105
|
+
*/
|
|
106
|
+
readOnly?: boolean;
|
|
101
107
|
execute(args: Record<string, unknown>): Promise<ToolResult>;
|
|
102
108
|
}
|
|
103
109
|
/** Result of running a client tool. Serialized to JSON as the tool message content. */
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Advise-only update check (spec: 2026-07-20-update-check-design.md).
|
|
3
|
+
*
|
|
4
|
+
* Codex-shaped: a 20h-TTL file cache is all the startup path ever reads; when it
|
|
5
|
+
* is stale a background registry fetch refreshes it for the NEXT start, so the
|
|
6
|
+
* TUI is never blocked by the network and every failure is silent. One deliberate
|
|
7
|
+
* divergence: our channel lives on prerelease versions (0.1.0-test.N), so the
|
|
8
|
+
* comparison implements semver prerelease precedence instead of ignoring it.
|
|
9
|
+
* The registry is fetched over HTTPS directly — never by shelling npm — which
|
|
10
|
+
* sidesteps project-level .npmrc registry redirection entirely.
|
|
11
|
+
*/
|
|
12
|
+
export declare const UPDATE_CHECK_TTL_MS: number;
|
|
13
|
+
export interface UpdateCache {
|
|
14
|
+
latestVersion: string;
|
|
15
|
+
channel: string;
|
|
16
|
+
lastCheckedAt: number;
|
|
17
|
+
}
|
|
18
|
+
export interface UpdateNotice {
|
|
19
|
+
latest: string;
|
|
20
|
+
command: string;
|
|
21
|
+
}
|
|
22
|
+
/** Injectable deps so tests never touch the network, the real cache, or the clock. */
|
|
23
|
+
export interface UpdateCheckDeps {
|
|
24
|
+
currentVersion?: string;
|
|
25
|
+
cacheFile?: string;
|
|
26
|
+
fetchImpl?: typeof fetch;
|
|
27
|
+
now?: () => number;
|
|
28
|
+
/** Resolved from env/config when omitted. */
|
|
29
|
+
enabled?: boolean;
|
|
30
|
+
}
|
|
31
|
+
/** The npm dist-tag this build tracks: prerelease test builds follow `test`. */
|
|
32
|
+
export declare function channelForVersion(version: string): 'test' | 'latest';
|
|
33
|
+
export declare function upgradeCommand(channel: string): string;
|
|
34
|
+
/**
|
|
35
|
+
* Semver precedence: >0 when `a` is newer than `b`, 0 when equal or either side
|
|
36
|
+
* is malformed (malformed must never read as "newer" — a registry hiccup would
|
|
37
|
+
* otherwise nag every start). Numeric prerelease identifiers compare numerically
|
|
38
|
+
* (test.10 > test.9); a release outranks its own prereleases.
|
|
39
|
+
*/
|
|
40
|
+
export declare function compareVersions(a: string, b: string): number;
|
|
41
|
+
/**
|
|
42
|
+
* Cache-only answer for THIS start; spawns the background refresh when the cache
|
|
43
|
+
* is stale, missing, or from another channel. Returns a notice only when the
|
|
44
|
+
* cached latest genuinely outranks the running version.
|
|
45
|
+
*/
|
|
46
|
+
export declare function maybeCheckForUpdate(deps?: UpdateCheckDeps): Promise<UpdateNotice | null>;
|