z0gcode 0.3.0 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/bin/z0g.mjs +22 -6
- package/package.json +1 -1
- package/src/agent.mjs +30 -2
- package/src/config.mjs +9 -0
- package/src/goal.mjs +2 -2
package/README.md
CHANGED
|
@@ -75,7 +75,7 @@ z0g transcribe memo.mp3 # transcribe audio on 0
|
|
|
75
75
|
z0g serve --mcp # expose z0gcode's 0G tools over MCP
|
|
76
76
|
```
|
|
77
77
|
|
|
78
|
-
**In the REPL**, type `/` then **Tab** to autocomplete slash commands: `/chats`, `/new`, `/rename`, `/init`, `/goal`, `/mode`, `/model`, `/effort`, `/subagents`, `/onchain`, `/skills`, `/attest`, `/share`, `/pull`, `/mint`, `/plan`, `/verify`, `/clear`, `/help`, `/exit`. `/chats` opens an arrow-key session picker (type to search, `ctrl-r` rename, `ctrl-x` delete); `/new [title]` starts a chat and `/rename <title>` renames the current one. `/mode ask|auto|plan` switches the permission mode; `/model` opens the model picker (saved to `~/.z0gcode/settings.json`); `/effort low|medium|high` (or `default`) tunes reasoning depth vs speed and cost; `/subagents on|off` toggles parallel subagents; `/onchain on|off` toggles gas-spending on-chain actions (off by default); `/skills` lists and toggles your skills; `/share [anchor]` exports the session (encrypted) to 0G Storage (and anchors it on 0G Chain), and `/pull <root>` fetches, verifies, and decrypts one back. A short intro animation and a "thinking on 0G" indicator play on a color TTY; set `Z0G_NO_ANIM=1` to disable. Each turn is separated by a divider carrying a running session token and cost counter.
|
|
78
|
+
**In the REPL**, type `/` then **Tab** to autocomplete slash commands: `/chats`, `/new`, `/rename`, `/init`, `/goal`, `/mode`, `/model`, `/effort`, `/subagents`, `/onchain`, `/skills`, `/attest`, `/share`, `/pull`, `/mint`, `/plan`, `/verify`, `/clear`, `/help`, `/exit`. `/chats` opens an arrow-key session picker (type to search, `ctrl-r` rename, `ctrl-x` delete); `/new [title]` starts a chat and `/rename <title>` renames the current one. `/mode ask|auto|plan` switches the permission mode; `/model` opens the model picker (saved to `~/.z0gcode/settings.json`); `/effort low|medium|high` (or `default`) tunes reasoning depth vs speed and cost; `/subagents on|off` toggles parallel subagents; `/onchain on|off` toggles gas-spending on-chain actions (off by default); `/escalate on|off` toggles switching to a stronger model on repeated failures; `/skills` lists and toggles your skills; `/share [anchor]` exports the session (encrypted) to 0G Storage (and anchors it on 0G Chain), and `/pull <root>` fetches, verifies, and decrypts one back. A short intro animation and a "thinking on 0G" indicator play on a color TTY; set `Z0G_NO_ANIM=1` to disable. Each turn is separated by a divider carrying a running session token and cost counter.
|
|
79
79
|
|
|
80
80
|
**Options:** `--auto`, `--onchain`, `--continue`, `--resume`, `--new`, `--model <id>`, `--effort low|medium|high`, `--no-subagents`, `--verify "<cmd>"`, `--auto-verify`, `--max-steps <n>`, `--cwd <dir>`, and `--json` (with `models`).
|
|
81
81
|
|
|
@@ -90,7 +90,7 @@ z0g serve --mcp # expose z0gcode's 0G t
|
|
|
90
90
|
- **Project context**: `AGENTS.md` (and `.z0g/context.md`) are auto-loaded into the agent's system prompt on every run, so it follows your conventions and uses your real build/test/run commands. `z0g init` (or `/init`) analyzes the repo and writes an accurate `AGENTS.md` for you.
|
|
91
91
|
- **Checkpoints and undo**: every file edit is logged with its before/after content per turn, so `z0g undo` (or `/undo`) reverts the last turn's changes (restoring files, deleting ones it created); `z0g checkpoints` lists what you can step back through.
|
|
92
92
|
- **Custom commands and hooks**: drop `.z0g/commands/<name>.md` to add a `/<name>` slash command (the file is a prompt template; `$ARGUMENTS` is substituted, optional `description:` frontmatter, Tab-completed and listed by `/commands`). Define lifecycle hooks in `.z0g/hooks.json` (`preRun` / `postRun` shell commands, e.g. run your formatter or tests after each turn); hooks run shell, so they only fire with `--auto`.
|
|
93
|
-
- Reliability on a decentralized backend: app-level multi-model fallback, retry and backoff, tool-JSON repair, a loop breaker, and model escalation.
|
|
93
|
+
- Reliability on a decentralized backend: app-level multi-model fallback, retry and backoff, tool-JSON repair, a loop breaker (on repeated identical calls, or the same tool result with no progress, it first escalates to a stronger model to try to get unstuck and only then hands control back, instead of spinning), and model escalation (when a tool fails repeatedly it moves to a stronger fallback for the rest of the turn; on by default after 3 failures, toggle with `/escalate on|off` or `--no-escalate`).
|
|
94
94
|
- **Parallel subagents**: `spawn_subagents` fans out independent, read-only subtasks (review many files, research, audit, map a codebase) as isolated agents running in parallel, capped by `ZOG_MAX_PARALLEL`. The parent synthesizes the results and each subagent's transcript is saved. With 0G's cheap inference, fanning out to many agents costs cents: massively parallel agents at 0G prices. On by default; toggle with `/subagents on|off` or `--no-subagents`.
|
|
95
95
|
- **Parallel write subagents**: with `--auto` in a git repo, `spawn_write_subagents` fans out subtasks that WRITE code, each in its own isolated **git worktree**, then merges every diff back into the working tree. Disjoint file sets merge automatically; overlapping edits are reported and skipped (never half-applied), and the merged changes are checkpointed so `z0g undo` reverts them. Verified end-to-end on 0G.
|
|
96
96
|
|
package/bin/z0g.mjs
CHANGED
|
@@ -60,6 +60,7 @@ function helpText() {
|
|
|
60
60
|
["--model <id>", "Override the model (default " + CONFIG.model + ")"],
|
|
61
61
|
["--effort <l>", "Reasoning effort: low, medium, high (default: model's own)"],
|
|
62
62
|
["--no-subagents", "Disable parallel subagents for this run"],
|
|
63
|
+
["--no-escalate", "Do not switch to a stronger model on repeated tool failures"],
|
|
63
64
|
['--verify "<cmd>"', "Run, then verify and self-correct with this command"],
|
|
64
65
|
["--auto-verify", "Same, auto-detecting the verify command"],
|
|
65
66
|
["--max-steps <n>", "Max agent steps (default " + CONFIG.maxSteps + ")"],
|
|
@@ -93,6 +94,7 @@ const SLASH_COMMANDS = [
|
|
|
93
94
|
["/effort", "Set reasoning effort (low|medium|high|default)"],
|
|
94
95
|
["/subagents", "Enable or disable parallel subagents (on|off)"],
|
|
95
96
|
["/onchain", "Enable or disable gas-spending on-chain actions (on|off)"],
|
|
97
|
+
["/escalate", "Toggle switching to a stronger model on repeated failures (on|off)"],
|
|
96
98
|
["/skills", "List skills; /skills enable|disable <name>"],
|
|
97
99
|
["/commands", "List project custom commands (.z0g/commands/*.md)"],
|
|
98
100
|
["/attest", "Show the provenance manifest"],
|
|
@@ -249,6 +251,7 @@ function parse(argv) {
|
|
|
249
251
|
else if (a === "--no-subagents") flags.subagents = false;
|
|
250
252
|
else if (a === "--onchain") flags.onchain = true;
|
|
251
253
|
else if (a === "--no-onchain") flags.onchain = false;
|
|
254
|
+
else if (a === "--no-escalate") flags.escalate = false;
|
|
252
255
|
else if (a === "--force") flags.force = true;
|
|
253
256
|
else if (a === "--import") flags.import = true;
|
|
254
257
|
else if (a === "--verify") flags.verify = argv[++i];
|
|
@@ -714,13 +717,13 @@ async function runTask(task, flags) {
|
|
|
714
717
|
// Auto-verify: a normal run becomes self-correcting when a verify command is present.
|
|
715
718
|
const verifyCmd = flags.verify || (flags.autoVerify ? detectVerifyCmd(cwd) : null);
|
|
716
719
|
if (verifyCmd) {
|
|
717
|
-
await runGoal({ client: makeClient(), objective: task, cwd, sessionId, sessionDir: sessionDirPath, allowBash: flags.auto, preferredModel: flags.model, preferredEffort: flags.effort, preferredSubagents: flags.subagents, preferredOnchain: flags.onchain, verifyCmd, maxIters: 3, history: opened.history });
|
|
720
|
+
await runGoal({ client: makeClient(), objective: task, cwd, sessionId, sessionDir: sessionDirPath, allowBash: flags.auto, preferredModel: flags.model, preferredEffort: flags.effort, preferredSubagents: flags.subagents, preferredOnchain: flags.onchain, preferredEscalate: flags.escalate, verifyCmd, maxIters: 3, history: opened.history });
|
|
718
721
|
return;
|
|
719
722
|
}
|
|
720
723
|
const client = makeClient();
|
|
721
724
|
const mcp = await loadMcp(cwd);
|
|
722
725
|
if (mcp?.count) ui.info(`MCP: ${mcp.count} tool(s) from configured servers`);
|
|
723
|
-
const res = await runAgent({ client, task, cwd, sessionDir: sessionDirPath, allowBash: flags.auto, preferredModel: flags.model, preferredEffort: flags.effort, preferredSubagents: flags.subagents, preferredOnchain: flags.onchain, history: opened.history, mcp });
|
|
726
|
+
const res = await runAgent({ client, task, cwd, sessionDir: sessionDirPath, allowBash: flags.auto, preferredModel: flags.model, preferredEffort: flags.effort, preferredSubagents: flags.subagents, preferredOnchain: flags.onchain, preferredEscalate: flags.escalate, history: opened.history, mcp });
|
|
724
727
|
if (res?.messages) await saveMessages(cwd, sessionId, res.messages);
|
|
725
728
|
await mcp?.close();
|
|
726
729
|
} finally {
|
|
@@ -743,7 +746,7 @@ async function cmdGoal(objective, flags) {
|
|
|
743
746
|
try {
|
|
744
747
|
const verifyCmd = flags.verify || detectVerifyCmd(cwd);
|
|
745
748
|
if (!flags.auto) ui.info("tip: run goal with --auto so the agent can run and verify its own work.");
|
|
746
|
-
await runGoal({ client, objective, cwd, sessionId: opened.id, sessionDir: opened.dir, allowBash: flags.auto, preferredModel: flags.model, preferredEffort: flags.effort, preferredSubagents: flags.subagents, preferredOnchain: flags.onchain, verifyCmd, maxIters: 3, history: opened.history });
|
|
749
|
+
await runGoal({ client, objective, cwd, sessionId: opened.id, sessionDir: opened.dir, allowBash: flags.auto, preferredModel: flags.model, preferredEffort: flags.effort, preferredSubagents: flags.subagents, preferredOnchain: flags.onchain, preferredEscalate: flags.escalate, verifyCmd, maxIters: 3, history: opened.history });
|
|
747
750
|
} finally {
|
|
748
751
|
process.removeListener("SIGINT", onSig);
|
|
749
752
|
pruneEmptySync(cwd, opened.id);
|
|
@@ -762,6 +765,7 @@ async function repl(flags) {
|
|
|
762
765
|
let effort = flags.effort;
|
|
763
766
|
let subagents = flags.subagents;
|
|
764
767
|
let onchain = flags.onchain;
|
|
768
|
+
let escalate = flags.escalate;
|
|
765
769
|
// Permission mode: auto (run all) | ask (approve each) | plan (read-only).
|
|
766
770
|
let mode = flags.auto ? "auto" : "ask";
|
|
767
771
|
const allowedCmds = new Set(loadSettings(cwd).allowedCommands || []);
|
|
@@ -786,6 +790,7 @@ async function repl(flags) {
|
|
|
786
790
|
const activeModel = () => model || CONFIG.model;
|
|
787
791
|
const activeEffort = () => (effort === "" ? null : (effort || CONFIG.effort));
|
|
788
792
|
const activeOnchain = () => (onchain !== undefined ? onchain : CONFIG.onchain);
|
|
793
|
+
const activeEscalate = () => (escalate !== undefined ? escalate : CONFIG.escalate);
|
|
789
794
|
// Approve a gated action in "ask" mode. Remembers "always" choices in settings
|
|
790
795
|
// so it never asks again for that program (or for on-chain actions).
|
|
791
796
|
const approve = async (kind, desc) => {
|
|
@@ -821,7 +826,7 @@ async function repl(flags) {
|
|
|
821
826
|
// Run one agent turn: hooks, the agent, then persist history + tokens.
|
|
822
827
|
const runTurn = async (task) => {
|
|
823
828
|
await runHooks(cwd, "preRun", hooks, flags.auto, task);
|
|
824
|
-
const res = await runAgent({ client, task, cwd, sessionDir: sessionDirPath, preferredMode: mode, approve, preferredModel: model, preferredEffort: effort, preferredSubagents: subagents, preferredOnchain: activeOnchain(), history, mcp });
|
|
829
|
+
const res = await runAgent({ client, task, cwd, sessionDir: sessionDirPath, preferredMode: mode, approve, preferredModel: model, preferredEffort: effort, preferredSubagents: subagents, preferredOnchain: activeOnchain(), preferredEscalate: activeEscalate(), history, mcp });
|
|
825
830
|
history = res.messages;
|
|
826
831
|
if (res.usageTotal) { sessTokens.in += res.usageTotal.prompt || 0; sessTokens.out += res.usageTotal.completion || 0; }
|
|
827
832
|
await saveMessages(cwd, sessionId, history);
|
|
@@ -926,6 +931,17 @@ async function repl(flags) {
|
|
|
926
931
|
console.log(ui.warn("usage: /onchain on|off"));
|
|
927
932
|
}
|
|
928
933
|
}
|
|
934
|
+
else if (cmd === "escalate") {
|
|
935
|
+
const a = arg.toLowerCase().trim();
|
|
936
|
+
if (a === "on" || a === "off") {
|
|
937
|
+
escalate = a === "on"; saveSetting("escalate", escalate);
|
|
938
|
+
ui.info("escalate " + (escalate ? "on" : "off") + " (saved)" + (escalate ? " · switches to a stronger model after " + CONFIG.escalateAfter + " tool failures" : " · stays on the chosen model"));
|
|
939
|
+
} else if (!a) {
|
|
940
|
+
ui.info("escalate: " + (activeEscalate() ? "on" : "off") + " · after " + CONFIG.escalateAfter + " failures · usage: /escalate on|off");
|
|
941
|
+
} else {
|
|
942
|
+
console.log(ui.warn("usage: /escalate on|off"));
|
|
943
|
+
}
|
|
944
|
+
}
|
|
929
945
|
else if (cmd === "mode") {
|
|
930
946
|
const a = arg.toLowerCase().trim();
|
|
931
947
|
if (["ask", "auto", "plan"].includes(a)) {
|
|
@@ -1002,7 +1018,7 @@ async function repl(flags) {
|
|
|
1002
1018
|
else { console.log(ui.section("Custom commands", customCmds.length + " loaded")); for (const c of customCmds) console.log(" " + ui.accent("/" + c.name) + " " + ui.muted(c.description)); }
|
|
1003
1019
|
}
|
|
1004
1020
|
else if (cmd === "goal") {
|
|
1005
|
-
await runGoal({ client, objective: arg, cwd, sessionId, sessionDir: sessionDirPath, preferredMode: mode, approve, preferredModel: model, preferredEffort: effort, preferredSubagents: subagents, preferredOnchain: activeOnchain(), verifyCmd: flags.verify || detectVerifyCmd(cwd), maxIters: 3, history });
|
|
1021
|
+
await runGoal({ client, objective: arg, cwd, sessionId, sessionDir: sessionDirPath, preferredMode: mode, approve, preferredModel: model, preferredEffort: effort, preferredSubagents: subagents, preferredOnchain: activeOnchain(), preferredEscalate: activeEscalate(), verifyCmd: flags.verify || detectVerifyCmd(cwd), maxIters: 3, history });
|
|
1006
1022
|
history = await readMessages(cwd, sessionId) || history;
|
|
1007
1023
|
}
|
|
1008
1024
|
else if (customMap[cmd]) {
|
|
@@ -1029,7 +1045,7 @@ async function main() {
|
|
|
1029
1045
|
if (flags.version) {
|
|
1030
1046
|
let v = "";
|
|
1031
1047
|
try { v = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")).version; } catch {}
|
|
1032
|
-
console.log("z0gcode " + (v || "0.3.
|
|
1048
|
+
console.log("z0gcode " + (v || "0.3.2"));
|
|
1033
1049
|
return;
|
|
1034
1050
|
}
|
|
1035
1051
|
|
package/package.json
CHANGED
package/src/agent.mjs
CHANGED
|
@@ -57,7 +57,7 @@ function parseArgs(raw) {
|
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
export async function runAgent({ client, task, cwd, sessionDir, allowBash, preferredMode, approve, preferredModel, preferredEffort, preferredSubagents, preferredOnchain, onModel, history, mcp, quiet = false, toolNames = null, isSubagent = false }) {
|
|
60
|
+
export async function runAgent({ client, task, cwd, sessionDir, allowBash, preferredMode, approve, preferredModel, preferredEffort, preferredSubagents, preferredOnchain, preferredEscalate, onModel, history, mcp, quiet = false, toolNames = null, isSubagent = false }) {
|
|
61
61
|
const q = !!quiet;
|
|
62
62
|
// "" means an explicit unset (use the model's own default); undefined falls back.
|
|
63
63
|
const effort = preferredEffort === "" ? null : (preferredEffort || CONFIG.effort);
|
|
@@ -91,7 +91,11 @@ export async function runAgent({ client, task, cwd, sessionDir, allowBash, prefe
|
|
|
91
91
|
: [{ role: "system", content: systemPrompt(cwd) }, { role: "user", content: task }];
|
|
92
92
|
|
|
93
93
|
const recent = []; // circuit breaker on repeated identical tool calls
|
|
94
|
+
const results = []; // no-progress breaker: repeated identical tool RESULTS
|
|
95
|
+
let loopEscalated = false; // escalate once on a spin before giving up
|
|
94
96
|
const failCounts = {}; // per-tool failure counter, drives model escalation
|
|
97
|
+
const escalateOn = preferredEscalate !== undefined ? preferredEscalate : CONFIG.escalate;
|
|
98
|
+
const escalateAfter = CONFIG.escalateAfter;
|
|
95
99
|
let escalate = false;
|
|
96
100
|
let activeModel = models[0];
|
|
97
101
|
const usageTotal = { prompt: 0, completion: 0, total: 0 };
|
|
@@ -158,6 +162,7 @@ export async function runAgent({ client, task, cwd, sessionDir, allowBash, prefe
|
|
|
158
162
|
return { ok: true, steps: step + 1, changes: prov.count(), messages, finalText, usageTotal };
|
|
159
163
|
}
|
|
160
164
|
|
|
165
|
+
let stuck = false;
|
|
161
166
|
for (const tc of toolCalls) {
|
|
162
167
|
const name = tc.function?.name;
|
|
163
168
|
const args = parseArgs(tc.function?.arguments);
|
|
@@ -286,10 +291,33 @@ export async function runAgent({ client, task, cwd, sessionDir, allowBash, prefe
|
|
|
286
291
|
}
|
|
287
292
|
} else {
|
|
288
293
|
failCounts[name] = (failCounts[name] || 0) + 1;
|
|
289
|
-
if (failCounts[name] >=
|
|
294
|
+
if (escalateOn && failCounts[name] >= escalateAfter) escalate = true; // stuck on this tool: try a stronger model
|
|
290
295
|
}
|
|
291
296
|
|
|
292
297
|
messages.push({ role: "tool", tool_call_id: tc.id, content: String(res.content ?? (res.ok ? "OK" : "ERROR")) });
|
|
298
|
+
|
|
299
|
+
// No-progress breaker: the same tool returning the same result repeatedly
|
|
300
|
+
// means the agent is spinning (e.g. re-checking a state only a human can
|
|
301
|
+
// change). Stop cleanly and hand control back, instead of burning steps.
|
|
302
|
+
const rkey = `${name}|${res.summary || ""}|${String(res.content ?? "").slice(0, 300)}`;
|
|
303
|
+
results.push(rkey);
|
|
304
|
+
if (results.length > 8) results.shift();
|
|
305
|
+
const repeat = results.filter((k) => k === rkey).length;
|
|
306
|
+
// Spinning on the same result: it is usually a model limitation, so first
|
|
307
|
+
// escalate to a stronger model (and stay on it) to give it a chance to get
|
|
308
|
+
// unstuck; only give up and hand control back if it still makes no progress.
|
|
309
|
+
if (repeat === 2 && escalateOn && !loopEscalated && activeModel === models[0]) {
|
|
310
|
+
escalate = true; loopEscalated = true;
|
|
311
|
+
if (!q) ui.info("spinning on the same result: trying a stronger model");
|
|
312
|
+
}
|
|
313
|
+
if (repeat >= (escalateOn ? 4 : 3)) stuck = true;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
if (stuck) {
|
|
317
|
+
finalText = "Stopped: I kept getting the same result without new progress. If this needs a manual step (a permission or setting change), do it and ask me again, or refine the task.";
|
|
318
|
+
if (!q) { ui.error("Stopped: no progress (same tool result 3 times)."); ui.assistant(finalText); }
|
|
319
|
+
usageTotal.total = usageTotal.prompt + usageTotal.completion;
|
|
320
|
+
return { ok: false, steps: step + 1, changes: prov.count(), messages, finalText, usageTotal };
|
|
293
321
|
}
|
|
294
322
|
}
|
|
295
323
|
|
package/src/config.mjs
CHANGED
|
@@ -65,6 +65,15 @@ export const CONFIG = {
|
|
|
65
65
|
if (e !== undefined) return e;
|
|
66
66
|
return typeof settings.onchain === "boolean" ? settings.onchain : false;
|
|
67
67
|
})(),
|
|
68
|
+
|
|
69
|
+
// Escalate to a stronger fallback model when a tool fails repeatedly. ON by
|
|
70
|
+
// default (env > settings > on); escalateAfter is the failure threshold.
|
|
71
|
+
escalate: (() => {
|
|
72
|
+
const e = boolOf(process.env.ZOG_ESCALATE);
|
|
73
|
+
if (e !== undefined) return e;
|
|
74
|
+
return typeof settings.escalate === "boolean" ? settings.escalate : true;
|
|
75
|
+
})(),
|
|
76
|
+
escalateAfter: Math.max(2, Number(process.env.ZOG_ESCALATE_AFTER || settings.escalateAfter || 3)),
|
|
68
77
|
};
|
|
69
78
|
|
|
70
79
|
export function modelChain(preferred) {
|
package/src/goal.mjs
CHANGED
|
@@ -14,12 +14,12 @@ function runCmd(cmd, cwd) {
|
|
|
14
14
|
});
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
export async function runGoal({ client, objective, cwd, sessionId, sessionDir, allowBash, preferredMode, approve, preferredModel, preferredEffort, preferredSubagents, preferredOnchain, verifyCmd, maxIters = 3, history: historyParam = null }) {
|
|
17
|
+
export async function runGoal({ client, objective, cwd, sessionId, sessionDir, allowBash, preferredMode, approve, preferredModel, preferredEffort, preferredSubagents, preferredOnchain, preferredEscalate, verifyCmd, maxIters = 3, history: historyParam = null }) {
|
|
18
18
|
let history = historyParam ?? null;
|
|
19
19
|
let task = objective;
|
|
20
20
|
for (let iter = 1; iter <= maxIters; iter++) {
|
|
21
21
|
console.log(ui.section("Goal", "iteration " + iter + "/" + maxIters));
|
|
22
|
-
const res = await runAgent({ client, task, cwd, sessionDir, allowBash, preferredMode, approve, preferredModel, preferredEffort, preferredSubagents, preferredOnchain, history });
|
|
22
|
+
const res = await runAgent({ client, task, cwd, sessionDir, allowBash, preferredMode, approve, preferredModel, preferredEffort, preferredSubagents, preferredOnchain, preferredEscalate, history });
|
|
23
23
|
history = res.messages;
|
|
24
24
|
if (sessionId && Array.isArray(history)) {
|
|
25
25
|
try { await saveMessages(cwd, sessionId, history); } catch {}
|