agent.libx.js 0.87.3 → 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-tfPQy4k5.d.ts → Agent-B0l9qT_j.d.ts} +35 -1
- package/dist/cli.d.ts +4 -1
- package/dist/cli.js +422 -20
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +42 -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 {
|
|
@@ -2559,6 +2583,10 @@ var AgentOptions = class {
|
|
|
2559
2583
|
autoTest;
|
|
2560
2584
|
/** Provider-specific options forwarded to ai.chat() (e.g. cursor mcpServers, cwd). */
|
|
2561
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;
|
|
2562
2590
|
};
|
|
2563
2591
|
var Agent = class _Agent {
|
|
2564
2592
|
options;
|
|
@@ -2574,6 +2602,12 @@ var Agent = class _Agent {
|
|
|
2574
2602
|
// the assembled system prompt from the last prepare()
|
|
2575
2603
|
started = false;
|
|
2576
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
|
+
}
|
|
2577
2611
|
/** Inject tools into a running agent (e.g. dynamically mounted MCP servers). Takes effect on the next turn. */
|
|
2578
2612
|
addTools(tools) {
|
|
2579
2613
|
this.activeTools.push(...tools);
|
|
@@ -2755,12 +2789,17 @@ var Agent = class _Agent {
|
|
|
2755
2789
|
steps++;
|
|
2756
2790
|
let res;
|
|
2757
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
|
+
};
|
|
2758
2797
|
try {
|
|
2759
2798
|
if (useStream) {
|
|
2760
|
-
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 });
|
|
2761
2800
|
res = await this.consumeStream(r);
|
|
2762
2801
|
} else {
|
|
2763
|
-
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 });
|
|
2764
2803
|
res = r;
|
|
2765
2804
|
}
|
|
2766
2805
|
} catch (err2) {
|
|
@@ -3881,6 +3920,7 @@ The filesystem root '/' is the real machine root \u2014 you have full filesystem
|
|
|
3881
3920
|
return [...filtered, ...realShell, ...o.extraTools ?? []];
|
|
3882
3921
|
})(),
|
|
3883
3922
|
maxSteps: o.maxSteps ?? 30,
|
|
3923
|
+
...o.reasoning != null ? { reasoning: o.reasoning } : {},
|
|
3884
3924
|
stream: o.stream ?? false,
|
|
3885
3925
|
host: o.host,
|
|
3886
3926
|
hooks,
|
|
@@ -3947,6 +3987,10 @@ function loadSettings(dir) {
|
|
|
3947
3987
|
if (raw.mcpServers && typeof raw.mcpServers === "object") cfg.mcpServers = raw.mcpServers;
|
|
3948
3988
|
if (raw.permissions && typeof raw.permissions === "object") cfg.permissions = raw.permissions;
|
|
3949
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;
|
|
3950
3994
|
if (raw.env && typeof raw.env === "object") {
|
|
3951
3995
|
for (const [k, v] of Object.entries(raw.env)) {
|
|
3952
3996
|
if (typeof v === "string" && !process.env[k]) process.env[k] = v;
|
|
@@ -4597,12 +4641,13 @@ function completePath(listDir, ref) {
|
|
|
4597
4641
|
import { emitKeypressEvents } from "readline";
|
|
4598
4642
|
var visibleWidth = (s) => s.replace(/\x1b\[[0-9;]*m/g, "").length;
|
|
4599
4643
|
var EditorState = class _EditorState {
|
|
4600
|
-
//
|
|
4601
|
-
constructor(suggest, history = [], classifyPaste, onEmptyPaste) {
|
|
4644
|
+
// pending operator awaiting a motion (dd/dw/cc)
|
|
4645
|
+
constructor(suggest, history = [], classifyPaste, onEmptyPaste, vimEnabled = false) {
|
|
4602
4646
|
this.suggest = suggest;
|
|
4603
4647
|
this.history = history;
|
|
4604
4648
|
this.classifyPaste = classifyPaste;
|
|
4605
4649
|
this.onEmptyPaste = onEmptyPaste;
|
|
4650
|
+
if (vimEnabled) this.vim = "insert";
|
|
4606
4651
|
}
|
|
4607
4652
|
suggest;
|
|
4608
4653
|
history;
|
|
@@ -4629,6 +4674,24 @@ var EditorState = class _EditorState {
|
|
|
4629
4674
|
pasteBuf = "";
|
|
4630
4675
|
pasteSeq = 0;
|
|
4631
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
|
+
}
|
|
4632
4695
|
/** Collapse pastes that would break the single-line editor (multi-line) or flood it (≥ this many chars). */
|
|
4633
4696
|
static PASTE_INLINE_MAX = 800;
|
|
4634
4697
|
beginPaste() {
|
|
@@ -4882,11 +4945,119 @@ var EditorState = class _EditorState {
|
|
|
4882
4945
|
this.searchQuery = "";
|
|
4883
4946
|
this.pastes.clear();
|
|
4884
4947
|
this.closeMenu();
|
|
4948
|
+
this.vimOp = void 0;
|
|
4949
|
+
if (this.vim) this.vim = "insert";
|
|
4885
4950
|
}
|
|
4886
4951
|
};
|
|
4887
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
|
+
}
|
|
4888
5055
|
function applyKey(s, key, str) {
|
|
4889
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
|
+
}
|
|
4890
5061
|
if (k === "paste-start") {
|
|
4891
5062
|
s.beginPaste();
|
|
4892
5063
|
return "none";
|
|
@@ -4965,13 +5136,23 @@ function applyKey(s, key, str) {
|
|
|
4965
5136
|
}
|
|
4966
5137
|
return "submit";
|
|
4967
5138
|
case "tab":
|
|
4968
|
-
if (s.menuOpen)
|
|
5139
|
+
if (s.menuOpen) {
|
|
5140
|
+
s.accept();
|
|
5141
|
+
return "none";
|
|
5142
|
+
}
|
|
5143
|
+
if (s.acceptGhost()) return "none";
|
|
4969
5144
|
return "none";
|
|
5145
|
+
// menu accept, else accept history ghost
|
|
4970
5146
|
case "escape":
|
|
4971
5147
|
if (s.menuOpen) {
|
|
4972
5148
|
s.closeMenu();
|
|
4973
5149
|
return "none";
|
|
4974
5150
|
}
|
|
5151
|
+
if (s.vim === "insert") {
|
|
5152
|
+
s.vim = "normal";
|
|
5153
|
+
return "none";
|
|
5154
|
+
}
|
|
5155
|
+
if (s.vim === "normal" && s.buf.length) return "none";
|
|
4975
5156
|
if (s.buf.length) return "cancel";
|
|
4976
5157
|
if (wasEsc) return "rewind";
|
|
4977
5158
|
s.prevEsc = true;
|
|
@@ -4987,8 +5168,10 @@ function applyKey(s, key, str) {
|
|
|
4987
5168
|
s.left();
|
|
4988
5169
|
return "none";
|
|
4989
5170
|
case "right":
|
|
5171
|
+
if (s.cursor === s.buf.length && s.acceptGhost()) return "none";
|
|
4990
5172
|
s.right();
|
|
4991
5173
|
return "none";
|
|
5174
|
+
// at EOL → accept ghost
|
|
4992
5175
|
case "home":
|
|
4993
5176
|
s.home();
|
|
4994
5177
|
return "none";
|
|
@@ -5022,13 +5205,16 @@ function createLineEditor(out) {
|
|
|
5022
5205
|
function render(s, promptArg, maxVisible, status) {
|
|
5023
5206
|
const cols = out.columns ?? 80;
|
|
5024
5207
|
const mode = !s.searching ? inputMode(s.buf) : void 0;
|
|
5025
|
-
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;
|
|
5026
5210
|
if (curRow > 0) out.write(`\x1B[${curRow}A`);
|
|
5027
5211
|
out.write("\r\x1B[J");
|
|
5028
5212
|
const viewBuf = mode ? s.buf.slice(mode.pfx.length) : s.buf;
|
|
5029
5213
|
const viewCursor = mode ? Math.max(0, s.cursor - mode.pfx.length) : s.cursor;
|
|
5030
5214
|
const { rows, cursorRow, cursorCol } = wrapLayout(visibleWidth(prompt), viewBuf, viewCursor, cols);
|
|
5031
|
-
|
|
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) : ""));
|
|
5032
5218
|
for (let i = 1; i < rows.length; i++) out.write("\r\n" + rows[i]);
|
|
5033
5219
|
const inputRows = rows.length - 1;
|
|
5034
5220
|
let menuRows = 0;
|
|
@@ -5059,7 +5245,7 @@ function createLineEditor(out) {
|
|
|
5059
5245
|
async function readLine(opts) {
|
|
5060
5246
|
const maxVisible = opts.maxVisible ?? 8;
|
|
5061
5247
|
if (!isTTY) return readPlainLine();
|
|
5062
|
-
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);
|
|
5063
5249
|
curRow = 0;
|
|
5064
5250
|
if (opts.initial) s.insert(opts.initial);
|
|
5065
5251
|
s.refresh();
|
|
@@ -5074,11 +5260,31 @@ function createLineEditor(out) {
|
|
|
5074
5260
|
};
|
|
5075
5261
|
process.on("SIGWINCH", onResize);
|
|
5076
5262
|
return new Promise((resolve4) => {
|
|
5263
|
+
const redraw = () => render(s, opts.prompt, maxVisible, opts.status);
|
|
5077
5264
|
const onKey = (str, key) => {
|
|
5078
5265
|
if (key?.ctrl && key.name === "l") {
|
|
5079
5266
|
out.write("\x1B[2J\x1B[3J\x1B[H");
|
|
5080
5267
|
curRow = 0;
|
|
5081
|
-
|
|
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
|
+
});
|
|
5082
5288
|
return;
|
|
5083
5289
|
}
|
|
5084
5290
|
const action = applyKey(s, key ?? {}, str);
|
|
@@ -5452,6 +5658,12 @@ function numFlag(raw, flag) {
|
|
|
5452
5658
|
if (!Number.isFinite(n) || n < 0) throw new Error(`invalid ${flag}: ${raw ?? "(missing value)"}`);
|
|
5453
5659
|
return n;
|
|
5454
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
|
+
}
|
|
5455
5667
|
function parseArgs(argv) {
|
|
5456
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" };
|
|
5457
5669
|
const rest = [];
|
|
@@ -5489,6 +5701,9 @@ function parseArgs(argv) {
|
|
|
5489
5701
|
else if (x === "--disallowedTools" || x === "--disallowed-tools") a.disallowedTools = val(++i, x).split(",").map((s) => s.trim()).filter(Boolean);
|
|
5490
5702
|
else if (x === "--append-system-prompt") a.appendSystemPrompt = val(++i, x);
|
|
5491
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;
|
|
5492
5707
|
else if (x === "--max-steps") a.maxSteps = numFlag(argv[++i], "--max-steps");
|
|
5493
5708
|
else if (x === "--max-tokens") a.maxTokens = numFlag(argv[++i], "--max-tokens");
|
|
5494
5709
|
else if (x === "--timeout") a.timeoutMs = numFlag(argv[++i], "--timeout") * 1e3;
|
|
@@ -5532,6 +5747,9 @@ Flags:
|
|
|
5532
5747
|
--append-system-prompt <t> extra instructions appended to the system prompt for this run
|
|
5533
5748
|
--add-dir <path> mount another directory into the workspace (repeatable; disk mode only)
|
|
5534
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)
|
|
5535
5753
|
--max-steps <n> step budget (default 30)
|
|
5536
5754
|
--max-tokens <n> token budget kill-switch (default 200000)
|
|
5537
5755
|
--timeout <sec> wall-clock kill-switch (default 120)
|
|
@@ -5543,7 +5761,7 @@ Prompts may reference files with @path (e.g. "explain @src/Agent.ts") \u2014 the
|
|
|
5543
5761
|
|
|
5544
5762
|
Providers: set any of ANTHROPIC_API_KEY / OPENAI_API_KEY / GOOGLE_API_KEY / GROQ_API_KEY.
|
|
5545
5763
|
Config: ./.agent/config.{ts,js,json} (project) or ~/.agent/config.* (user).
|
|
5546
|
-
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,
|
|
5547
5765
|
timeoutMs, maxRepeats, maxToolCalls, keepToolOutputs, maxContextTokens,
|
|
5548
5766
|
learnFromMistakes, reflectOnFailure, budget: {\u2026} }
|
|
5549
5767
|
hooks: { preToolUse|postToolUse|onStop: [{ tool?, command, block? }] } \u2014 shell hooks
|
|
@@ -5556,10 +5774,11 @@ Project instructions: ./AGENTS.md or ./CLAUDE.md are auto-loaded (scaffold with
|
|
|
5556
5774
|
Auto-loaded from ./.agent/: commands/, skills/, memory/, agents/.
|
|
5557
5775
|
|
|
5558
5776
|
REPL shortcuts: !<cmd> runs a shell command inline \xB7 #<note> saves a memory \xB7 @path inlines a file
|
|
5559
|
-
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
|
|
5560
5778
|
REPL completion: type / (commands+skills) or @ (files) for a LIVE menu \u2014 \u2191/\u2193 select, \u23CE/Tab accept, Esc dismiss.
|
|
5561
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.
|
|
5562
|
-
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.
|
|
5563
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).`;
|
|
5564
5783
|
function newestModel() {
|
|
5565
5784
|
return listModels().slice().sort((a, b) => (getModelInfo(b)?.releaseDate ?? "").localeCompare(getModelInfo(a)?.releaseDate ?? ""))[0] ?? "";
|
|
@@ -5875,6 +6094,7 @@ function optsFor(args, ai, cfg = {}, extraTools = []) {
|
|
|
5875
6094
|
permissions: rules.length ? new PermissionPolicy({ rules, default: "allow", host: makeHost(), ask: makeAskResolver(resolve3(args.cwd ?? process.cwd())) }) : void 0,
|
|
5876
6095
|
subagents: args.subagents || cfg.subagents || false,
|
|
5877
6096
|
maxSteps: args.maxSteps ?? cfg.maxSteps,
|
|
6097
|
+
reasoning: args.reasoning ?? cfg.reasoning,
|
|
5878
6098
|
// kill-switches: CLI flag > config > Agent default
|
|
5879
6099
|
maxTokens: args.maxTokens ?? cfg.maxTokens,
|
|
5880
6100
|
timeoutMs: args.timeoutMs ?? cfg.timeoutMs,
|
|
@@ -6069,6 +6289,14 @@ function startSession(args, store, agent, cwd) {
|
|
|
6069
6289
|
const data = args.resume ? store.load(args.resume) : store.latestData();
|
|
6070
6290
|
if (data) {
|
|
6071
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
|
+
}
|
|
6072
6300
|
err(dim(` resumed session ${data.meta.id} (${data.meta.turns} turns) \u2014 ${data.meta.title}
|
|
6073
6301
|
`));
|
|
6074
6302
|
if (!args.task) printHistory(data.messages);
|
|
@@ -6078,7 +6306,7 @@ function startSession(args, store, agent, cwd) {
|
|
|
6078
6306
|
`));
|
|
6079
6307
|
}
|
|
6080
6308
|
const now = Date.now();
|
|
6081
|
-
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: [] };
|
|
6082
6310
|
}
|
|
6083
6311
|
var AGENTS_MD_TEMPLATE = `# ${"${name}"}
|
|
6084
6312
|
|
|
@@ -6109,23 +6337,56 @@ function initInstructions(cwd) {
|
|
|
6109
6337
|
err(green(` created ${path}
|
|
6110
6338
|
`) + dim(" edit it, then it auto-loads into every run.\n"));
|
|
6111
6339
|
}
|
|
6112
|
-
function
|
|
6340
|
+
function persistSetting(cwd, key, value) {
|
|
6113
6341
|
const path = join8(cwd, ".agent", "settings.json");
|
|
6114
6342
|
try {
|
|
6115
6343
|
const obj = existsSync7(path) ? JSON.parse(readFileSync5(path, "utf8")) : {};
|
|
6116
|
-
if (obj
|
|
6117
|
-
obj
|
|
6344
|
+
if (obj[key] === value) return;
|
|
6345
|
+
obj[key] = value;
|
|
6118
6346
|
mkdirSync6(dirname3(path), { recursive: true });
|
|
6119
6347
|
writeFileSync6(path, JSON.stringify(obj, null, 2) + "\n");
|
|
6120
6348
|
} catch (e) {
|
|
6121
|
-
err(yellow(` \u26A0 couldn't persist
|
|
6349
|
+
err(yellow(` \u26A0 couldn't persist ${key} to ${path} \u2014 ${e?.message ?? e}
|
|
6122
6350
|
`));
|
|
6123
6351
|
}
|
|
6124
6352
|
}
|
|
6353
|
+
var persistModel = (cwd, model) => persistSetting(cwd, "model", model);
|
|
6125
6354
|
async function repl(args, ai, cfg, cwd) {
|
|
6126
6355
|
const oauth = new McpOAuth({ storePath: join8(cwd, ".agent", "mcp-auth.json") });
|
|
6127
6356
|
const mounted = await mountMcp(cfg, oauth);
|
|
6128
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
|
+
};
|
|
6129
6390
|
const pendingImages = [];
|
|
6130
6391
|
const grabClipboardAttachment = () => {
|
|
6131
6392
|
const dir = join8(tmpdir(), "agentx-pasted");
|
|
@@ -6144,6 +6405,18 @@ async function repl(args, ai, cfg, cwd) {
|
|
|
6144
6405
|
void closeMcp(mounted);
|
|
6145
6406
|
process.exit(130);
|
|
6146
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"));
|
|
6147
6420
|
const store = new SessionStore(cwd);
|
|
6148
6421
|
let session = startSession(args, store, agent, cwd);
|
|
6149
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 });
|
|
@@ -6401,6 +6674,92 @@ ${extra}` : body, checkpoints, cwd);
|
|
|
6401
6674
|
} else err(dim(" " + agent.options.model + "\n"));
|
|
6402
6675
|
}
|
|
6403
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
|
+
},
|
|
6404
6763
|
compact: {
|
|
6405
6764
|
desc: "summarize older context to free up the window",
|
|
6406
6765
|
run: () => {
|
|
@@ -6695,11 +7054,20 @@ ${extra}` : body, checkpoints, cwd);
|
|
|
6695
7054
|
return { hits, token, describe };
|
|
6696
7055
|
};
|
|
6697
7056
|
const editor = createLineEditor(process.stderr);
|
|
7057
|
+
let aborting = false;
|
|
7058
|
+
let pendingRewind = false;
|
|
6698
7059
|
if (process.stdin.isTTY) {
|
|
6699
7060
|
process.stdin.on("keypress", (_s, key) => {
|
|
6700
|
-
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;
|
|
6701
7066
|
activeTurn.abort();
|
|
6702
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"));
|
|
6703
7071
|
}
|
|
6704
7072
|
});
|
|
6705
7073
|
}
|
|
@@ -6717,14 +7085,48 @@ ${extra}` : body, checkpoints, cwd);
|
|
|
6717
7085
|
};
|
|
6718
7086
|
let prefill;
|
|
6719
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;
|
|
6720
7094
|
err("\n");
|
|
6721
7095
|
const initial = prefill;
|
|
6722
7096
|
prefill = void 0;
|
|
6723
7097
|
const ctxTok = estimateTranscriptTokens(agent.transcript);
|
|
6724
7098
|
const ctxCap = agent.options.maxTokens || 2e5;
|
|
6725
7099
|
const usd = session.meta.costUsd ?? 0;
|
|
6726
|
-
const
|
|
6727
|
-
|
|
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
|
+
}));
|
|
6728
7130
|
if (result === null) break;
|
|
6729
7131
|
if (result === REWIND) {
|
|
6730
7132
|
prefill = await rewindToMessage();
|