@glbgpt/cli 0.2.1 → 0.2.3
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 +21 -4
- package/dist/src/commands/configCommand.js +14 -1
- package/dist/src/commands/configCommand.js.map +1 -1
- package/dist/src/config/configFile.d.ts +4 -0
- package/dist/src/config/configFile.js +4 -0
- package/dist/src/config/configFile.js.map +1 -1
- package/dist/src/core/contextPrune.d.ts +18 -6
- package/dist/src/core/contextPrune.js +108 -20
- package/dist/src/core/contextPrune.js.map +1 -1
- package/dist/src/core/runChat.d.ts +1 -1
- package/dist/src/core/runChat.js +8 -5
- package/dist/src/core/runChat.js.map +1 -1
- package/dist/src/core/tools/askUserTool.d.ts +3 -0
- package/dist/src/core/tools/askUserTool.js +61 -0
- package/dist/src/core/tools/askUserTool.js.map +1 -0
- package/dist/src/core/tools/planTool.d.ts +15 -0
- package/dist/src/core/tools/planTool.js +58 -0
- package/dist/src/core/tools/planTool.js.map +1 -0
- package/dist/src/core/turnLimits.d.ts +5 -0
- package/dist/src/core/turnLimits.js +28 -0
- package/dist/src/core/turnLimits.js.map +1 -0
- package/dist/src/core/turnLoop.d.ts +1 -1
- package/dist/src/core/turnLoop.js +124 -57
- package/dist/src/core/turnLoop.js.map +1 -1
- package/dist/src/core/types.d.ts +3 -1
- package/dist/src/exec/output.d.ts +1 -1
- package/dist/src/exec/output.js +3 -2
- package/dist/src/exec/output.js.map +1 -1
- package/dist/src/index.js +4 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/mcp/mediaTools.d.ts +2 -2
- package/dist/src/mcp/mediaTools.js +24 -5
- package/dist/src/mcp/mediaTools.js.map +1 -1
- package/dist/src/mcp/server.js +30 -1
- package/dist/src/mcp/server.js.map +1 -1
- package/dist/src/media/taskLedger.d.ts +21 -1
- package/dist/src/media/taskLedger.js +27 -0
- package/dist/src/media/taskLedger.js.map +1 -1
- package/dist/src/tui/App.d.ts +3 -1
- package/dist/src/tui/App.js +78 -14
- package/dist/src/tui/App.js.map +1 -1
- package/dist/src/tui/chatController.d.ts +16 -2
- package/dist/src/tui/chatController.js +106 -31
- package/dist/src/tui/chatController.js.map +1 -1
- package/dist/src/tui/commands.js +2 -0
- package/dist/src/tui/commands.js.map +1 -1
- package/dist/src/tui/questionBridge.d.ts +24 -0
- package/dist/src/tui/questionBridge.js +33 -0
- package/dist/src/tui/questionBridge.js.map +1 -0
- package/dist/src/tui/run.js +9 -1
- package/dist/src/tui/run.js.map +1 -1
- package/dist/src/tui/staticSplit.d.ts +17 -0
- package/dist/src/tui/staticSplit.js +34 -0
- package/dist/src/tui/staticSplit.js.map +1 -0
- package/dist/src/web/index.d.ts +3 -1
- package/dist/src/web/index.js +1 -1
- package/dist/src/web/index.js.map +1 -1
- package/dist/src/web/webFetchTool.d.ts +5 -0
- package/dist/src/web/webFetchTool.js +1 -1
- package/dist/src/web/webFetchTool.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -32,8 +32,11 @@ Run `glbgpt <command> --help` for the full flags of any command.
|
|
|
32
32
|
| `glbgpt resume last` · `glbgpt resume <id>` | Resume a previous conversation. |
|
|
33
33
|
|
|
34
34
|
Inside the TUI, type `/` for commands: `/model` `/models` `/image` `/video` `/tasks` `/status`
|
|
35
|
-
`/permissions` `/compact` `/
|
|
36
|
-
`/help` `/exit`.
|
|
35
|
+
`/permissions` `/compact` `/diff` `/export` `/account` `/transactions` `/cost` `/login` `/logout`
|
|
36
|
+
`/new` `/resume` `/help` `/exit`. `/diff` shows working-tree changes (plus untracked files);
|
|
37
|
+
`/export` writes the conversation to a markdown file. On multi-step tasks the model keeps a
|
|
38
|
+
visible plan checklist, and when it hits a decision only you can make it asks with an
|
|
39
|
+
interactive picker (↑/↓ or a number key; Esc lets it proceed on its own judgment).
|
|
37
40
|
Attach images with `@image` (last generation), `Ctrl+V` or `@clipboard`, or `@./photo.png`.
|
|
38
41
|
Multi-line: end a line with `\` then Enter (or Ctrl+J); plain Enter sends; Esc interrupts.
|
|
39
42
|
On tool-capable models the assistant can also read files under the directory you started
|
|
@@ -143,6 +146,7 @@ account. Sign in once with `glbgpt login` first.
|
|
|
143
146
|
| `glbgpt_generate_video` | Generate a video; returns a `task_id` to check. |
|
|
144
147
|
| `glbgpt_check_task` | Check a generation task. Waits server-side — the agent can just call it again immediately until done. |
|
|
145
148
|
| `glbgpt_list_models` | List available chat/image/video models and what parameters each supports. |
|
|
149
|
+
| `glbgpt_account` | Signed-in account: email, plan, and remaining credit balance. |
|
|
146
150
|
|
|
147
151
|
Register it:
|
|
148
152
|
|
|
@@ -231,12 +235,25 @@ re-points the link and leaves nothing to lose.
|
|
|
231
235
|
| `glbgpt config list` | Show persisted settings. |
|
|
232
236
|
| `glbgpt config get <key>` · `set <key> <value>` · `unset <key>` | Manage settings. |
|
|
233
237
|
|
|
234
|
-
Keys: `lang`, `max-credits` (unattended media budget), `
|
|
238
|
+
Keys: `lang`, `max-credits` (unattended media budget), `max-tool-calls` (tool calls per
|
|
239
|
+
chat turn, default 40), `context-pruning` (set `false` to
|
|
235
240
|
keep old tool results in long chats), `update-check` (set `false` to disable the startup
|
|
236
241
|
update prompt and notices about newer versions). One-shot overrides via env vars (`GLBGPT_LANG`,
|
|
237
|
-
`GLBGPT_TOKEN`, `GLBGPT_MAX_CREDITS`, `GLBGPT_CONTEXT_PRUNING`, `GLBGPT_UPDATE_CHECK`)
|
|
242
|
+
`GLBGPT_TOKEN`, `GLBGPT_MAX_CREDITS`, `GLBGPT_MAX_TOOL_CALLS`, `GLBGPT_CONTEXT_PRUNING`, `GLBGPT_UPDATE_CHECK`)
|
|
238
243
|
always win.
|
|
239
244
|
|
|
245
|
+
For longer coding tasks, use `glbgpt config set max-tool-calls 100` or set
|
|
246
|
+
`GLBGPT_MAX_TOOL_CALLS=100` for one run. Values must be integers from 0 to 1000;
|
|
247
|
+
`0` disables tool calls. This counts all dispatched tools in a chat turn and is
|
|
248
|
+
**not a token or credit limit**: raising it can increase model usage and cost.
|
|
249
|
+
When the budget is reached, the CLI preserves completed work in the conversation
|
|
250
|
+
and requests a final answer with no further tool execution, including any remaining
|
|
251
|
+
work. Send a follow-up message to continue with a new turn's budget.
|
|
252
|
+
|
|
253
|
+
Context pruning also runs between tool rounds, preserving the raw history on disk.
|
|
254
|
+
`exec` and MCP usage totals include every model request in the turn; these token
|
|
255
|
+
counts are not a credit charge estimate.
|
|
256
|
+
|
|
240
257
|
## Notes
|
|
241
258
|
|
|
242
259
|
**Updates.** When a newer version exists, an interactive start shows a chooser (Enter
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { CONFIG_KEYS, isConfigKey, readConfigSync, writeConfigValue } from '../config/configFile.js';
|
|
2
2
|
import { apiBaseUrl, webBaseUrl, lang } from '../config/env.js';
|
|
3
3
|
import { DEFAULT_SESSION_MAX_CREDITS } from '../fs/limits.js';
|
|
4
|
+
import { parseMaxToolCalls, resolveMaxToolCalls } from '../core/turnLimits.js';
|
|
4
5
|
/**
|
|
5
6
|
* `glbgpt config list|get|set|unset` — persistent CLI settings.
|
|
6
7
|
*
|
|
@@ -23,7 +24,9 @@ export function effective(key) {
|
|
|
23
24
|
? webBaseUrl()
|
|
24
25
|
: key === 'lang'
|
|
25
26
|
? lang()
|
|
26
|
-
:
|
|
27
|
+
: key === 'max-tool-calls'
|
|
28
|
+
? String(resolveMaxToolCalls())
|
|
29
|
+
: (fromEnv ?? stored ?? KEY_DEFAULTS[key] ?? '');
|
|
27
30
|
return { value, source: fromEnv ? 'env' : stored ? 'config' : 'default' };
|
|
28
31
|
}
|
|
29
32
|
function badKey(key) {
|
|
@@ -50,6 +53,16 @@ export async function runConfigSet(key, value) {
|
|
|
50
53
|
process.exitCode = 1;
|
|
51
54
|
return;
|
|
52
55
|
}
|
|
56
|
+
if (key === 'max-tool-calls') {
|
|
57
|
+
try {
|
|
58
|
+
parseMaxToolCalls(value);
|
|
59
|
+
}
|
|
60
|
+
catch (err) {
|
|
61
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
62
|
+
process.exitCode = 1;
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
53
66
|
await writeConfigValue(key, value.trim());
|
|
54
67
|
console.error(`Set ${key} = ${value.trim()}`);
|
|
55
68
|
const envName = CONFIG_KEYS[key].env;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"configCommand.js","sourceRoot":"","sources":["../../../src/commands/configCommand.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,cAAc,EAAE,gBAAgB,EAAkB,MAAM,yBAAyB,CAAC;AACrH,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAChE,OAAO,EAAE,2BAA2B,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"configCommand.js","sourceRoot":"","sources":["../../../src/commands/configCommand.ts"],"names":[],"mappings":"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"}
|
|
@@ -27,6 +27,10 @@ export declare const CONFIG_KEYS: {
|
|
|
27
27
|
readonly env: "GLBGPT_MAX_CREDITS";
|
|
28
28
|
readonly help: "Session credit budget for unattended media generation (mcp/exec); default 4000; 0 disables media generation";
|
|
29
29
|
};
|
|
30
|
+
readonly 'max-tool-calls': {
|
|
31
|
+
readonly env: "GLBGPT_MAX_TOOL_CALLS";
|
|
32
|
+
readonly help: "Maximum tool calls per chat turn (0–1000); default 40; 0 disables tool calls; not a credit limit";
|
|
33
|
+
};
|
|
30
34
|
readonly 'update-check': {
|
|
31
35
|
readonly env: "GLBGPT_UPDATE_CHECK";
|
|
32
36
|
readonly help: "Set to \"false\" to disable the startup check for newer published versions";
|
|
@@ -22,6 +22,10 @@ export const CONFIG_KEYS = {
|
|
|
22
22
|
env: 'GLBGPT_MAX_CREDITS',
|
|
23
23
|
help: 'Session credit budget for unattended media generation (mcp/exec); default 4000; 0 disables media generation',
|
|
24
24
|
},
|
|
25
|
+
'max-tool-calls': {
|
|
26
|
+
env: 'GLBGPT_MAX_TOOL_CALLS',
|
|
27
|
+
help: 'Maximum tool calls per chat turn (0–1000); default 40; 0 disables tool calls; not a credit limit',
|
|
28
|
+
},
|
|
25
29
|
'update-check': {
|
|
26
30
|
env: 'GLBGPT_UPDATE_CHECK',
|
|
27
31
|
help: 'Set to "false" to disable the startup check for newer published versions',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"configFile.js","sourceRoot":"","sources":["../../../src/config/configFile.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC;;;;;;GAMG;AAEH,wEAAwE;AACxE,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,cAAc,EAAE,EAAE,GAAG,EAAE,iBAAiB,EAAE,IAAI,EAAE,4CAA4C,EAAE;IAC9F,cAAc,EAAE,EAAE,GAAG,EAAE,gBAAgB,EAAE,IAAI,EAAE,qCAAqC,EAAE;IACtF,IAAI,EAAE,EAAE,GAAG,EAAE,aAAa,EAAE,IAAI,EAAE,sCAAsC,EAAE;IAC1E,iBAAiB,EAAE;QACjB,GAAG,EAAE,wBAAwB;QAC7B,IAAI,EAAE,mFAAmF;KAC1F;IACD,aAAa,EAAE;QACb,GAAG,EAAE,oBAAoB;QACzB,IAAI,EAAE,6GAA6G;KACpH;IACD,cAAc,EAAE;QACd,GAAG,EAAE,qBAAqB;QAC1B,IAAI,EAAE,0EAA0E;KACjF;CACO,CAAC;AAIX,MAAM,UAAU,WAAW,CAAC,GAAW;IACrC,OAAO,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC;AAChE,CAAC;AAED,SAAS,IAAI;IACX,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,aAAa,CAAC,CAAC;AAC/C,CAAC;AAED,4DAA4D;AAC5D,MAAM,UAAU,cAAc;IAC5B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,EAAE,EAAE,MAAM,CAAC,CAAC;QACzC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAA4B,CAAC;QAC1D,MAAM,GAAG,GAAuC,EAAE,CAAC;QACnD,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,WAAW,CAAgB,EAAE,CAAC;YAC1D,IAAI,OAAO,MAAM,CAAC,GAAG,CAAC,KAAK,QAAQ;gBAAE,GAAG,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,GAAG,CAAW,CAAC;QACxE,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,+CAA+C;AAC/C,MAAM,UAAU,kBAAkB,CAAC,GAAc;IAC/C,OAAO,cAAc,EAAE,CAAC,GAAG,CAAC,CAAC;AAC/B,CAAC;AAED,kEAAkE;AAClE,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,GAAc,EAAE,KAAyB;IAC9E,MAAM,OAAO,GAAG,cAAc,EAAE,CAAC;IACjC,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC;;QACxC,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;IAC1B,MAAM,EAAE,CAAC,KAAK,CAAC,SAAS,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACjD,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,MAAM,CAAC,CAAC;AAC9E,CAAC"}
|
|
1
|
+
{"version":3,"file":"configFile.js","sourceRoot":"","sources":["../../../src/config/configFile.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC;;;;;;GAMG;AAEH,wEAAwE;AACxE,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,cAAc,EAAE,EAAE,GAAG,EAAE,iBAAiB,EAAE,IAAI,EAAE,4CAA4C,EAAE;IAC9F,cAAc,EAAE,EAAE,GAAG,EAAE,gBAAgB,EAAE,IAAI,EAAE,qCAAqC,EAAE;IACtF,IAAI,EAAE,EAAE,GAAG,EAAE,aAAa,EAAE,IAAI,EAAE,sCAAsC,EAAE;IAC1E,iBAAiB,EAAE;QACjB,GAAG,EAAE,wBAAwB;QAC7B,IAAI,EAAE,mFAAmF;KAC1F;IACD,aAAa,EAAE;QACb,GAAG,EAAE,oBAAoB;QACzB,IAAI,EAAE,6GAA6G;KACpH;IACD,gBAAgB,EAAE;QAChB,GAAG,EAAE,uBAAuB;QAC5B,IAAI,EAAE,kGAAkG;KACzG;IACD,cAAc,EAAE;QACd,GAAG,EAAE,qBAAqB;QAC1B,IAAI,EAAE,0EAA0E;KACjF;CACO,CAAC;AAIX,MAAM,UAAU,WAAW,CAAC,GAAW;IACrC,OAAO,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC;AAChE,CAAC;AAED,SAAS,IAAI;IACX,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,aAAa,CAAC,CAAC;AAC/C,CAAC;AAED,4DAA4D;AAC5D,MAAM,UAAU,cAAc;IAC5B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,EAAE,EAAE,MAAM,CAAC,CAAC;QACzC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAA4B,CAAC;QAC1D,MAAM,GAAG,GAAuC,EAAE,CAAC;QACnD,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,WAAW,CAAgB,EAAE,CAAC;YAC1D,IAAI,OAAO,MAAM,CAAC,GAAG,CAAC,KAAK,QAAQ;gBAAE,GAAG,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,GAAG,CAAW,CAAC;QACxE,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,+CAA+C;AAC/C,MAAM,UAAU,kBAAkB,CAAC,GAAc;IAC/C,OAAO,cAAc,EAAE,CAAC,GAAG,CAAC,CAAC;AAC/B,CAAC;AAED,kEAAkE;AAClE,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,GAAc,EAAE,KAAyB;IAC9E,MAAM,OAAO,GAAG,cAAc,EAAE,CAAC;IACjC,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC;;QACxC,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;IAC1B,MAAM,EAAE,CAAC,KAAK,CAAC,SAAS,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACjD,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,MAAM,CAAC,CAAC;AAC9E,CAAC"}
|
|
@@ -1,13 +1,25 @@
|
|
|
1
1
|
import type { ChatMessage } from './types.js';
|
|
2
|
+
import type { Session } from './session.js';
|
|
2
3
|
/**
|
|
3
|
-
* The ids to clear at this trigger:
|
|
4
|
-
*
|
|
4
|
+
* The ids to clear at this trigger: old eligible results except the most recent
|
|
5
|
+
* PRUNE_KEEP_RECENT and ALL results in the latest batch. A parallel batch can exceed
|
|
6
|
+
* the keep count and must reach the model once before any of its results are cleared.
|
|
5
7
|
*/
|
|
6
|
-
export declare function selectPrunable(messages: ChatMessage[], alreadyPruned: ReadonlySet<string>): string[];
|
|
8
|
+
export declare function selectPrunable(messages: ChatMessage[], alreadyPruned: ReadonlySet<string>, protectedIds?: ReadonlySet<string>): string[];
|
|
7
9
|
/**
|
|
8
|
-
* The request-time projection:
|
|
9
|
-
*
|
|
10
|
-
*
|
|
10
|
+
* The request-time projection: replace old read results or shorten old command output.
|
|
11
|
+
* Assistant calls and their arguments stay intact, along with mutation confirmations.
|
|
12
|
+
* Returns the input
|
|
11
13
|
* array unchanged when the set is empty (zero-cost fast path).
|
|
12
14
|
*/
|
|
13
15
|
export declare function applyPruning(messages: ChatMessage[], prunedIds: ReadonlySet<string>): ChatMessage[];
|
|
16
|
+
/**
|
|
17
|
+
* Apply the shared pruning policy before a model request, including tool rounds within
|
|
18
|
+
* one user turn. Real prompt usage catches tokenizer/schema overhead; the current
|
|
19
|
+
* projection estimate also catches new tool output and providers that omit usage.
|
|
20
|
+
* The full transcript stays intact for persistence and recovery.
|
|
21
|
+
*/
|
|
22
|
+
export declare function pruneSessionContext(session: Session, lastPromptTokens?: number): {
|
|
23
|
+
ids: string[];
|
|
24
|
+
clearedBytes: number;
|
|
25
|
+
} | null;
|
|
@@ -1,26 +1,48 @@
|
|
|
1
1
|
import { FS_TOOL_NAMES } from '../fs/index.js';
|
|
2
2
|
import { CODING_TOOL_NAMES } from '../coding/index.js';
|
|
3
|
-
import {
|
|
3
|
+
import { cutToBytesFromEnd } from '../coding/truncate.js';
|
|
4
|
+
import { PRUNE_KEEP_RECENT, PRUNE_MIN_CONTENT_BYTES, PRUNE_PLACEHOLDER, PRUNE_TRIGGER_TOKENS } from '../fs/limits.js';
|
|
5
|
+
import { estimateMessageTokens } from './compaction.js';
|
|
6
|
+
import { getConfigValueSync } from '../config/configFile.js';
|
|
4
7
|
/**
|
|
5
8
|
* Microcompact selection and projection (spec: 2026-07-19-context-pruning-design.md).
|
|
6
9
|
*
|
|
7
|
-
*
|
|
10
|
+
* Selection and projection are pure. Selection runs only at trigger events (context over
|
|
8
11
|
* threshold); the returned ids join the session's persisted decision set, which
|
|
9
12
|
* only ever grows — so between triggers the projection is byte-stable and the
|
|
10
13
|
* upstream prompt cache keeps hitting.
|
|
11
14
|
*/
|
|
12
15
|
/**
|
|
13
|
-
*
|
|
14
|
-
*
|
|
16
|
+
* Read results can be fetched again at no cost. Commands need a different projection:
|
|
17
|
+
* retain their outcome and output tail without suggesting a repeat of side effects.
|
|
18
|
+
* web_search stays intact because repeating it bills again.
|
|
15
19
|
*/
|
|
16
|
-
// Coding tool results are prunable too: bash output goes stale quickly, and edit/write
|
|
17
|
-
// results are one-line confirmations whose loss costs nothing.
|
|
18
20
|
const PRUNABLE_TOOL_NAMES = new Set([
|
|
19
21
|
...FS_TOOL_NAMES,
|
|
20
|
-
|
|
22
|
+
'bash',
|
|
23
|
+
'bash_output',
|
|
21
24
|
'web_fetch',
|
|
22
25
|
]);
|
|
23
|
-
|
|
26
|
+
const COMMAND_OUTPUT_TAIL_BYTES = 1024;
|
|
27
|
+
const COMMAND_PRUNE_NOTICE = 'This tool already ran. Earlier output was omitted to save context; the output tail is retained. ' +
|
|
28
|
+
'Do not repeat commands to recover omitted output. Read the saved output file if one is shown.';
|
|
29
|
+
/** Preserve metadata and the terminal status/log-file footnote at the end of command output. */
|
|
30
|
+
function compactCommandResult(result) {
|
|
31
|
+
if ('error' in result || 'task_id' in result)
|
|
32
|
+
return null;
|
|
33
|
+
const projected = { ...result };
|
|
34
|
+
let changed = false;
|
|
35
|
+
for (const key of ['output', 'stdout', 'stderr']) {
|
|
36
|
+
const value = result[key];
|
|
37
|
+
if (typeof value === 'string' &&
|
|
38
|
+
Buffer.byteLength(value, 'utf8') > COMMAND_OUTPUT_TAIL_BYTES + PRUNE_MIN_CONTENT_BYTES) {
|
|
39
|
+
projected[key] = `[Earlier output omitted]\n${cutToBytesFromEnd(value, COMMAND_OUTPUT_TAIL_BYTES)}`;
|
|
40
|
+
changed = true;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return changed ? JSON.stringify({ ...projected, context_pruning_notice: COMMAND_PRUNE_NOTICE }) : null;
|
|
44
|
+
}
|
|
45
|
+
/** True for a tool result whose output can shrink without losing execution/task anchors. */
|
|
24
46
|
function isPrunable(m) {
|
|
25
47
|
if (m.role !== 'tool' || typeof m.content !== 'string' || !m.tool_call_id)
|
|
26
48
|
return false;
|
|
@@ -30,10 +52,14 @@ function isPrunable(m) {
|
|
|
30
52
|
return false;
|
|
31
53
|
try {
|
|
32
54
|
const parsed = JSON.parse(m.content);
|
|
33
|
-
if (typeof parsed !== 'object' || parsed === null)
|
|
55
|
+
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed))
|
|
34
56
|
return false;
|
|
35
|
-
if ('error' in parsed)
|
|
57
|
+
if ('error' in parsed || 'task_id' in parsed)
|
|
36
58
|
return false;
|
|
59
|
+
if (m.name === 'bash' || m.name === 'bash_output') {
|
|
60
|
+
const compacted = compactCommandResult(parsed);
|
|
61
|
+
return compacted !== null && Buffer.byteLength(compacted, 'utf8') < Buffer.byteLength(m.content, 'utf8');
|
|
62
|
+
}
|
|
37
63
|
}
|
|
38
64
|
catch {
|
|
39
65
|
return false; // unparseable content: leave it alone
|
|
@@ -41,10 +67,11 @@ function isPrunable(m) {
|
|
|
41
67
|
return true;
|
|
42
68
|
}
|
|
43
69
|
/**
|
|
44
|
-
* The ids to clear at this trigger:
|
|
45
|
-
*
|
|
70
|
+
* The ids to clear at this trigger: old eligible results except the most recent
|
|
71
|
+
* PRUNE_KEEP_RECENT and ALL results in the latest batch. A parallel batch can exceed
|
|
72
|
+
* the keep count and must reach the model once before any of its results are cleared.
|
|
46
73
|
*/
|
|
47
|
-
export function selectPrunable(messages, alreadyPruned) {
|
|
74
|
+
export function selectPrunable(messages, alreadyPruned, protectedIds = latestToolBatchIds(messages)) {
|
|
48
75
|
const candidates = [];
|
|
49
76
|
for (const m of messages) {
|
|
50
77
|
// Cheap O(1) set check FIRST: already-pruned messages keep their full raw
|
|
@@ -55,19 +82,80 @@ export function selectPrunable(messages, alreadyPruned) {
|
|
|
55
82
|
if (isPrunable(m))
|
|
56
83
|
candidates.push(m.tool_call_id);
|
|
57
84
|
}
|
|
58
|
-
return candidates
|
|
85
|
+
return candidates
|
|
86
|
+
.slice(0, Math.max(0, candidates.length - PRUNE_KEEP_RECENT))
|
|
87
|
+
.filter((id) => !protectedIds.has(id));
|
|
88
|
+
}
|
|
89
|
+
function latestToolBatchIds(messages) {
|
|
90
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
91
|
+
const message = messages[i];
|
|
92
|
+
if (message.role === 'assistant' && message.tool_calls?.length) {
|
|
93
|
+
return new Set(message.tool_calls.map((call) => call.id));
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return new Set();
|
|
59
97
|
}
|
|
60
98
|
/**
|
|
61
|
-
* The request-time projection:
|
|
62
|
-
*
|
|
63
|
-
*
|
|
99
|
+
* The request-time projection: replace old read results or shorten old command output.
|
|
100
|
+
* Assistant calls and their arguments stay intact, along with mutation confirmations.
|
|
101
|
+
* Returns the input
|
|
64
102
|
* array unchanged when the set is empty (zero-cost fast path).
|
|
65
103
|
*/
|
|
66
104
|
export function applyPruning(messages, prunedIds) {
|
|
67
105
|
if (prunedIds.size === 0)
|
|
68
106
|
return messages;
|
|
69
|
-
return messages.map((m) =>
|
|
70
|
-
|
|
71
|
-
|
|
107
|
+
return messages.map((m) => {
|
|
108
|
+
if (m.role !== 'tool' || !m.tool_call_id || !prunedIds.has(m.tool_call_id))
|
|
109
|
+
return m;
|
|
110
|
+
if (m.name && CODING_TOOL_NAMES.has(m.name)) {
|
|
111
|
+
// Old snapshots may include write/edit/kill or background-start IDs. Restore their
|
|
112
|
+
// confirmations verbatim instead of applying the former generic re-run placeholder.
|
|
113
|
+
if (m.name !== 'bash' && m.name !== 'bash_output')
|
|
114
|
+
return m;
|
|
115
|
+
if (typeof m.content !== 'string')
|
|
116
|
+
return m;
|
|
117
|
+
try {
|
|
118
|
+
const parsed = JSON.parse(m.content);
|
|
119
|
+
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed))
|
|
120
|
+
return m;
|
|
121
|
+
const content = compactCommandResult(parsed);
|
|
122
|
+
return content ? { ...m, content } : m;
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
return m;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return { ...m, content: PRUNE_PLACEHOLDER };
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Apply the shared pruning policy before a model request, including tool rounds within
|
|
133
|
+
* one user turn. Real prompt usage catches tokenizer/schema overhead; the current
|
|
134
|
+
* projection estimate also catches new tool output and providers that omit usage.
|
|
135
|
+
* The full transcript stays intact for persistence and recovery.
|
|
136
|
+
*/
|
|
137
|
+
export function pruneSessionContext(session, lastPromptTokens = 0) {
|
|
138
|
+
const messages = session.requestMessages();
|
|
139
|
+
if (lastPromptTokens <= PRUNE_TRIGGER_TOKENS &&
|
|
140
|
+
messages.reduce((sum, m) => sum + estimateMessageTokens(m), 0) <= PRUNE_TRIGGER_TOKENS) {
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
// Match config precedence: an explicit environment value overrides the stored value.
|
|
144
|
+
const enabled = process.env.GLBGPT_CONTEXT_PRUNING ?? getConfigValueSync('context-pruning');
|
|
145
|
+
if (enabled === 'false')
|
|
146
|
+
return null;
|
|
147
|
+
// Select from the projection so already summarized history cannot trigger a trim note.
|
|
148
|
+
const ids = selectPrunable(messages, session.prunedToolCallIds);
|
|
149
|
+
if (ids.length === 0)
|
|
150
|
+
return null;
|
|
151
|
+
const idSet = new Set(ids);
|
|
152
|
+
const projected = applyPruning(messages, idSet);
|
|
153
|
+
const clearedBytes = messages.reduce((sum, m, i) => sum +
|
|
154
|
+
(m.role === 'tool' && m.tool_call_id && idSet.has(m.tool_call_id) && typeof m.content === 'string'
|
|
155
|
+
? Math.max(0, Buffer.byteLength(m.content, 'utf8') - Buffer.byteLength(String(projected[i].content), 'utf8'))
|
|
156
|
+
: 0), 0);
|
|
157
|
+
for (const id of ids)
|
|
158
|
+
session.prunedToolCallIds.add(id);
|
|
159
|
+
return { ids, clearedBytes };
|
|
72
160
|
}
|
|
73
161
|
//# sourceMappingURL=contextPrune.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contextPrune.js","sourceRoot":"","sources":["../../../src/core/contextPrune.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"contextPrune.js","sourceRoot":"","sources":["../../../src/core/contextPrune.ts"],"names":[],"mappings":"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"}
|
|
@@ -33,7 +33,7 @@ export interface ChatTurnParams {
|
|
|
33
33
|
/**
|
|
34
34
|
* Run a single chat turn (client tools optional via `params.registry`; defaults to none)
|
|
35
35
|
* and return the final message, parsed structured output (when a schema was requested), and
|
|
36
|
-
* token usage.
|
|
36
|
+
* token usage summed across all model calls in the turn.
|
|
37
37
|
*
|
|
38
38
|
* Shared by `exec` and the MCP server so both behave identically. `onEvent` streams deltas/usage/
|
|
39
39
|
* tool events live (exec's --jsonl uses it); callers that only want the final result omit it.
|
package/dist/src/core/runChat.js
CHANGED
|
@@ -6,7 +6,7 @@ import { planStructuredOutput, planForMode, parseStructuredJson, stripSyntheticN
|
|
|
6
6
|
/**
|
|
7
7
|
* Run a single chat turn (client tools optional via `params.registry`; defaults to none)
|
|
8
8
|
* and return the final message, parsed structured output (when a schema was requested), and
|
|
9
|
-
* token usage.
|
|
9
|
+
* token usage summed across all model calls in the turn.
|
|
10
10
|
*
|
|
11
11
|
* Shared by `exec` and the MCP server so both behave identically. `onEvent` streams deltas/usage/
|
|
12
12
|
* tool events live (exec's --jsonl uses it); callers that only want the final result omit it.
|
|
@@ -26,6 +26,10 @@ export async function runChatTurn(params, onEvent) {
|
|
|
26
26
|
// call is refunded server-side → one retry in json_object mode with the schema in the prompt.
|
|
27
27
|
const fallback = planForMode('json_object', params.outputSchema);
|
|
28
28
|
const second = await runOnce(params, fallback, onEvent);
|
|
29
|
+
// Keep any usage the rejected request reported, matching the streamed usage events.
|
|
30
|
+
// These are token totals, not a claim about whether the backend charged/refunded it.
|
|
31
|
+
second.usage.prompt_tokens += first.usage.prompt_tokens;
|
|
32
|
+
second.usage.completion_tokens += first.usage.completion_tokens;
|
|
29
33
|
return finish(second, fallback, params);
|
|
30
34
|
}
|
|
31
35
|
return finish(first, plan, params);
|
|
@@ -55,10 +59,9 @@ async function runOnce(params, plan, onEvent) {
|
|
|
55
59
|
result.sawOutput = true;
|
|
56
60
|
break;
|
|
57
61
|
case 'usage':
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
};
|
|
62
|
+
// TurnLoop emits one final usage snapshot per model call, including tool rounds.
|
|
63
|
+
result.usage.prompt_tokens += event.promptTokens;
|
|
64
|
+
result.usage.completion_tokens += event.completionTokens;
|
|
62
65
|
break;
|
|
63
66
|
case 'error':
|
|
64
67
|
result.engineError = event.message;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runChat.js","sourceRoot":"","sources":["../../../src/core/runChat.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"runChat.js","sourceRoot":"","sources":["../../../src/core/runChat.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `request_user_input` tool (Codex-style): when the model hits a decision only the user
|
|
3
|
+
* can make, it asks a structured multiple-choice question and the TUI renders a picker —
|
|
4
|
+
* far better than ambiguity buried in prose. Interactive surfaces only (the TUI registers
|
|
5
|
+
* it; exec/MCP never see it).
|
|
6
|
+
*/
|
|
7
|
+
const MAX_OPTIONS = 4;
|
|
8
|
+
export function createAskUserTool(bridge) {
|
|
9
|
+
return {
|
|
10
|
+
name: 'request_user_input',
|
|
11
|
+
description: 'Ask the user ONE multiple-choice question when you are blocked on a decision that is ' +
|
|
12
|
+
'genuinely theirs to make (ambiguous requirements, an irreversible choice between real ' +
|
|
13
|
+
'alternatives). Give 2-4 distinct options, your recommended one first with ' +
|
|
14
|
+
'"(Recommended)" in its label. Do not use it for choices with an obvious default. The ' +
|
|
15
|
+
'user may also cancel — then proceed on your best judgment.',
|
|
16
|
+
parameters: {
|
|
17
|
+
type: 'object',
|
|
18
|
+
properties: {
|
|
19
|
+
question: { type: 'string', description: 'The complete question, ending with "?".' },
|
|
20
|
+
options: {
|
|
21
|
+
type: 'array',
|
|
22
|
+
description: '2-4 mutually exclusive choices.',
|
|
23
|
+
items: {
|
|
24
|
+
type: 'object',
|
|
25
|
+
properties: {
|
|
26
|
+
label: { type: 'string', description: 'Concise choice text (1-6 words).' },
|
|
27
|
+
description: { type: 'string', description: 'One-line explanation or trade-off.' },
|
|
28
|
+
},
|
|
29
|
+
required: ['label'],
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
required: ['question', 'options'],
|
|
34
|
+
},
|
|
35
|
+
readOnly: true,
|
|
36
|
+
async execute(args) {
|
|
37
|
+
const question = typeof args.question === 'string' ? args.question.trim() : '';
|
|
38
|
+
const raw = Array.isArray(args.options) ? args.options : [];
|
|
39
|
+
const options = raw
|
|
40
|
+
.map((o) => ({
|
|
41
|
+
label: typeof o?.label === 'string'
|
|
42
|
+
? o.label.trim()
|
|
43
|
+
: '',
|
|
44
|
+
description: typeof o?.description === 'string'
|
|
45
|
+
? o.description
|
|
46
|
+
: undefined,
|
|
47
|
+
}))
|
|
48
|
+
.filter((o) => o.label.length > 0)
|
|
49
|
+
.slice(0, MAX_OPTIONS);
|
|
50
|
+
if (!question || options.length < 2) {
|
|
51
|
+
return { error: 'Provide a question and 2-4 options with non-empty labels.' };
|
|
52
|
+
}
|
|
53
|
+
const answer = await bridge.request({ question, options });
|
|
54
|
+
if (answer === null) {
|
|
55
|
+
return { cancelled: true, note: 'The user dismissed the question — proceed on your best judgment.' };
|
|
56
|
+
}
|
|
57
|
+
return { selected: answer.selected };
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=askUserTool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"askUserTool.js","sourceRoot":"","sources":["../../../../src/core/tools/askUserTool.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,MAAM,WAAW,GAAG,CAAC,CAAC;AAEtB,MAAM,UAAU,iBAAiB,CAAC,MAAsB;IACtD,OAAO;QACL,IAAI,EAAE,oBAAoB;QAC1B,WAAW,EACT,uFAAuF;YACvF,wFAAwF;YACxF,4EAA4E;YAC5E,uFAAuF;YACvF,4DAA4D;QAC9D,UAAU,EAAE;YACV,IAAI,EAAE,QAAQ;YACd,UAAU,EAAE;gBACV,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,yCAAyC,EAAE;gBACpF,OAAO,EAAE;oBACP,IAAI,EAAE,OAAO;oBACb,WAAW,EAAE,iCAAiC;oBAC9C,KAAK,EAAE;wBACL,IAAI,EAAE,QAAQ;wBACd,UAAU,EAAE;4BACV,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,kCAAkC,EAAE;4BAC1E,WAAW,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,oCAAoC,EAAE;yBACnF;wBACD,QAAQ,EAAE,CAAC,OAAO,CAAC;qBACpB;iBACF;aACF;YACD,QAAQ,EAAE,CAAC,UAAU,EAAE,SAAS,CAAC;SAClC;QACD,QAAQ,EAAE,IAAI;QACd,KAAK,CAAC,OAAO,CAAC,IAA6B;YACzC,MAAM,QAAQ,GAAG,OAAO,IAAI,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC/E,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;YAC5D,MAAM,OAAO,GAAG,GAAG;iBAChB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBACX,KAAK,EACH,OAAQ,CAAyB,EAAE,KAAK,KAAK,QAAQ;oBACnD,CAAC,CAAE,CAAuB,CAAC,KAAK,CAAC,IAAI,EAAE;oBACvC,CAAC,CAAC,EAAE;gBACR,WAAW,EACT,OAAQ,CAA+B,EAAE,WAAW,KAAK,QAAQ;oBAC/D,CAAC,CAAE,CAA6B,CAAC,WAAW;oBAC5C,CAAC,CAAC,SAAS;aAChB,CAAC,CAAC;iBACF,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;iBACjC,KAAK,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC;YACzB,IAAI,CAAC,QAAQ,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACpC,OAAO,EAAE,KAAK,EAAE,2DAA2D,EAAE,CAAC;YAChF,CAAC;YACD,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;YAC3D,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;gBACpB,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,kEAAkE,EAAE,CAAC;YACvG,CAAC;YACD,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC;QACvC,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ClientTool } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* The `update_plan` tool (Codex/Claude Code-style task checklist). The model maintains a
|
|
4
|
+
* short step list with statuses; the host renders it (TUI panel) so the user can follow a
|
|
5
|
+
* long task at a glance. Pure presentation state — no side effects, replacing wholesale on
|
|
6
|
+
* every call, at most one step in_progress.
|
|
7
|
+
*/
|
|
8
|
+
export type PlanStepStatus = 'pending' | 'in_progress' | 'completed';
|
|
9
|
+
export interface PlanStep {
|
|
10
|
+
step: string;
|
|
11
|
+
status: PlanStepStatus;
|
|
12
|
+
}
|
|
13
|
+
export declare function createPlanTool(deps: {
|
|
14
|
+
onUpdate: (steps: PlanStep[]) => void;
|
|
15
|
+
}): ClientTool;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
const STATUSES = new Set(['pending', 'in_progress', 'completed']);
|
|
2
|
+
const MAX_STEPS = 20;
|
|
3
|
+
export function createPlanTool(deps) {
|
|
4
|
+
return {
|
|
5
|
+
name: 'update_plan',
|
|
6
|
+
description: 'Maintain a short visible task plan for the user while working on a multi-step task. ' +
|
|
7
|
+
'Call it when you start (all steps pending, first in_progress), and again whenever a ' +
|
|
8
|
+
'step completes or the plan changes — always send the FULL updated list. Keep steps ' +
|
|
9
|
+
'short (5-7 words); at most one step may be in_progress.',
|
|
10
|
+
parameters: {
|
|
11
|
+
type: 'object',
|
|
12
|
+
properties: {
|
|
13
|
+
steps: {
|
|
14
|
+
type: 'array',
|
|
15
|
+
description: 'The complete plan, replacing any previous one.',
|
|
16
|
+
items: {
|
|
17
|
+
type: 'object',
|
|
18
|
+
properties: {
|
|
19
|
+
step: { type: 'string', description: 'Short imperative description.' },
|
|
20
|
+
status: { type: 'string', enum: ['pending', 'in_progress', 'completed'] },
|
|
21
|
+
},
|
|
22
|
+
required: ['step', 'status'],
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
required: ['steps'],
|
|
27
|
+
},
|
|
28
|
+
readOnly: true,
|
|
29
|
+
async execute(args) {
|
|
30
|
+
const raw = args.steps;
|
|
31
|
+
if (!Array.isArray(raw) || raw.length === 0) {
|
|
32
|
+
return { error: 'steps must be a non-empty array of {step, status}.' };
|
|
33
|
+
}
|
|
34
|
+
if (raw.length > MAX_STEPS) {
|
|
35
|
+
return { error: `Too many steps (${raw.length}); keep the plan under ${MAX_STEPS}.` };
|
|
36
|
+
}
|
|
37
|
+
const steps = [];
|
|
38
|
+
for (const item of raw) {
|
|
39
|
+
const step = typeof item?.step === 'string'
|
|
40
|
+
? item.step.trim()
|
|
41
|
+
: '';
|
|
42
|
+
const status = item?.status;
|
|
43
|
+
if (!step || typeof status !== 'string' || !STATUSES.has(status)) {
|
|
44
|
+
return {
|
|
45
|
+
error: 'Each item needs a non-empty step string and status pending|in_progress|completed.',
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
steps.push({ step, status: status });
|
|
49
|
+
}
|
|
50
|
+
if (steps.filter((s) => s.status === 'in_progress').length > 1) {
|
|
51
|
+
return { error: 'At most one step may be in_progress.' };
|
|
52
|
+
}
|
|
53
|
+
deps.onUpdate(steps);
|
|
54
|
+
return { ok: true, steps: steps.length };
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=planTool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"planTool.js","sourceRoot":"","sources":["../../../../src/core/tools/planTool.ts"],"names":[],"mappings":"AAgBA,MAAM,QAAQ,GAAwB,IAAI,GAAG,CAAC,CAAC,SAAS,EAAE,aAAa,EAAE,WAAW,CAAC,CAAC,CAAC;AACvF,MAAM,SAAS,GAAG,EAAE,CAAC;AAErB,MAAM,UAAU,cAAc,CAAC,IAA+C;IAC5E,OAAO;QACL,IAAI,EAAE,aAAa;QACnB,WAAW,EACT,sFAAsF;YACtF,sFAAsF;YACtF,qFAAqF;YACrF,yDAAyD;QAC3D,UAAU,EAAE;YACV,IAAI,EAAE,QAAQ;YACd,UAAU,EAAE;gBACV,KAAK,EAAE;oBACL,IAAI,EAAE,OAAO;oBACb,WAAW,EAAE,gDAAgD;oBAC7D,KAAK,EAAE;wBACL,IAAI,EAAE,QAAQ;wBACd,UAAU,EAAE;4BACV,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,+BAA+B,EAAE;4BACtE,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,SAAS,EAAE,aAAa,EAAE,WAAW,CAAC,EAAE;yBAC1E;wBACD,QAAQ,EAAE,CAAC,MAAM,EAAE,QAAQ,CAAC;qBAC7B;iBACF;aACF;YACD,QAAQ,EAAE,CAAC,OAAO,CAAC;SACpB;QACD,QAAQ,EAAE,IAAI;QACd,KAAK,CAAC,OAAO,CAAC,IAA6B;YACzC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC;YACvB,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC5C,OAAO,EAAE,KAAK,EAAE,oDAAoD,EAAE,CAAC;YACzE,CAAC;YACD,IAAI,GAAG,CAAC,MAAM,GAAG,SAAS,EAAE,CAAC;gBAC3B,OAAO,EAAE,KAAK,EAAE,mBAAmB,GAAG,CAAC,MAAM,0BAA0B,SAAS,GAAG,EAAE,CAAC;YACxF,CAAC;YACD,MAAM,KAAK,GAAe,EAAE,CAAC;YAC7B,KAAK,MAAM,IAAI,IAAI,GAAG,EAAE,CAAC;gBACvB,MAAM,IAAI,GACR,OAAQ,IAA2B,EAAE,IAAI,KAAK,QAAQ;oBACpD,CAAC,CAAE,IAAyB,CAAC,IAAI,CAAC,IAAI,EAAE;oBACxC,CAAC,CAAC,EAAE,CAAC;gBACT,MAAM,MAAM,GAAI,IAA6B,EAAE,MAAM,CAAC;gBACtD,IAAI,CAAC,IAAI,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;oBACjE,OAAO;wBACL,KAAK,EAAE,mFAAmF;qBAC3F,CAAC;gBACJ,CAAC;gBACD,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAwB,EAAE,CAAC,CAAC;YACzD,CAAC;YACD,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,aAAa,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC/D,OAAO,EAAE,KAAK,EAAE,sCAAsC,EAAE,CAAC;YAC3D,CAAC;YACD,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;YACrB,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC;QAC3C,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Keep accidental configuration values from allowing an effectively unbounded turn. */
|
|
2
|
+
export declare const MAX_CONFIGURED_TOOL_CALLS = 1000;
|
|
3
|
+
export declare function parseMaxToolCalls(value: string | number, source?: string): number;
|
|
4
|
+
/** Explicit turn option > environment > saved configuration > the bounded default. */
|
|
5
|
+
export declare function resolveMaxToolCalls(override?: number): number;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { getConfigValueSync } from '../config/configFile.js';
|
|
2
|
+
import { MAX_TOOL_CALLS_PER_TURN } from '../fs/limits.js';
|
|
3
|
+
/** Keep accidental configuration values from allowing an effectively unbounded turn. */
|
|
4
|
+
export const MAX_CONFIGURED_TOOL_CALLS = 1000;
|
|
5
|
+
export function parseMaxToolCalls(value, source = 'max-tool-calls') {
|
|
6
|
+
const raw = typeof value === 'string' ? value.trim() : value;
|
|
7
|
+
const parsed = typeof raw === 'string' && /^\d+$/.test(raw) ? Number(raw) : raw;
|
|
8
|
+
if (typeof parsed !== 'number' ||
|
|
9
|
+
!Number.isSafeInteger(parsed) ||
|
|
10
|
+
parsed < 0 ||
|
|
11
|
+
parsed > MAX_CONFIGURED_TOOL_CALLS) {
|
|
12
|
+
throw new Error(`${source} must be an integer between 0 and ${MAX_CONFIGURED_TOOL_CALLS} (0 disables tool calls).`);
|
|
13
|
+
}
|
|
14
|
+
return parsed;
|
|
15
|
+
}
|
|
16
|
+
/** Explicit turn option > environment > saved configuration > the bounded default. */
|
|
17
|
+
export function resolveMaxToolCalls(override) {
|
|
18
|
+
if (override !== undefined)
|
|
19
|
+
return parseMaxToolCalls(override, 'maxToolCalls');
|
|
20
|
+
const env = process.env.GLBGPT_MAX_TOOL_CALLS;
|
|
21
|
+
if (env !== undefined)
|
|
22
|
+
return parseMaxToolCalls(env, 'GLBGPT_MAX_TOOL_CALLS');
|
|
23
|
+
const stored = getConfigValueSync('max-tool-calls');
|
|
24
|
+
if (stored !== undefined)
|
|
25
|
+
return parseMaxToolCalls(stored);
|
|
26
|
+
return MAX_TOOL_CALLS_PER_TURN;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=turnLimits.js.map
|