agent.libx.js 0.87.2 → 0.89.1
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-WTkHB8RY.d.ts → Agent-B0l9qT_j.d.ts} +37 -1
- package/dist/cli.d.ts +4 -1
- package/dist/cli.js +424 -20
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +44 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -2482,6 +2482,30 @@ function checkSyntax(path, content) {
|
|
|
2482
2482
|
}
|
|
2483
2483
|
}
|
|
2484
2484
|
|
|
2485
|
+
// src/reasoning.ts
|
|
2486
|
+
var BUDGET = { low: 2048, medium: 8192, high: 24576 };
|
|
2487
|
+
function toLabel(effort) {
|
|
2488
|
+
if (typeof effort !== "number") return effort === "off" ? "low" : effort;
|
|
2489
|
+
return effort <= BUDGET.low ? "low" : effort < BUDGET.high ? "medium" : "high";
|
|
2490
|
+
}
|
|
2491
|
+
function toBudget(effort) {
|
|
2492
|
+
return typeof effort === "number" ? effort : BUDGET[effort];
|
|
2493
|
+
}
|
|
2494
|
+
function reasoningToChatFragment(model, effort) {
|
|
2495
|
+
if (effort == null || effort === "off") return {};
|
|
2496
|
+
const provider = model.split("/")[0];
|
|
2497
|
+
switch (provider) {
|
|
2498
|
+
case "anthropic": {
|
|
2499
|
+
const budget = toBudget(effort);
|
|
2500
|
+
return { providerOptions: { thinking: { type: "enabled", budget_tokens: budget } }, maxTokens: budget + 8192 };
|
|
2501
|
+
}
|
|
2502
|
+
case "openai":
|
|
2503
|
+
return { providerOptions: { reasoning_effort: toLabel(effort) } };
|
|
2504
|
+
default:
|
|
2505
|
+
return {};
|
|
2506
|
+
}
|
|
2507
|
+
}
|
|
2508
|
+
|
|
2485
2509
|
// src/Agent.ts
|
|
2486
2510
|
var log3 = forComponent("Agent");
|
|
2487
2511
|
var AgentOptions = class {
|
|
@@ -2557,6 +2581,12 @@ var AgentOptions = class {
|
|
|
2557
2581
|
/** Opt-in: after a write-class tool runs, run `command` over the VFS and append any failure to the tool result.
|
|
2558
2582
|
* `tools` defaults to ['Write','Edit','MultiEdit','ApplyEdits']. */
|
|
2559
2583
|
autoTest;
|
|
2584
|
+
/** Provider-specific options forwarded to ai.chat() (e.g. cursor mcpServers, cwd). */
|
|
2585
|
+
providerOptions;
|
|
2586
|
+
/** Extended-thinking / reasoning effort, normalized across providers (anthropic, openai).
|
|
2587
|
+
* `'off'`/undefined = none; `'low'|'medium'|'high'` or a raw token budget. Mapped to the
|
|
2588
|
+
* provider-specific request shape via {@link reasoningToChatFragment}; explicit `providerOptions` wins. */
|
|
2589
|
+
reasoning;
|
|
2560
2590
|
};
|
|
2561
2591
|
var Agent = class _Agent {
|
|
2562
2592
|
options;
|
|
@@ -2572,6 +2602,12 @@ var Agent = class _Agent {
|
|
|
2572
2602
|
// the assembled system prompt from the last prepare()
|
|
2573
2603
|
started = false;
|
|
2574
2604
|
// session-start lifecycle hook fires once per conversation
|
|
2605
|
+
/** Force the next `send()`/`run()` to rebuild the system prompt, tools, plan-mode and permission hooks
|
|
2606
|
+
* from `options` — apply mid-conversation changes to `planMode`/`permissions`/`model` etc. (prepare()
|
|
2607
|
+
* is otherwise memoized per conversation). */
|
|
2608
|
+
reprepare() {
|
|
2609
|
+
this.prepared = false;
|
|
2610
|
+
}
|
|
2575
2611
|
/** Inject tools into a running agent (e.g. dynamically mounted MCP servers). Takes effect on the next turn. */
|
|
2576
2612
|
addTools(tools) {
|
|
2577
2613
|
this.activeTools.push(...tools);
|
|
@@ -2753,12 +2789,17 @@ var Agent = class _Agent {
|
|
|
2753
2789
|
steps++;
|
|
2754
2790
|
let res;
|
|
2755
2791
|
const sent = this.trimContext();
|
|
2792
|
+
const frag = reasoningToChatFragment(o.model, o.reasoning);
|
|
2793
|
+
const reasonOpts = {
|
|
2794
|
+
...frag,
|
|
2795
|
+
...o.providerOptions ? { providerOptions: { ...frag.providerOptions, ...o.providerOptions } } : {}
|
|
2796
|
+
};
|
|
2756
2797
|
try {
|
|
2757
2798
|
if (useStream) {
|
|
2758
|
-
const r = await o.ai.chat({ model: o.model, messages: sent, tools: wireTools, stream: true, signal: o.signal });
|
|
2799
|
+
const r = await o.ai.chat({ model: o.model, messages: sent, tools: wireTools, stream: true, signal: o.signal, ...reasonOpts });
|
|
2759
2800
|
res = await this.consumeStream(r);
|
|
2760
2801
|
} else {
|
|
2761
|
-
const r = await o.ai.chat({ model: o.model, messages: sent, tools: wireTools, stream: false, signal: o.signal });
|
|
2802
|
+
const r = await o.ai.chat({ model: o.model, messages: sent, tools: wireTools, stream: false, signal: o.signal, ...reasonOpts });
|
|
2762
2803
|
res = r;
|
|
2763
2804
|
}
|
|
2764
2805
|
} catch (err2) {
|
|
@@ -3879,6 +3920,7 @@ The filesystem root '/' is the real machine root \u2014 you have full filesystem
|
|
|
3879
3920
|
return [...filtered, ...realShell, ...o.extraTools ?? []];
|
|
3880
3921
|
})(),
|
|
3881
3922
|
maxSteps: o.maxSteps ?? 30,
|
|
3923
|
+
...o.reasoning != null ? { reasoning: o.reasoning } : {},
|
|
3882
3924
|
stream: o.stream ?? false,
|
|
3883
3925
|
host: o.host,
|
|
3884
3926
|
hooks,
|
|
@@ -3945,6 +3987,10 @@ function loadSettings(dir) {
|
|
|
3945
3987
|
if (raw.mcpServers && typeof raw.mcpServers === "object") cfg.mcpServers = raw.mcpServers;
|
|
3946
3988
|
if (raw.permissions && typeof raw.permissions === "object") cfg.permissions = raw.permissions;
|
|
3947
3989
|
if (raw.model && typeof raw.model === "string") cfg.model = raw.model;
|
|
3990
|
+
if (raw.reasoning != null) cfg.reasoning = raw.reasoning;
|
|
3991
|
+
if (raw.permissionMode === "default" || raw.permissionMode === "acceptEdits" || raw.permissionMode === "plan") cfg.permissionMode = raw.permissionMode;
|
|
3992
|
+
if (raw.editorMode === "normal" || raw.editorMode === "vim") cfg.editorMode = raw.editorMode;
|
|
3993
|
+
if (typeof raw.stream === "boolean") cfg.stream = raw.stream;
|
|
3948
3994
|
if (raw.env && typeof raw.env === "object") {
|
|
3949
3995
|
for (const [k, v] of Object.entries(raw.env)) {
|
|
3950
3996
|
if (typeof v === "string" && !process.env[k]) process.env[k] = v;
|
|
@@ -4595,12 +4641,13 @@ function completePath(listDir, ref) {
|
|
|
4595
4641
|
import { emitKeypressEvents } from "readline";
|
|
4596
4642
|
var visibleWidth = (s) => s.replace(/\x1b\[[0-9;]*m/g, "").length;
|
|
4597
4643
|
var EditorState = class _EditorState {
|
|
4598
|
-
//
|
|
4599
|
-
constructor(suggest, history = [], classifyPaste, onEmptyPaste) {
|
|
4644
|
+
// pending operator awaiting a motion (dd/dw/cc)
|
|
4645
|
+
constructor(suggest, history = [], classifyPaste, onEmptyPaste, vimEnabled = false) {
|
|
4600
4646
|
this.suggest = suggest;
|
|
4601
4647
|
this.history = history;
|
|
4602
4648
|
this.classifyPaste = classifyPaste;
|
|
4603
4649
|
this.onEmptyPaste = onEmptyPaste;
|
|
4650
|
+
if (vimEnabled) this.vim = "insert";
|
|
4604
4651
|
}
|
|
4605
4652
|
suggest;
|
|
4606
4653
|
history;
|
|
@@ -4627,6 +4674,24 @@ var EditorState = class _EditorState {
|
|
|
4627
4674
|
pasteBuf = "";
|
|
4628
4675
|
pasteSeq = 0;
|
|
4629
4676
|
pastes = /* @__PURE__ */ new Map();
|
|
4677
|
+
// placeholder → full pasted content
|
|
4678
|
+
// Vim modal editing (opt-in): undefined = emacs/readline keymap; else 'insert'|'normal'.
|
|
4679
|
+
vim;
|
|
4680
|
+
vimOp;
|
|
4681
|
+
/** The most-recent history entry that EXTENDS the current buffer — the inline ghost-suggestion suffix.
|
|
4682
|
+
* Empty unless the buffer is a single non-empty line with the cursor at its end and no menu/search open. */
|
|
4683
|
+
ghost() {
|
|
4684
|
+
if (!this.buf || this.cursor !== this.buf.length || this.menuOpen || this.searching || this.buf.includes("\n")) return "";
|
|
4685
|
+
for (const h of this.history) if (h.length > this.buf.length && h.startsWith(this.buf)) return h.slice(this.buf.length);
|
|
4686
|
+
return "";
|
|
4687
|
+
}
|
|
4688
|
+
/** Accept the ghost suggestion (append its remainder). Returns false when there's nothing to accept. */
|
|
4689
|
+
acceptGhost() {
|
|
4690
|
+
const g = this.ghost();
|
|
4691
|
+
if (!g) return false;
|
|
4692
|
+
this.insert(g);
|
|
4693
|
+
return true;
|
|
4694
|
+
}
|
|
4630
4695
|
/** Collapse pastes that would break the single-line editor (multi-line) or flood it (≥ this many chars). */
|
|
4631
4696
|
static PASTE_INLINE_MAX = 800;
|
|
4632
4697
|
beginPaste() {
|
|
@@ -4880,11 +4945,119 @@ var EditorState = class _EditorState {
|
|
|
4880
4945
|
this.searchQuery = "";
|
|
4881
4946
|
this.pastes.clear();
|
|
4882
4947
|
this.closeMenu();
|
|
4948
|
+
this.vimOp = void 0;
|
|
4949
|
+
if (this.vim) this.vim = "insert";
|
|
4883
4950
|
}
|
|
4884
4951
|
};
|
|
4885
4952
|
var REWIND = "\0REWIND";
|
|
4953
|
+
function applyVimNormal(s, key, str) {
|
|
4954
|
+
const k = key?.name;
|
|
4955
|
+
if (key?.ctrl || key?.meta) return "pass";
|
|
4956
|
+
if (k === "return" || k === "enter") return "pass";
|
|
4957
|
+
if (k === "escape") {
|
|
4958
|
+
s.vimOp = void 0;
|
|
4959
|
+
return "pass";
|
|
4960
|
+
}
|
|
4961
|
+
const op = s.vimOp;
|
|
4962
|
+
s.vimOp = void 0;
|
|
4963
|
+
if (op) {
|
|
4964
|
+
if (str === "d" && op === "d") s.killToStart(), s.killToEnd();
|
|
4965
|
+
else if (str === "c" && op === "c") {
|
|
4966
|
+
s.killToStart();
|
|
4967
|
+
s.killToEnd();
|
|
4968
|
+
s.vim = "insert";
|
|
4969
|
+
} else if (str === "w") {
|
|
4970
|
+
s.killWordForward();
|
|
4971
|
+
while (s.buf[s.cursor] === " ") s.del();
|
|
4972
|
+
if (op === "c") s.vim = "insert";
|
|
4973
|
+
}
|
|
4974
|
+
return "none";
|
|
4975
|
+
}
|
|
4976
|
+
switch (str) {
|
|
4977
|
+
case "i":
|
|
4978
|
+
s.vim = "insert";
|
|
4979
|
+
return "none";
|
|
4980
|
+
case "a":
|
|
4981
|
+
s.right();
|
|
4982
|
+
s.vim = "insert";
|
|
4983
|
+
return "none";
|
|
4984
|
+
case "A":
|
|
4985
|
+
s.end();
|
|
4986
|
+
s.vim = "insert";
|
|
4987
|
+
return "none";
|
|
4988
|
+
case "I":
|
|
4989
|
+
s.home();
|
|
4990
|
+
s.vim = "insert";
|
|
4991
|
+
return "none";
|
|
4992
|
+
case "o":
|
|
4993
|
+
s.end();
|
|
4994
|
+
s.insert("\n");
|
|
4995
|
+
s.vim = "insert";
|
|
4996
|
+
return "none";
|
|
4997
|
+
case "h":
|
|
4998
|
+
s.left();
|
|
4999
|
+
return "none";
|
|
5000
|
+
case "l":
|
|
5001
|
+
s.right();
|
|
5002
|
+
return "none";
|
|
5003
|
+
case "0":
|
|
5004
|
+
s.home();
|
|
5005
|
+
return "none";
|
|
5006
|
+
case "$":
|
|
5007
|
+
s.end();
|
|
5008
|
+
return "none";
|
|
5009
|
+
case "w":
|
|
5010
|
+
case "e":
|
|
5011
|
+
s.wordRight();
|
|
5012
|
+
return "none";
|
|
5013
|
+
case "b":
|
|
5014
|
+
s.wordLeft();
|
|
5015
|
+
return "none";
|
|
5016
|
+
case "x":
|
|
5017
|
+
s.del();
|
|
5018
|
+
return "none";
|
|
5019
|
+
case "D":
|
|
5020
|
+
s.killToEnd();
|
|
5021
|
+
return "none";
|
|
5022
|
+
case "C":
|
|
5023
|
+
s.killToEnd();
|
|
5024
|
+
s.vim = "insert";
|
|
5025
|
+
return "none";
|
|
5026
|
+
case "d":
|
|
5027
|
+
s.vimOp = "d";
|
|
5028
|
+
return "none";
|
|
5029
|
+
case "c":
|
|
5030
|
+
s.vimOp = "c";
|
|
5031
|
+
return "none";
|
|
5032
|
+
}
|
|
5033
|
+
if (k === "left") {
|
|
5034
|
+
s.left();
|
|
5035
|
+
return "none";
|
|
5036
|
+
}
|
|
5037
|
+
if (k === "right") {
|
|
5038
|
+
s.right();
|
|
5039
|
+
return "none";
|
|
5040
|
+
}
|
|
5041
|
+
if (k === "up") {
|
|
5042
|
+
s.historyPrev();
|
|
5043
|
+
return "none";
|
|
5044
|
+
}
|
|
5045
|
+
if (k === "down") {
|
|
5046
|
+
s.historyNext();
|
|
5047
|
+
return "none";
|
|
5048
|
+
}
|
|
5049
|
+
if (k === "backspace") {
|
|
5050
|
+
s.left();
|
|
5051
|
+
return "none";
|
|
5052
|
+
}
|
|
5053
|
+
return "none";
|
|
5054
|
+
}
|
|
4886
5055
|
function applyKey(s, key, str) {
|
|
4887
5056
|
const k = key?.name;
|
|
5057
|
+
if (s.vim === "normal" && !s.pasting && !s.searching) {
|
|
5058
|
+
const r = applyVimNormal(s, key ?? {}, str);
|
|
5059
|
+
if (r !== "pass") return r;
|
|
5060
|
+
}
|
|
4888
5061
|
if (k === "paste-start") {
|
|
4889
5062
|
s.beginPaste();
|
|
4890
5063
|
return "none";
|
|
@@ -4963,13 +5136,23 @@ function applyKey(s, key, str) {
|
|
|
4963
5136
|
}
|
|
4964
5137
|
return "submit";
|
|
4965
5138
|
case "tab":
|
|
4966
|
-
if (s.menuOpen)
|
|
5139
|
+
if (s.menuOpen) {
|
|
5140
|
+
s.accept();
|
|
5141
|
+
return "none";
|
|
5142
|
+
}
|
|
5143
|
+
if (s.acceptGhost()) return "none";
|
|
4967
5144
|
return "none";
|
|
5145
|
+
// menu accept, else accept history ghost
|
|
4968
5146
|
case "escape":
|
|
4969
5147
|
if (s.menuOpen) {
|
|
4970
5148
|
s.closeMenu();
|
|
4971
5149
|
return "none";
|
|
4972
5150
|
}
|
|
5151
|
+
if (s.vim === "insert") {
|
|
5152
|
+
s.vim = "normal";
|
|
5153
|
+
return "none";
|
|
5154
|
+
}
|
|
5155
|
+
if (s.vim === "normal" && s.buf.length) return "none";
|
|
4973
5156
|
if (s.buf.length) return "cancel";
|
|
4974
5157
|
if (wasEsc) return "rewind";
|
|
4975
5158
|
s.prevEsc = true;
|
|
@@ -4985,8 +5168,10 @@ function applyKey(s, key, str) {
|
|
|
4985
5168
|
s.left();
|
|
4986
5169
|
return "none";
|
|
4987
5170
|
case "right":
|
|
5171
|
+
if (s.cursor === s.buf.length && s.acceptGhost()) return "none";
|
|
4988
5172
|
s.right();
|
|
4989
5173
|
return "none";
|
|
5174
|
+
// at EOL → accept ghost
|
|
4990
5175
|
case "home":
|
|
4991
5176
|
s.home();
|
|
4992
5177
|
return "none";
|
|
@@ -5020,13 +5205,16 @@ function createLineEditor(out) {
|
|
|
5020
5205
|
function render(s, promptArg, maxVisible, status) {
|
|
5021
5206
|
const cols = out.columns ?? 80;
|
|
5022
5207
|
const mode = !s.searching ? inputMode(s.buf) : void 0;
|
|
5023
|
-
const
|
|
5208
|
+
const vimTag = s.vim === "normal" && !s.searching && !mode ? inverse(" N ") + " " : "";
|
|
5209
|
+
const prompt = s.searching ? dim2(`(${s.searchMiss ? "failing " : ""}reverse-i-search)\`${s.searchQuery}': `) : mode ? COLOR[mode.name](`${mode.pfx} ${mode.name} \u203A `) : vimTag + promptArg;
|
|
5024
5210
|
if (curRow > 0) out.write(`\x1B[${curRow}A`);
|
|
5025
5211
|
out.write("\r\x1B[J");
|
|
5026
5212
|
const viewBuf = mode ? s.buf.slice(mode.pfx.length) : s.buf;
|
|
5027
5213
|
const viewCursor = mode ? Math.max(0, s.cursor - mode.pfx.length) : s.cursor;
|
|
5028
5214
|
const { rows, cursorRow, cursorCol } = wrapLayout(visibleWidth(prompt), viewBuf, viewCursor, cols);
|
|
5029
|
-
|
|
5215
|
+
const ghost = !mode && !s.searching ? s.ghost() : "";
|
|
5216
|
+
const ghostFits = ghost && rows.length === 1 && visibleWidth(prompt) + viewBuf.length + ghost.length < cols;
|
|
5217
|
+
out.write(prompt + (rows[0] ?? "") + (ghostFits ? dim2(ghost) : ""));
|
|
5030
5218
|
for (let i = 1; i < rows.length; i++) out.write("\r\n" + rows[i]);
|
|
5031
5219
|
const inputRows = rows.length - 1;
|
|
5032
5220
|
let menuRows = 0;
|
|
@@ -5057,7 +5245,7 @@ function createLineEditor(out) {
|
|
|
5057
5245
|
async function readLine(opts) {
|
|
5058
5246
|
const maxVisible = opts.maxVisible ?? 8;
|
|
5059
5247
|
if (!isTTY) return readPlainLine();
|
|
5060
|
-
const s = new EditorState(opts.suggest, opts.history ?? [], opts.classifyPaste, opts.onEmptyPaste);
|
|
5248
|
+
const s = new EditorState(opts.suggest, opts.history ?? [], opts.classifyPaste, opts.onEmptyPaste, opts.vimMode);
|
|
5061
5249
|
curRow = 0;
|
|
5062
5250
|
if (opts.initial) s.insert(opts.initial);
|
|
5063
5251
|
s.refresh();
|
|
@@ -5072,11 +5260,31 @@ function createLineEditor(out) {
|
|
|
5072
5260
|
};
|
|
5073
5261
|
process.on("SIGWINCH", onResize);
|
|
5074
5262
|
return new Promise((resolve4) => {
|
|
5263
|
+
const redraw = () => render(s, opts.prompt, maxVisible, opts.status);
|
|
5075
5264
|
const onKey = (str, key) => {
|
|
5076
5265
|
if (key?.ctrl && key.name === "l") {
|
|
5077
5266
|
out.write("\x1B[2J\x1B[3J\x1B[H");
|
|
5078
5267
|
curRow = 0;
|
|
5079
|
-
|
|
5268
|
+
redraw();
|
|
5269
|
+
return;
|
|
5270
|
+
}
|
|
5271
|
+
if (key?.name === "tab" && key.shift && opts.onCyclePosture) {
|
|
5272
|
+
opts.onCyclePosture();
|
|
5273
|
+
redraw();
|
|
5274
|
+
return;
|
|
5275
|
+
}
|
|
5276
|
+
if (key?.meta && key.name === "t" && opts.onToggleThinking) {
|
|
5277
|
+
opts.onToggleThinking();
|
|
5278
|
+
redraw();
|
|
5279
|
+
return;
|
|
5280
|
+
}
|
|
5281
|
+
if (key?.meta && key.name === "p" && opts.onPickModel) {
|
|
5282
|
+
process.stdin.off("keypress", onKey);
|
|
5283
|
+
void opts.onPickModel().finally(() => {
|
|
5284
|
+
process.stdin.on("keypress", onKey);
|
|
5285
|
+
curRow = 0;
|
|
5286
|
+
redraw();
|
|
5287
|
+
});
|
|
5080
5288
|
return;
|
|
5081
5289
|
}
|
|
5082
5290
|
const action = applyKey(s, key ?? {}, str);
|
|
@@ -5450,6 +5658,12 @@ function numFlag(raw, flag) {
|
|
|
5450
5658
|
if (!Number.isFinite(n) || n < 0) throw new Error(`invalid ${flag}: ${raw ?? "(missing value)"}`);
|
|
5451
5659
|
return n;
|
|
5452
5660
|
}
|
|
5661
|
+
function parseReasoning(raw) {
|
|
5662
|
+
if (raw === "off" || raw === "low" || raw === "medium" || raw === "high") return raw;
|
|
5663
|
+
const n = Number(raw);
|
|
5664
|
+
if (Number.isFinite(n) && n > 0) return n;
|
|
5665
|
+
throw new Error(`invalid --reasoning: ${raw} (use off|low|medium|high or a token budget)`);
|
|
5666
|
+
}
|
|
5453
5667
|
function parseArgs(argv) {
|
|
5454
5668
|
const a = { stream: true, plan: false, ask: false, yes: false, vfs: false, shell: void 0, seed: false, subagents: false, help: false, version: false, cont: false, outputFormat: "text" };
|
|
5455
5669
|
const rest = [];
|
|
@@ -5487,6 +5701,9 @@ function parseArgs(argv) {
|
|
|
5487
5701
|
else if (x === "--disallowedTools" || x === "--disallowed-tools") a.disallowedTools = val(++i, x).split(",").map((s) => s.trim()).filter(Boolean);
|
|
5488
5702
|
else if (x === "--append-system-prompt") a.appendSystemPrompt = val(++i, x);
|
|
5489
5703
|
else if (x === "--add-dir") (a.addDirs ??= []).push(val(++i, x));
|
|
5704
|
+
else if (x === "--reasoning") a.reasoning = parseReasoning(val(++i, x));
|
|
5705
|
+
else if (x === "--session-id") a.sessionId = val(++i, x);
|
|
5706
|
+
else if (x === "--fork-session") a.fork = true;
|
|
5490
5707
|
else if (x === "--max-steps") a.maxSteps = numFlag(argv[++i], "--max-steps");
|
|
5491
5708
|
else if (x === "--max-tokens") a.maxTokens = numFlag(argv[++i], "--max-tokens");
|
|
5492
5709
|
else if (x === "--timeout") a.timeoutMs = numFlag(argv[++i], "--timeout") * 1e3;
|
|
@@ -5530,6 +5747,9 @@ Flags:
|
|
|
5530
5747
|
--append-system-prompt <t> extra instructions appended to the system prompt for this run
|
|
5531
5748
|
--add-dir <path> mount another directory into the workspace (repeatable; disk mode only)
|
|
5532
5749
|
--subagents allow the Task tool (spawn child agents)
|
|
5750
|
+
--reasoning <e> extended thinking: off|low|medium|high or a token budget (anthropic/openai)
|
|
5751
|
+
--session-id <id> use this id for the session (instead of an auto-generated one)
|
|
5752
|
+
--fork-session with -r/-c: branch the resumed session into a new id (source left untouched)
|
|
5533
5753
|
--max-steps <n> step budget (default 30)
|
|
5534
5754
|
--max-tokens <n> token budget kill-switch (default 200000)
|
|
5535
5755
|
--timeout <sec> wall-clock kill-switch (default 120)
|
|
@@ -5541,7 +5761,7 @@ Prompts may reference files with @path (e.g. "explain @src/Agent.ts") \u2014 the
|
|
|
5541
5761
|
|
|
5542
5762
|
Providers: set any of ANTHROPIC_API_KEY / OPENAI_API_KEY / GOOGLE_API_KEY / GROQ_API_KEY.
|
|
5543
5763
|
Config: ./.agent/config.{ts,js,json} (project) or ~/.agent/config.* (user).
|
|
5544
|
-
export default { model, maxSteps, tools, apiKeys, baseUrls, hooks, permissions, mcpServers, maxTokens,
|
|
5764
|
+
export default { model, maxSteps, reasoning, permissionMode, editorMode, tools, apiKeys, baseUrls, hooks, permissions, mcpServers, maxTokens,
|
|
5545
5765
|
timeoutMs, maxRepeats, maxToolCalls, keepToolOutputs, maxContextTokens,
|
|
5546
5766
|
learnFromMistakes, reflectOnFailure, budget: {\u2026} }
|
|
5547
5767
|
hooks: { preToolUse|postToolUse|onStop: [{ tool?, command, block? }] } \u2014 shell hooks
|
|
@@ -5554,10 +5774,11 @@ Project instructions: ./AGENTS.md or ./CLAUDE.md are auto-loaded (scaffold with
|
|
|
5554
5774
|
Auto-loaded from ./.agent/: commands/, skills/, memory/, agents/.
|
|
5555
5775
|
|
|
5556
5776
|
REPL shortcuts: !<cmd> runs a shell command inline \xB7 #<note> saves a memory \xB7 @path inlines a file
|
|
5557
|
-
REPL slash commands: /help /version /tools /permissions /status /cost /context /cwd /model /compact /rewind /undo /clear /sessions /resume /commands /skills /mcp /init /export /paste /exit
|
|
5777
|
+
REPL slash commands: /help /version /tools /permissions /status /cost /context /cwd /model /reasoning /config /rename /compact /rewind /undo /clear /sessions /resume /commands /skills /mcp /init /export /paste /exit
|
|
5558
5778
|
REPL completion: type / (commands+skills) or @ (files) for a LIVE menu \u2014 \u2191/\u2193 select, \u23CE/Tab accept, Esc dismiss.
|
|
5559
5779
|
REPL multi-line: Option/Alt+Enter inserts a newline, or end a line with \\ to continue. Esc cancels a running turn / clears the input line; double-Esc jumps back to edit a previous message.
|
|
5560
|
-
REPL
|
|
5780
|
+
REPL shortcuts: Shift+Tab cycles permission posture (ask \u2192 accept-edits \u2192 plan) \xB7 Alt+T toggles reasoning \xB7 Alt+P switches model \xB7 \u2192 or Tab accepts the dim history ghost-suggestion.
|
|
5781
|
+
REPL editing (emacs/readline): Ctrl-A/E line start/end \xB7 Ctrl-B/F char \xB7 Alt-B/F or Alt/Ctrl-\u2190/\u2192 word \xB7 Ctrl-W kill word \xB7 Ctrl-U/K kill to start/end \xB7 Ctrl-Y yank \xB7 Alt-D kill word fwd \xB7 Ctrl-L clear screen. Set editorMode:'vim' (or /config) for modal vim editing.
|
|
5561
5782
|
REPL paste: large/multi-line pastes collapse to a [Pasted text +N lines] preview (expands on send); a pasted image/file path attaches as [Image]/[File]; /paste grabs a clipboard image (macOS).`;
|
|
5562
5783
|
function newestModel() {
|
|
5563
5784
|
return listModels().slice().sort((a, b) => (getModelInfo(b)?.releaseDate ?? "").localeCompare(getModelInfo(a)?.releaseDate ?? ""))[0] ?? "";
|
|
@@ -5873,6 +6094,7 @@ function optsFor(args, ai, cfg = {}, extraTools = []) {
|
|
|
5873
6094
|
permissions: rules.length ? new PermissionPolicy({ rules, default: "allow", host: makeHost(), ask: makeAskResolver(resolve3(args.cwd ?? process.cwd())) }) : void 0,
|
|
5874
6095
|
subagents: args.subagents || cfg.subagents || false,
|
|
5875
6096
|
maxSteps: args.maxSteps ?? cfg.maxSteps,
|
|
6097
|
+
reasoning: args.reasoning ?? cfg.reasoning,
|
|
5876
6098
|
// kill-switches: CLI flag > config > Agent default
|
|
5877
6099
|
maxTokens: args.maxTokens ?? cfg.maxTokens,
|
|
5878
6100
|
timeoutMs: args.timeoutMs ?? cfg.timeoutMs,
|
|
@@ -6067,6 +6289,14 @@ function startSession(args, store, agent, cwd) {
|
|
|
6067
6289
|
const data = args.resume ? store.load(args.resume) : store.latestData();
|
|
6068
6290
|
if (data) {
|
|
6069
6291
|
agent.transcript = data.messages;
|
|
6292
|
+
if (args.fork) {
|
|
6293
|
+
const now2 = Date.now();
|
|
6294
|
+
const forked = { meta: { ...data.meta, id: args.sessionId ?? store.newId(now2), created: now2, updated: now2, turns: data.meta.turns }, messages: data.messages };
|
|
6295
|
+
err(dim(` forked ${data.meta.id} \u2192 ${forked.meta.id} (${data.meta.turns} turns)
|
|
6296
|
+
`));
|
|
6297
|
+
if (!args.task) printHistory(data.messages);
|
|
6298
|
+
return forked;
|
|
6299
|
+
}
|
|
6070
6300
|
err(dim(` resumed session ${data.meta.id} (${data.meta.turns} turns) \u2014 ${data.meta.title}
|
|
6071
6301
|
`));
|
|
6072
6302
|
if (!args.task) printHistory(data.messages);
|
|
@@ -6076,7 +6306,7 @@ function startSession(args, store, agent, cwd) {
|
|
|
6076
6306
|
`));
|
|
6077
6307
|
}
|
|
6078
6308
|
const now = Date.now();
|
|
6079
|
-
return { meta: { id: store.newId(now), created: now, updated: now, cwd, model: agent.options.model, turns: 0, title: "" }, messages: [] };
|
|
6309
|
+
return { meta: { id: args.sessionId ?? store.newId(now), created: now, updated: now, cwd, model: agent.options.model, turns: 0, title: "" }, messages: [] };
|
|
6080
6310
|
}
|
|
6081
6311
|
var AGENTS_MD_TEMPLATE = `# ${"${name}"}
|
|
6082
6312
|
|
|
@@ -6107,23 +6337,56 @@ function initInstructions(cwd) {
|
|
|
6107
6337
|
err(green(` created ${path}
|
|
6108
6338
|
`) + dim(" edit it, then it auto-loads into every run.\n"));
|
|
6109
6339
|
}
|
|
6110
|
-
function
|
|
6340
|
+
function persistSetting(cwd, key, value) {
|
|
6111
6341
|
const path = join8(cwd, ".agent", "settings.json");
|
|
6112
6342
|
try {
|
|
6113
6343
|
const obj = existsSync7(path) ? JSON.parse(readFileSync5(path, "utf8")) : {};
|
|
6114
|
-
if (obj
|
|
6115
|
-
obj
|
|
6344
|
+
if (obj[key] === value) return;
|
|
6345
|
+
obj[key] = value;
|
|
6116
6346
|
mkdirSync6(dirname3(path), { recursive: true });
|
|
6117
6347
|
writeFileSync6(path, JSON.stringify(obj, null, 2) + "\n");
|
|
6118
6348
|
} catch (e) {
|
|
6119
|
-
err(yellow(` \u26A0 couldn't persist
|
|
6349
|
+
err(yellow(` \u26A0 couldn't persist ${key} to ${path} \u2014 ${e?.message ?? e}
|
|
6120
6350
|
`));
|
|
6121
6351
|
}
|
|
6122
6352
|
}
|
|
6353
|
+
var persistModel = (cwd, model) => persistSetting(cwd, "model", model);
|
|
6123
6354
|
async function repl(args, ai, cfg, cwd) {
|
|
6124
6355
|
const oauth = new McpOAuth({ storePath: join8(cwd, ".agent", "mcp-auth.json") });
|
|
6125
6356
|
const mounted = await mountMcp(cfg, oauth);
|
|
6126
6357
|
const agent = await makeAgent(args, ai, cfg, mounted.flatMap((m) => m.tools));
|
|
6358
|
+
const baseRules = () => [
|
|
6359
|
+
...parsePermRules({ deny: args.disallowedTools }),
|
|
6360
|
+
...parsePermRules({ allow: args.allowedTools }),
|
|
6361
|
+
...parsePermRules(mergePerms(loadPersistedRules(cwd), cfg.permissions))
|
|
6362
|
+
];
|
|
6363
|
+
const askFor = (tools) => tools.map((t) => ({ tool: t, decision: "ask" }));
|
|
6364
|
+
const POSTURES = ["default", "acceptEdits", "plan"];
|
|
6365
|
+
let posture = args.plan || cfg.permissionMode === "plan" ? "plan" : cfg.permissionMode === "acceptEdits" ? "acceptEdits" : "default";
|
|
6366
|
+
const postureLabel = () => posture === "default" ? "ask (default)" : posture === "acceptEdits" ? "accept edits" : "plan mode";
|
|
6367
|
+
const applyPosture = (p) => {
|
|
6368
|
+
posture = p;
|
|
6369
|
+
const ask = p === "acceptEdits" ? askFor(["bash", "Shell"]) : askFor(ASK_MUTATING);
|
|
6370
|
+
agent.options.permissions = new PermissionPolicy({ rules: [...baseRules(), ...ask], default: "allow", host: makeHost(), ask: makeAskResolver(cwd) });
|
|
6371
|
+
agent.options.planMode = p === "plan";
|
|
6372
|
+
agent.reprepare();
|
|
6373
|
+
};
|
|
6374
|
+
const cyclePosture = () => {
|
|
6375
|
+
applyPosture(POSTURES[(POSTURES.indexOf(posture) + 1) % POSTURES.length]);
|
|
6376
|
+
err(dim(` \u21E5 ${postureLabel()}
|
|
6377
|
+
`));
|
|
6378
|
+
return postureLabel();
|
|
6379
|
+
};
|
|
6380
|
+
if (!args.yes) applyPosture(posture);
|
|
6381
|
+
const REASONING_CYCLE = ["off", "low", "medium", "high"];
|
|
6382
|
+
const toggleReasoning = () => {
|
|
6383
|
+
const cur = String(agent.options.reasoning ?? "off");
|
|
6384
|
+
const next = REASONING_CYCLE[(Math.max(0, REASONING_CYCLE.indexOf(cur)) + 1) % REASONING_CYCLE.length];
|
|
6385
|
+
agent.options.reasoning = next;
|
|
6386
|
+
err(dim(` ~ reasoning \u2192 ${next}
|
|
6387
|
+
`));
|
|
6388
|
+
return next;
|
|
6389
|
+
};
|
|
6127
6390
|
const pendingImages = [];
|
|
6128
6391
|
const grabClipboardAttachment = () => {
|
|
6129
6392
|
const dir = join8(tmpdir(), "agentx-pasted");
|
|
@@ -6142,6 +6405,18 @@ async function repl(args, ai, cfg, cwd) {
|
|
|
6142
6405
|
void closeMcp(mounted);
|
|
6143
6406
|
process.exit(130);
|
|
6144
6407
|
});
|
|
6408
|
+
const isStreamTeardown = (e) => /NGHTTP2_FRAME_SIZE_ERROR|ERR_HTTP2_STREAM_ERROR/.test(`${e?.code ?? ""} ${e?.rawMessage ?? ""} ${e?.message ?? ""}`);
|
|
6409
|
+
const onFatal = (kind) => (e) => {
|
|
6410
|
+
if (isStreamTeardown(e)) {
|
|
6411
|
+
log11.debug(`suppressed ${kind} (connect/HTTP-2 stream teardown on cancel)`, e);
|
|
6412
|
+
return;
|
|
6413
|
+
}
|
|
6414
|
+
console.error(e);
|
|
6415
|
+
void closeMcp(mounted);
|
|
6416
|
+
process.exit(1);
|
|
6417
|
+
};
|
|
6418
|
+
process.on("unhandledRejection", onFatal("unhandledRejection"));
|
|
6419
|
+
process.on("uncaughtException", onFatal("uncaughtException"));
|
|
6145
6420
|
const store = new SessionStore(cwd);
|
|
6146
6421
|
let session = startSession(args, store, agent, cwd);
|
|
6147
6422
|
const checkpoints = args.vfs || args.boddb ? new CheckpointStack(agent.options.fs) : new GitCheckpoints({ workTree: cwd, gitDir: join8(cwd, ".agent", "checkpoints.git"), addDirs: args.addDirs, sessionId: session.meta.id });
|
|
@@ -6399,6 +6674,92 @@ ${extra}` : body, checkpoints, cwd);
|
|
|
6399
6674
|
} else err(dim(" " + agent.options.model + "\n"));
|
|
6400
6675
|
}
|
|
6401
6676
|
},
|
|
6677
|
+
reasoning: {
|
|
6678
|
+
desc: "extended thinking \u2014 /reasoning <off|low|medium|high|tokens>, or alone for an interactive picker",
|
|
6679
|
+
run: async (a) => {
|
|
6680
|
+
const current = String(agent.options.reasoning ?? "off");
|
|
6681
|
+
let next;
|
|
6682
|
+
if (a[0]) {
|
|
6683
|
+
try {
|
|
6684
|
+
next = parseReasoning(a[0]);
|
|
6685
|
+
} catch (e) {
|
|
6686
|
+
err(yellow(" " + (e?.message ?? e) + "\n"));
|
|
6687
|
+
return;
|
|
6688
|
+
}
|
|
6689
|
+
} else {
|
|
6690
|
+
const items = [
|
|
6691
|
+
{ label: "off", value: "off", desc: "no extended thinking" },
|
|
6692
|
+
{ label: "low", value: "low", desc: "minimal reasoning (~2k tokens)" },
|
|
6693
|
+
{ label: "medium", value: "medium", desc: "balanced (~8k tokens)" },
|
|
6694
|
+
{ label: "high", value: "high", desc: "maximal reasoning (~24k tokens)" }
|
|
6695
|
+
];
|
|
6696
|
+
const picked = await selectMenu(process.stderr, { title: `Reasoning effort \xB7 current: ${current}`, items, current });
|
|
6697
|
+
if (!picked) {
|
|
6698
|
+
err(dim(" " + current + "\n"));
|
|
6699
|
+
return;
|
|
6700
|
+
}
|
|
6701
|
+
next = picked;
|
|
6702
|
+
}
|
|
6703
|
+
agent.options.reasoning = next;
|
|
6704
|
+
if (next !== "off" && getModelInfo(agent.options.model)?.reasoning === false)
|
|
6705
|
+
err(yellow(` note: ${agent.options.model} has no reasoning capability \u2014 setting may be ignored
|
|
6706
|
+
`));
|
|
6707
|
+
err(dim(" reasoning \u2192 " + next + "\n"));
|
|
6708
|
+
}
|
|
6709
|
+
},
|
|
6710
|
+
config: {
|
|
6711
|
+
desc: "view/change settings \u2014 model, reasoning, permission posture, streaming, editor mode",
|
|
6712
|
+
run: async () => {
|
|
6713
|
+
for (; ; ) {
|
|
6714
|
+
const items = [
|
|
6715
|
+
{ label: "model", value: "model", desc: agent.options.model },
|
|
6716
|
+
{ label: "reasoning", value: "reasoning", desc: String(agent.options.reasoning ?? "off") },
|
|
6717
|
+
{ label: "permission posture", value: "posture", desc: postureLabel() + " (Shift+Tab)" },
|
|
6718
|
+
{ label: "streaming", value: "stream", desc: agent.options.stream ? "on" : "off" },
|
|
6719
|
+
{ label: "editor mode", value: "editor", desc: cfg.editorMode === "vim" ? "vim" : "normal" }
|
|
6720
|
+
];
|
|
6721
|
+
const pick = await selectMenu(process.stderr, { title: "Settings \xB7 \u21B5 change \xB7 esc close", items });
|
|
6722
|
+
if (!pick) return;
|
|
6723
|
+
if (pick === "model") {
|
|
6724
|
+
const m = await pickModel(agent.options.model);
|
|
6725
|
+
if (m) {
|
|
6726
|
+
agent.options.model = m;
|
|
6727
|
+
persistModel(cwd, m);
|
|
6728
|
+
}
|
|
6729
|
+
} else if (pick === "reasoning") {
|
|
6730
|
+
await builtins.reasoning.run([]);
|
|
6731
|
+
persistSetting(cwd, "reasoning", agent.options.reasoning ?? "off");
|
|
6732
|
+
} else if (pick === "posture") {
|
|
6733
|
+
cyclePosture();
|
|
6734
|
+
persistSetting(cwd, "permissionMode", posture);
|
|
6735
|
+
} else if (pick === "stream") {
|
|
6736
|
+
agent.options.stream = !agent.options.stream;
|
|
6737
|
+
persistSetting(cwd, "stream", agent.options.stream);
|
|
6738
|
+
err(dim(" streaming \u2192 " + (agent.options.stream ? "on" : "off") + "\n"));
|
|
6739
|
+
} else if (pick === "editor") {
|
|
6740
|
+
cfg.editorMode = cfg.editorMode === "vim" ? "normal" : "vim";
|
|
6741
|
+
persistSetting(cwd, "editorMode", cfg.editorMode);
|
|
6742
|
+
err(dim(" editor \u2192 " + cfg.editorMode + "\n"));
|
|
6743
|
+
}
|
|
6744
|
+
}
|
|
6745
|
+
}
|
|
6746
|
+
},
|
|
6747
|
+
rename: {
|
|
6748
|
+
desc: "rename the current session \u2014 /rename <title>",
|
|
6749
|
+
run: (a) => {
|
|
6750
|
+
const t = a.join(" ").trim();
|
|
6751
|
+
if (!t) {
|
|
6752
|
+
err(dim(" title: " + (session.meta.title || "(none)") + "\n"));
|
|
6753
|
+
return;
|
|
6754
|
+
}
|
|
6755
|
+
session.meta.title = t;
|
|
6756
|
+
try {
|
|
6757
|
+
store.save(session);
|
|
6758
|
+
} catch {
|
|
6759
|
+
}
|
|
6760
|
+
err(dim(" renamed \u2192 " + t + "\n"));
|
|
6761
|
+
}
|
|
6762
|
+
},
|
|
6402
6763
|
compact: {
|
|
6403
6764
|
desc: "summarize older context to free up the window",
|
|
6404
6765
|
run: () => {
|
|
@@ -6693,11 +7054,20 @@ ${extra}` : body, checkpoints, cwd);
|
|
|
6693
7054
|
return { hits, token, describe };
|
|
6694
7055
|
};
|
|
6695
7056
|
const editor = createLineEditor(process.stderr);
|
|
7057
|
+
let aborting = false;
|
|
7058
|
+
let pendingRewind = false;
|
|
6696
7059
|
if (process.stdin.isTTY) {
|
|
6697
7060
|
process.stdin.on("keypress", (_s, key) => {
|
|
6698
|
-
if (activeTurn
|
|
7061
|
+
if (!activeTurn) return;
|
|
7062
|
+
const cancel = key?.name === "escape" || key?.ctrl && key?.name === "c";
|
|
7063
|
+
if (!cancel) return;
|
|
7064
|
+
if (!aborting) {
|
|
7065
|
+
aborting = true;
|
|
6699
7066
|
activeTurn.abort();
|
|
6700
7067
|
err(yellow("\n \u238B cancelling\u2026\n"));
|
|
7068
|
+
} else if (key?.name === "escape" && !pendingRewind) {
|
|
7069
|
+
pendingRewind = true;
|
|
7070
|
+
err(dim(" \u238B\u238B jumping back to edit\u2026\n"));
|
|
6701
7071
|
}
|
|
6702
7072
|
});
|
|
6703
7073
|
}
|
|
@@ -6715,14 +7085,48 @@ ${extra}` : body, checkpoints, cwd);
|
|
|
6715
7085
|
};
|
|
6716
7086
|
let prefill;
|
|
6717
7087
|
while (true) {
|
|
7088
|
+
if (pendingRewind) {
|
|
7089
|
+
pendingRewind = false;
|
|
7090
|
+
const t = await rewindToMessage();
|
|
7091
|
+
if (t !== void 0) prefill = t;
|
|
7092
|
+
}
|
|
7093
|
+
aborting = false;
|
|
6718
7094
|
err("\n");
|
|
6719
7095
|
const initial = prefill;
|
|
6720
7096
|
prefill = void 0;
|
|
6721
7097
|
const ctxTok = estimateTranscriptTokens(agent.transcript);
|
|
6722
7098
|
const ctxCap = agent.options.maxTokens || 2e5;
|
|
6723
7099
|
const usd = session.meta.costUsd ?? 0;
|
|
6724
|
-
const
|
|
6725
|
-
|
|
7100
|
+
const computeFooter = () => {
|
|
7101
|
+
const parts = [];
|
|
7102
|
+
if (ctxTok > 400) parts.push(`${Math.round(ctxTok / ctxCap * 100)}% ctx (~${(ctxTok / 1e3).toFixed(1)}k/${Math.round(ctxCap / 1e3)}k)`);
|
|
7103
|
+
if (usd > 0) parts.push(`${session.meta.costEstimated ? "~" : ""}${fmtUsd(usd)}`);
|
|
7104
|
+
if (posture !== "default") parts.push(postureLabel());
|
|
7105
|
+
const r = agent.options.reasoning;
|
|
7106
|
+
if (r && r !== "off") parts.push(`reasoning:${r}`);
|
|
7107
|
+
return parts.join(" \xB7 ");
|
|
7108
|
+
};
|
|
7109
|
+
const result = await readMultiline((cont) => editor.readLine({
|
|
7110
|
+
prompt: cont ? contPrompt : promptStr,
|
|
7111
|
+
suggest,
|
|
7112
|
+
history,
|
|
7113
|
+
classifyPaste,
|
|
7114
|
+
onEmptyPaste: grabClipboardAttachment,
|
|
7115
|
+
initial: cont ? void 0 : initial,
|
|
7116
|
+
status: computeFooter,
|
|
7117
|
+
vimMode: cfg.editorMode === "vim",
|
|
7118
|
+
onCyclePosture: cyclePosture,
|
|
7119
|
+
onToggleThinking: toggleReasoning,
|
|
7120
|
+
onPickModel: async () => {
|
|
7121
|
+
const picked = await pickModel(agent.options.model);
|
|
7122
|
+
if (picked) {
|
|
7123
|
+
agent.options.model = picked;
|
|
7124
|
+
persistModel(cwd, picked);
|
|
7125
|
+
err(dim(" model \u2192 " + picked + "\n"));
|
|
7126
|
+
}
|
|
7127
|
+
return picked;
|
|
7128
|
+
}
|
|
7129
|
+
}));
|
|
6726
7130
|
if (result === null) break;
|
|
6727
7131
|
if (result === REWIND) {
|
|
6728
7132
|
prefill = await rewindToMessage();
|