agent.libx.js 0.87.3 → 0.89.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/dist/{Agent-tfPQy4k5.d.ts → Agent-B0l9qT_j.d.ts} +35 -1
- package/dist/cli.d.ts +4 -1
- package/dist/cli.js +436 -23
- 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;
|
|
@@ -4596,13 +4640,15 @@ function completePath(listDir, ref) {
|
|
|
4596
4640
|
// cli/lineEditor.ts
|
|
4597
4641
|
import { emitKeypressEvents } from "readline";
|
|
4598
4642
|
var visibleWidth = (s) => s.replace(/\x1b\[[0-9;]*m/g, "").length;
|
|
4643
|
+
var isPrintable = (str) => !!str && !/[\x00-\x1f\x7f]/.test(str);
|
|
4599
4644
|
var EditorState = class _EditorState {
|
|
4600
|
-
//
|
|
4601
|
-
constructor(suggest, history = [], classifyPaste, onEmptyPaste) {
|
|
4645
|
+
// pending operator awaiting a motion (dd/dw/cc)
|
|
4646
|
+
constructor(suggest, history = [], classifyPaste, onEmptyPaste, vimEnabled = false) {
|
|
4602
4647
|
this.suggest = suggest;
|
|
4603
4648
|
this.history = history;
|
|
4604
4649
|
this.classifyPaste = classifyPaste;
|
|
4605
4650
|
this.onEmptyPaste = onEmptyPaste;
|
|
4651
|
+
if (vimEnabled) this.vim = "insert";
|
|
4606
4652
|
}
|
|
4607
4653
|
suggest;
|
|
4608
4654
|
history;
|
|
@@ -4629,6 +4675,24 @@ var EditorState = class _EditorState {
|
|
|
4629
4675
|
pasteBuf = "";
|
|
4630
4676
|
pasteSeq = 0;
|
|
4631
4677
|
pastes = /* @__PURE__ */ new Map();
|
|
4678
|
+
// placeholder → full pasted content
|
|
4679
|
+
// Vim modal editing (opt-in): undefined = emacs/readline keymap; else 'insert'|'normal'.
|
|
4680
|
+
vim;
|
|
4681
|
+
vimOp;
|
|
4682
|
+
/** The most-recent history entry that EXTENDS the current buffer — the inline ghost-suggestion suffix.
|
|
4683
|
+
* Empty unless the buffer is a single non-empty line with the cursor at its end and no menu/search open. */
|
|
4684
|
+
ghost() {
|
|
4685
|
+
if (!this.buf || this.cursor !== this.buf.length || this.menuOpen || this.searching || this.buf.includes("\n")) return "";
|
|
4686
|
+
for (const h of this.history) if (h.length > this.buf.length && h.startsWith(this.buf)) return h.slice(this.buf.length);
|
|
4687
|
+
return "";
|
|
4688
|
+
}
|
|
4689
|
+
/** Accept the ghost suggestion (append its remainder). Returns false when there's nothing to accept. */
|
|
4690
|
+
acceptGhost() {
|
|
4691
|
+
const g = this.ghost();
|
|
4692
|
+
if (!g) return false;
|
|
4693
|
+
this.insert(g);
|
|
4694
|
+
return true;
|
|
4695
|
+
}
|
|
4632
4696
|
/** Collapse pastes that would break the single-line editor (multi-line) or flood it (≥ this many chars). */
|
|
4633
4697
|
static PASTE_INLINE_MAX = 800;
|
|
4634
4698
|
beginPaste() {
|
|
@@ -4882,11 +4946,119 @@ var EditorState = class _EditorState {
|
|
|
4882
4946
|
this.searchQuery = "";
|
|
4883
4947
|
this.pastes.clear();
|
|
4884
4948
|
this.closeMenu();
|
|
4949
|
+
this.vimOp = void 0;
|
|
4950
|
+
if (this.vim) this.vim = "insert";
|
|
4885
4951
|
}
|
|
4886
4952
|
};
|
|
4887
4953
|
var REWIND = "\0REWIND";
|
|
4954
|
+
function applyVimNormal(s, key, str) {
|
|
4955
|
+
const k = key?.name;
|
|
4956
|
+
if (key?.ctrl || key?.meta) return "pass";
|
|
4957
|
+
if (k === "return" || k === "enter") return "pass";
|
|
4958
|
+
if (k === "escape") {
|
|
4959
|
+
s.vimOp = void 0;
|
|
4960
|
+
return "pass";
|
|
4961
|
+
}
|
|
4962
|
+
const op = s.vimOp;
|
|
4963
|
+
s.vimOp = void 0;
|
|
4964
|
+
if (op) {
|
|
4965
|
+
if (str === "d" && op === "d") s.killToStart(), s.killToEnd();
|
|
4966
|
+
else if (str === "c" && op === "c") {
|
|
4967
|
+
s.killToStart();
|
|
4968
|
+
s.killToEnd();
|
|
4969
|
+
s.vim = "insert";
|
|
4970
|
+
} else if (str === "w") {
|
|
4971
|
+
s.killWordForward();
|
|
4972
|
+
while (s.buf[s.cursor] === " ") s.del();
|
|
4973
|
+
if (op === "c") s.vim = "insert";
|
|
4974
|
+
}
|
|
4975
|
+
return "none";
|
|
4976
|
+
}
|
|
4977
|
+
switch (str) {
|
|
4978
|
+
case "i":
|
|
4979
|
+
s.vim = "insert";
|
|
4980
|
+
return "none";
|
|
4981
|
+
case "a":
|
|
4982
|
+
s.right();
|
|
4983
|
+
s.vim = "insert";
|
|
4984
|
+
return "none";
|
|
4985
|
+
case "A":
|
|
4986
|
+
s.end();
|
|
4987
|
+
s.vim = "insert";
|
|
4988
|
+
return "none";
|
|
4989
|
+
case "I":
|
|
4990
|
+
s.home();
|
|
4991
|
+
s.vim = "insert";
|
|
4992
|
+
return "none";
|
|
4993
|
+
case "o":
|
|
4994
|
+
s.end();
|
|
4995
|
+
s.insert("\n");
|
|
4996
|
+
s.vim = "insert";
|
|
4997
|
+
return "none";
|
|
4998
|
+
case "h":
|
|
4999
|
+
s.left();
|
|
5000
|
+
return "none";
|
|
5001
|
+
case "l":
|
|
5002
|
+
s.right();
|
|
5003
|
+
return "none";
|
|
5004
|
+
case "0":
|
|
5005
|
+
s.home();
|
|
5006
|
+
return "none";
|
|
5007
|
+
case "$":
|
|
5008
|
+
s.end();
|
|
5009
|
+
return "none";
|
|
5010
|
+
case "w":
|
|
5011
|
+
case "e":
|
|
5012
|
+
s.wordRight();
|
|
5013
|
+
return "none";
|
|
5014
|
+
case "b":
|
|
5015
|
+
s.wordLeft();
|
|
5016
|
+
return "none";
|
|
5017
|
+
case "x":
|
|
5018
|
+
s.del();
|
|
5019
|
+
return "none";
|
|
5020
|
+
case "D":
|
|
5021
|
+
s.killToEnd();
|
|
5022
|
+
return "none";
|
|
5023
|
+
case "C":
|
|
5024
|
+
s.killToEnd();
|
|
5025
|
+
s.vim = "insert";
|
|
5026
|
+
return "none";
|
|
5027
|
+
case "d":
|
|
5028
|
+
s.vimOp = "d";
|
|
5029
|
+
return "none";
|
|
5030
|
+
case "c":
|
|
5031
|
+
s.vimOp = "c";
|
|
5032
|
+
return "none";
|
|
5033
|
+
}
|
|
5034
|
+
if (k === "left") {
|
|
5035
|
+
s.left();
|
|
5036
|
+
return "none";
|
|
5037
|
+
}
|
|
5038
|
+
if (k === "right") {
|
|
5039
|
+
s.right();
|
|
5040
|
+
return "none";
|
|
5041
|
+
}
|
|
5042
|
+
if (k === "up") {
|
|
5043
|
+
s.historyPrev();
|
|
5044
|
+
return "none";
|
|
5045
|
+
}
|
|
5046
|
+
if (k === "down") {
|
|
5047
|
+
s.historyNext();
|
|
5048
|
+
return "none";
|
|
5049
|
+
}
|
|
5050
|
+
if (k === "backspace") {
|
|
5051
|
+
s.left();
|
|
5052
|
+
return "none";
|
|
5053
|
+
}
|
|
5054
|
+
return "none";
|
|
5055
|
+
}
|
|
4888
5056
|
function applyKey(s, key, str) {
|
|
4889
5057
|
const k = key?.name;
|
|
5058
|
+
if (s.vim === "normal" && !s.pasting && !s.searching) {
|
|
5059
|
+
const r = applyVimNormal(s, key ?? {}, str);
|
|
5060
|
+
if (r !== "pass") return r;
|
|
5061
|
+
}
|
|
4890
5062
|
if (k === "paste-start") {
|
|
4891
5063
|
s.beginPaste();
|
|
4892
5064
|
return "none";
|
|
@@ -4921,7 +5093,7 @@ function applyKey(s, key, str) {
|
|
|
4921
5093
|
s.searchBack();
|
|
4922
5094
|
return "none";
|
|
4923
5095
|
}
|
|
4924
|
-
if (
|
|
5096
|
+
if (!key?.ctrl && !key?.meta && isPrintable(str)) {
|
|
4925
5097
|
s.searchType(str);
|
|
4926
5098
|
return "none";
|
|
4927
5099
|
}
|
|
@@ -4965,13 +5137,23 @@ function applyKey(s, key, str) {
|
|
|
4965
5137
|
}
|
|
4966
5138
|
return "submit";
|
|
4967
5139
|
case "tab":
|
|
4968
|
-
if (s.menuOpen)
|
|
5140
|
+
if (s.menuOpen) {
|
|
5141
|
+
s.accept();
|
|
5142
|
+
return "none";
|
|
5143
|
+
}
|
|
5144
|
+
if (s.acceptGhost()) return "none";
|
|
4969
5145
|
return "none";
|
|
5146
|
+
// menu accept, else accept history ghost
|
|
4970
5147
|
case "escape":
|
|
4971
5148
|
if (s.menuOpen) {
|
|
4972
5149
|
s.closeMenu();
|
|
4973
5150
|
return "none";
|
|
4974
5151
|
}
|
|
5152
|
+
if (s.vim === "insert") {
|
|
5153
|
+
s.vim = "normal";
|
|
5154
|
+
return "none";
|
|
5155
|
+
}
|
|
5156
|
+
if (s.vim === "normal" && s.buf.length) return "none";
|
|
4975
5157
|
if (s.buf.length) return "cancel";
|
|
4976
5158
|
if (wasEsc) return "rewind";
|
|
4977
5159
|
s.prevEsc = true;
|
|
@@ -4987,8 +5169,10 @@ function applyKey(s, key, str) {
|
|
|
4987
5169
|
s.left();
|
|
4988
5170
|
return "none";
|
|
4989
5171
|
case "right":
|
|
5172
|
+
if (s.cursor === s.buf.length && s.acceptGhost()) return "none";
|
|
4990
5173
|
s.right();
|
|
4991
5174
|
return "none";
|
|
5175
|
+
// at EOL → accept ghost
|
|
4992
5176
|
case "home":
|
|
4993
5177
|
s.home();
|
|
4994
5178
|
return "none";
|
|
@@ -5002,7 +5186,7 @@ function applyKey(s, key, str) {
|
|
|
5002
5186
|
s.del();
|
|
5003
5187
|
return "none";
|
|
5004
5188
|
}
|
|
5005
|
-
if (
|
|
5189
|
+
if (!key?.ctrl && !key?.meta && isPrintable(str)) {
|
|
5006
5190
|
s.insert(str);
|
|
5007
5191
|
return "none";
|
|
5008
5192
|
}
|
|
@@ -5022,13 +5206,16 @@ function createLineEditor(out) {
|
|
|
5022
5206
|
function render(s, promptArg, maxVisible, status) {
|
|
5023
5207
|
const cols = out.columns ?? 80;
|
|
5024
5208
|
const mode = !s.searching ? inputMode(s.buf) : void 0;
|
|
5025
|
-
const
|
|
5209
|
+
const vimTag = s.vim === "normal" && !s.searching && !mode ? inverse(" N ") + " " : "";
|
|
5210
|
+
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
5211
|
if (curRow > 0) out.write(`\x1B[${curRow}A`);
|
|
5027
5212
|
out.write("\r\x1B[J");
|
|
5028
5213
|
const viewBuf = mode ? s.buf.slice(mode.pfx.length) : s.buf;
|
|
5029
5214
|
const viewCursor = mode ? Math.max(0, s.cursor - mode.pfx.length) : s.cursor;
|
|
5030
5215
|
const { rows, cursorRow, cursorCol } = wrapLayout(visibleWidth(prompt), viewBuf, viewCursor, cols);
|
|
5031
|
-
|
|
5216
|
+
const ghost = !mode && !s.searching ? s.ghost() : "";
|
|
5217
|
+
const ghostFits = ghost && rows.length === 1 && visibleWidth(prompt) + viewBuf.length + ghost.length < cols;
|
|
5218
|
+
out.write(prompt + (rows[0] ?? "") + (ghostFits ? dim2(ghost) : ""));
|
|
5032
5219
|
for (let i = 1; i < rows.length; i++) out.write("\r\n" + rows[i]);
|
|
5033
5220
|
const inputRows = rows.length - 1;
|
|
5034
5221
|
let menuRows = 0;
|
|
@@ -5059,7 +5246,7 @@ function createLineEditor(out) {
|
|
|
5059
5246
|
async function readLine(opts) {
|
|
5060
5247
|
const maxVisible = opts.maxVisible ?? 8;
|
|
5061
5248
|
if (!isTTY) return readPlainLine();
|
|
5062
|
-
const s = new EditorState(opts.suggest, opts.history ?? [], opts.classifyPaste, opts.onEmptyPaste);
|
|
5249
|
+
const s = new EditorState(opts.suggest, opts.history ?? [], opts.classifyPaste, opts.onEmptyPaste, opts.vimMode);
|
|
5063
5250
|
curRow = 0;
|
|
5064
5251
|
if (opts.initial) s.insert(opts.initial);
|
|
5065
5252
|
s.refresh();
|
|
@@ -5074,11 +5261,31 @@ function createLineEditor(out) {
|
|
|
5074
5261
|
};
|
|
5075
5262
|
process.on("SIGWINCH", onResize);
|
|
5076
5263
|
return new Promise((resolve4) => {
|
|
5264
|
+
const redraw = () => render(s, opts.prompt, maxVisible, opts.status);
|
|
5077
5265
|
const onKey = (str, key) => {
|
|
5078
5266
|
if (key?.ctrl && key.name === "l") {
|
|
5079
5267
|
out.write("\x1B[2J\x1B[3J\x1B[H");
|
|
5080
5268
|
curRow = 0;
|
|
5081
|
-
|
|
5269
|
+
redraw();
|
|
5270
|
+
return;
|
|
5271
|
+
}
|
|
5272
|
+
if (key?.name === "tab" && key.shift && opts.onCyclePosture) {
|
|
5273
|
+
opts.onCyclePosture();
|
|
5274
|
+
redraw();
|
|
5275
|
+
return;
|
|
5276
|
+
}
|
|
5277
|
+
if (key?.meta && key.name === "t" && opts.onToggleThinking) {
|
|
5278
|
+
opts.onToggleThinking();
|
|
5279
|
+
redraw();
|
|
5280
|
+
return;
|
|
5281
|
+
}
|
|
5282
|
+
if (key?.meta && key.name === "p" && opts.onPickModel) {
|
|
5283
|
+
process.stdin.off("keypress", onKey);
|
|
5284
|
+
void opts.onPickModel().finally(() => {
|
|
5285
|
+
process.stdin.on("keypress", onKey);
|
|
5286
|
+
curRow = 0;
|
|
5287
|
+
redraw();
|
|
5288
|
+
});
|
|
5082
5289
|
return;
|
|
5083
5290
|
}
|
|
5084
5291
|
const action = applyKey(s, key ?? {}, str);
|
|
@@ -5247,7 +5454,7 @@ function selectMenu(out, opts) {
|
|
|
5247
5454
|
filter = filter.slice(0, -1);
|
|
5248
5455
|
applyFilter();
|
|
5249
5456
|
draw();
|
|
5250
|
-
} else if (opts.filterable &&
|
|
5457
|
+
} else if (opts.filterable && !key?.ctrl && !key?.meta && isPrintable(_s)) {
|
|
5251
5458
|
filter += _s;
|
|
5252
5459
|
applyFilter();
|
|
5253
5460
|
draw();
|
|
@@ -5452,6 +5659,12 @@ function numFlag(raw, flag) {
|
|
|
5452
5659
|
if (!Number.isFinite(n) || n < 0) throw new Error(`invalid ${flag}: ${raw ?? "(missing value)"}`);
|
|
5453
5660
|
return n;
|
|
5454
5661
|
}
|
|
5662
|
+
function parseReasoning(raw) {
|
|
5663
|
+
if (raw === "off" || raw === "low" || raw === "medium" || raw === "high") return raw;
|
|
5664
|
+
const n = Number(raw);
|
|
5665
|
+
if (Number.isFinite(n) && n > 0) return n;
|
|
5666
|
+
throw new Error(`invalid --reasoning: ${raw} (use off|low|medium|high or a token budget)`);
|
|
5667
|
+
}
|
|
5455
5668
|
function parseArgs(argv) {
|
|
5456
5669
|
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
5670
|
const rest = [];
|
|
@@ -5489,6 +5702,9 @@ function parseArgs(argv) {
|
|
|
5489
5702
|
else if (x === "--disallowedTools" || x === "--disallowed-tools") a.disallowedTools = val(++i, x).split(",").map((s) => s.trim()).filter(Boolean);
|
|
5490
5703
|
else if (x === "--append-system-prompt") a.appendSystemPrompt = val(++i, x);
|
|
5491
5704
|
else if (x === "--add-dir") (a.addDirs ??= []).push(val(++i, x));
|
|
5705
|
+
else if (x === "--reasoning") a.reasoning = parseReasoning(val(++i, x));
|
|
5706
|
+
else if (x === "--session-id") a.sessionId = val(++i, x);
|
|
5707
|
+
else if (x === "--fork-session") a.fork = true;
|
|
5492
5708
|
else if (x === "--max-steps") a.maxSteps = numFlag(argv[++i], "--max-steps");
|
|
5493
5709
|
else if (x === "--max-tokens") a.maxTokens = numFlag(argv[++i], "--max-tokens");
|
|
5494
5710
|
else if (x === "--timeout") a.timeoutMs = numFlag(argv[++i], "--timeout") * 1e3;
|
|
@@ -5532,6 +5748,9 @@ Flags:
|
|
|
5532
5748
|
--append-system-prompt <t> extra instructions appended to the system prompt for this run
|
|
5533
5749
|
--add-dir <path> mount another directory into the workspace (repeatable; disk mode only)
|
|
5534
5750
|
--subagents allow the Task tool (spawn child agents)
|
|
5751
|
+
--reasoning <e> extended thinking: off|low|medium|high or a token budget (anthropic/openai)
|
|
5752
|
+
--session-id <id> use this id for the session (instead of an auto-generated one)
|
|
5753
|
+
--fork-session with -r/-c: branch the resumed session into a new id (source left untouched)
|
|
5535
5754
|
--max-steps <n> step budget (default 30)
|
|
5536
5755
|
--max-tokens <n> token budget kill-switch (default 200000)
|
|
5537
5756
|
--timeout <sec> wall-clock kill-switch (default 120)
|
|
@@ -5543,7 +5762,7 @@ Prompts may reference files with @path (e.g. "explain @src/Agent.ts") \u2014 the
|
|
|
5543
5762
|
|
|
5544
5763
|
Providers: set any of ANTHROPIC_API_KEY / OPENAI_API_KEY / GOOGLE_API_KEY / GROQ_API_KEY.
|
|
5545
5764
|
Config: ./.agent/config.{ts,js,json} (project) or ~/.agent/config.* (user).
|
|
5546
|
-
export default { model, maxSteps, tools, apiKeys, baseUrls, hooks, permissions, mcpServers, maxTokens,
|
|
5765
|
+
export default { model, maxSteps, reasoning, permissionMode, editorMode, tools, apiKeys, baseUrls, hooks, permissions, mcpServers, maxTokens,
|
|
5547
5766
|
timeoutMs, maxRepeats, maxToolCalls, keepToolOutputs, maxContextTokens,
|
|
5548
5767
|
learnFromMistakes, reflectOnFailure, budget: {\u2026} }
|
|
5549
5768
|
hooks: { preToolUse|postToolUse|onStop: [{ tool?, command, block? }] } \u2014 shell hooks
|
|
@@ -5556,10 +5775,11 @@ Project instructions: ./AGENTS.md or ./CLAUDE.md are auto-loaded (scaffold with
|
|
|
5556
5775
|
Auto-loaded from ./.agent/: commands/, skills/, memory/, agents/.
|
|
5557
5776
|
|
|
5558
5777
|
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
|
|
5778
|
+
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
5779
|
REPL completion: type / (commands+skills) or @ (files) for a LIVE menu \u2014 \u2191/\u2193 select, \u23CE/Tab accept, Esc dismiss.
|
|
5561
5780
|
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
|
|
5781
|
+
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.
|
|
5782
|
+
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
5783
|
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
5784
|
function newestModel() {
|
|
5565
5785
|
return listModels().slice().sort((a, b) => (getModelInfo(b)?.releaseDate ?? "").localeCompare(getModelInfo(a)?.releaseDate ?? ""))[0] ?? "";
|
|
@@ -5875,6 +6095,7 @@ function optsFor(args, ai, cfg = {}, extraTools = []) {
|
|
|
5875
6095
|
permissions: rules.length ? new PermissionPolicy({ rules, default: "allow", host: makeHost(), ask: makeAskResolver(resolve3(args.cwd ?? process.cwd())) }) : void 0,
|
|
5876
6096
|
subagents: args.subagents || cfg.subagents || false,
|
|
5877
6097
|
maxSteps: args.maxSteps ?? cfg.maxSteps,
|
|
6098
|
+
reasoning: args.reasoning ?? cfg.reasoning,
|
|
5878
6099
|
// kill-switches: CLI flag > config > Agent default
|
|
5879
6100
|
maxTokens: args.maxTokens ?? cfg.maxTokens,
|
|
5880
6101
|
timeoutMs: args.timeoutMs ?? cfg.timeoutMs,
|
|
@@ -6069,6 +6290,14 @@ function startSession(args, store, agent, cwd) {
|
|
|
6069
6290
|
const data = args.resume ? store.load(args.resume) : store.latestData();
|
|
6070
6291
|
if (data) {
|
|
6071
6292
|
agent.transcript = data.messages;
|
|
6293
|
+
if (args.fork) {
|
|
6294
|
+
const now2 = Date.now();
|
|
6295
|
+
const forked = { meta: { ...data.meta, id: args.sessionId ?? store.newId(now2), created: now2, updated: now2, turns: data.meta.turns }, messages: data.messages };
|
|
6296
|
+
err(dim(` forked ${data.meta.id} \u2192 ${forked.meta.id} (${data.meta.turns} turns)
|
|
6297
|
+
`));
|
|
6298
|
+
if (!args.task) printHistory(data.messages);
|
|
6299
|
+
return forked;
|
|
6300
|
+
}
|
|
6072
6301
|
err(dim(` resumed session ${data.meta.id} (${data.meta.turns} turns) \u2014 ${data.meta.title}
|
|
6073
6302
|
`));
|
|
6074
6303
|
if (!args.task) printHistory(data.messages);
|
|
@@ -6078,7 +6307,7 @@ function startSession(args, store, agent, cwd) {
|
|
|
6078
6307
|
`));
|
|
6079
6308
|
}
|
|
6080
6309
|
const now = Date.now();
|
|
6081
|
-
return { meta: { id: store.newId(now), created: now, updated: now, cwd, model: agent.options.model, turns: 0, title: "" }, messages: [] };
|
|
6310
|
+
return { meta: { id: args.sessionId ?? store.newId(now), created: now, updated: now, cwd, model: agent.options.model, turns: 0, title: "" }, messages: [] };
|
|
6082
6311
|
}
|
|
6083
6312
|
var AGENTS_MD_TEMPLATE = `# ${"${name}"}
|
|
6084
6313
|
|
|
@@ -6109,23 +6338,56 @@ function initInstructions(cwd) {
|
|
|
6109
6338
|
err(green(` created ${path}
|
|
6110
6339
|
`) + dim(" edit it, then it auto-loads into every run.\n"));
|
|
6111
6340
|
}
|
|
6112
|
-
function
|
|
6341
|
+
function persistSetting(cwd, key, value) {
|
|
6113
6342
|
const path = join8(cwd, ".agent", "settings.json");
|
|
6114
6343
|
try {
|
|
6115
6344
|
const obj = existsSync7(path) ? JSON.parse(readFileSync5(path, "utf8")) : {};
|
|
6116
|
-
if (obj
|
|
6117
|
-
obj
|
|
6345
|
+
if (obj[key] === value) return;
|
|
6346
|
+
obj[key] = value;
|
|
6118
6347
|
mkdirSync6(dirname3(path), { recursive: true });
|
|
6119
6348
|
writeFileSync6(path, JSON.stringify(obj, null, 2) + "\n");
|
|
6120
6349
|
} catch (e) {
|
|
6121
|
-
err(yellow(` \u26A0 couldn't persist
|
|
6350
|
+
err(yellow(` \u26A0 couldn't persist ${key} to ${path} \u2014 ${e?.message ?? e}
|
|
6122
6351
|
`));
|
|
6123
6352
|
}
|
|
6124
6353
|
}
|
|
6354
|
+
var persistModel = (cwd, model) => persistSetting(cwd, "model", model);
|
|
6125
6355
|
async function repl(args, ai, cfg, cwd) {
|
|
6126
6356
|
const oauth = new McpOAuth({ storePath: join8(cwd, ".agent", "mcp-auth.json") });
|
|
6127
6357
|
const mounted = await mountMcp(cfg, oauth);
|
|
6128
6358
|
const agent = await makeAgent(args, ai, cfg, mounted.flatMap((m) => m.tools));
|
|
6359
|
+
const baseRules = () => [
|
|
6360
|
+
...parsePermRules({ deny: args.disallowedTools }),
|
|
6361
|
+
...parsePermRules({ allow: args.allowedTools }),
|
|
6362
|
+
...parsePermRules(mergePerms(loadPersistedRules(cwd), cfg.permissions))
|
|
6363
|
+
];
|
|
6364
|
+
const askFor = (tools) => tools.map((t) => ({ tool: t, decision: "ask" }));
|
|
6365
|
+
const POSTURES = ["default", "acceptEdits", "plan"];
|
|
6366
|
+
let posture = args.plan || cfg.permissionMode === "plan" ? "plan" : cfg.permissionMode === "acceptEdits" ? "acceptEdits" : "default";
|
|
6367
|
+
const postureLabel = () => posture === "default" ? "ask (default)" : posture === "acceptEdits" ? "accept edits" : "plan mode";
|
|
6368
|
+
const applyPosture = (p) => {
|
|
6369
|
+
posture = p;
|
|
6370
|
+
const ask = p === "acceptEdits" ? askFor(["bash", "Shell"]) : askFor(ASK_MUTATING);
|
|
6371
|
+
agent.options.permissions = new PermissionPolicy({ rules: [...baseRules(), ...ask], default: "allow", host: makeHost(), ask: makeAskResolver(cwd) });
|
|
6372
|
+
agent.options.planMode = p === "plan";
|
|
6373
|
+
agent.reprepare();
|
|
6374
|
+
};
|
|
6375
|
+
const cyclePosture = () => {
|
|
6376
|
+
applyPosture(POSTURES[(POSTURES.indexOf(posture) + 1) % POSTURES.length]);
|
|
6377
|
+
err(dim(` \u21E5 ${postureLabel()}
|
|
6378
|
+
`));
|
|
6379
|
+
return postureLabel();
|
|
6380
|
+
};
|
|
6381
|
+
if (!args.yes) applyPosture(posture);
|
|
6382
|
+
const REASONING_CYCLE = ["off", "low", "medium", "high"];
|
|
6383
|
+
const toggleReasoning = () => {
|
|
6384
|
+
const cur = String(agent.options.reasoning ?? "off");
|
|
6385
|
+
const next = REASONING_CYCLE[(Math.max(0, REASONING_CYCLE.indexOf(cur)) + 1) % REASONING_CYCLE.length];
|
|
6386
|
+
agent.options.reasoning = next;
|
|
6387
|
+
err(dim(` ~ reasoning \u2192 ${next}
|
|
6388
|
+
`));
|
|
6389
|
+
return next;
|
|
6390
|
+
};
|
|
6129
6391
|
const pendingImages = [];
|
|
6130
6392
|
const grabClipboardAttachment = () => {
|
|
6131
6393
|
const dir = join8(tmpdir(), "agentx-pasted");
|
|
@@ -6144,6 +6406,28 @@ async function repl(args, ai, cfg, cwd) {
|
|
|
6144
6406
|
void closeMcp(mounted);
|
|
6145
6407
|
process.exit(130);
|
|
6146
6408
|
});
|
|
6409
|
+
const isCancelTeardown = (e) => {
|
|
6410
|
+
if (!e) return false;
|
|
6411
|
+
if (e.code === 20 || e.cause?.code === 20) return true;
|
|
6412
|
+
const blob = `${e.code ?? ""} ${e.name ?? ""} ${e.cause?.name ?? ""} ${e.rawMessage ?? ""} ${e.message ?? ""}`;
|
|
6413
|
+
return /NGHTTP2_FRAME_SIZE_ERROR|ERR_HTTP2_STREAM_ERROR|operation was aborted|\bAbortError\b/i.test(blob);
|
|
6414
|
+
};
|
|
6415
|
+
process.on("unhandledRejection", (e) => {
|
|
6416
|
+
if (isCancelTeardown(e)) {
|
|
6417
|
+
log11.debug("suppressed unhandledRejection (cursor stream cancel)", e);
|
|
6418
|
+
return;
|
|
6419
|
+
}
|
|
6420
|
+
log11.error("unhandledRejection", e);
|
|
6421
|
+
});
|
|
6422
|
+
process.on("uncaughtException", (e) => {
|
|
6423
|
+
if (isCancelTeardown(e)) {
|
|
6424
|
+
log11.debug("suppressed uncaughtException (cursor stream cancel)", e);
|
|
6425
|
+
return;
|
|
6426
|
+
}
|
|
6427
|
+
console.error(e);
|
|
6428
|
+
void closeMcp(mounted);
|
|
6429
|
+
process.exit(1);
|
|
6430
|
+
});
|
|
6147
6431
|
const store = new SessionStore(cwd);
|
|
6148
6432
|
let session = startSession(args, store, agent, cwd);
|
|
6149
6433
|
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 +6685,92 @@ ${extra}` : body, checkpoints, cwd);
|
|
|
6401
6685
|
} else err(dim(" " + agent.options.model + "\n"));
|
|
6402
6686
|
}
|
|
6403
6687
|
},
|
|
6688
|
+
reasoning: {
|
|
6689
|
+
desc: "extended thinking \u2014 /reasoning <off|low|medium|high|tokens>, or alone for an interactive picker",
|
|
6690
|
+
run: async (a) => {
|
|
6691
|
+
const current = String(agent.options.reasoning ?? "off");
|
|
6692
|
+
let next;
|
|
6693
|
+
if (a[0]) {
|
|
6694
|
+
try {
|
|
6695
|
+
next = parseReasoning(a[0]);
|
|
6696
|
+
} catch (e) {
|
|
6697
|
+
err(yellow(" " + (e?.message ?? e) + "\n"));
|
|
6698
|
+
return;
|
|
6699
|
+
}
|
|
6700
|
+
} else {
|
|
6701
|
+
const items = [
|
|
6702
|
+
{ label: "off", value: "off", desc: "no extended thinking" },
|
|
6703
|
+
{ label: "low", value: "low", desc: "minimal reasoning (~2k tokens)" },
|
|
6704
|
+
{ label: "medium", value: "medium", desc: "balanced (~8k tokens)" },
|
|
6705
|
+
{ label: "high", value: "high", desc: "maximal reasoning (~24k tokens)" }
|
|
6706
|
+
];
|
|
6707
|
+
const picked = await selectMenu(process.stderr, { title: `Reasoning effort \xB7 current: ${current}`, items, current });
|
|
6708
|
+
if (!picked) {
|
|
6709
|
+
err(dim(" " + current + "\n"));
|
|
6710
|
+
return;
|
|
6711
|
+
}
|
|
6712
|
+
next = picked;
|
|
6713
|
+
}
|
|
6714
|
+
agent.options.reasoning = next;
|
|
6715
|
+
if (next !== "off" && getModelInfo(agent.options.model)?.reasoning === false)
|
|
6716
|
+
err(yellow(` note: ${agent.options.model} has no reasoning capability \u2014 setting may be ignored
|
|
6717
|
+
`));
|
|
6718
|
+
err(dim(" reasoning \u2192 " + next + "\n"));
|
|
6719
|
+
}
|
|
6720
|
+
},
|
|
6721
|
+
config: {
|
|
6722
|
+
desc: "view/change settings \u2014 model, reasoning, permission posture, streaming, editor mode",
|
|
6723
|
+
run: async () => {
|
|
6724
|
+
for (; ; ) {
|
|
6725
|
+
const items = [
|
|
6726
|
+
{ label: "model", value: "model", desc: agent.options.model },
|
|
6727
|
+
{ label: "reasoning", value: "reasoning", desc: String(agent.options.reasoning ?? "off") },
|
|
6728
|
+
{ label: "permission posture", value: "posture", desc: postureLabel() + " (Shift+Tab)" },
|
|
6729
|
+
{ label: "streaming", value: "stream", desc: agent.options.stream ? "on" : "off" },
|
|
6730
|
+
{ label: "editor mode", value: "editor", desc: cfg.editorMode === "vim" ? "vim" : "normal" }
|
|
6731
|
+
];
|
|
6732
|
+
const pick = await selectMenu(process.stderr, { title: "Settings \xB7 \u21B5 change \xB7 esc close", items });
|
|
6733
|
+
if (!pick) return;
|
|
6734
|
+
if (pick === "model") {
|
|
6735
|
+
const m = await pickModel(agent.options.model);
|
|
6736
|
+
if (m) {
|
|
6737
|
+
agent.options.model = m;
|
|
6738
|
+
persistModel(cwd, m);
|
|
6739
|
+
}
|
|
6740
|
+
} else if (pick === "reasoning") {
|
|
6741
|
+
await builtins.reasoning.run([]);
|
|
6742
|
+
persistSetting(cwd, "reasoning", agent.options.reasoning ?? "off");
|
|
6743
|
+
} else if (pick === "posture") {
|
|
6744
|
+
cyclePosture();
|
|
6745
|
+
persistSetting(cwd, "permissionMode", posture);
|
|
6746
|
+
} else if (pick === "stream") {
|
|
6747
|
+
agent.options.stream = !agent.options.stream;
|
|
6748
|
+
persistSetting(cwd, "stream", agent.options.stream);
|
|
6749
|
+
err(dim(" streaming \u2192 " + (agent.options.stream ? "on" : "off") + "\n"));
|
|
6750
|
+
} else if (pick === "editor") {
|
|
6751
|
+
cfg.editorMode = cfg.editorMode === "vim" ? "normal" : "vim";
|
|
6752
|
+
persistSetting(cwd, "editorMode", cfg.editorMode);
|
|
6753
|
+
err(dim(" editor \u2192 " + cfg.editorMode + "\n"));
|
|
6754
|
+
}
|
|
6755
|
+
}
|
|
6756
|
+
}
|
|
6757
|
+
},
|
|
6758
|
+
rename: {
|
|
6759
|
+
desc: "rename the current session \u2014 /rename <title>",
|
|
6760
|
+
run: (a) => {
|
|
6761
|
+
const t = a.join(" ").trim();
|
|
6762
|
+
if (!t) {
|
|
6763
|
+
err(dim(" title: " + (session.meta.title || "(none)") + "\n"));
|
|
6764
|
+
return;
|
|
6765
|
+
}
|
|
6766
|
+
session.meta.title = t;
|
|
6767
|
+
try {
|
|
6768
|
+
store.save(session);
|
|
6769
|
+
} catch {
|
|
6770
|
+
}
|
|
6771
|
+
err(dim(" renamed \u2192 " + t + "\n"));
|
|
6772
|
+
}
|
|
6773
|
+
},
|
|
6404
6774
|
compact: {
|
|
6405
6775
|
desc: "summarize older context to free up the window",
|
|
6406
6776
|
run: () => {
|
|
@@ -6695,11 +7065,20 @@ ${extra}` : body, checkpoints, cwd);
|
|
|
6695
7065
|
return { hits, token, describe };
|
|
6696
7066
|
};
|
|
6697
7067
|
const editor = createLineEditor(process.stderr);
|
|
7068
|
+
let aborting = false;
|
|
7069
|
+
let pendingRewind = false;
|
|
6698
7070
|
if (process.stdin.isTTY) {
|
|
6699
7071
|
process.stdin.on("keypress", (_s, key) => {
|
|
6700
|
-
if (activeTurn
|
|
7072
|
+
if (!activeTurn) return;
|
|
7073
|
+
const cancel = key?.name === "escape" || key?.ctrl && key?.name === "c";
|
|
7074
|
+
if (!cancel) return;
|
|
7075
|
+
if (!aborting) {
|
|
7076
|
+
aborting = true;
|
|
6701
7077
|
activeTurn.abort();
|
|
6702
7078
|
err(yellow("\n \u238B cancelling\u2026\n"));
|
|
7079
|
+
} else if (key?.name === "escape" && !pendingRewind) {
|
|
7080
|
+
pendingRewind = true;
|
|
7081
|
+
err(dim(" \u238B\u238B jumping back to edit\u2026\n"));
|
|
6703
7082
|
}
|
|
6704
7083
|
});
|
|
6705
7084
|
}
|
|
@@ -6717,14 +7096,48 @@ ${extra}` : body, checkpoints, cwd);
|
|
|
6717
7096
|
};
|
|
6718
7097
|
let prefill;
|
|
6719
7098
|
while (true) {
|
|
7099
|
+
if (pendingRewind) {
|
|
7100
|
+
pendingRewind = false;
|
|
7101
|
+
const t = await rewindToMessage();
|
|
7102
|
+
if (t !== void 0) prefill = t;
|
|
7103
|
+
}
|
|
7104
|
+
aborting = false;
|
|
6720
7105
|
err("\n");
|
|
6721
7106
|
const initial = prefill;
|
|
6722
7107
|
prefill = void 0;
|
|
6723
7108
|
const ctxTok = estimateTranscriptTokens(agent.transcript);
|
|
6724
7109
|
const ctxCap = agent.options.maxTokens || 2e5;
|
|
6725
7110
|
const usd = session.meta.costUsd ?? 0;
|
|
6726
|
-
const
|
|
6727
|
-
|
|
7111
|
+
const computeFooter = () => {
|
|
7112
|
+
const parts = [];
|
|
7113
|
+
if (ctxTok > 400) parts.push(`${Math.round(ctxTok / ctxCap * 100)}% ctx (~${(ctxTok / 1e3).toFixed(1)}k/${Math.round(ctxCap / 1e3)}k)`);
|
|
7114
|
+
if (usd > 0) parts.push(`${session.meta.costEstimated ? "~" : ""}${fmtUsd(usd)}`);
|
|
7115
|
+
if (posture !== "default") parts.push(postureLabel());
|
|
7116
|
+
const r = agent.options.reasoning;
|
|
7117
|
+
if (r && r !== "off") parts.push(`reasoning:${r}`);
|
|
7118
|
+
return parts.join(" \xB7 ");
|
|
7119
|
+
};
|
|
7120
|
+
const result = await readMultiline((cont) => editor.readLine({
|
|
7121
|
+
prompt: cont ? contPrompt : promptStr,
|
|
7122
|
+
suggest,
|
|
7123
|
+
history,
|
|
7124
|
+
classifyPaste,
|
|
7125
|
+
onEmptyPaste: grabClipboardAttachment,
|
|
7126
|
+
initial: cont ? void 0 : initial,
|
|
7127
|
+
status: computeFooter,
|
|
7128
|
+
vimMode: cfg.editorMode === "vim",
|
|
7129
|
+
onCyclePosture: cyclePosture,
|
|
7130
|
+
onToggleThinking: toggleReasoning,
|
|
7131
|
+
onPickModel: async () => {
|
|
7132
|
+
const picked = await pickModel(agent.options.model);
|
|
7133
|
+
if (picked) {
|
|
7134
|
+
agent.options.model = picked;
|
|
7135
|
+
persistModel(cwd, picked);
|
|
7136
|
+
err(dim(" model \u2192 " + picked + "\n"));
|
|
7137
|
+
}
|
|
7138
|
+
return picked;
|
|
7139
|
+
}
|
|
7140
|
+
}));
|
|
6728
7141
|
if (result === null) break;
|
|
6729
7142
|
if (result === REWIND) {
|
|
6730
7143
|
prefill = await rewindToMessage();
|