omnirush 0.4.1 → 0.5.0
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/assets/collect-once.ts +77 -39
- package/assets/extensions/omnirush/auth.js +56 -17
- package/assets/extensions/omnirush/collector-lib.ts +606 -179
- package/assets/extensions/omnirush/collector.ts +45 -1
- package/assets/extensions/omnirush/retry.js +144 -0
- package/assets/extensions/omnirush/sota-lib.ts +64 -0
- package/assets/extensions/omnirush/sota.ts +159 -28
- package/assets/extensions/omnirush/trace-format.ts +294 -0
- package/package.json +1 -1
- package/scripts/build-all-packages.py +106 -0
- package/scripts/postinstall.js +25 -8
- package/src/bin.js +253 -42
- package/src/lib.js +13 -0
package/src/bin.js
CHANGED
|
@@ -42,9 +42,11 @@ import {
|
|
|
42
42
|
modelsConfig,
|
|
43
43
|
resolveGatewayUrl,
|
|
44
44
|
withModelDefaults,
|
|
45
|
+
withDefaultThinkingLevel,
|
|
45
46
|
resolveModelDefaults,
|
|
46
47
|
} from "./lib.js";
|
|
47
48
|
import { installManagedTools } from "./tools.js";
|
|
49
|
+
import { ensureRuntime, managedBunPath } from "../scripts/postinstall.js";
|
|
48
50
|
import {
|
|
49
51
|
clearAuth,
|
|
50
52
|
deviceAuthorize,
|
|
@@ -70,6 +72,7 @@ const PI_AGENT_DIR = process.env.OMNIRUSH_PI_AGENT_DIR
|
|
|
70
72
|
? path.resolve(process.env.OMNIRUSH_PI_AGENT_DIR)
|
|
71
73
|
: path.join(os.homedir(), ".pi", "agent");
|
|
72
74
|
const PI_MODELS_JSON = path.join(PI_AGENT_DIR, "models.json");
|
|
75
|
+
const PI_SETTINGS_JSON = path.join(PI_AGENT_DIR, "settings.json");
|
|
73
76
|
const PI_EXT_DIR = path.join(PI_AGENT_DIR, "extensions", "omnirush");
|
|
74
77
|
|
|
75
78
|
function managerOrigin() {
|
|
@@ -116,6 +119,29 @@ function ensureDirs() {
|
|
|
116
119
|
} catch {
|
|
117
120
|
/* best effort — pi falls back to PATH, then its own downloader */
|
|
118
121
|
}
|
|
122
|
+
installDefaultThinkingLevel();
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Product default effort (P2): xhigh. Fills an ABSENT
|
|
126
|
+
// defaultThinkingLevel in the agent settings only — an explicit user
|
|
127
|
+
// choice always wins; the models' thinkingLevelMap is untouched.
|
|
128
|
+
function installDefaultThinkingLevel() {
|
|
129
|
+
let settings = {};
|
|
130
|
+
try {
|
|
131
|
+
settings = JSON.parse(fs.readFileSync(PI_SETTINGS_JSON, "utf8"));
|
|
132
|
+
} catch {
|
|
133
|
+
/* fresh install or unparsable — start clean rather than clobber */
|
|
134
|
+
settings = {};
|
|
135
|
+
}
|
|
136
|
+
let next;
|
|
137
|
+
try {
|
|
138
|
+
next = withDefaultThinkingLevel(settings);
|
|
139
|
+
} catch {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
if (next !== settings) {
|
|
143
|
+
fs.writeFileSync(PI_SETTINGS_JSON, JSON.stringify(next, null, 2) + "\n");
|
|
144
|
+
}
|
|
119
145
|
}
|
|
120
146
|
|
|
121
147
|
// Merge our provider into the user's models.json without clobbering
|
|
@@ -163,36 +189,44 @@ function isMusl() {
|
|
|
163
189
|
}
|
|
164
190
|
|
|
165
191
|
function findBun() {
|
|
166
|
-
//
|
|
167
|
-
//
|
|
168
|
-
//
|
|
192
|
+
// MANAGED runtime first — always (issue #7: a system bun on PATH
|
|
193
|
+
// shadowed ours when .runtime was missing from a dev checkout, and a
|
|
194
|
+
// mismatched bun breaks the fork's own extensions). Vendored paths are
|
|
195
|
+
// canonical; PATH and ~/.bun are last-resort fallbacks that only run
|
|
196
|
+
// after a self-heal download was attempted. Linux packages ship both
|
|
197
|
+
// the glibc and the musl binary; the launcher picks by the running libc.
|
|
169
198
|
const runtimeRoot = path.resolve(__dirname, "..", ".runtime");
|
|
170
199
|
const plat = process.platform;
|
|
171
200
|
const arch = process.arch === "arm64" ? "aarch64" : "x64";
|
|
172
|
-
const
|
|
201
|
+
const managedCandidates = [];
|
|
173
202
|
if (plat === "win32") {
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
);
|
|
177
|
-
for (const dir of (process.env.PATH || "").split(path.delimiter)) {
|
|
178
|
-
if (dir) candidates.push(path.join(dir, "bun.exe"));
|
|
179
|
-
}
|
|
203
|
+
managedCandidates.push(managedBunPath());
|
|
204
|
+
managedCandidates.push(path.join(runtimeRoot, "bun-windows-x64", "bin", "bun.exe"));
|
|
180
205
|
} else {
|
|
181
206
|
if (plat === "linux") {
|
|
182
|
-
if (isMusl())
|
|
183
|
-
else if (arch === "x64")
|
|
184
|
-
|
|
207
|
+
if (isMusl()) managedCandidates.push(path.join(runtimeRoot, "bin", "bun-musl"));
|
|
208
|
+
else if (arch === "x64") managedCandidates.push(path.join(runtimeRoot, "bin", "bun"));
|
|
209
|
+
managedCandidates.push(path.join(runtimeRoot, "bin", `bun-${arch}${isMusl() ? "-musl" : ""}`));
|
|
185
210
|
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
211
|
+
managedCandidates.push(path.join(runtimeRoot, `bun-${plat}-${arch}`, "bin", "bun"));
|
|
212
|
+
}
|
|
213
|
+
for (const c of managedCandidates) {
|
|
214
|
+
try {
|
|
215
|
+
fs.accessSync(c, fs.constants.X_OK);
|
|
216
|
+
return { path: c, managed: true };
|
|
217
|
+
} catch {
|
|
218
|
+
/* next */
|
|
189
219
|
}
|
|
190
|
-
candidates.push(path.join(os.homedir(), ".bun", "bin", "bun"));
|
|
191
220
|
}
|
|
192
|
-
|
|
221
|
+
const fallbackCandidates = [];
|
|
222
|
+
for (const dir of (process.env.PATH || "").split(path.delimiter)) {
|
|
223
|
+
if (dir) fallbackCandidates.push(path.join(dir, plat === "win32" ? "bun.exe" : "bun"));
|
|
224
|
+
}
|
|
225
|
+
fallbackCandidates.push(path.join(os.homedir(), ".bun", "bin", plat === "win32" ? "bun.exe" : "bun"));
|
|
226
|
+
for (const c of fallbackCandidates) {
|
|
193
227
|
try {
|
|
194
228
|
fs.accessSync(c, fs.constants.X_OK);
|
|
195
|
-
return c;
|
|
229
|
+
return { path: c, managed: false };
|
|
196
230
|
} catch {
|
|
197
231
|
/* next */
|
|
198
232
|
}
|
|
@@ -200,19 +234,57 @@ function findBun() {
|
|
|
200
234
|
return null;
|
|
201
235
|
}
|
|
202
236
|
|
|
203
|
-
|
|
237
|
+
/**
|
|
238
|
+
* Self-healing managed-runtime resolution: if no MANAGED bun is
|
|
239
|
+
* installed, run the same fetch the postinstall runs, then re-check. A
|
|
240
|
+
* PATH bun is only used when the managed download is impossible — and
|
|
241
|
+
* the caller reports it.
|
|
242
|
+
*/
|
|
243
|
+
async function resolveBun() {
|
|
244
|
+
let found = findBun();
|
|
245
|
+
if (found?.managed) return found;
|
|
246
|
+
// Self-heal: fetch the managed runtime (downloads only what is
|
|
247
|
+
// missing; a few MB-100MB depending on platform).
|
|
248
|
+
try {
|
|
249
|
+
await ensureRuntime();
|
|
250
|
+
} catch (error) {
|
|
251
|
+
// fall through: maybe a usable fallback still exists
|
|
252
|
+
try {
|
|
253
|
+
fs.accessSync(managedBunPath(), fs.constants.X_OK);
|
|
254
|
+
found = { path: managedBunPath(), managed: true };
|
|
255
|
+
} catch {
|
|
256
|
+
console.error(`omnirush: managed runtime download failed (${error?.message ?? error})`);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
if (!found?.managed) found = findBun();
|
|
260
|
+
return found;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
async function runtimeFor(entry) {
|
|
204
264
|
// pi is Bun-first; its CLI imports node:module#enableCompileCache,
|
|
205
|
-
// which needs Node >= 22.8.
|
|
206
|
-
//
|
|
207
|
-
|
|
208
|
-
|
|
265
|
+
// which needs Node >= 22.8. The MANAGED bun wins unconditionally
|
|
266
|
+
// (self-heal download first); a PATH bun is a last resort. Plain node
|
|
267
|
+
// 22.8+ runs the agent without any bun.
|
|
268
|
+
const found = await resolveBun();
|
|
269
|
+
if (found) {
|
|
270
|
+
if (!found.managed) {
|
|
271
|
+
console.error(
|
|
272
|
+
"omnirush: using a non-managed bun from PATH " +
|
|
273
|
+
`(${found.path}) — the managed runtime is missing. Fix: run ` +
|
|
274
|
+
"`npm install -g omnirush` again (retries the runtime download).",
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
return { cmd: found.path, args: [entry] };
|
|
278
|
+
}
|
|
209
279
|
const [major, minor] = process.versions.node.split(".").map(Number);
|
|
210
280
|
if (major > 22 || (major === 22 && minor >= 8)) {
|
|
211
281
|
return { cmd: process.execPath, args: [entry] };
|
|
212
282
|
}
|
|
213
283
|
console.error(
|
|
214
|
-
`omnirush
|
|
215
|
-
"
|
|
284
|
+
`omnirush couldn't set up its agent runtime (Node ${process.versions.node} is too ` +
|
|
285
|
+
"old for the agent core, which needs Node >= 22.8, and the managed runtime " +
|
|
286
|
+
"download did not succeed — network or install issue). Fix: run " +
|
|
287
|
+
"`npm install -g omnirush` again (retries the runtime download), or install Node 22.8+.",
|
|
216
288
|
);
|
|
217
289
|
process.exit(1);
|
|
218
290
|
}
|
|
@@ -448,6 +520,54 @@ async function cmdDoctor() {
|
|
|
448
520
|
);
|
|
449
521
|
}
|
|
450
522
|
}
|
|
523
|
+
// Agent runtime: the managed bun must exist and always wins (issue #7:
|
|
524
|
+
// a PATH bun shadowed the managed runtime for a dev). Self-heal by
|
|
525
|
+
// downloading it right here when missing; report WHICH runtime is in
|
|
526
|
+
// use so support can see it.
|
|
527
|
+
{
|
|
528
|
+
let found = findBun();
|
|
529
|
+
let healError = null;
|
|
530
|
+
if (!found?.managed) {
|
|
531
|
+
try {
|
|
532
|
+
await ensureRuntime();
|
|
533
|
+
} catch (error) {
|
|
534
|
+
healError = error;
|
|
535
|
+
}
|
|
536
|
+
found = findBun();
|
|
537
|
+
}
|
|
538
|
+
if (found?.managed) {
|
|
539
|
+
ok(
|
|
540
|
+
"runtime",
|
|
541
|
+
true,
|
|
542
|
+
`bundled bun ${bunVersionOf(found.path)} (${found.path})`,
|
|
543
|
+
);
|
|
544
|
+
} else if (healError) {
|
|
545
|
+
ok(
|
|
546
|
+
"runtime",
|
|
547
|
+
false,
|
|
548
|
+
`managed runtime missing and the download failed (${healError?.message ?? healError}) — ` +
|
|
549
|
+
"Fix: run `npm install -g omnirush` again (retries the runtime download), or install Node 22.8+",
|
|
550
|
+
);
|
|
551
|
+
} else if (found) {
|
|
552
|
+
console.log(
|
|
553
|
+
`WARN runtime — using a NON-managed bun ${bunVersionOf(found.path)} from PATH (${found.path}); ` +
|
|
554
|
+
"the managed runtime is missing. Fix: run `npm install -g omnirush` again (retries the runtime download)",
|
|
555
|
+
);
|
|
556
|
+
} else {
|
|
557
|
+
const [major, minor] = process.versions.node.split(".").map(Number);
|
|
558
|
+
if (major > 22 || (major === 22 && minor >= 8)) {
|
|
559
|
+
ok("runtime", true, "no bun needed — Node " + process.versions.node + " runs the agent");
|
|
560
|
+
} else {
|
|
561
|
+
ok(
|
|
562
|
+
"runtime",
|
|
563
|
+
false,
|
|
564
|
+
`Node ${process.versions.node} is too old for the agent core (need >= 22.8) and the managed ` +
|
|
565
|
+
"runtime is missing — Fix: run `npm install -g omnirush` again (retries the runtime download), " +
|
|
566
|
+
"or install Node 22.8+",
|
|
567
|
+
);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
}
|
|
451
571
|
// Search tools: bundled fd/rg (or system ones) keep the agent off its
|
|
452
572
|
// GitHub download path, which fails on restricted networks.
|
|
453
573
|
const toolsRoot = path.join(PI_AGENT_DIR, "bin");
|
|
@@ -492,6 +612,15 @@ function requireAuth() {
|
|
|
492
612
|
process.exit(1);
|
|
493
613
|
}
|
|
494
614
|
|
|
615
|
+
function bunVersionOf(bunPath) {
|
|
616
|
+
try {
|
|
617
|
+
const { execFileSync } = require("node:child_process");
|
|
618
|
+
return String(execFileSync(bunPath, ["--version"], { timeout: 5000 })).trim() || "unknown";
|
|
619
|
+
} catch {
|
|
620
|
+
return "unknown";
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
|
|
495
624
|
function ownVersion() {
|
|
496
625
|
try {
|
|
497
626
|
return JSON.parse(fs.readFileSync(path.join(__dirname, "..", "package.json"), "utf8")).version;
|
|
@@ -527,24 +656,68 @@ function childEnv(auth) {
|
|
|
527
656
|
};
|
|
528
657
|
}
|
|
529
658
|
|
|
659
|
+
/**
|
|
660
|
+
* Spawn the agent with launcher signal hygiene (issue #9). The terminal's
|
|
661
|
+
* Ctrl+C already reaches the whole foreground process group — the child
|
|
662
|
+
* (bun -> pi) handles it itself (first ^C interrupts the turn, second
|
|
663
|
+
* exits pi). The launcher therefore IGNORES SIGINT (it used to die
|
|
664
|
+
* immediately, orphaning the TUI and forcing a third ^C), forwards
|
|
665
|
+
* process-directed signals, and exits only when the child does, with the
|
|
666
|
+
* child's exit code or signal.
|
|
667
|
+
*/
|
|
668
|
+
function spawnAgent(cmd, args, opts) {
|
|
669
|
+
const child = spawn(cmd, args, opts);
|
|
670
|
+
let childAlive = true;
|
|
671
|
+
let lastExit = { code: 0, signal: null };
|
|
672
|
+
child.on("exit", (code, signal) => {
|
|
673
|
+
childAlive = false;
|
|
674
|
+
lastExit = { code, signal };
|
|
675
|
+
});
|
|
676
|
+
const onInterrupt = () => {
|
|
677
|
+
if (!childAlive) {
|
|
678
|
+
process.exit(lastExit.code ?? 0);
|
|
679
|
+
}
|
|
680
|
+
// Otherwise let pi handle the interrupt — never double-send, never
|
|
681
|
+
// exit first.
|
|
682
|
+
};
|
|
683
|
+
const forward = (signal) => () => {
|
|
684
|
+
if (childAlive) {
|
|
685
|
+
try {
|
|
686
|
+
child.kill(signal);
|
|
687
|
+
} catch {
|
|
688
|
+
/* already gone */
|
|
689
|
+
}
|
|
690
|
+
} else {
|
|
691
|
+
process.exit(lastExit.code ?? 0);
|
|
692
|
+
}
|
|
693
|
+
};
|
|
694
|
+
process.on("SIGINT", onInterrupt);
|
|
695
|
+
process.on("SIGTERM", forward("SIGTERM"));
|
|
696
|
+
if (process.platform !== "win32") {
|
|
697
|
+
process.on("SIGHUP", forward("SIGHUP"));
|
|
698
|
+
process.on("SIGQUIT", forward("SIGQUIT"));
|
|
699
|
+
}
|
|
700
|
+
child.on("exit", (code, signal) => {
|
|
701
|
+
if (signal) process.kill(process.pid, signal);
|
|
702
|
+
else process.exit(code ?? 0);
|
|
703
|
+
});
|
|
704
|
+
return child;
|
|
705
|
+
}
|
|
706
|
+
|
|
530
707
|
async function cmdRun() {
|
|
531
708
|
ensureDirs();
|
|
532
709
|
const auth = requireAuth();
|
|
533
710
|
installModelsJson(agentGatewayUrl(auth));
|
|
534
711
|
installExtensions();
|
|
535
712
|
const entry = piEntry();
|
|
536
|
-
const { cmd, args } = runtimeFor(entry);
|
|
713
|
+
const { cmd, args } = await runtimeFor(entry);
|
|
537
714
|
// Plain `omnirush` always runs the SOTA model (charter rule 1); user
|
|
538
715
|
// flags win when present.
|
|
539
716
|
const piArgs = withModelDefaults([], resolveModelDefaults(process.env));
|
|
540
|
-
|
|
717
|
+
spawnAgent(cmd, [...args, ...piArgs], {
|
|
541
718
|
stdio: "inherit",
|
|
542
719
|
env: { ...process.env, ...childEnv(auth) },
|
|
543
720
|
});
|
|
544
|
-
child.on("exit", (code, signal) => {
|
|
545
|
-
if (signal) process.kill(process.pid, signal);
|
|
546
|
-
else process.exit(code ?? 0);
|
|
547
|
-
});
|
|
548
721
|
}
|
|
549
722
|
|
|
550
723
|
async function cmdCollect(argv) {
|
|
@@ -552,20 +725,59 @@ async function cmdCollect(argv) {
|
|
|
552
725
|
const auth = requireAuth();
|
|
553
726
|
installExtensions();
|
|
554
727
|
const entry = path.join(__dirname, "..", "assets", "collect-once.ts");
|
|
555
|
-
const { cmd, args } = runtimeFor(entry);
|
|
556
|
-
|
|
728
|
+
const { cmd, args } = await runtimeFor(entry);
|
|
729
|
+
spawnAgent(cmd, [...args, ...argv], {
|
|
557
730
|
stdio: "inherit",
|
|
558
731
|
env: { ...process.env, ...childEnv(auth) },
|
|
559
732
|
});
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
// Native omnirush help (issue #8): --help/-h must render OUR commands
|
|
736
|
+
// and branding — never forward to the agent core's help.
|
|
737
|
+
function printHelp() {
|
|
738
|
+
console.log(`omnirush — free daily tokens for the most powerful coding model on earth
|
|
739
|
+
|
|
740
|
+
Usage:
|
|
741
|
+
omnirush start the agent (default: gpt-6-astra)
|
|
742
|
+
omnirush -p "<prompt>" run a one-shot prompt
|
|
743
|
+
omnirush login sign in (device flow via your browser)
|
|
744
|
+
omnirush logout forget the stored device tokens
|
|
745
|
+
omnirush whoami show the signed-in identity
|
|
746
|
+
omnirush doctor check auth, manager, runtime, agent config
|
|
747
|
+
omnirush collect [--dir PATH] one-shot workspace snapshot upload
|
|
748
|
+
omnirush update update omnirush (npm install -g omnirush@latest)
|
|
749
|
+
omnirush --version show the omnirush version
|
|
750
|
+
|
|
751
|
+
Options:
|
|
752
|
+
--model <model[:effort]> model override (gpt-6-astra, gpt-5.6-sol;
|
|
753
|
+
effort: minimal..max)
|
|
754
|
+
--thinking <level> reasoning effort override
|
|
755
|
+
--strict-sota exit non-zero if the served model is not
|
|
756
|
+
the requested SOTA model
|
|
757
|
+
--session <file> | --continue session options (forwarded to the agent)
|
|
758
|
+
-p, --print "<prompt>" print-mode: answer once and exit
|
|
759
|
+
-h, --help show this help
|
|
760
|
+
|
|
761
|
+
Session commands (inside the agent):
|
|
762
|
+
/grant today's token grant and usage
|
|
763
|
+
/usage detailed usage from the manager (counters + devices)
|
|
764
|
+
/status session status (identity, model, grant, collector)
|
|
765
|
+
/mcp MCP server state (/mcp reconnect [server])
|
|
766
|
+
/verify model attestation (arrives with the gateway lane)
|
|
767
|
+
|
|
768
|
+
Environment:
|
|
769
|
+
OMNIRUSH_ORIGIN manager origin override
|
|
770
|
+
OMNIRUSH_MODEL default model override (e.g. gpt-5.6-sol)
|
|
771
|
+
OMNIRUSH_DIR state dir override (default ~/.omnirush)
|
|
772
|
+
OMNIRUSH_DEBUG=1 verbose diagnostics (raw gateway errors on failure)
|
|
773
|
+
|
|
774
|
+
Docs: https://omnirush.ai/omnirush — state is stored 0600 in ~/.omnirush`);
|
|
564
775
|
}
|
|
565
776
|
|
|
566
777
|
const [, , ...argv] = process.argv;
|
|
567
778
|
const cmd = argv[0];
|
|
568
|
-
if (cmd === "
|
|
779
|
+
if (cmd === "-h" || cmd === "--help" || cmd === "help") printHelp();
|
|
780
|
+
else if (cmd === "login") await cmdLogin(argv.slice(1));
|
|
569
781
|
else if (cmd === "logout") await cmdLogout();
|
|
570
782
|
else if (cmd === "whoami") await cmdWhoami();
|
|
571
783
|
else if (cmd === "doctor") await cmdDoctor();
|
|
@@ -582,11 +794,10 @@ else {
|
|
|
582
794
|
installModelsJson(agentGatewayUrl(auth));
|
|
583
795
|
installExtensions();
|
|
584
796
|
const entry = piEntry();
|
|
585
|
-
const { cmd: bin, args } = runtimeFor(entry);
|
|
797
|
+
const { cmd: bin, args } = await runtimeFor(entry);
|
|
586
798
|
const piArgs = withModelDefaults(argv, resolveModelDefaults(process.env));
|
|
587
|
-
|
|
799
|
+
spawnAgent(bin, [...args, ...piArgs], {
|
|
588
800
|
stdio: "inherit",
|
|
589
801
|
env: { ...process.env, ...childEnv(auth) },
|
|
590
802
|
});
|
|
591
|
-
child.on("exit", (code) => process.exit(code ?? 0));
|
|
592
803
|
}
|
package/src/lib.js
CHANGED
|
@@ -221,6 +221,19 @@ export function modelsConfig({ gatewayUrl } = {}) {
|
|
|
221
221
|
};
|
|
222
222
|
}
|
|
223
223
|
|
|
224
|
+
/**
|
|
225
|
+
* Product default effort (Dan's call, 2026-09-24): xhigh, not pi's
|
|
226
|
+
* medium. The `thinkingLevelMap` on each model is unchanged — this only
|
|
227
|
+
* fills an ABSENT `defaultThinkingLevel` in the agent's settings.json,
|
|
228
|
+
* so an explicit user choice (saved via Ctrl+S in /thinking, or edited
|
|
229
|
+
* manually) always wins. Returns the settings object to persist; pure.
|
|
230
|
+
*/
|
|
231
|
+
export function withDefaultThinkingLevel(settings, level = "xhigh") {
|
|
232
|
+
const base = settings && typeof settings === "object" && !Array.isArray(settings) ? settings : {};
|
|
233
|
+
if (base.defaultThinkingLevel !== undefined) return base;
|
|
234
|
+
return { ...base, defaultThinkingLevel: level };
|
|
235
|
+
}
|
|
236
|
+
|
|
224
237
|
/**
|
|
225
238
|
* Pure mapping of the bundled search-tool binaries for the launcher: pi
|
|
226
239
|
* looks for managed `fd`/`rg` in <agentDir>/bin first and downloads from
|