mini-coder 0.0.22 → 0.1.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/.claude/settings.local.json +2 -1
- package/dist/mc.js +979 -1593
- package/package.json +4 -4
package/dist/mc.js
CHANGED
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
|
|
4
4
|
// src/index.ts
|
|
5
5
|
import { writeSync } from "fs";
|
|
6
|
-
import * as
|
|
6
|
+
import * as c23 from "yoctocolors";
|
|
7
7
|
|
|
8
8
|
// src/agent/agent.ts
|
|
9
|
-
import * as
|
|
9
|
+
import * as c12 from "yoctocolors";
|
|
10
10
|
|
|
11
11
|
// src/cli/load-markdown-configs.ts
|
|
12
12
|
import { existsSync as existsSync4, readdirSync as readdirSync2, readFileSync as readFileSync3, statSync as statSync2 } from "fs";
|
|
@@ -14,13 +14,13 @@ import { homedir as homedir4 } from "os";
|
|
|
14
14
|
import { basename, join as join5 } from "path";
|
|
15
15
|
|
|
16
16
|
// src/cli/config-conflicts.ts
|
|
17
|
-
import * as
|
|
17
|
+
import * as c8 from "yoctocolors";
|
|
18
18
|
|
|
19
19
|
// src/cli/output.ts
|
|
20
20
|
import { existsSync as existsSync3 } from "fs";
|
|
21
21
|
import { homedir as homedir3 } from "os";
|
|
22
22
|
import { join as join4 } from "path";
|
|
23
|
-
import * as
|
|
23
|
+
import * as c7 from "yoctocolors";
|
|
24
24
|
|
|
25
25
|
// src/cli/custom-commands.ts
|
|
26
26
|
import { existsSync, readFileSync } from "fs";
|
|
@@ -290,159 +290,6 @@ function parseAppError(err) {
|
|
|
290
290
|
return { headline: firstLine };
|
|
291
291
|
}
|
|
292
292
|
|
|
293
|
-
// src/cli/live-output.ts
|
|
294
|
-
import * as c from "yoctocolors";
|
|
295
|
-
|
|
296
|
-
// src/cli/terminal-io.ts
|
|
297
|
-
class TerminalIO {
|
|
298
|
-
rawModeEnabled = false;
|
|
299
|
-
interruptHandler = null;
|
|
300
|
-
setInterruptHandler(handler) {
|
|
301
|
-
this.interruptHandler = handler;
|
|
302
|
-
}
|
|
303
|
-
beforeWriteCallback = null;
|
|
304
|
-
setBeforeWriteCallback(cb) {
|
|
305
|
-
this.beforeWriteCallback = cb;
|
|
306
|
-
}
|
|
307
|
-
skipCallback = false;
|
|
308
|
-
stdoutWrite(text) {
|
|
309
|
-
if (text && this.beforeWriteCallback && !this.skipCallback) {
|
|
310
|
-
this.skipCallback = true;
|
|
311
|
-
this.beforeWriteCallback();
|
|
312
|
-
this.skipCallback = false;
|
|
313
|
-
}
|
|
314
|
-
this.doStdoutWrite(text);
|
|
315
|
-
}
|
|
316
|
-
doStdoutWrite(text) {
|
|
317
|
-
process.stdout.write(text);
|
|
318
|
-
}
|
|
319
|
-
stderrWrite(text) {
|
|
320
|
-
if (text && this.beforeWriteCallback && !this.skipCallback) {
|
|
321
|
-
this.skipCallback = true;
|
|
322
|
-
this.beforeWriteCallback();
|
|
323
|
-
this.skipCallback = false;
|
|
324
|
-
}
|
|
325
|
-
this.doStderrWrite(text);
|
|
326
|
-
}
|
|
327
|
-
doStderrWrite(text) {
|
|
328
|
-
process.stderr.write(text);
|
|
329
|
-
}
|
|
330
|
-
get isTTY() {
|
|
331
|
-
return process.stdin.isTTY;
|
|
332
|
-
}
|
|
333
|
-
get isStdoutTTY() {
|
|
334
|
-
return process.stdout.isTTY;
|
|
335
|
-
}
|
|
336
|
-
get isStderrTTY() {
|
|
337
|
-
return process.stderr.isTTY;
|
|
338
|
-
}
|
|
339
|
-
get stdoutColumns() {
|
|
340
|
-
return process.stdout.columns ?? 0;
|
|
341
|
-
}
|
|
342
|
-
setRawMode(enable) {
|
|
343
|
-
if (this.isTTY) {
|
|
344
|
-
process.stdin.setRawMode(enable);
|
|
345
|
-
this.rawModeEnabled = enable;
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
restoreTerminal() {
|
|
349
|
-
try {
|
|
350
|
-
if (this.isStderrTTY) {
|
|
351
|
-
this.stderrWrite("\x1B[?25h");
|
|
352
|
-
this.stderrWrite("\r\x1B[2K");
|
|
353
|
-
}
|
|
354
|
-
} catch {}
|
|
355
|
-
try {
|
|
356
|
-
if (this.rawModeEnabled) {
|
|
357
|
-
this.setRawMode(false);
|
|
358
|
-
}
|
|
359
|
-
} catch {}
|
|
360
|
-
}
|
|
361
|
-
registerCleanup() {
|
|
362
|
-
const cleanup = () => this.restoreTerminal();
|
|
363
|
-
process.on("exit", cleanup);
|
|
364
|
-
process.on("SIGTERM", () => {
|
|
365
|
-
cleanup();
|
|
366
|
-
process.exit(143);
|
|
367
|
-
});
|
|
368
|
-
process.on("SIGINT", () => {
|
|
369
|
-
if (this.interruptHandler) {
|
|
370
|
-
this.interruptHandler();
|
|
371
|
-
} else {
|
|
372
|
-
cleanup();
|
|
373
|
-
process.exit(130);
|
|
374
|
-
}
|
|
375
|
-
});
|
|
376
|
-
process.on("uncaughtException", (err) => {
|
|
377
|
-
cleanup();
|
|
378
|
-
throw err;
|
|
379
|
-
});
|
|
380
|
-
process.on("unhandledRejection", (reason) => {
|
|
381
|
-
cleanup();
|
|
382
|
-
throw reason instanceof Error ? reason : new Error(String(reason));
|
|
383
|
-
});
|
|
384
|
-
}
|
|
385
|
-
onStdinData(handler) {
|
|
386
|
-
process.stdin.on("data", handler);
|
|
387
|
-
return () => process.stdin.off("data", handler);
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
var terminal = new TerminalIO;
|
|
391
|
-
|
|
392
|
-
// src/cli/live-output.ts
|
|
393
|
-
var LIVE_OUTPUT_PREFIX = ` ${c.dim("\u2502")} `;
|
|
394
|
-
function write(text) {
|
|
395
|
-
terminal.stdoutWrite(text);
|
|
396
|
-
}
|
|
397
|
-
function writeln(text = "") {
|
|
398
|
-
terminal.stdoutWrite(`${text}
|
|
399
|
-
`);
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
class LiveOutputBlock {
|
|
403
|
-
pending = "";
|
|
404
|
-
lineOpen = false;
|
|
405
|
-
append(chunk) {
|
|
406
|
-
if (!chunk)
|
|
407
|
-
return;
|
|
408
|
-
this.pending += chunk.replace(/\r\n/g, `
|
|
409
|
-
`).replace(/\r/g, `
|
|
410
|
-
`);
|
|
411
|
-
this.flushCompleteLines();
|
|
412
|
-
}
|
|
413
|
-
finish() {
|
|
414
|
-
if (this.pending.length > 0) {
|
|
415
|
-
this.openLine();
|
|
416
|
-
write(this.pending);
|
|
417
|
-
this.pending = "";
|
|
418
|
-
}
|
|
419
|
-
if (this.lineOpen) {
|
|
420
|
-
writeln();
|
|
421
|
-
this.lineOpen = false;
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
flushCompleteLines() {
|
|
425
|
-
let boundary = this.pending.indexOf(`
|
|
426
|
-
`);
|
|
427
|
-
while (boundary !== -1) {
|
|
428
|
-
const line = this.pending.slice(0, boundary);
|
|
429
|
-
this.openLine();
|
|
430
|
-
write(line);
|
|
431
|
-
writeln();
|
|
432
|
-
this.lineOpen = false;
|
|
433
|
-
this.pending = this.pending.slice(boundary + 1);
|
|
434
|
-
boundary = this.pending.indexOf(`
|
|
435
|
-
`);
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
openLine() {
|
|
439
|
-
if (this.lineOpen)
|
|
440
|
-
return;
|
|
441
|
-
write(LIVE_OUTPUT_PREFIX);
|
|
442
|
-
this.lineOpen = true;
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
|
|
446
293
|
// src/cli/skills.ts
|
|
447
294
|
import {
|
|
448
295
|
closeSync,
|
|
@@ -567,7 +414,7 @@ function warnInvalidSkill(filePath, reason) {
|
|
|
567
414
|
if (warnedInvalidSkills.has(key))
|
|
568
415
|
return;
|
|
569
416
|
warnedInvalidSkills.add(key);
|
|
570
|
-
|
|
417
|
+
writeln(`${G.warn} skipping invalid skill ${filePath}: ${reason}`);
|
|
571
418
|
}
|
|
572
419
|
function validateSkill(candidate) {
|
|
573
420
|
const meta = getCandidateFrontmatter(candidate);
|
|
@@ -639,7 +486,105 @@ function loadSkillContent(name, cwd, homeDir) {
|
|
|
639
486
|
}
|
|
640
487
|
|
|
641
488
|
// src/cli/spinner.ts
|
|
642
|
-
import * as
|
|
489
|
+
import * as c from "yoctocolors";
|
|
490
|
+
|
|
491
|
+
// src/cli/terminal-io.ts
|
|
492
|
+
class TerminalIO {
|
|
493
|
+
rawModeEnabled = false;
|
|
494
|
+
interruptHandler = null;
|
|
495
|
+
setInterruptHandler(handler) {
|
|
496
|
+
this.interruptHandler = handler;
|
|
497
|
+
}
|
|
498
|
+
beforeWriteCallback = null;
|
|
499
|
+
setBeforeWriteCallback(cb) {
|
|
500
|
+
this.beforeWriteCallback = cb;
|
|
501
|
+
}
|
|
502
|
+
skipCallback = false;
|
|
503
|
+
stdoutWrite(text) {
|
|
504
|
+
if (text && this.beforeWriteCallback && !this.skipCallback) {
|
|
505
|
+
this.skipCallback = true;
|
|
506
|
+
this.beforeWriteCallback();
|
|
507
|
+
this.skipCallback = false;
|
|
508
|
+
}
|
|
509
|
+
this.doStdoutWrite(text);
|
|
510
|
+
}
|
|
511
|
+
doStdoutWrite(text) {
|
|
512
|
+
process.stdout.write(text);
|
|
513
|
+
}
|
|
514
|
+
stderrWrite(text) {
|
|
515
|
+
if (text && this.beforeWriteCallback && !this.skipCallback) {
|
|
516
|
+
this.skipCallback = true;
|
|
517
|
+
this.beforeWriteCallback();
|
|
518
|
+
this.skipCallback = false;
|
|
519
|
+
}
|
|
520
|
+
this.doStderrWrite(text);
|
|
521
|
+
}
|
|
522
|
+
doStderrWrite(text) {
|
|
523
|
+
process.stderr.write(text);
|
|
524
|
+
}
|
|
525
|
+
get isTTY() {
|
|
526
|
+
return process.stdin.isTTY;
|
|
527
|
+
}
|
|
528
|
+
get isStdoutTTY() {
|
|
529
|
+
return process.stdout.isTTY;
|
|
530
|
+
}
|
|
531
|
+
get isStderrTTY() {
|
|
532
|
+
return process.stderr.isTTY;
|
|
533
|
+
}
|
|
534
|
+
get stdoutColumns() {
|
|
535
|
+
return process.stdout.columns ?? 0;
|
|
536
|
+
}
|
|
537
|
+
setRawMode(enable) {
|
|
538
|
+
if (this.isTTY) {
|
|
539
|
+
process.stdin.setRawMode(enable);
|
|
540
|
+
this.rawModeEnabled = enable;
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
restoreTerminal() {
|
|
544
|
+
try {
|
|
545
|
+
if (this.isStderrTTY) {
|
|
546
|
+
this.stderrWrite("\x1B[?25h");
|
|
547
|
+
this.stderrWrite("\r\x1B[2K");
|
|
548
|
+
}
|
|
549
|
+
} catch {}
|
|
550
|
+
try {
|
|
551
|
+
if (this.rawModeEnabled) {
|
|
552
|
+
this.setRawMode(false);
|
|
553
|
+
}
|
|
554
|
+
} catch {}
|
|
555
|
+
}
|
|
556
|
+
registerCleanup() {
|
|
557
|
+
const cleanup = () => this.restoreTerminal();
|
|
558
|
+
process.on("exit", cleanup);
|
|
559
|
+
process.on("SIGTERM", () => {
|
|
560
|
+
cleanup();
|
|
561
|
+
process.exit(143);
|
|
562
|
+
});
|
|
563
|
+
process.on("SIGINT", () => {
|
|
564
|
+
if (this.interruptHandler) {
|
|
565
|
+
this.interruptHandler();
|
|
566
|
+
} else {
|
|
567
|
+
cleanup();
|
|
568
|
+
process.exit(130);
|
|
569
|
+
}
|
|
570
|
+
});
|
|
571
|
+
process.on("uncaughtException", (err) => {
|
|
572
|
+
cleanup();
|
|
573
|
+
throw err;
|
|
574
|
+
});
|
|
575
|
+
process.on("unhandledRejection", (reason) => {
|
|
576
|
+
cleanup();
|
|
577
|
+
throw reason instanceof Error ? reason : new Error(String(reason));
|
|
578
|
+
});
|
|
579
|
+
}
|
|
580
|
+
onStdinData(handler) {
|
|
581
|
+
process.stdin.on("data", handler);
|
|
582
|
+
return () => process.stdin.off("data", handler);
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
var terminal = new TerminalIO;
|
|
586
|
+
|
|
587
|
+
// src/cli/spinner.ts
|
|
643
588
|
var SPINNER_FRAMES = ["\u28FE", "\u28FD", "\u28FB", "\u28BF", "\u287F", "\u28DF", "\u28EF", "\u28F7"];
|
|
644
589
|
|
|
645
590
|
class Spinner {
|
|
@@ -675,15 +620,15 @@ class Spinner {
|
|
|
675
620
|
}
|
|
676
621
|
_tick() {
|
|
677
622
|
const f = SPINNER_FRAMES[this.frame++ % SPINNER_FRAMES.length] ?? "\u28FE";
|
|
678
|
-
const label = this.label ?
|
|
679
|
-
terminal.stderrWrite(`\r${
|
|
623
|
+
const label = this.label ? c.dim(` ${this.label}`) : "";
|
|
624
|
+
terminal.stderrWrite(`\r${c.dim(f)}${label}`);
|
|
680
625
|
}
|
|
681
626
|
}
|
|
682
627
|
|
|
683
628
|
// src/cli/status-bar.ts
|
|
684
|
-
import * as
|
|
629
|
+
import * as c2 from "yoctocolors";
|
|
685
630
|
var ANSI_ESCAPE = "\x1B";
|
|
686
|
-
var STATUS_SEP =
|
|
631
|
+
var STATUS_SEP = c2.dim(" \xB7 ");
|
|
687
632
|
function stripAnsi(s) {
|
|
688
633
|
if (!s.includes(ANSI_ESCAPE))
|
|
689
634
|
return s;
|
|
@@ -705,12 +650,12 @@ function buildContextSegment(opts) {
|
|
|
705
650
|
if (opts.contextTokens <= 0)
|
|
706
651
|
return null;
|
|
707
652
|
if (opts.contextWindow === null) {
|
|
708
|
-
return
|
|
653
|
+
return c2.dim(`ctx ${fmtTokens(opts.contextTokens)}`);
|
|
709
654
|
}
|
|
710
655
|
const pct = Math.round(opts.contextTokens / opts.contextWindow * 100);
|
|
711
656
|
const pctStr = `${pct}%`;
|
|
712
|
-
const pctColored = pct >= 90 ?
|
|
713
|
-
return
|
|
657
|
+
const pctColored = pct >= 90 ? c2.red(pctStr) : pct >= 75 ? c2.yellow(pctStr) : c2.dim(pctStr);
|
|
658
|
+
return c2.dim(`ctx ${fmtTokens(opts.contextTokens)}/${fmtTokens(opts.contextWindow)} `) + pctColored;
|
|
714
659
|
}
|
|
715
660
|
function renderStatusLine(segments) {
|
|
716
661
|
return segments.join(STATUS_SEP);
|
|
@@ -747,26 +692,26 @@ function fitStatusSegments(required, optional, cols) {
|
|
|
747
692
|
return truncatePlainText(fixedPrefix, cols);
|
|
748
693
|
const maxTailLen = Math.max(8, cols - fixedPrefix.length - sepLen);
|
|
749
694
|
const truncatedTail = truncatePlainText(plainRequired[1] ?? "", maxTailLen);
|
|
750
|
-
return `${required[0]}${STATUS_SEP}${
|
|
695
|
+
return `${required[0]}${STATUS_SEP}${c2.dim(truncatedTail)}`;
|
|
751
696
|
}
|
|
752
697
|
function renderStatusBar(opts) {
|
|
753
698
|
const cols = Math.max(20, terminal.stdoutColumns || 80);
|
|
754
699
|
const required = [
|
|
755
|
-
|
|
756
|
-
|
|
700
|
+
c2.cyan(opts.model),
|
|
701
|
+
c2.dim(`#${opts.sessionId.slice(0, 8)}`)
|
|
757
702
|
];
|
|
758
703
|
const optional = [];
|
|
759
704
|
if (opts.provider && opts.provider !== "zen") {
|
|
760
|
-
optional.push(
|
|
705
|
+
optional.push(c2.dim(opts.provider));
|
|
761
706
|
}
|
|
762
707
|
if (opts.activeAgent)
|
|
763
|
-
optional.push(
|
|
708
|
+
optional.push(c2.green(`@${opts.activeAgent}`));
|
|
764
709
|
if (opts.thinkingEffort)
|
|
765
|
-
optional.push(
|
|
710
|
+
optional.push(c2.dim(`\u2726 ${opts.thinkingEffort}`));
|
|
766
711
|
if (opts.gitBranch)
|
|
767
|
-
optional.push(
|
|
712
|
+
optional.push(c2.dim(`\u2387 ${opts.gitBranch}`));
|
|
768
713
|
if (opts.inputTokens > 0 || opts.outputTokens > 0) {
|
|
769
|
-
optional.push(
|
|
714
|
+
optional.push(c2.dim(`tok ${fmtTokens(opts.inputTokens)}/${fmtTokens(opts.outputTokens)}`));
|
|
770
715
|
}
|
|
771
716
|
const contextSegment = buildContextSegment({
|
|
772
717
|
contextTokens: opts.contextTokens,
|
|
@@ -774,14 +719,14 @@ function renderStatusBar(opts) {
|
|
|
774
719
|
});
|
|
775
720
|
if (contextSegment)
|
|
776
721
|
optional.push(contextSegment);
|
|
777
|
-
optional.push(
|
|
722
|
+
optional.push(c2.dim(opts.cwd));
|
|
778
723
|
const out = fitStatusSegments(required, optional, cols);
|
|
779
724
|
terminal.stdoutWrite(`${out}
|
|
780
725
|
`);
|
|
781
726
|
}
|
|
782
727
|
|
|
783
728
|
// src/cli/stream-render.ts
|
|
784
|
-
import * as
|
|
729
|
+
import * as c6 from "yoctocolors";
|
|
785
730
|
|
|
786
731
|
// src/llm-api/model-routing.ts
|
|
787
732
|
function parseModelString(modelString) {
|
|
@@ -866,7 +811,7 @@ function isToolCallPart(part) {
|
|
|
866
811
|
function hasObjectToolCallInput(part) {
|
|
867
812
|
return isToolCallPart(part) && "input" in part && isRecord(part.input) && !Array.isArray(part.input);
|
|
868
813
|
}
|
|
869
|
-
var TOOL_RUNTIME_INPUT_KEYS = new Set(["cwd"
|
|
814
|
+
var TOOL_RUNTIME_INPUT_KEYS = new Set(["cwd"]);
|
|
870
815
|
function stripToolRuntimeInputFields(messages) {
|
|
871
816
|
let mutated = false;
|
|
872
817
|
const result = messages.map((message) => {
|
|
@@ -1181,9 +1126,9 @@ function extractAssistantText(newMessages) {
|
|
|
1181
1126
|
}
|
|
1182
1127
|
|
|
1183
1128
|
// src/cli/live-reasoning.ts
|
|
1184
|
-
import * as
|
|
1129
|
+
import * as c3 from "yoctocolors";
|
|
1185
1130
|
function styleReasoningText(text) {
|
|
1186
|
-
return
|
|
1131
|
+
return c3.italic(c3.dim(text));
|
|
1187
1132
|
}
|
|
1188
1133
|
|
|
1189
1134
|
class LiveReasoningBlock {
|
|
@@ -1209,27 +1154,27 @@ class LiveReasoningBlock {
|
|
|
1209
1154
|
if (!this.blockOpen)
|
|
1210
1155
|
return;
|
|
1211
1156
|
if (this.lineOpen)
|
|
1212
|
-
|
|
1157
|
+
writeln();
|
|
1213
1158
|
this.blockOpen = false;
|
|
1214
1159
|
this.lineOpen = false;
|
|
1215
1160
|
}
|
|
1216
1161
|
openBlock() {
|
|
1217
1162
|
if (this.blockOpen)
|
|
1218
1163
|
return;
|
|
1219
|
-
|
|
1164
|
+
writeln(`${G.info} ${c3.dim("reasoning")}`);
|
|
1220
1165
|
this.blockOpen = true;
|
|
1221
1166
|
}
|
|
1222
1167
|
writeText(text) {
|
|
1223
1168
|
if (!this.lineOpen) {
|
|
1224
|
-
|
|
1169
|
+
write(" ");
|
|
1225
1170
|
this.lineOpen = true;
|
|
1226
1171
|
}
|
|
1227
|
-
|
|
1172
|
+
write(styleReasoningText(text));
|
|
1228
1173
|
}
|
|
1229
1174
|
endLine() {
|
|
1230
1175
|
if (!this.lineOpen)
|
|
1231
|
-
|
|
1232
|
-
|
|
1176
|
+
write(" ");
|
|
1177
|
+
writeln();
|
|
1233
1178
|
this.lineOpen = false;
|
|
1234
1179
|
}
|
|
1235
1180
|
}
|
|
@@ -1288,14 +1233,17 @@ class StreamRenderContent {
|
|
|
1288
1233
|
return this.inText;
|
|
1289
1234
|
}
|
|
1290
1235
|
appendTextDelta(delta, renderedVisibleOutput) {
|
|
1291
|
-
|
|
1236
|
+
let text = delta ?? "";
|
|
1292
1237
|
if (!text)
|
|
1293
1238
|
return;
|
|
1294
1239
|
if (!this.inText) {
|
|
1240
|
+
text = text.trimStart();
|
|
1241
|
+
if (!text)
|
|
1242
|
+
return;
|
|
1295
1243
|
this.spinner.stop();
|
|
1296
1244
|
if (renderedVisibleOutput)
|
|
1297
|
-
|
|
1298
|
-
|
|
1245
|
+
writeln();
|
|
1246
|
+
write(`${G.reply} `);
|
|
1299
1247
|
this.inText = true;
|
|
1300
1248
|
if (terminal.isStdoutTTY)
|
|
1301
1249
|
this.highlighter = createHighlighter();
|
|
@@ -1310,10 +1258,10 @@ class StreamRenderContent {
|
|
|
1310
1258
|
if (isFirstLine && colored.startsWith("\x1B[2K\r")) {
|
|
1311
1259
|
colored = `\x1B[2K\r${G.reply} ${colored.slice(5)}`;
|
|
1312
1260
|
}
|
|
1313
|
-
|
|
1261
|
+
write(colored);
|
|
1314
1262
|
}
|
|
1315
1263
|
} else {
|
|
1316
|
-
|
|
1264
|
+
write(text);
|
|
1317
1265
|
}
|
|
1318
1266
|
}
|
|
1319
1267
|
appendReasoningDelta(delta) {
|
|
@@ -1340,43 +1288,43 @@ ${text}`;
|
|
|
1340
1288
|
if (isFirstLine && finalColored.startsWith("\x1B[2K\r")) {
|
|
1341
1289
|
finalColored = `\x1B[2K\r${G.reply} ${finalColored.slice(5)}`;
|
|
1342
1290
|
}
|
|
1343
|
-
|
|
1291
|
+
write(finalColored);
|
|
1344
1292
|
}
|
|
1345
1293
|
}
|
|
1346
|
-
|
|
1294
|
+
writeln();
|
|
1347
1295
|
this.inText = false;
|
|
1348
1296
|
}
|
|
1349
1297
|
}
|
|
1350
1298
|
|
|
1351
1299
|
// src/cli/tool-render.ts
|
|
1352
|
-
import * as
|
|
1300
|
+
import * as c5 from "yoctocolors";
|
|
1353
1301
|
|
|
1354
1302
|
// src/cli/tool-result-renderers.ts
|
|
1355
|
-
import * as
|
|
1303
|
+
import * as c4 from "yoctocolors";
|
|
1356
1304
|
function writePreviewLines(opts) {
|
|
1357
1305
|
if (!opts.value.trim())
|
|
1358
1306
|
return;
|
|
1359
1307
|
const lines = opts.value.split(`
|
|
1360
1308
|
`);
|
|
1361
|
-
|
|
1309
|
+
writeln(` ${c4.dim(opts.label)} ${c4.dim(`(${lines.length} lines)`)}`);
|
|
1362
1310
|
if (!Number.isFinite(opts.maxLines) || lines.length <= opts.maxLines) {
|
|
1363
1311
|
for (const line of lines) {
|
|
1364
|
-
|
|
1312
|
+
writeln(` ${opts.lineColor("\u2502")} ${line}`);
|
|
1365
1313
|
}
|
|
1366
1314
|
return;
|
|
1367
1315
|
}
|
|
1368
1316
|
const headCount = Math.max(1, Math.ceil(opts.maxLines / 2));
|
|
1369
1317
|
const tailCount = Math.max(0, Math.floor(opts.maxLines / 2));
|
|
1370
1318
|
for (const line of lines.slice(0, headCount)) {
|
|
1371
|
-
|
|
1319
|
+
writeln(` ${opts.lineColor("\u2502")} ${line}`);
|
|
1372
1320
|
}
|
|
1373
1321
|
const hiddenLines = Math.max(0, lines.length - (headCount + tailCount));
|
|
1374
1322
|
if (hiddenLines > 0) {
|
|
1375
|
-
|
|
1323
|
+
writeln(` ${opts.lineColor("\u2502")} ${c4.dim(`\u2026 +${hiddenLines} lines`)}`);
|
|
1376
1324
|
}
|
|
1377
1325
|
if (tailCount > 0) {
|
|
1378
1326
|
for (const line of lines.slice(-tailCount)) {
|
|
1379
|
-
|
|
1327
|
+
writeln(` ${opts.lineColor("\u2502")} ${line}`);
|
|
1380
1328
|
}
|
|
1381
1329
|
}
|
|
1382
1330
|
}
|
|
@@ -1401,15 +1349,6 @@ function getSingleShellLine(value) {
|
|
|
1401
1349
|
}
|
|
1402
1350
|
function buildShellSummaryParts(opts) {
|
|
1403
1351
|
const parts = [`exit ${opts.exitCode}`];
|
|
1404
|
-
if (opts.streamedOutput) {
|
|
1405
|
-
if (opts.stdoutLines === 0 && opts.stderrLines === 0) {
|
|
1406
|
-
parts.push("no output");
|
|
1407
|
-
}
|
|
1408
|
-
if (opts.stderrLines > 0) {
|
|
1409
|
-
parts.push(`stderr ${opts.stderrLines}L`);
|
|
1410
|
-
}
|
|
1411
|
-
return parts;
|
|
1412
|
-
}
|
|
1413
1352
|
if (opts.stderrLines === 0 && opts.stdoutSingleLine !== null && opts.stdoutSingleLine.length > 0) {
|
|
1414
1353
|
parts.push(`out: ${truncateOneLine(opts.stdoutSingleLine, 100, opts.verboseOutput)}`);
|
|
1415
1354
|
return parts;
|
|
@@ -1428,6 +1367,8 @@ function buildShellSummaryParts(opts) {
|
|
|
1428
1367
|
function shouldPreviewShellStdout(opts) {
|
|
1429
1368
|
if (opts.stdoutLines === 0)
|
|
1430
1369
|
return false;
|
|
1370
|
+
if (opts.stdoutSingleLine !== null && opts.stderrLines === 0)
|
|
1371
|
+
return false;
|
|
1431
1372
|
if (!opts.success || opts.stderrLines > 0)
|
|
1432
1373
|
return true;
|
|
1433
1374
|
return opts.stdoutSingleLine === null;
|
|
@@ -1437,28 +1378,23 @@ function renderShellResult(result, opts) {
|
|
|
1437
1378
|
if (!r || typeof r.stdout !== "string" || typeof r.stderr !== "string") {
|
|
1438
1379
|
return false;
|
|
1439
1380
|
}
|
|
1440
|
-
const streamedOutput = r.streamedOutput === true;
|
|
1441
1381
|
const verboseOutput = opts?.verboseOutput === true;
|
|
1442
1382
|
const stdoutLines = countShellLines(r.stdout);
|
|
1443
1383
|
const stderrLines = countShellLines(r.stderr);
|
|
1444
1384
|
const stdoutSingleLine = getSingleShellLine(r.stdout);
|
|
1445
|
-
const badge = r.timedOut ?
|
|
1385
|
+
const badge = r.timedOut ? c4.yellow("timeout") : r.success ? c4.green("done") : c4.red("error");
|
|
1446
1386
|
const parts = buildShellSummaryParts({
|
|
1447
1387
|
exitCode: r.exitCode,
|
|
1448
1388
|
stdoutLines,
|
|
1449
1389
|
stderrLines,
|
|
1450
1390
|
stdoutSingleLine,
|
|
1451
|
-
streamedOutput,
|
|
1452
1391
|
verboseOutput
|
|
1453
1392
|
});
|
|
1454
|
-
|
|
1455
|
-
if (streamedOutput) {
|
|
1456
|
-
return true;
|
|
1457
|
-
}
|
|
1393
|
+
writeln(` ${badge} ${c4.dim(parts.join(" \xB7 "))}`);
|
|
1458
1394
|
writePreviewLines({
|
|
1459
1395
|
label: "stderr",
|
|
1460
1396
|
value: r.stderr,
|
|
1461
|
-
lineColor:
|
|
1397
|
+
lineColor: c4.red,
|
|
1462
1398
|
maxLines: verboseOutput ? Number.POSITIVE_INFINITY : 10
|
|
1463
1399
|
});
|
|
1464
1400
|
if (shouldPreviewShellStdout({
|
|
@@ -1470,7 +1406,7 @@ function renderShellResult(result, opts) {
|
|
|
1470
1406
|
writePreviewLines({
|
|
1471
1407
|
label: "stdout",
|
|
1472
1408
|
value: r.stdout,
|
|
1473
|
-
lineColor:
|
|
1409
|
+
lineColor: c4.dim,
|
|
1474
1410
|
maxLines: verboseOutput ? Number.POSITIVE_INFINITY : 20
|
|
1475
1411
|
});
|
|
1476
1412
|
}
|
|
@@ -1480,8 +1416,8 @@ function renderSubagentResult(result, _opts) {
|
|
|
1480
1416
|
const r = result;
|
|
1481
1417
|
if (!r || typeof r !== "object")
|
|
1482
1418
|
return false;
|
|
1483
|
-
const label = r.agentName ? ` ${
|
|
1484
|
-
|
|
1419
|
+
const label = r.agentName ? ` ${c4.dim(c4.cyan(`[@${r.agentName}]`))}` : "";
|
|
1420
|
+
writeln(` ${G.agent}${label} ${c4.dim(`subagent done (${r.inputTokens ?? 0}in / ${r.outputTokens ?? 0}out tokens)`)}`);
|
|
1485
1421
|
return true;
|
|
1486
1422
|
}
|
|
1487
1423
|
function buildSkillDescriptionPart(description, verboseOutput = false) {
|
|
@@ -1489,21 +1425,21 @@ function buildSkillDescriptionPart(description, verboseOutput = false) {
|
|
|
1489
1425
|
if (!trimmed)
|
|
1490
1426
|
return "";
|
|
1491
1427
|
if (verboseOutput)
|
|
1492
|
-
return ` ${
|
|
1493
|
-
return ` ${
|
|
1428
|
+
return ` ${c4.dim("\xB7")} ${c4.dim(trimmed)}`;
|
|
1429
|
+
return ` ${c4.dim("\xB7")} ${c4.dim(trimmed.length > 60 ? `${trimmed.slice(0, 57)}\u2026` : trimmed)}`;
|
|
1494
1430
|
}
|
|
1495
1431
|
function renderSkillSummaryLine(skill, opts) {
|
|
1496
1432
|
const name = skill.name ?? "(unknown)";
|
|
1497
1433
|
const source = skill.source ?? "unknown";
|
|
1498
|
-
const labelPrefix = opts?.label ? `${
|
|
1499
|
-
|
|
1434
|
+
const labelPrefix = opts?.label ? `${c4.dim(opts.label)} ` : "";
|
|
1435
|
+
writeln(` ${G.info} ${labelPrefix}${name} ${c4.dim("\xB7")} ${c4.dim(source)}${buildSkillDescriptionPart(skill.description, opts?.verboseOutput === true)}`);
|
|
1500
1436
|
}
|
|
1501
1437
|
function renderListSkillsResult(result, opts) {
|
|
1502
1438
|
const r = result;
|
|
1503
1439
|
if (!Array.isArray(r?.skills))
|
|
1504
1440
|
return false;
|
|
1505
1441
|
if (r.skills.length === 0) {
|
|
1506
|
-
|
|
1442
|
+
writeln(` ${G.info} ${c4.dim("no skills")}`);
|
|
1507
1443
|
return true;
|
|
1508
1444
|
}
|
|
1509
1445
|
const maxSkills = opts?.verboseOutput ? r.skills.length : 6;
|
|
@@ -1513,7 +1449,7 @@ function renderListSkillsResult(result, opts) {
|
|
|
1513
1449
|
});
|
|
1514
1450
|
}
|
|
1515
1451
|
if (r.skills.length > maxSkills) {
|
|
1516
|
-
|
|
1452
|
+
writeln(` ${c4.dim(`+${r.skills.length - maxSkills} more skills`)}`);
|
|
1517
1453
|
}
|
|
1518
1454
|
return true;
|
|
1519
1455
|
}
|
|
@@ -1522,7 +1458,7 @@ function renderReadSkillResult(result, _opts) {
|
|
|
1522
1458
|
if (!r || typeof r !== "object")
|
|
1523
1459
|
return false;
|
|
1524
1460
|
if (!r.skill) {
|
|
1525
|
-
|
|
1461
|
+
writeln(` ${G.info} ${c4.dim("skill")} ${c4.dim("(not found)")}`);
|
|
1526
1462
|
return true;
|
|
1527
1463
|
}
|
|
1528
1464
|
renderSkillSummaryLine(r.skill, {
|
|
@@ -1536,19 +1472,19 @@ function renderWebSearchResult(result, opts) {
|
|
|
1536
1472
|
if (!Array.isArray(r?.results))
|
|
1537
1473
|
return false;
|
|
1538
1474
|
if (r.results.length === 0) {
|
|
1539
|
-
|
|
1475
|
+
writeln(` ${G.info} ${c4.dim("no results")}`);
|
|
1540
1476
|
return true;
|
|
1541
1477
|
}
|
|
1542
1478
|
const maxResults = opts?.verboseOutput ? r.results.length : 5;
|
|
1543
1479
|
for (const item of r.results.slice(0, maxResults)) {
|
|
1544
1480
|
const title = (item.title?.trim() || item.url || "(untitled)").replace(/\s+/g, " ");
|
|
1545
|
-
const score = typeof item.score === "number" ?
|
|
1546
|
-
|
|
1481
|
+
const score = typeof item.score === "number" ? c4.dim(` (${item.score.toFixed(2)})`) : "";
|
|
1482
|
+
writeln(` ${c4.dim("\u2022")} ${title}${score}`);
|
|
1547
1483
|
if (item.url)
|
|
1548
|
-
|
|
1484
|
+
writeln(` ${c4.dim(item.url)}`);
|
|
1549
1485
|
}
|
|
1550
1486
|
if (r.results.length > maxResults) {
|
|
1551
|
-
|
|
1487
|
+
writeln(` ${c4.dim(` +${r.results.length - maxResults} more`)}`);
|
|
1552
1488
|
}
|
|
1553
1489
|
return true;
|
|
1554
1490
|
}
|
|
@@ -1557,39 +1493,41 @@ function renderWebContentResult(result, opts) {
|
|
|
1557
1493
|
if (!Array.isArray(r?.results))
|
|
1558
1494
|
return false;
|
|
1559
1495
|
if (r.results.length === 0) {
|
|
1560
|
-
|
|
1496
|
+
writeln(` ${G.info} ${c4.dim("no pages")}`);
|
|
1561
1497
|
return true;
|
|
1562
1498
|
}
|
|
1563
1499
|
const maxPages = opts?.verboseOutput ? r.results.length : 3;
|
|
1564
1500
|
for (const item of r.results.slice(0, maxPages)) {
|
|
1565
1501
|
const title = (item.title?.trim() || item.url || "(untitled)").replace(/\s+/g, " ");
|
|
1566
|
-
|
|
1502
|
+
writeln(` ${c4.dim("\u2022")} ${title}`);
|
|
1567
1503
|
if (item.url)
|
|
1568
|
-
|
|
1504
|
+
writeln(` ${c4.dim(item.url)}`);
|
|
1569
1505
|
const preview = (item.text ?? "").replace(/\s+/g, " ").trim();
|
|
1570
1506
|
if (preview) {
|
|
1571
1507
|
const trimmed = opts?.verboseOutput || preview.length <= 220 ? preview : `${preview.slice(0, 217)}\u2026`;
|
|
1572
|
-
|
|
1508
|
+
writeln(` ${c4.dim(trimmed)}`);
|
|
1573
1509
|
}
|
|
1574
1510
|
}
|
|
1575
1511
|
if (r.results.length > maxPages) {
|
|
1576
|
-
|
|
1512
|
+
writeln(` ${c4.dim(` +${r.results.length - maxPages} more`)}`);
|
|
1577
1513
|
}
|
|
1578
1514
|
return true;
|
|
1579
1515
|
}
|
|
1580
1516
|
function renderMcpResult(result, opts) {
|
|
1581
1517
|
const content = Array.isArray(result) ? result : [result];
|
|
1582
1518
|
const maxBlocks = opts?.verboseOutput ? content.length : 5;
|
|
1519
|
+
let rendered = false;
|
|
1583
1520
|
for (const block of content.slice(0, maxBlocks)) {
|
|
1584
1521
|
if (block?.type === "text" && block.text) {
|
|
1585
1522
|
const maxLines = opts?.verboseOutput ? Number.POSITIVE_INFINITY : 6;
|
|
1586
1523
|
const lines = block.text.split(`
|
|
1587
1524
|
`).slice(0, maxLines);
|
|
1588
1525
|
for (const line of lines)
|
|
1589
|
-
|
|
1526
|
+
writeln(` ${c4.dim("\u2502")} ${line}`);
|
|
1527
|
+
rendered = true;
|
|
1590
1528
|
}
|
|
1591
1529
|
}
|
|
1592
|
-
return
|
|
1530
|
+
return rendered;
|
|
1593
1531
|
}
|
|
1594
1532
|
var TOOL_RESULT_RENDERERS = {
|
|
1595
1533
|
shell: renderShellResult,
|
|
@@ -1619,6 +1557,10 @@ function toolGlyph(name) {
|
|
|
1619
1557
|
return G.read;
|
|
1620
1558
|
if (name === "listSkills")
|
|
1621
1559
|
return G.search;
|
|
1560
|
+
if (name === "webSearch")
|
|
1561
|
+
return G.search;
|
|
1562
|
+
if (name === "webContent")
|
|
1563
|
+
return G.read;
|
|
1622
1564
|
if (name.startsWith("mcp_"))
|
|
1623
1565
|
return G.mcp;
|
|
1624
1566
|
return G.info;
|
|
@@ -1629,40 +1571,51 @@ function buildToolCallLine(name, args) {
|
|
|
1629
1571
|
const prompt = typeof a.prompt === "string" ? a.prompt : "";
|
|
1630
1572
|
const short = prompt.length > 60 ? `${prompt.slice(0, 57)}\u2026` : prompt;
|
|
1631
1573
|
const agentName = typeof a.agentName === "string" && a.agentName ? a.agentName : "";
|
|
1632
|
-
const label = agentName ? ` ${
|
|
1633
|
-
return `${G.agent}${label} ${
|
|
1574
|
+
const label = agentName ? ` ${c5.dim(c5.cyan(`[@${agentName}]`))}` : "";
|
|
1575
|
+
return `${G.agent}${label} ${c5.dim("\u2014")} ${short}`;
|
|
1634
1576
|
}
|
|
1635
1577
|
if (name === "shell") {
|
|
1636
1578
|
const cmd = String(a.command ?? "").trim();
|
|
1637
1579
|
if (!cmd)
|
|
1638
|
-
return `${G.run} ${
|
|
1580
|
+
return `${G.run} ${c5.dim("shell")}`;
|
|
1639
1581
|
const shortCmd = cmd.length > 72 ? `${cmd.slice(0, 69)}\u2026` : cmd;
|
|
1640
1582
|
return `${G.run} ${shortCmd}`;
|
|
1641
1583
|
}
|
|
1642
1584
|
if (name === "listSkills") {
|
|
1643
|
-
return `${G.search} ${
|
|
1585
|
+
return `${G.search} ${c5.dim("list skills")}`;
|
|
1644
1586
|
}
|
|
1645
1587
|
if (name === "readSkill") {
|
|
1646
1588
|
const skillName = typeof a.name === "string" ? a.name : "";
|
|
1647
|
-
return `${G.read} ${
|
|
1589
|
+
return `${G.read} ${c5.dim("read skill")}${skillName ? ` ${skillName}` : ""}`;
|
|
1648
1590
|
}
|
|
1649
|
-
if (name
|
|
1650
|
-
|
|
1591
|
+
if (name === "webSearch") {
|
|
1592
|
+
const query = typeof a.query === "string" ? a.query : "";
|
|
1593
|
+
const short = query.length > 60 ? `${query.slice(0, 57)}\u2026` : query;
|
|
1594
|
+
return `${G.search} ${c5.dim("search")}${short ? ` ${short}` : ""}`;
|
|
1651
1595
|
}
|
|
1652
|
-
|
|
1596
|
+
if (name === "webContent") {
|
|
1597
|
+
const urls = Array.isArray(a.urls) ? a.urls : [];
|
|
1598
|
+
const label = urls.length === 1 ? String(urls[0]) : `${urls.length} url${urls.length !== 1 ? "s" : ""}`;
|
|
1599
|
+
const short = label.length > 60 ? `${label.slice(0, 57)}\u2026` : label;
|
|
1600
|
+
return `${G.read} ${c5.dim("fetch")} ${short}`;
|
|
1601
|
+
}
|
|
1602
|
+
if (name.startsWith("mcp_")) {
|
|
1603
|
+
return `${G.mcp} ${c5.dim(name)}`;
|
|
1604
|
+
}
|
|
1605
|
+
return `${toolGlyph(name)} ${c5.dim(name)}`;
|
|
1653
1606
|
}
|
|
1654
1607
|
function renderToolCall(toolName, args) {
|
|
1655
|
-
|
|
1608
|
+
writeln(` ${buildToolCallLine(toolName, args)}`);
|
|
1656
1609
|
}
|
|
1657
1610
|
function formatErrorBadge(result) {
|
|
1658
1611
|
const msg = typeof result === "string" ? result : result instanceof Error ? result.message : JSON.stringify(result);
|
|
1659
1612
|
const oneLiner = msg.split(`
|
|
1660
1613
|
`)[0] ?? msg;
|
|
1661
|
-
return `${G.err} ${
|
|
1614
|
+
return `${G.err} ${c5.red(oneLiner)}`;
|
|
1662
1615
|
}
|
|
1663
1616
|
function renderToolResult(toolName, result, isError, opts) {
|
|
1664
1617
|
if (isError) {
|
|
1665
|
-
|
|
1618
|
+
writeln(` ${formatErrorBadge(result)}`);
|
|
1666
1619
|
return;
|
|
1667
1620
|
}
|
|
1668
1621
|
if (renderToolResultByName(toolName, result, opts)) {
|
|
@@ -1670,10 +1623,10 @@ function renderToolResult(toolName, result, isError, opts) {
|
|
|
1670
1623
|
}
|
|
1671
1624
|
const text = JSON.stringify(result);
|
|
1672
1625
|
if (opts?.verboseOutput || text.length <= 120) {
|
|
1673
|
-
|
|
1626
|
+
writeln(` ${c5.dim(text)}`);
|
|
1674
1627
|
return;
|
|
1675
1628
|
}
|
|
1676
|
-
|
|
1629
|
+
writeln(` ${c5.dim(`${text.slice(0, 117)}\u2026`)}`);
|
|
1677
1630
|
}
|
|
1678
1631
|
|
|
1679
1632
|
// src/cli/stream-render.ts
|
|
@@ -1687,6 +1640,8 @@ async function renderTurn(events, spinner, opts) {
|
|
|
1687
1640
|
let contextTokens = 0;
|
|
1688
1641
|
let newMessages = [];
|
|
1689
1642
|
const startedToolCalls = new Set;
|
|
1643
|
+
const toolCallInfo = new Map;
|
|
1644
|
+
let parallelCallCount = 0;
|
|
1690
1645
|
let renderedVisibleOutput = false;
|
|
1691
1646
|
let reasoningComputed = false;
|
|
1692
1647
|
let reasoningText = "";
|
|
@@ -1702,7 +1657,7 @@ async function renderTurn(events, spinner, opts) {
|
|
|
1702
1657
|
case "text-delta": {
|
|
1703
1658
|
liveReasoning.finish();
|
|
1704
1659
|
content.appendTextDelta(event.delta, renderedVisibleOutput);
|
|
1705
|
-
if (
|
|
1660
|
+
if (content.hasOpenContent())
|
|
1706
1661
|
renderedVisibleOutput = true;
|
|
1707
1662
|
break;
|
|
1708
1663
|
}
|
|
@@ -1713,7 +1668,7 @@ async function renderTurn(events, spinner, opts) {
|
|
|
1713
1668
|
if (showReasoning && delta) {
|
|
1714
1669
|
spinner.stop();
|
|
1715
1670
|
if (renderedVisibleOutput && !liveReasoning.isOpen())
|
|
1716
|
-
|
|
1671
|
+
writeln();
|
|
1717
1672
|
liveReasoning.append(delta);
|
|
1718
1673
|
renderedVisibleOutput = true;
|
|
1719
1674
|
}
|
|
@@ -1723,12 +1678,20 @@ async function renderTurn(events, spinner, opts) {
|
|
|
1723
1678
|
if (startedToolCalls.has(event.toolCallId)) {
|
|
1724
1679
|
break;
|
|
1725
1680
|
}
|
|
1681
|
+
const isConsecutiveToolCall = startedToolCalls.size > 0 && toolCallInfo.size > 0;
|
|
1726
1682
|
startedToolCalls.add(event.toolCallId);
|
|
1683
|
+
toolCallInfo.set(event.toolCallId, {
|
|
1684
|
+
toolName: event.toolName,
|
|
1685
|
+
label: buildToolCallLine(event.toolName, event.args)
|
|
1686
|
+
});
|
|
1687
|
+
if (toolCallInfo.size > 1) {
|
|
1688
|
+
parallelCallCount = toolCallInfo.size;
|
|
1689
|
+
}
|
|
1727
1690
|
liveReasoning.finish();
|
|
1728
1691
|
content.flushOpenContent();
|
|
1729
1692
|
spinner.stop();
|
|
1730
|
-
if (renderedVisibleOutput)
|
|
1731
|
-
|
|
1693
|
+
if (renderedVisibleOutput && !isConsecutiveToolCall)
|
|
1694
|
+
writeln();
|
|
1732
1695
|
renderToolCall(event.toolName, event.args);
|
|
1733
1696
|
renderedVisibleOutput = true;
|
|
1734
1697
|
spinner.start(event.toolName);
|
|
@@ -1736,8 +1699,15 @@ async function renderTurn(events, spinner, opts) {
|
|
|
1736
1699
|
}
|
|
1737
1700
|
case "tool-result": {
|
|
1738
1701
|
startedToolCalls.delete(event.toolCallId);
|
|
1702
|
+
const callInfo = toolCallInfo.get(event.toolCallId);
|
|
1703
|
+
toolCallInfo.delete(event.toolCallId);
|
|
1739
1704
|
liveReasoning.finish();
|
|
1740
1705
|
spinner.stop();
|
|
1706
|
+
if (parallelCallCount > 1 && callInfo) {
|
|
1707
|
+
writeln(` ${c6.dim("\u21B3")} ${callInfo.label}`);
|
|
1708
|
+
}
|
|
1709
|
+
if (toolCallInfo.size === 0)
|
|
1710
|
+
parallelCallCount = 0;
|
|
1741
1711
|
renderToolResult(event.toolName, event.result, event.isError, {
|
|
1742
1712
|
verboseOutput
|
|
1743
1713
|
});
|
|
@@ -1750,7 +1720,7 @@ async function renderTurn(events, spinner, opts) {
|
|
|
1750
1720
|
content.flushOpenContent();
|
|
1751
1721
|
spinner.stop();
|
|
1752
1722
|
const removedKb = (event.removedBytes / 1024).toFixed(1);
|
|
1753
|
-
|
|
1723
|
+
writeln(`${G.info} ${c6.dim("context pruned")} ${c6.dim(event.mode)} ${c6.dim(`\u2013${event.removedMessageCount} messages`)} ${c6.dim(`\u2013${removedKb} KB`)}`);
|
|
1754
1724
|
renderedVisibleOutput = true;
|
|
1755
1725
|
break;
|
|
1756
1726
|
}
|
|
@@ -1759,7 +1729,7 @@ async function renderTurn(events, spinner, opts) {
|
|
|
1759
1729
|
content.flushOpenContent();
|
|
1760
1730
|
spinner.stop();
|
|
1761
1731
|
if (!renderedVisibleOutput)
|
|
1762
|
-
|
|
1732
|
+
writeln();
|
|
1763
1733
|
inputTokens = event.inputTokens;
|
|
1764
1734
|
outputTokens = event.outputTokens;
|
|
1765
1735
|
contextTokens = event.contextTokens;
|
|
@@ -1791,7 +1761,7 @@ async function renderTurn(events, spinner, opts) {
|
|
|
1791
1761
|
|
|
1792
1762
|
// src/cli/output.ts
|
|
1793
1763
|
var HOME = homedir3();
|
|
1794
|
-
var PACKAGE_VERSION = "0.0
|
|
1764
|
+
var PACKAGE_VERSION = "0.1.0";
|
|
1795
1765
|
function tildePath(p) {
|
|
1796
1766
|
return p.startsWith(HOME) ? `~${p.slice(HOME.length)}` : p;
|
|
1797
1767
|
}
|
|
@@ -1801,38 +1771,38 @@ function restoreTerminal() {
|
|
|
1801
1771
|
function registerTerminalCleanup() {
|
|
1802
1772
|
terminal.registerCleanup();
|
|
1803
1773
|
}
|
|
1804
|
-
function
|
|
1774
|
+
function writeln(text = "") {
|
|
1805
1775
|
terminal.stdoutWrite(`${text}
|
|
1806
1776
|
`);
|
|
1807
1777
|
}
|
|
1808
|
-
function
|
|
1778
|
+
function write(text) {
|
|
1809
1779
|
terminal.stdoutWrite(text);
|
|
1810
1780
|
}
|
|
1811
1781
|
function renderUserMessage(text) {
|
|
1812
1782
|
const lines = text.split(`
|
|
1813
1783
|
`);
|
|
1814
1784
|
if (lines.length === 0) {
|
|
1815
|
-
|
|
1785
|
+
writeln(`${G.prompt}`);
|
|
1816
1786
|
return;
|
|
1817
1787
|
}
|
|
1818
|
-
|
|
1788
|
+
writeln(`${G.prompt} ${lines[0] ?? ""}`);
|
|
1819
1789
|
for (const line of lines.slice(1)) {
|
|
1820
|
-
|
|
1790
|
+
writeln(` ${line}`);
|
|
1821
1791
|
}
|
|
1822
1792
|
}
|
|
1823
1793
|
var G = {
|
|
1824
|
-
prompt:
|
|
1825
|
-
reply:
|
|
1826
|
-
search:
|
|
1827
|
-
read:
|
|
1828
|
-
write:
|
|
1829
|
-
run:
|
|
1830
|
-
agent:
|
|
1831
|
-
mcp:
|
|
1832
|
-
ok:
|
|
1833
|
-
err:
|
|
1834
|
-
warn:
|
|
1835
|
-
info:
|
|
1794
|
+
prompt: c7.green("\u203A"),
|
|
1795
|
+
reply: c7.cyan("\u25C6"),
|
|
1796
|
+
search: c7.yellow("?"),
|
|
1797
|
+
read: c7.dim("\u2190"),
|
|
1798
|
+
write: c7.green("\u270E"),
|
|
1799
|
+
run: c7.dim("$"),
|
|
1800
|
+
agent: c7.cyan("\u21E2"),
|
|
1801
|
+
mcp: c7.yellow("\u2699"),
|
|
1802
|
+
ok: c7.green("\u2714"),
|
|
1803
|
+
err: c7.red("\u2716"),
|
|
1804
|
+
warn: c7.yellow("!"),
|
|
1805
|
+
info: c7.dim("\xB7")
|
|
1836
1806
|
};
|
|
1837
1807
|
var PREFIX = {
|
|
1838
1808
|
user: G.prompt,
|
|
@@ -1854,9 +1824,9 @@ class RenderedError extends Error {
|
|
|
1854
1824
|
function renderError(err, context = "render") {
|
|
1855
1825
|
logError(err, context);
|
|
1856
1826
|
const parsed = parseAppError(err);
|
|
1857
|
-
|
|
1827
|
+
writeln(`${G.err} ${c7.red(parsed.headline)}`);
|
|
1858
1828
|
if (parsed.hint) {
|
|
1859
|
-
|
|
1829
|
+
writeln(` ${c7.dim(parsed.hint)}`);
|
|
1860
1830
|
}
|
|
1861
1831
|
}
|
|
1862
1832
|
function discoverContextFiles(cwd) {
|
|
@@ -1876,11 +1846,11 @@ function discoverContextFiles(cwd) {
|
|
|
1876
1846
|
return found;
|
|
1877
1847
|
}
|
|
1878
1848
|
function renderBanner(model, cwd) {
|
|
1879
|
-
|
|
1849
|
+
writeln();
|
|
1880
1850
|
const title = PACKAGE_VERSION ? `mini-coder \xB7 v${PACKAGE_VERSION}` : "mini-coder";
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1851
|
+
writeln(` ${c7.cyan("mc")} ${c7.dim(title)}`);
|
|
1852
|
+
writeln(` ${c7.dim(model)} ${c7.dim("\xB7")} ${c7.dim(tildePath(cwd))}`);
|
|
1853
|
+
writeln(` ${c7.dim("/help for commands \xB7 esc cancel \xB7 ctrl+c/ctrl+d exit")}`);
|
|
1884
1854
|
const items = [];
|
|
1885
1855
|
const contextFiles = discoverContextFiles(cwd);
|
|
1886
1856
|
if (contextFiles.length > 0)
|
|
@@ -1895,27 +1865,19 @@ function renderBanner(model, cwd) {
|
|
|
1895
1865
|
if (commands.size > 0)
|
|
1896
1866
|
items.push(`${commands.size} custom cmd${commands.size > 1 ? "s" : ""}`);
|
|
1897
1867
|
if (items.length > 0) {
|
|
1898
|
-
|
|
1868
|
+
writeln(` ${c7.dim(items.join(" \xB7 "))}`);
|
|
1899
1869
|
}
|
|
1900
|
-
|
|
1870
|
+
writeln();
|
|
1901
1871
|
}
|
|
1902
1872
|
|
|
1903
1873
|
class CliReporter {
|
|
1904
1874
|
spinner = new Spinner;
|
|
1905
|
-
liveOutput = new LiveOutputBlock;
|
|
1906
|
-
haltSpinner() {
|
|
1907
|
-
this.spinner.stop();
|
|
1908
|
-
}
|
|
1909
|
-
haltSpinnerAndFlushLiveOutput() {
|
|
1910
|
-
this.spinner.stop();
|
|
1911
|
-
this.liveOutput.finish();
|
|
1912
|
-
}
|
|
1913
1875
|
info(msg) {
|
|
1914
|
-
this.
|
|
1915
|
-
|
|
1876
|
+
this.spinner.stop();
|
|
1877
|
+
writeln(`${G.info} ${c7.dim(msg)}`);
|
|
1916
1878
|
}
|
|
1917
1879
|
error(msg, hint) {
|
|
1918
|
-
this.
|
|
1880
|
+
this.spinner.stop();
|
|
1919
1881
|
if (typeof msg === "string") {
|
|
1920
1882
|
renderError(msg, hint);
|
|
1921
1883
|
} else {
|
|
@@ -1923,29 +1885,23 @@ class CliReporter {
|
|
|
1923
1885
|
}
|
|
1924
1886
|
}
|
|
1925
1887
|
warn(msg) {
|
|
1926
|
-
this.
|
|
1927
|
-
|
|
1888
|
+
this.spinner.stop();
|
|
1889
|
+
writeln(`${G.warn} ${msg}`);
|
|
1928
1890
|
}
|
|
1929
1891
|
writeText(text) {
|
|
1930
|
-
this.
|
|
1931
|
-
|
|
1932
|
-
}
|
|
1933
|
-
streamChunk(text) {
|
|
1934
|
-
this.haltSpinner();
|
|
1935
|
-
this.liveOutput.append(text);
|
|
1892
|
+
this.spinner.stop();
|
|
1893
|
+
writeln(text);
|
|
1936
1894
|
}
|
|
1937
1895
|
startSpinner(label) {
|
|
1938
1896
|
this.spinner.start(label);
|
|
1939
1897
|
}
|
|
1940
1898
|
stopSpinner() {
|
|
1941
|
-
this.
|
|
1899
|
+
this.spinner.stop();
|
|
1942
1900
|
}
|
|
1943
1901
|
async renderTurn(events, opts) {
|
|
1944
|
-
this.liveOutput.finish();
|
|
1945
1902
|
return renderTurn(events, this.spinner, opts);
|
|
1946
1903
|
}
|
|
1947
1904
|
renderStatusBar(data) {
|
|
1948
|
-
this.liveOutput.finish();
|
|
1949
1905
|
renderStatusBar(data);
|
|
1950
1906
|
}
|
|
1951
1907
|
restoreTerminal() {
|
|
@@ -1965,8 +1921,8 @@ function warnConventionConflicts(kind, scope, agentsNames, claudeNames) {
|
|
|
1965
1921
|
if (conflicts.length === 0)
|
|
1966
1922
|
return;
|
|
1967
1923
|
conflicts.sort((a, b) => a.localeCompare(b));
|
|
1968
|
-
const list = conflicts.map((n) =>
|
|
1969
|
-
|
|
1924
|
+
const list = conflicts.map((n) => c8.cyan(n)).join(c8.dim(", "));
|
|
1925
|
+
writeln(`${G.warn} conflicting ${kind} in ${scope} .agents and .claude: ${list} ${c8.dim("\u2014 using .agents version")}`);
|
|
1970
1926
|
}
|
|
1971
1927
|
|
|
1972
1928
|
// src/cli/frontmatter.ts
|
|
@@ -2627,10 +2583,10 @@ function setPreferredGoogleCachedContent(contentId) {
|
|
|
2627
2583
|
}
|
|
2628
2584
|
}
|
|
2629
2585
|
// src/agent/session-runner.ts
|
|
2630
|
-
import * as
|
|
2586
|
+
import * as c11 from "yoctocolors";
|
|
2631
2587
|
|
|
2632
2588
|
// src/cli/input.ts
|
|
2633
|
-
import * as
|
|
2589
|
+
import * as c9 from "yoctocolors";
|
|
2634
2590
|
|
|
2635
2591
|
// src/cli/input-buffer.ts
|
|
2636
2592
|
var PASTE_TOKEN_START = 57344;
|
|
@@ -3064,7 +3020,7 @@ function watchForCancel(abortController, options) {
|
|
|
3064
3020
|
process.stdin.on("data", onData);
|
|
3065
3021
|
return cleanup;
|
|
3066
3022
|
}
|
|
3067
|
-
var PROMPT =
|
|
3023
|
+
var PROMPT = c9.green("\u25B6 ");
|
|
3068
3024
|
var PROMPT_RAW_LEN = 2;
|
|
3069
3025
|
async function readline(opts) {
|
|
3070
3026
|
const cwd = opts.cwd ?? process.cwd();
|
|
@@ -3113,7 +3069,7 @@ async function readline(opts) {
|
|
|
3113
3069
|
process.stdout.write(`${CLEAR_LINE}${prompt}${display}${CSI}${PROMPT_RAW_LEN + displayCursor + 1}G`);
|
|
3114
3070
|
}
|
|
3115
3071
|
function renderSearchPrompt() {
|
|
3116
|
-
process.stdout.write(`${CLEAR_LINE}${
|
|
3072
|
+
process.stdout.write(`${CLEAR_LINE}${c9.cyan("search:")} ${searchQuery}\u2588`);
|
|
3117
3073
|
}
|
|
3118
3074
|
function applyHistory() {
|
|
3119
3075
|
if (histIdx < history.length) {
|
|
@@ -4565,10 +4521,6 @@ function normalizeUnknownError(error) {
|
|
|
4565
4521
|
return new Error(stringifyUnknown(error));
|
|
4566
4522
|
}
|
|
4567
4523
|
|
|
4568
|
-
// src/llm-api/turn-anthropic-oauth.ts
|
|
4569
|
-
import Anthropic from "@anthropic-ai/sdk";
|
|
4570
|
-
import { zodSchema } from "ai";
|
|
4571
|
-
|
|
4572
4524
|
// src/llm-api/turn-context.ts
|
|
4573
4525
|
import { pruneMessages } from "ai";
|
|
4574
4526
|
var DEFAULT_TOOL_RESULT_PAYLOAD_CAP_BYTES = 16 * 1024;
|
|
@@ -4769,994 +4721,184 @@ function annotateAnthropicCacheBreakpoints(turnMessages, systemPrompt) {
|
|
|
4769
4721
|
};
|
|
4770
4722
|
}
|
|
4771
4723
|
|
|
4772
|
-
// src/llm-api/turn-
|
|
4773
|
-
import {
|
|
4724
|
+
// src/llm-api/turn-execution.ts
|
|
4725
|
+
import { dynamicTool, jsonSchema } from "ai";
|
|
4774
4726
|
|
|
4775
|
-
// src/llm-api/
|
|
4776
|
-
|
|
4777
|
-
|
|
4778
|
-
medium: 8192,
|
|
4779
|
-
high: 16384,
|
|
4780
|
-
xhigh: 32768
|
|
4781
|
-
};
|
|
4782
|
-
var GEMINI_BUDGET = {
|
|
4783
|
-
low: 4096,
|
|
4784
|
-
medium: 8192,
|
|
4785
|
-
high: 16384,
|
|
4786
|
-
xhigh: 24575
|
|
4787
|
-
};
|
|
4788
|
-
function clampEffort(effort, max) {
|
|
4789
|
-
const ORDER = ["low", "medium", "high", "xhigh"];
|
|
4790
|
-
const effortIdx = ORDER.indexOf(effort);
|
|
4791
|
-
const maxIdx = ORDER.indexOf(max);
|
|
4792
|
-
return ORDER[Math.min(effortIdx, maxIdx)];
|
|
4727
|
+
// src/llm-api/turn-stream-events.ts
|
|
4728
|
+
function shouldLogStreamChunk(c10) {
|
|
4729
|
+
return c10.type !== "text-delta" && c10.type !== "reasoning" && c10.type !== "reasoning-delta";
|
|
4793
4730
|
}
|
|
4794
|
-
function
|
|
4795
|
-
|
|
4796
|
-
|
|
4797
|
-
|
|
4798
|
-
|
|
4799
|
-
return
|
|
4800
|
-
|
|
4801
|
-
|
|
4802
|
-
|
|
4803
|
-
|
|
4804
|
-
|
|
4731
|
+
function extractToolArgs(c10) {
|
|
4732
|
+
return c10.input ?? c10.args;
|
|
4733
|
+
}
|
|
4734
|
+
function hasRenderableToolArgs(args) {
|
|
4735
|
+
if (args === null || args === undefined)
|
|
4736
|
+
return false;
|
|
4737
|
+
if (typeof args === "string")
|
|
4738
|
+
return args.trim().length > 0;
|
|
4739
|
+
if (Array.isArray(args))
|
|
4740
|
+
return args.length > 0;
|
|
4741
|
+
if (typeof args === "object")
|
|
4742
|
+
return Object.keys(args).length > 0;
|
|
4743
|
+
return true;
|
|
4744
|
+
}
|
|
4745
|
+
function mapStreamChunkToTurnEvent(c10) {
|
|
4746
|
+
switch (c10.type) {
|
|
4747
|
+
case "text-delta": {
|
|
4748
|
+
const delta = typeof c10.text === "string" ? c10.text : "";
|
|
4749
|
+
return {
|
|
4750
|
+
type: "text-delta",
|
|
4751
|
+
delta
|
|
4752
|
+
};
|
|
4805
4753
|
}
|
|
4806
|
-
|
|
4754
|
+
case "reasoning-delta":
|
|
4755
|
+
case "reasoning": {
|
|
4756
|
+
const delta = getReasoningDeltaFromStreamChunk(c10);
|
|
4757
|
+
if (delta === null)
|
|
4758
|
+
return null;
|
|
4759
|
+
return {
|
|
4760
|
+
type: "reasoning-delta",
|
|
4761
|
+
delta
|
|
4762
|
+
};
|
|
4763
|
+
}
|
|
4764
|
+
case "tool-input-start": {
|
|
4765
|
+
const args = extractToolArgs(c10);
|
|
4766
|
+
const hasStableToolCallId = typeof c10.toolCallId === "string" && c10.toolCallId.trim().length > 0;
|
|
4767
|
+
if (hasStableToolCallId && !hasRenderableToolArgs(args))
|
|
4768
|
+
return null;
|
|
4769
|
+
return {
|
|
4770
|
+
type: "tool-call-start",
|
|
4771
|
+
toolCallId: String(c10.toolCallId ?? ""),
|
|
4772
|
+
toolName: String(c10.toolName ?? ""),
|
|
4773
|
+
args
|
|
4774
|
+
};
|
|
4775
|
+
}
|
|
4776
|
+
case "tool-call": {
|
|
4777
|
+
return {
|
|
4778
|
+
type: "tool-call-start",
|
|
4779
|
+
toolCallId: String(c10.toolCallId ?? ""),
|
|
4780
|
+
toolName: String(c10.toolName ?? ""),
|
|
4781
|
+
args: extractToolArgs(c10)
|
|
4782
|
+
};
|
|
4783
|
+
}
|
|
4784
|
+
case "tool-result": {
|
|
4785
|
+
return {
|
|
4786
|
+
type: "tool-result",
|
|
4787
|
+
toolCallId: String(c10.toolCallId ?? ""),
|
|
4788
|
+
toolName: String(c10.toolName ?? ""),
|
|
4789
|
+
result: "output" in c10 ? c10.output : ("result" in c10) ? c10.result : undefined,
|
|
4790
|
+
isError: "isError" in c10 ? Boolean(c10.isError) : false
|
|
4791
|
+
};
|
|
4792
|
+
}
|
|
4793
|
+
case "tool-error":
|
|
4794
|
+
return {
|
|
4795
|
+
type: "tool-result",
|
|
4796
|
+
toolCallId: String(c10.toolCallId ?? ""),
|
|
4797
|
+
toolName: String(c10.toolName ?? ""),
|
|
4798
|
+
result: c10.error ?? "Tool execution failed",
|
|
4799
|
+
isError: true
|
|
4800
|
+
};
|
|
4801
|
+
case "error": {
|
|
4802
|
+
throw normalizeUnknownError(c10.error);
|
|
4803
|
+
}
|
|
4804
|
+
default:
|
|
4805
|
+
return null;
|
|
4806
|
+
}
|
|
4807
4807
|
}
|
|
4808
|
-
|
|
4809
|
-
|
|
4810
|
-
|
|
4811
|
-
return
|
|
4808
|
+
|
|
4809
|
+
// src/llm-api/turn-execution.ts
|
|
4810
|
+
function isZodSchema(s) {
|
|
4811
|
+
return s !== null && typeof s === "object" && "_def" in s;
|
|
4812
4812
|
}
|
|
4813
|
-
function
|
|
4814
|
-
|
|
4815
|
-
|
|
4816
|
-
|
|
4817
|
-
|
|
4818
|
-
|
|
4819
|
-
|
|
4820
|
-
|
|
4813
|
+
function toCoreTool(def) {
|
|
4814
|
+
const schema = isZodSchema(def.schema) ? def.schema : jsonSchema(def.schema);
|
|
4815
|
+
return dynamicTool({
|
|
4816
|
+
description: def.description,
|
|
4817
|
+
inputSchema: schema,
|
|
4818
|
+
execute: async (input) => {
|
|
4819
|
+
try {
|
|
4820
|
+
return await def.execute(input);
|
|
4821
|
+
} catch (err) {
|
|
4822
|
+
throw normalizeUnknownError(err);
|
|
4821
4823
|
}
|
|
4822
|
-
}
|
|
4824
|
+
}
|
|
4825
|
+
});
|
|
4826
|
+
}
|
|
4827
|
+
function buildToolSet(tools) {
|
|
4828
|
+
const toolSet = {};
|
|
4829
|
+
for (const def of tools) {
|
|
4830
|
+
toolSet[def.name] = toCoreTool(def);
|
|
4823
4831
|
}
|
|
4832
|
+
return toolSet;
|
|
4833
|
+
}
|
|
4834
|
+
function createTurnStepTracker(opts) {
|
|
4835
|
+
let stepCount = 0;
|
|
4836
|
+
let inputTokens = 0;
|
|
4837
|
+
let outputTokens = 0;
|
|
4838
|
+
let contextTokens = 0;
|
|
4839
|
+
let partialMessages = [];
|
|
4824
4840
|
return {
|
|
4825
|
-
|
|
4826
|
-
|
|
4827
|
-
|
|
4828
|
-
|
|
4829
|
-
|
|
4830
|
-
|
|
4841
|
+
onStepFinish: (step) => {
|
|
4842
|
+
opts.onStepLog({
|
|
4843
|
+
stepNumber: stepCount + 1,
|
|
4844
|
+
finishReason: step.finishReason,
|
|
4845
|
+
usage: step.usage
|
|
4846
|
+
});
|
|
4847
|
+
inputTokens += step.usage?.inputTokens ?? 0;
|
|
4848
|
+
outputTokens += step.usage?.outputTokens ?? 0;
|
|
4849
|
+
contextTokens = step.usage?.inputTokens ?? contextTokens;
|
|
4850
|
+
stepCount += 1;
|
|
4851
|
+
const s = step;
|
|
4852
|
+
partialMessages = s.response?.messages ?? s.messages ?? partialMessages;
|
|
4853
|
+
},
|
|
4854
|
+
getState: () => ({
|
|
4855
|
+
stepCount,
|
|
4856
|
+
inputTokens,
|
|
4857
|
+
outputTokens,
|
|
4858
|
+
contextTokens,
|
|
4859
|
+
partialMessages
|
|
4860
|
+
})
|
|
4831
4861
|
};
|
|
4832
4862
|
}
|
|
4833
|
-
var
|
|
4834
|
-
|
|
4835
|
-
|
|
4836
|
-
build: getAnthropicThinkingOptions
|
|
4837
|
-
},
|
|
4838
|
-
{
|
|
4839
|
-
supports: isOpenAIReasoningModelFamily,
|
|
4840
|
-
build: getOpenAIThinkingOptions
|
|
4841
|
-
},
|
|
4842
|
-
{
|
|
4843
|
-
supports: isGeminiModelFamily,
|
|
4844
|
-
build: getGeminiThinkingOptions
|
|
4845
|
-
}
|
|
4846
|
-
];
|
|
4847
|
-
function getThinkingProviderOptions(modelString, effort) {
|
|
4848
|
-
if (!supportsThinking(modelString))
|
|
4863
|
+
var TOOL_RESULT_CHUNK_TYPES = new Set(["tool-result", "tool-error"]);
|
|
4864
|
+
function normalizeToolCallId(raw) {
|
|
4865
|
+
if (typeof raw !== "string")
|
|
4849
4866
|
return null;
|
|
4850
|
-
const
|
|
4851
|
-
|
|
4852
|
-
if (!strategy.supports(modelString))
|
|
4853
|
-
continue;
|
|
4854
|
-
return strategy.build(modelId, effort);
|
|
4855
|
-
}
|
|
4856
|
-
return null;
|
|
4867
|
+
const trimmed = raw.trim();
|
|
4868
|
+
return trimmed ? trimmed : null;
|
|
4857
4869
|
}
|
|
4858
|
-
|
|
4859
|
-
|
|
4860
|
-
|
|
4861
|
-
|
|
4862
|
-
|
|
4863
|
-
for (const [match, family] of CACHE_FAMILY_RULES) {
|
|
4864
|
-
if (match(modelString))
|
|
4865
|
-
return family;
|
|
4866
|
-
}
|
|
4867
|
-
return "none";
|
|
4870
|
+
function normalizeToolName(raw) {
|
|
4871
|
+
if (typeof raw !== "string")
|
|
4872
|
+
return "tool";
|
|
4873
|
+
const trimmed = raw.trim();
|
|
4874
|
+
return trimmed || "tool";
|
|
4868
4875
|
}
|
|
4869
|
-
function
|
|
4870
|
-
|
|
4876
|
+
function isRecord5(value) {
|
|
4877
|
+
return value !== null && typeof value === "object";
|
|
4878
|
+
}
|
|
4879
|
+
function normalizeTextPartId(raw) {
|
|
4880
|
+
if (typeof raw !== "string")
|
|
4871
4881
|
return null;
|
|
4872
|
-
const
|
|
4873
|
-
|
|
4874
|
-
|
|
4875
|
-
|
|
4876
|
-
|
|
4877
|
-
|
|
4878
|
-
|
|
4879
|
-
|
|
4880
|
-
|
|
4881
|
-
|
|
4882
|
-
|
|
4883
|
-
|
|
4884
|
-
|
|
4885
|
-
|
|
4886
|
-
|
|
4887
|
-
|
|
4888
|
-
|
|
4889
|
-
|
|
4890
|
-
|
|
4891
|
-
|
|
4892
|
-
if (strippedRuntimeToolFields !== messages && apiLogOn) {
|
|
4893
|
-
logApiEvent("runtime tool input fields stripped", { modelString });
|
|
4894
|
-
}
|
|
4895
|
-
const geminiResult = sanitizeGeminiToolMessagesWithMetadata(strippedRuntimeToolFields, modelString, toolCount > 0);
|
|
4896
|
-
if (geminiResult.repaired && apiLogOn) {
|
|
4897
|
-
logApiEvent("gemini tool history repaired", {
|
|
4898
|
-
modelString,
|
|
4899
|
-
reason: geminiResult.reason,
|
|
4900
|
-
repairedFromIndex: geminiResult.repairedFromIndex,
|
|
4901
|
-
droppedMessageCount: geminiResult.droppedMessageCount,
|
|
4902
|
-
tailOnlyAffected: geminiResult.tailOnlyAffected
|
|
4903
|
-
});
|
|
4904
|
-
}
|
|
4905
|
-
const openaiStripped = stripOpenAIHistoryTransforms(geminiResult.messages, modelString);
|
|
4906
|
-
if (openaiStripped !== geminiResult.messages && apiLogOn) {
|
|
4907
|
-
logApiEvent("openai history transforms applied", { modelString });
|
|
4908
|
-
}
|
|
4909
|
-
const normalised = normalizeOpenAICompatibleToolCallInputs(openaiStripped, modelString);
|
|
4910
|
-
if (normalised !== openaiStripped && apiLogOn) {
|
|
4911
|
-
logApiEvent("openai-compatible tool input normalized", { modelString });
|
|
4912
|
-
}
|
|
4913
|
-
const preStats = apiLogOn ? getMessageDiagnostics(normalised) : getMessageStats(normalised);
|
|
4914
|
-
if (apiLogOn)
|
|
4915
|
-
logApiEvent("turn context pre-prune", preStats);
|
|
4916
|
-
const pruned = applyContextPruning(normalised, pruningMode);
|
|
4917
|
-
const postStats = apiLogOn ? getMessageDiagnostics(pruned) : getMessageStats(pruned);
|
|
4918
|
-
if (apiLogOn)
|
|
4919
|
-
logApiEvent("turn context post-prune", postStats);
|
|
4920
|
-
const compacted = compactToolResultPayloads(pruned, toolResultPayloadCapBytes);
|
|
4921
|
-
if (compacted !== pruned && apiLogOn) {
|
|
4922
|
-
logApiEvent("turn context post-compaction", {
|
|
4923
|
-
capBytes: toolResultPayloadCapBytes,
|
|
4924
|
-
diagnostics: getMessageDiagnostics(compacted)
|
|
4925
|
-
});
|
|
4926
|
-
}
|
|
4927
|
-
let finalMessages = compacted;
|
|
4928
|
-
let finalSystemPrompt = systemPrompt;
|
|
4929
|
-
const cacheFamily = getCacheFamily(modelString);
|
|
4930
|
-
if (cacheFamily === "anthropic" && promptCachingEnabled) {
|
|
4931
|
-
const annotated = annotateAnthropicCacheBreakpoints(compacted, systemPrompt);
|
|
4932
|
-
finalMessages = annotated.messages;
|
|
4933
|
-
finalSystemPrompt = annotated.systemPrompt;
|
|
4934
|
-
if (apiLogOn) {
|
|
4935
|
-
logApiEvent("Anthropic prompt caching", annotated.diagnostics);
|
|
4936
|
-
}
|
|
4937
|
-
}
|
|
4938
|
-
if (isAnthropicModelFamily(modelString) && isAnthropicOAuth()) {
|
|
4939
|
-
const prefix = `You are Claude Code, Anthropic's official CLI for Claude.
|
|
4940
|
-
`;
|
|
4941
|
-
if (finalSystemPrompt) {
|
|
4942
|
-
finalSystemPrompt = prefix + finalSystemPrompt;
|
|
4943
|
-
} else {
|
|
4944
|
-
const sysMsg = finalMessages.find((m) => m.role === "system");
|
|
4945
|
-
if (sysMsg && typeof sysMsg.content === "string") {
|
|
4946
|
-
const idx = finalMessages.indexOf(sysMsg);
|
|
4947
|
-
finalMessages[idx] = { ...sysMsg, content: prefix + sysMsg.content };
|
|
4948
|
-
}
|
|
4949
|
-
}
|
|
4950
|
-
}
|
|
4951
|
-
const wasPruned = (pruningMode === "balanced" || pruningMode === "aggressive") && (postStats.messageCount < preStats.messageCount || postStats.totalBytes < preStats.totalBytes);
|
|
4952
|
-
return {
|
|
4953
|
-
messages: finalMessages,
|
|
4954
|
-
systemPrompt: finalSystemPrompt,
|
|
4955
|
-
pruned: wasPruned,
|
|
4956
|
-
prePruneMessageCount: preStats.messageCount,
|
|
4957
|
-
prePruneTotalBytes: preStats.totalBytes,
|
|
4958
|
-
postPruneMessageCount: postStats.messageCount,
|
|
4959
|
-
postPruneTotalBytes: postStats.totalBytes
|
|
4960
|
-
};
|
|
4961
|
-
}
|
|
4962
|
-
|
|
4963
|
-
// src/llm-api/turn-provider-options.ts
|
|
4964
|
-
function isRecord5(value) {
|
|
4965
|
-
return value !== null && typeof value === "object";
|
|
4966
|
-
}
|
|
4967
|
-
function mergeDeep(target, source) {
|
|
4968
|
-
const output = { ...target };
|
|
4969
|
-
for (const key in source) {
|
|
4970
|
-
const sVal = source[key];
|
|
4971
|
-
const tVal = target[key];
|
|
4972
|
-
output[key] = isRecord5(sVal) && isRecord5(tVal) ? { ...tVal, ...sVal } : sVal;
|
|
4973
|
-
}
|
|
4974
|
-
return output;
|
|
4975
|
-
}
|
|
4976
|
-
function buildTurnProviderOptions(input) {
|
|
4977
|
-
const {
|
|
4978
|
-
modelString,
|
|
4979
|
-
thinkingEffort,
|
|
4980
|
-
promptCachingEnabled,
|
|
4981
|
-
openaiPromptCacheRetention,
|
|
4982
|
-
googleCachedContent,
|
|
4983
|
-
toolCount,
|
|
4984
|
-
hasSystemPrompt
|
|
4985
|
-
} = input;
|
|
4986
|
-
const thinkingOpts = thinkingEffort ? getThinkingProviderOptions(modelString, thinkingEffort) : null;
|
|
4987
|
-
const reasoningSummaryRequested = isRecord5(thinkingOpts) && isRecord5(thinkingOpts.openai) && typeof thinkingOpts.openai.reasoningSummary === "string";
|
|
4988
|
-
const cacheFamily = getCacheFamily(modelString);
|
|
4989
|
-
const cacheOpts = getCachingProviderOptions(modelString, {
|
|
4990
|
-
enabled: promptCachingEnabled,
|
|
4991
|
-
openaiRetention: openaiPromptCacheRetention,
|
|
4992
|
-
googleCachedContent,
|
|
4993
|
-
googleExplicitCachingCompatible: toolCount === 0 && !hasSystemPrompt
|
|
4994
|
-
});
|
|
4995
|
-
const baseProviderOpts = {
|
|
4996
|
-
...thinkingOpts ?? {},
|
|
4997
|
-
...isOpenAIGPT(modelString) ? {
|
|
4998
|
-
openai: {
|
|
4999
|
-
store: false,
|
|
5000
|
-
...isRecord5(thinkingOpts?.openai) ? thinkingOpts.openai : {}
|
|
5001
|
-
}
|
|
5002
|
-
} : {}
|
|
5003
|
-
};
|
|
5004
|
-
const providerOptions = cacheOpts ? mergeDeep(baseProviderOpts, cacheOpts) : baseProviderOpts;
|
|
5005
|
-
return {
|
|
5006
|
-
cacheFamily,
|
|
5007
|
-
thinkingOpts,
|
|
5008
|
-
cacheOpts,
|
|
5009
|
-
providerOptions,
|
|
5010
|
-
reasoningSummaryRequested
|
|
5011
|
-
};
|
|
5012
|
-
}
|
|
5013
|
-
|
|
5014
|
-
// src/llm-api/turn-request.ts
|
|
5015
|
-
function buildTurnPreparation(input) {
|
|
5016
|
-
const providerOptionsResult = buildTurnProviderOptions({
|
|
5017
|
-
modelString: input.modelString,
|
|
5018
|
-
thinkingEffort: input.thinkingEffort,
|
|
5019
|
-
promptCachingEnabled: input.promptCachingEnabled,
|
|
5020
|
-
openaiPromptCacheRetention: input.openaiPromptCacheRetention,
|
|
5021
|
-
googleCachedContent: input.googleCachedContent,
|
|
5022
|
-
toolCount: input.toolCount,
|
|
5023
|
-
hasSystemPrompt: Boolean(input.systemPrompt)
|
|
5024
|
-
});
|
|
5025
|
-
const prepared = prepareTurnMessages({
|
|
5026
|
-
messages: input.messages,
|
|
5027
|
-
modelString: input.modelString,
|
|
5028
|
-
toolCount: input.toolCount,
|
|
5029
|
-
systemPrompt: input.systemPrompt,
|
|
5030
|
-
pruningMode: input.pruningMode,
|
|
5031
|
-
toolResultPayloadCapBytes: input.toolResultPayloadCapBytes,
|
|
5032
|
-
promptCachingEnabled: input.promptCachingEnabled
|
|
5033
|
-
});
|
|
5034
|
-
return { providerOptionsResult, prepared };
|
|
5035
|
-
}
|
|
5036
|
-
function buildStreamTextRequest(input) {
|
|
5037
|
-
return {
|
|
5038
|
-
model: input.model,
|
|
5039
|
-
maxOutputTokens: 16384,
|
|
5040
|
-
messages: input.prepared.messages,
|
|
5041
|
-
tools: input.toolSet,
|
|
5042
|
-
stopWhen: stepCountIs(input.maxSteps),
|
|
5043
|
-
onStepFinish: input.onStepFinish,
|
|
5044
|
-
prepareStep: ({ stepNumber }) => {
|
|
5045
|
-
if (stepNumber >= input.maxSteps - 1) {
|
|
5046
|
-
return { activeTools: [] };
|
|
5047
|
-
}
|
|
5048
|
-
return;
|
|
5049
|
-
},
|
|
5050
|
-
...input.prepared.systemPrompt ? { system: input.prepared.systemPrompt } : {},
|
|
5051
|
-
...Object.keys(input.providerOptions).length > 0 ? {
|
|
5052
|
-
providerOptions: input.providerOptions
|
|
5053
|
-
} : {},
|
|
5054
|
-
...input.signal ? { abortSignal: input.signal } : {},
|
|
5055
|
-
onError: () => {},
|
|
5056
|
-
timeout: { chunkMs: 120000 }
|
|
5057
|
-
};
|
|
5058
|
-
}
|
|
5059
|
-
|
|
5060
|
-
// src/llm-api/turn-anthropic-oauth.ts
|
|
5061
|
-
var MAX_STEPS = 50;
|
|
5062
|
-
var MAX_OUTPUT_TOKENS = 16384;
|
|
5063
|
-
var CC_VERSION = "2.1.75";
|
|
5064
|
-
var cachedClient = null;
|
|
5065
|
-
function getClient(token) {
|
|
5066
|
-
if (cachedClient?.token === token && cachedClient.client)
|
|
5067
|
-
return cachedClient.client;
|
|
5068
|
-
const client = new Anthropic({
|
|
5069
|
-
apiKey: null,
|
|
5070
|
-
authToken: token,
|
|
5071
|
-
maxRetries: 5,
|
|
5072
|
-
dangerouslyAllowBrowser: true,
|
|
5073
|
-
defaultHeaders: {
|
|
5074
|
-
accept: "application/json",
|
|
5075
|
-
"anthropic-dangerous-direct-browser-access": "true",
|
|
5076
|
-
"anthropic-beta": "claude-code-20250219,oauth-2025-04-20",
|
|
5077
|
-
"user-agent": `claude-cli/${CC_VERSION}`,
|
|
5078
|
-
"x-app": "cli"
|
|
5079
|
-
}
|
|
5080
|
-
});
|
|
5081
|
-
cachedClient = { token, client };
|
|
5082
|
-
return client;
|
|
5083
|
-
}
|
|
5084
|
-
function supportsAdaptiveThinking(modelId) {
|
|
5085
|
-
return modelId.includes("opus-4-6") || modelId.includes("sonnet-4-6");
|
|
5086
|
-
}
|
|
5087
|
-
function mapEffort(effort, modelId) {
|
|
5088
|
-
if (!effort)
|
|
5089
|
-
return;
|
|
5090
|
-
const map = {
|
|
5091
|
-
low: "low",
|
|
5092
|
-
medium: "medium",
|
|
5093
|
-
high: "high",
|
|
5094
|
-
xhigh: modelId.includes("opus-4-6") ? "max" : "high"
|
|
5095
|
-
};
|
|
5096
|
-
return map[effort];
|
|
5097
|
-
}
|
|
5098
|
-
function coreToAnthropicMessages(messages) {
|
|
5099
|
-
let systemPrompt;
|
|
5100
|
-
const params = [];
|
|
5101
|
-
const toolUseIds = new Set;
|
|
5102
|
-
for (const msg of messages) {
|
|
5103
|
-
if (msg.role !== "assistant" || !Array.isArray(msg.content))
|
|
5104
|
-
continue;
|
|
5105
|
-
for (const part of msg.content) {
|
|
5106
|
-
if (part.type === "tool-call" && part.toolCallId) {
|
|
5107
|
-
toolUseIds.add(part.toolCallId);
|
|
5108
|
-
}
|
|
5109
|
-
}
|
|
5110
|
-
}
|
|
5111
|
-
for (const msg of messages) {
|
|
5112
|
-
if (msg.role === "system") {
|
|
5113
|
-
systemPrompt = typeof msg.content === "string" ? msg.content : Array.isArray(msg.content) ? msg.content.filter((p) => p.type === "text").map((p) => p.text).join(`
|
|
5114
|
-
`) : undefined;
|
|
5115
|
-
continue;
|
|
5116
|
-
}
|
|
5117
|
-
if (msg.role === "user") {
|
|
5118
|
-
if (typeof msg.content === "string") {
|
|
5119
|
-
if (msg.content.trim())
|
|
5120
|
-
params.push({ role: "user", content: msg.content });
|
|
5121
|
-
continue;
|
|
5122
|
-
}
|
|
5123
|
-
if (Array.isArray(msg.content)) {
|
|
5124
|
-
const blocks = [];
|
|
5125
|
-
for (const part of msg.content) {
|
|
5126
|
-
if (part.type === "text" && part.text?.trim()) {
|
|
5127
|
-
blocks.push({ type: "text", text: part.text });
|
|
5128
|
-
} else if (part.type === "tool-result") {
|
|
5129
|
-
if (!toolUseIds.has(part.toolCallId))
|
|
5130
|
-
continue;
|
|
5131
|
-
blocks.push({
|
|
5132
|
-
type: "tool_result",
|
|
5133
|
-
tool_use_id: part.toolCallId,
|
|
5134
|
-
content: typeof part.result === "string" ? part.result : JSON.stringify(part.result ?? part.output ?? ""),
|
|
5135
|
-
is_error: part.isError ?? false
|
|
5136
|
-
});
|
|
5137
|
-
} else if (part.type === "image") {
|
|
5138
|
-
blocks.push({
|
|
5139
|
-
type: "image",
|
|
5140
|
-
source: {
|
|
5141
|
-
type: "base64",
|
|
5142
|
-
media_type: part.mimeType ?? "image/png",
|
|
5143
|
-
data: part.data
|
|
5144
|
-
}
|
|
5145
|
-
});
|
|
5146
|
-
}
|
|
5147
|
-
}
|
|
5148
|
-
if (blocks.length > 0)
|
|
5149
|
-
params.push({ role: "user", content: blocks });
|
|
5150
|
-
}
|
|
5151
|
-
continue;
|
|
5152
|
-
}
|
|
5153
|
-
if (msg.role === "assistant") {
|
|
5154
|
-
if (typeof msg.content === "string") {
|
|
5155
|
-
if (msg.content.trim())
|
|
5156
|
-
params.push({ role: "assistant", content: msg.content });
|
|
5157
|
-
continue;
|
|
5158
|
-
}
|
|
5159
|
-
if (Array.isArray(msg.content)) {
|
|
5160
|
-
const blocks = [];
|
|
5161
|
-
for (const part of msg.content) {
|
|
5162
|
-
if (part.type === "text" && part.text?.trim()) {
|
|
5163
|
-
blocks.push({ type: "text", text: part.text });
|
|
5164
|
-
} else if (part.type === "tool-call") {
|
|
5165
|
-
blocks.push({
|
|
5166
|
-
type: "tool_use",
|
|
5167
|
-
id: part.toolCallId,
|
|
5168
|
-
name: part.toolName,
|
|
5169
|
-
input: part.args ?? {}
|
|
5170
|
-
});
|
|
5171
|
-
} else if (part.type === "thinking") {
|
|
5172
|
-
if (part.redacted && part.signature) {
|
|
5173
|
-
blocks.push({
|
|
5174
|
-
type: "redacted_thinking",
|
|
5175
|
-
data: part.signature
|
|
5176
|
-
});
|
|
5177
|
-
} else if (part.text?.trim() && part.signature?.trim()) {
|
|
5178
|
-
blocks.push({
|
|
5179
|
-
type: "thinking",
|
|
5180
|
-
thinking: part.text,
|
|
5181
|
-
signature: part.signature
|
|
5182
|
-
});
|
|
5183
|
-
}
|
|
5184
|
-
}
|
|
5185
|
-
}
|
|
5186
|
-
if (blocks.length > 0)
|
|
5187
|
-
params.push({ role: "assistant", content: blocks });
|
|
5188
|
-
}
|
|
5189
|
-
}
|
|
5190
|
-
}
|
|
5191
|
-
return { system: systemPrompt, params };
|
|
5192
|
-
}
|
|
5193
|
-
function convertTools(tools) {
|
|
5194
|
-
return tools.map((tool) => {
|
|
5195
|
-
const schema = zodSchema(tool.schema).jsonSchema;
|
|
5196
|
-
return {
|
|
5197
|
-
name: tool.name,
|
|
5198
|
-
description: tool.description,
|
|
5199
|
-
input_schema: {
|
|
5200
|
-
type: "object",
|
|
5201
|
-
properties: schema.properties ?? {},
|
|
5202
|
-
required: schema.required ?? []
|
|
5203
|
-
}
|
|
5204
|
-
};
|
|
5205
|
-
});
|
|
5206
|
-
}
|
|
5207
|
-
function buildCoreMessages(assistantText, thinkingBlocks, toolCalls, toolResults) {
|
|
5208
|
-
const messages = [];
|
|
5209
|
-
const parts = [];
|
|
5210
|
-
for (const tb of thinkingBlocks) {
|
|
5211
|
-
if (tb.redacted) {
|
|
5212
|
-
parts.push({
|
|
5213
|
-
type: "thinking",
|
|
5214
|
-
text: "[Reasoning redacted]",
|
|
5215
|
-
signature: tb.signature,
|
|
5216
|
-
redacted: true
|
|
5217
|
-
});
|
|
5218
|
-
} else {
|
|
5219
|
-
parts.push({
|
|
5220
|
-
type: "thinking",
|
|
5221
|
-
text: tb.text,
|
|
5222
|
-
signature: tb.signature
|
|
5223
|
-
});
|
|
5224
|
-
}
|
|
5225
|
-
}
|
|
5226
|
-
if (assistantText.trim()) {
|
|
5227
|
-
parts.push({ type: "text", text: assistantText });
|
|
5228
|
-
}
|
|
5229
|
-
for (const tc of toolCalls) {
|
|
5230
|
-
parts.push({
|
|
5231
|
-
type: "tool-call",
|
|
5232
|
-
toolCallId: tc.id,
|
|
5233
|
-
toolName: tc.name,
|
|
5234
|
-
args: tc.args
|
|
5235
|
-
});
|
|
5236
|
-
}
|
|
5237
|
-
if (parts.length > 0) {
|
|
5238
|
-
messages.push({
|
|
5239
|
-
role: "assistant",
|
|
5240
|
-
content: parts
|
|
5241
|
-
});
|
|
5242
|
-
}
|
|
5243
|
-
if (toolResults.length > 0) {
|
|
5244
|
-
const resultParts = toolResults.map((tr) => ({
|
|
5245
|
-
type: "tool-result",
|
|
5246
|
-
toolCallId: tr.toolCallId,
|
|
5247
|
-
toolName: tr.toolName,
|
|
5248
|
-
result: tr.result,
|
|
5249
|
-
isError: tr.isError
|
|
5250
|
-
}));
|
|
5251
|
-
messages.push({
|
|
5252
|
-
role: "user",
|
|
5253
|
-
content: resultParts
|
|
5254
|
-
});
|
|
5255
|
-
}
|
|
5256
|
-
return messages;
|
|
5257
|
-
}
|
|
5258
|
-
function parseStreamingJson(partial) {
|
|
5259
|
-
try {
|
|
5260
|
-
return JSON.parse(partial);
|
|
5261
|
-
} catch {
|
|
5262
|
-
try {
|
|
5263
|
-
let fixed = partial;
|
|
5264
|
-
const opens = (fixed.match(/{/g) || []).length;
|
|
5265
|
-
const closes = (fixed.match(/}/g) || []).length;
|
|
5266
|
-
for (let i = 0;i < opens - closes; i++)
|
|
5267
|
-
fixed += "}";
|
|
5268
|
-
return JSON.parse(fixed);
|
|
5269
|
-
} catch {
|
|
5270
|
-
return {};
|
|
5271
|
-
}
|
|
5272
|
-
}
|
|
5273
|
-
}
|
|
5274
|
-
async function* runTurnAnthropicOAuth(options) {
|
|
5275
|
-
const {
|
|
5276
|
-
token,
|
|
5277
|
-
modelString,
|
|
5278
|
-
messages,
|
|
5279
|
-
tools,
|
|
5280
|
-
systemPrompt,
|
|
5281
|
-
signal,
|
|
5282
|
-
thinkingEffort,
|
|
5283
|
-
pruningMode = "balanced",
|
|
5284
|
-
promptCachingEnabled = true,
|
|
5285
|
-
toolResultPayloadCapBytes = DEFAULT_TOOL_RESULT_PAYLOAD_CAP_BYTES
|
|
5286
|
-
} = options;
|
|
5287
|
-
const modelId = modelString.replace(/^anthropic\//, "");
|
|
5288
|
-
const client = getClient(token);
|
|
5289
|
-
const anthropicTools = convertTools(tools);
|
|
5290
|
-
const toolExecutors = new Map(tools.map((t) => [t.name, t.execute]));
|
|
5291
|
-
let totalInputTokens = 0;
|
|
5292
|
-
let totalOutputTokens = 0;
|
|
5293
|
-
let contextTokens = 0;
|
|
5294
|
-
let stepCount = 0;
|
|
5295
|
-
const allNewMessages = [];
|
|
5296
|
-
try {
|
|
5297
|
-
const { prepared } = buildTurnPreparation({
|
|
5298
|
-
modelString,
|
|
5299
|
-
messages,
|
|
5300
|
-
thinkingEffort,
|
|
5301
|
-
promptCachingEnabled,
|
|
5302
|
-
openaiPromptCacheRetention: "in_memory",
|
|
5303
|
-
googleCachedContent: null,
|
|
5304
|
-
toolCount: tools.length,
|
|
5305
|
-
systemPrompt,
|
|
5306
|
-
pruningMode,
|
|
5307
|
-
toolResultPayloadCapBytes
|
|
5308
|
-
});
|
|
5309
|
-
logApiEvent("turn start", {
|
|
5310
|
-
modelString,
|
|
5311
|
-
messageCount: messages.length,
|
|
5312
|
-
reasoningSummaryRequested: false,
|
|
5313
|
-
pruningMode,
|
|
5314
|
-
toolResultPayloadCapBytes
|
|
5315
|
-
});
|
|
5316
|
-
if (prepared.pruned) {
|
|
5317
|
-
yield {
|
|
5318
|
-
type: "context-pruned",
|
|
5319
|
-
mode: pruningMode,
|
|
5320
|
-
beforeMessageCount: prepared.prePruneMessageCount,
|
|
5321
|
-
afterMessageCount: prepared.postPruneMessageCount,
|
|
5322
|
-
removedMessageCount: prepared.prePruneMessageCount - prepared.postPruneMessageCount,
|
|
5323
|
-
beforeTotalBytes: prepared.prePruneTotalBytes,
|
|
5324
|
-
afterTotalBytes: prepared.postPruneTotalBytes,
|
|
5325
|
-
removedBytes: prepared.prePruneTotalBytes - prepared.postPruneTotalBytes
|
|
5326
|
-
};
|
|
5327
|
-
}
|
|
5328
|
-
const { system: extractedSystem, params: anthropicMessages } = coreToAnthropicMessages(prepared.messages);
|
|
5329
|
-
const ccPrefix = "You are Claude Code, Anthropic's official CLI for Claude.";
|
|
5330
|
-
const sysText = prepared.systemPrompt ?? extractedSystem;
|
|
5331
|
-
const systemBlocks = [
|
|
5332
|
-
{
|
|
5333
|
-
type: "text",
|
|
5334
|
-
text: ccPrefix,
|
|
5335
|
-
cache_control: { type: "ephemeral" }
|
|
5336
|
-
}
|
|
5337
|
-
];
|
|
5338
|
-
if (sysText) {
|
|
5339
|
-
const clean = sysText.startsWith(ccPrefix) ? sysText.slice(ccPrefix.length).replace(/^\n/, "") : sysText;
|
|
5340
|
-
if (clean.trim()) {
|
|
5341
|
-
systemBlocks.push({
|
|
5342
|
-
type: "text",
|
|
5343
|
-
text: clean,
|
|
5344
|
-
cache_control: { type: "ephemeral" }
|
|
5345
|
-
});
|
|
5346
|
-
}
|
|
5347
|
-
}
|
|
5348
|
-
const currentMessages = [...anthropicMessages];
|
|
5349
|
-
while (stepCount < MAX_STEPS) {
|
|
5350
|
-
stepCount++;
|
|
5351
|
-
const isLastStep = stepCount >= MAX_STEPS;
|
|
5352
|
-
const params = {
|
|
5353
|
-
model: modelId,
|
|
5354
|
-
max_tokens: MAX_OUTPUT_TOKENS,
|
|
5355
|
-
system: systemBlocks,
|
|
5356
|
-
messages: currentMessages,
|
|
5357
|
-
tools: isLastStep ? [] : anthropicTools,
|
|
5358
|
-
stream: true
|
|
5359
|
-
};
|
|
5360
|
-
if (thinkingEffort && supportsAdaptiveThinking(modelId)) {
|
|
5361
|
-
params.thinking = { type: "adaptive" };
|
|
5362
|
-
const effort = mapEffort(thinkingEffort, modelId);
|
|
5363
|
-
if (effort) {
|
|
5364
|
-
params.output_config = {
|
|
5365
|
-
effort
|
|
5366
|
-
};
|
|
5367
|
-
}
|
|
5368
|
-
}
|
|
5369
|
-
for (const m of currentMessages) {
|
|
5370
|
-
if (!Array.isArray(m.content))
|
|
5371
|
-
continue;
|
|
5372
|
-
for (const block of m.content) {
|
|
5373
|
-
if (typeof block === "object" && block !== null) {
|
|
5374
|
-
delete block.cache_control;
|
|
5375
|
-
}
|
|
5376
|
-
}
|
|
5377
|
-
}
|
|
5378
|
-
const lastMsg = currentMessages.length > 0 ? currentMessages[currentMessages.length - 1] : undefined;
|
|
5379
|
-
if (lastMsg && lastMsg.role === "user" && Array.isArray(lastMsg.content)) {
|
|
5380
|
-
const lastBlock = lastMsg.content[lastMsg.content.length - 1];
|
|
5381
|
-
if (lastBlock && typeof lastBlock === "object") {
|
|
5382
|
-
lastBlock.cache_control = {
|
|
5383
|
-
type: "ephemeral"
|
|
5384
|
-
};
|
|
5385
|
-
}
|
|
5386
|
-
}
|
|
5387
|
-
if (isApiLogEnabled()) {
|
|
5388
|
-
logApiEvent("Provider Request", {
|
|
5389
|
-
url: "https://api.anthropic.com/v1/messages",
|
|
5390
|
-
method: "POST",
|
|
5391
|
-
model: modelId,
|
|
5392
|
-
messageCount: currentMessages.length,
|
|
5393
|
-
toolCount: params.tools?.length ?? 0
|
|
5394
|
-
});
|
|
5395
|
-
}
|
|
5396
|
-
const stream = client.messages.stream(params, { signal });
|
|
5397
|
-
let assistantText = "";
|
|
5398
|
-
const thinkingBlocks = [];
|
|
5399
|
-
const toolCalls = [];
|
|
5400
|
-
let partialJson = "";
|
|
5401
|
-
let currentToolId = "";
|
|
5402
|
-
let currentToolName = "";
|
|
5403
|
-
let stepInputTokens = 0;
|
|
5404
|
-
let stepOutputTokens = 0;
|
|
5405
|
-
let stopReason;
|
|
5406
|
-
for await (const event of stream) {
|
|
5407
|
-
if (event.type === "message_start") {
|
|
5408
|
-
stepInputTokens = event.message.usage.input_tokens || 0;
|
|
5409
|
-
stepOutputTokens = event.message.usage.output_tokens || 0;
|
|
5410
|
-
} else if (event.type === "content_block_start") {
|
|
5411
|
-
if (event.content_block.type === "text") {} else if (event.content_block.type === "thinking") {
|
|
5412
|
-
thinkingBlocks.push({
|
|
5413
|
-
text: "",
|
|
5414
|
-
signature: ""
|
|
5415
|
-
});
|
|
5416
|
-
} else if (event.content_block.type === "redacted_thinking") {
|
|
5417
|
-
thinkingBlocks.push({
|
|
5418
|
-
text: "[Reasoning redacted]",
|
|
5419
|
-
signature: event.content_block.data,
|
|
5420
|
-
redacted: true
|
|
5421
|
-
});
|
|
5422
|
-
} else if (event.content_block.type === "tool_use") {
|
|
5423
|
-
currentToolId = event.content_block.id;
|
|
5424
|
-
currentToolName = event.content_block.name;
|
|
5425
|
-
partialJson = "";
|
|
5426
|
-
}
|
|
5427
|
-
} else if (event.type === "content_block_delta") {
|
|
5428
|
-
if (event.delta.type === "text_delta") {
|
|
5429
|
-
assistantText += event.delta.text;
|
|
5430
|
-
yield { type: "text-delta", delta: event.delta.text };
|
|
5431
|
-
} else if (event.delta.type === "thinking_delta") {
|
|
5432
|
-
const tb = thinkingBlocks[thinkingBlocks.length - 1];
|
|
5433
|
-
if (tb)
|
|
5434
|
-
tb.text += event.delta.thinking;
|
|
5435
|
-
yield {
|
|
5436
|
-
type: "reasoning-delta",
|
|
5437
|
-
delta: event.delta.thinking
|
|
5438
|
-
};
|
|
5439
|
-
} else if (event.delta.type === "input_json_delta") {
|
|
5440
|
-
partialJson += event.delta.partial_json;
|
|
5441
|
-
} else if (event.delta.type === "signature_delta") {
|
|
5442
|
-
const tb = thinkingBlocks[thinkingBlocks.length - 1];
|
|
5443
|
-
if (tb)
|
|
5444
|
-
tb.signature += event.delta.signature;
|
|
5445
|
-
}
|
|
5446
|
-
} else if (event.type === "content_block_stop") {
|
|
5447
|
-
if (currentToolId && currentToolName) {
|
|
5448
|
-
const args = parseStreamingJson(partialJson);
|
|
5449
|
-
toolCalls.push({
|
|
5450
|
-
id: currentToolId,
|
|
5451
|
-
name: currentToolName,
|
|
5452
|
-
args
|
|
5453
|
-
});
|
|
5454
|
-
yield {
|
|
5455
|
-
type: "tool-call-start",
|
|
5456
|
-
toolCallId: currentToolId,
|
|
5457
|
-
toolName: currentToolName,
|
|
5458
|
-
args
|
|
5459
|
-
};
|
|
5460
|
-
currentToolId = "";
|
|
5461
|
-
currentToolName = "";
|
|
5462
|
-
partialJson = "";
|
|
5463
|
-
}
|
|
5464
|
-
} else if (event.type === "message_delta") {
|
|
5465
|
-
if (event.delta.stop_reason) {
|
|
5466
|
-
stopReason = event.delta.stop_reason;
|
|
5467
|
-
}
|
|
5468
|
-
if (event.usage.output_tokens != null) {
|
|
5469
|
-
stepOutputTokens = event.usage.output_tokens;
|
|
5470
|
-
}
|
|
5471
|
-
}
|
|
5472
|
-
}
|
|
5473
|
-
totalInputTokens += stepInputTokens;
|
|
5474
|
-
totalOutputTokens += stepOutputTokens;
|
|
5475
|
-
contextTokens = stepInputTokens;
|
|
5476
|
-
logApiEvent("step finish", {
|
|
5477
|
-
stepNumber: stepCount,
|
|
5478
|
-
finishReason: stopReason,
|
|
5479
|
-
usage: {
|
|
5480
|
-
inputTokens: stepInputTokens,
|
|
5481
|
-
outputTokens: stepOutputTokens
|
|
5482
|
-
}
|
|
5483
|
-
});
|
|
5484
|
-
const toolResults = [];
|
|
5485
|
-
if (stopReason === "tool_use" && toolCalls.length > 0) {
|
|
5486
|
-
for (const tc of toolCalls) {
|
|
5487
|
-
const executor = toolExecutors.get(tc.name);
|
|
5488
|
-
let result;
|
|
5489
|
-
let isError = false;
|
|
5490
|
-
if (!executor) {
|
|
5491
|
-
result = `Unknown tool: ${tc.name}`;
|
|
5492
|
-
isError = true;
|
|
5493
|
-
} else {
|
|
5494
|
-
try {
|
|
5495
|
-
result = await executor(tc.args);
|
|
5496
|
-
} catch (err) {
|
|
5497
|
-
result = normalizeUnknownError(err).message;
|
|
5498
|
-
isError = true;
|
|
5499
|
-
}
|
|
5500
|
-
}
|
|
5501
|
-
toolResults.push({
|
|
5502
|
-
toolCallId: tc.id,
|
|
5503
|
-
toolName: tc.name,
|
|
5504
|
-
result,
|
|
5505
|
-
isError
|
|
5506
|
-
});
|
|
5507
|
-
yield {
|
|
5508
|
-
type: "tool-result",
|
|
5509
|
-
toolCallId: tc.id,
|
|
5510
|
-
toolName: tc.name,
|
|
5511
|
-
result,
|
|
5512
|
-
isError
|
|
5513
|
-
};
|
|
5514
|
-
}
|
|
5515
|
-
}
|
|
5516
|
-
const stepMessages = buildCoreMessages(assistantText, thinkingBlocks, toolCalls, toolResults);
|
|
5517
|
-
allNewMessages.push(...stepMessages);
|
|
5518
|
-
if (stopReason !== "tool_use" || toolCalls.length === 0) {
|
|
5519
|
-
break;
|
|
5520
|
-
}
|
|
5521
|
-
const assistantBlocks = [];
|
|
5522
|
-
for (const tb of thinkingBlocks) {
|
|
5523
|
-
if (tb.redacted) {
|
|
5524
|
-
assistantBlocks.push({
|
|
5525
|
-
type: "redacted_thinking",
|
|
5526
|
-
data: tb.signature
|
|
5527
|
-
});
|
|
5528
|
-
} else if (tb.text.trim() && tb.signature.trim()) {
|
|
5529
|
-
assistantBlocks.push({
|
|
5530
|
-
type: "thinking",
|
|
5531
|
-
thinking: tb.text,
|
|
5532
|
-
signature: tb.signature
|
|
5533
|
-
});
|
|
5534
|
-
}
|
|
5535
|
-
}
|
|
5536
|
-
if (assistantText.trim()) {
|
|
5537
|
-
assistantBlocks.push({ type: "text", text: assistantText });
|
|
5538
|
-
}
|
|
5539
|
-
for (const tc of toolCalls) {
|
|
5540
|
-
assistantBlocks.push({
|
|
5541
|
-
type: "tool_use",
|
|
5542
|
-
id: tc.id,
|
|
5543
|
-
name: tc.name,
|
|
5544
|
-
input: tc.args
|
|
5545
|
-
});
|
|
5546
|
-
}
|
|
5547
|
-
currentMessages.push({
|
|
5548
|
-
role: "assistant",
|
|
5549
|
-
content: assistantBlocks
|
|
5550
|
-
});
|
|
5551
|
-
const resultBlocks = toolResults.map((tr) => ({
|
|
5552
|
-
type: "tool_result",
|
|
5553
|
-
tool_use_id: tr.toolCallId,
|
|
5554
|
-
content: typeof tr.result === "string" ? tr.result : JSON.stringify(tr.result ?? ""),
|
|
5555
|
-
is_error: tr.isError
|
|
5556
|
-
}));
|
|
5557
|
-
currentMessages.push({ role: "user", content: resultBlocks });
|
|
5558
|
-
}
|
|
5559
|
-
logApiEvent("turn complete", {
|
|
5560
|
-
newMessagesCount: allNewMessages.length,
|
|
5561
|
-
inputTokens: totalInputTokens,
|
|
5562
|
-
outputTokens: totalOutputTokens
|
|
5563
|
-
});
|
|
5564
|
-
yield {
|
|
5565
|
-
type: "turn-complete",
|
|
5566
|
-
inputTokens: totalInputTokens,
|
|
5567
|
-
outputTokens: totalOutputTokens,
|
|
5568
|
-
contextTokens,
|
|
5569
|
-
messages: allNewMessages
|
|
5570
|
-
};
|
|
5571
|
-
} catch (err) {
|
|
5572
|
-
const normalizedError = normalizeUnknownError(err);
|
|
5573
|
-
logApiEvent("turn error", normalizedError);
|
|
5574
|
-
yield {
|
|
5575
|
-
type: "turn-error",
|
|
5576
|
-
error: normalizedError,
|
|
5577
|
-
partialMessages: allNewMessages
|
|
5578
|
-
};
|
|
5579
|
-
}
|
|
5580
|
-
}
|
|
5581
|
-
|
|
5582
|
-
// src/llm-api/turn-execution.ts
|
|
5583
|
-
import { dynamicTool, jsonSchema } from "ai";
|
|
5584
|
-
|
|
5585
|
-
// src/llm-api/turn-stream-events.ts
|
|
5586
|
-
function shouldLogStreamChunk(c11) {
|
|
5587
|
-
return c11.type !== "text-delta" && c11.type !== "reasoning" && c11.type !== "reasoning-delta";
|
|
5588
|
-
}
|
|
5589
|
-
function extractToolArgs(c11) {
|
|
5590
|
-
return c11.input ?? c11.args;
|
|
5591
|
-
}
|
|
5592
|
-
function hasRenderableToolArgs(args) {
|
|
5593
|
-
if (args === null || args === undefined)
|
|
5594
|
-
return false;
|
|
5595
|
-
if (typeof args === "string")
|
|
5596
|
-
return args.trim().length > 0;
|
|
5597
|
-
if (Array.isArray(args))
|
|
5598
|
-
return args.length > 0;
|
|
5599
|
-
if (typeof args === "object")
|
|
5600
|
-
return Object.keys(args).length > 0;
|
|
5601
|
-
return true;
|
|
5602
|
-
}
|
|
5603
|
-
function mapStreamChunkToTurnEvent(c11) {
|
|
5604
|
-
switch (c11.type) {
|
|
5605
|
-
case "text-delta": {
|
|
5606
|
-
const delta = typeof c11.text === "string" ? c11.text : "";
|
|
5607
|
-
return {
|
|
5608
|
-
type: "text-delta",
|
|
5609
|
-
delta
|
|
5610
|
-
};
|
|
5611
|
-
}
|
|
5612
|
-
case "reasoning-delta":
|
|
5613
|
-
case "reasoning": {
|
|
5614
|
-
const delta = getReasoningDeltaFromStreamChunk(c11);
|
|
5615
|
-
if (delta === null)
|
|
5616
|
-
return null;
|
|
5617
|
-
return {
|
|
5618
|
-
type: "reasoning-delta",
|
|
5619
|
-
delta
|
|
5620
|
-
};
|
|
5621
|
-
}
|
|
5622
|
-
case "tool-input-start": {
|
|
5623
|
-
const args = extractToolArgs(c11);
|
|
5624
|
-
const hasStableToolCallId = typeof c11.toolCallId === "string" && c11.toolCallId.trim().length > 0;
|
|
5625
|
-
if (hasStableToolCallId && !hasRenderableToolArgs(args))
|
|
5626
|
-
return null;
|
|
5627
|
-
return {
|
|
5628
|
-
type: "tool-call-start",
|
|
5629
|
-
toolCallId: String(c11.toolCallId ?? ""),
|
|
5630
|
-
toolName: String(c11.toolName ?? ""),
|
|
5631
|
-
args
|
|
5632
|
-
};
|
|
5633
|
-
}
|
|
5634
|
-
case "tool-call": {
|
|
5635
|
-
return {
|
|
5636
|
-
type: "tool-call-start",
|
|
5637
|
-
toolCallId: String(c11.toolCallId ?? ""),
|
|
5638
|
-
toolName: String(c11.toolName ?? ""),
|
|
5639
|
-
args: extractToolArgs(c11)
|
|
5640
|
-
};
|
|
5641
|
-
}
|
|
5642
|
-
case "tool-result": {
|
|
5643
|
-
return {
|
|
5644
|
-
type: "tool-result",
|
|
5645
|
-
toolCallId: String(c11.toolCallId ?? ""),
|
|
5646
|
-
toolName: String(c11.toolName ?? ""),
|
|
5647
|
-
result: "output" in c11 ? c11.output : ("result" in c11) ? c11.result : undefined,
|
|
5648
|
-
isError: "isError" in c11 ? Boolean(c11.isError) : false
|
|
5649
|
-
};
|
|
5650
|
-
}
|
|
5651
|
-
case "tool-error":
|
|
5652
|
-
return {
|
|
5653
|
-
type: "tool-result",
|
|
5654
|
-
toolCallId: String(c11.toolCallId ?? ""),
|
|
5655
|
-
toolName: String(c11.toolName ?? ""),
|
|
5656
|
-
result: c11.error ?? "Tool execution failed",
|
|
5657
|
-
isError: true
|
|
5658
|
-
};
|
|
5659
|
-
case "error": {
|
|
5660
|
-
throw normalizeUnknownError(c11.error);
|
|
5661
|
-
}
|
|
5662
|
-
default:
|
|
5663
|
-
return null;
|
|
5664
|
-
}
|
|
5665
|
-
}
|
|
5666
|
-
|
|
5667
|
-
// src/llm-api/turn-execution.ts
|
|
5668
|
-
function isZodSchema(s) {
|
|
5669
|
-
return s !== null && typeof s === "object" && "_def" in s;
|
|
5670
|
-
}
|
|
5671
|
-
function toCoreTool(def) {
|
|
5672
|
-
const schema = isZodSchema(def.schema) ? def.schema : jsonSchema(def.schema);
|
|
5673
|
-
return dynamicTool({
|
|
5674
|
-
description: def.description,
|
|
5675
|
-
inputSchema: schema,
|
|
5676
|
-
execute: async (input) => {
|
|
5677
|
-
try {
|
|
5678
|
-
return await def.execute(input);
|
|
5679
|
-
} catch (err) {
|
|
5680
|
-
throw normalizeUnknownError(err);
|
|
5681
|
-
}
|
|
5682
|
-
}
|
|
5683
|
-
});
|
|
5684
|
-
}
|
|
5685
|
-
function buildToolSet(tools) {
|
|
5686
|
-
const toolSet = {};
|
|
5687
|
-
for (const def of tools) {
|
|
5688
|
-
toolSet[def.name] = toCoreTool(def);
|
|
5689
|
-
}
|
|
5690
|
-
return toolSet;
|
|
5691
|
-
}
|
|
5692
|
-
function createTurnStepTracker(opts) {
|
|
5693
|
-
let stepCount = 0;
|
|
5694
|
-
let inputTokens = 0;
|
|
5695
|
-
let outputTokens = 0;
|
|
5696
|
-
let contextTokens = 0;
|
|
5697
|
-
let partialMessages = [];
|
|
5698
|
-
return {
|
|
5699
|
-
onStepFinish: (step) => {
|
|
5700
|
-
opts.onStepLog({
|
|
5701
|
-
stepNumber: stepCount + 1,
|
|
5702
|
-
finishReason: step.finishReason,
|
|
5703
|
-
usage: step.usage
|
|
5704
|
-
});
|
|
5705
|
-
inputTokens += step.usage?.inputTokens ?? 0;
|
|
5706
|
-
outputTokens += step.usage?.outputTokens ?? 0;
|
|
5707
|
-
contextTokens = step.usage?.inputTokens ?? contextTokens;
|
|
5708
|
-
stepCount += 1;
|
|
5709
|
-
const s = step;
|
|
5710
|
-
partialMessages = s.response?.messages ?? s.messages ?? partialMessages;
|
|
5711
|
-
},
|
|
5712
|
-
getState: () => ({
|
|
5713
|
-
stepCount,
|
|
5714
|
-
inputTokens,
|
|
5715
|
-
outputTokens,
|
|
5716
|
-
contextTokens,
|
|
5717
|
-
partialMessages
|
|
5718
|
-
})
|
|
5719
|
-
};
|
|
5720
|
-
}
|
|
5721
|
-
var TOOL_RESULT_CHUNK_TYPES = new Set(["tool-result", "tool-error"]);
|
|
5722
|
-
function normalizeToolCallId(raw) {
|
|
5723
|
-
if (typeof raw !== "string")
|
|
5724
|
-
return null;
|
|
5725
|
-
const trimmed = raw.trim();
|
|
5726
|
-
return trimmed ? trimmed : null;
|
|
5727
|
-
}
|
|
5728
|
-
function normalizeToolName(raw) {
|
|
5729
|
-
if (typeof raw !== "string")
|
|
5730
|
-
return "tool";
|
|
5731
|
-
const trimmed = raw.trim();
|
|
5732
|
-
return trimmed || "tool";
|
|
5733
|
-
}
|
|
5734
|
-
function isRecord6(value) {
|
|
5735
|
-
return value !== null && typeof value === "object";
|
|
5736
|
-
}
|
|
5737
|
-
function normalizeTextPartId(raw) {
|
|
5738
|
-
if (typeof raw !== "string")
|
|
5739
|
-
return null;
|
|
5740
|
-
const trimmed = raw.trim();
|
|
5741
|
-
return trimmed ? trimmed : null;
|
|
5742
|
-
}
|
|
5743
|
-
function getOpenAITextPhase2(chunk) {
|
|
5744
|
-
const providerData = isRecord6(chunk.providerMetadata) ? chunk.providerMetadata : isRecord6(chunk.providerOptions) ? chunk.providerOptions : null;
|
|
5745
|
-
if (!providerData)
|
|
5746
|
-
return null;
|
|
5747
|
-
const openai = providerData.openai;
|
|
5748
|
-
if (!isRecord6(openai))
|
|
5749
|
-
return null;
|
|
5750
|
-
return openai.phase === "commentary" || openai.phase === "final_answer" ? openai.phase : null;
|
|
5751
|
-
}
|
|
5752
|
-
function extractTextDelta(chunk) {
|
|
5753
|
-
if (typeof chunk.text === "string")
|
|
5754
|
-
return chunk.text;
|
|
5755
|
-
if (typeof chunk.textDelta === "string")
|
|
5756
|
-
return chunk.textDelta;
|
|
5757
|
-
if (typeof chunk.delta === "string")
|
|
5758
|
-
return chunk.delta;
|
|
5759
|
-
return "";
|
|
4882
|
+
const trimmed = raw.trim();
|
|
4883
|
+
return trimmed ? trimmed : null;
|
|
4884
|
+
}
|
|
4885
|
+
function getOpenAITextPhase2(chunk) {
|
|
4886
|
+
const providerData = isRecord5(chunk.providerMetadata) ? chunk.providerMetadata : isRecord5(chunk.providerOptions) ? chunk.providerOptions : null;
|
|
4887
|
+
if (!providerData)
|
|
4888
|
+
return null;
|
|
4889
|
+
const openai = providerData.openai;
|
|
4890
|
+
if (!isRecord5(openai))
|
|
4891
|
+
return null;
|
|
4892
|
+
return openai.phase === "commentary" || openai.phase === "final_answer" ? openai.phase : null;
|
|
4893
|
+
}
|
|
4894
|
+
function extractTextDelta(chunk) {
|
|
4895
|
+
if (typeof chunk.text === "string")
|
|
4896
|
+
return chunk.text;
|
|
4897
|
+
if (typeof chunk.textDelta === "string")
|
|
4898
|
+
return chunk.textDelta;
|
|
4899
|
+
if (typeof chunk.delta === "string")
|
|
4900
|
+
return chunk.delta;
|
|
4901
|
+
return "";
|
|
5760
4902
|
}
|
|
5761
4903
|
|
|
5762
4904
|
class StreamTextPhaseTracker {
|
|
@@ -5857,84 +4999,376 @@ class StreamToolCallTracker {
|
|
|
5857
4999
|
}
|
|
5858
5000
|
return { chunk, suppressTurnEvent: false };
|
|
5859
5001
|
}
|
|
5860
|
-
trackRenderableStart(chunk, toolName, existingToolCallId) {
|
|
5861
|
-
const toolCallId = existingToolCallId ?? this.nextSyntheticToolCallId();
|
|
5862
|
-
this.trackStart(toolName, toolCallId);
|
|
5863
|
-
if (toolCallId === chunk.toolCallId)
|
|
5864
|
-
return chunk;
|
|
5865
|
-
return { ...chunk, toolCallId };
|
|
5002
|
+
trackRenderableStart(chunk, toolName, existingToolCallId) {
|
|
5003
|
+
const toolCallId = existingToolCallId ?? this.nextSyntheticToolCallId();
|
|
5004
|
+
this.trackStart(toolName, toolCallId);
|
|
5005
|
+
if (toolCallId === chunk.toolCallId)
|
|
5006
|
+
return chunk;
|
|
5007
|
+
return { ...chunk, toolCallId };
|
|
5008
|
+
}
|
|
5009
|
+
nextSyntheticToolCallId() {
|
|
5010
|
+
this.syntheticCount += 1;
|
|
5011
|
+
return `synthetic-tool-call-${this.syntheticCount}`;
|
|
5012
|
+
}
|
|
5013
|
+
trackStart(toolName, toolCallId) {
|
|
5014
|
+
const pending = this.pendingByTool.get(toolName) ?? [];
|
|
5015
|
+
pending.push(toolCallId);
|
|
5016
|
+
this.pendingByTool.set(toolName, pending);
|
|
5017
|
+
}
|
|
5018
|
+
trackDeferredStart(toolName) {
|
|
5019
|
+
this.deferredStartsByTool.set(toolName, (this.deferredStartsByTool.get(toolName) ?? 0) + 1);
|
|
5020
|
+
}
|
|
5021
|
+
consumeDeferredStart(toolName) {
|
|
5022
|
+
const count = this.deferredStartsByTool.get(toolName) ?? 0;
|
|
5023
|
+
if (count <= 0)
|
|
5024
|
+
return;
|
|
5025
|
+
if (count === 1) {
|
|
5026
|
+
this.deferredStartsByTool.delete(toolName);
|
|
5027
|
+
return;
|
|
5028
|
+
}
|
|
5029
|
+
this.deferredStartsByTool.set(toolName, count - 1);
|
|
5030
|
+
}
|
|
5031
|
+
consumeTracked(toolName, toolCallId) {
|
|
5032
|
+
const pending = this.pendingByTool.get(toolName);
|
|
5033
|
+
if (!pending || pending.length === 0)
|
|
5034
|
+
return;
|
|
5035
|
+
const idx = pending.indexOf(toolCallId);
|
|
5036
|
+
if (idx === -1)
|
|
5037
|
+
return;
|
|
5038
|
+
pending.splice(idx, 1);
|
|
5039
|
+
if (pending.length === 0)
|
|
5040
|
+
this.pendingByTool.delete(toolName);
|
|
5041
|
+
}
|
|
5042
|
+
consumeNextTracked(toolName) {
|
|
5043
|
+
const pending = this.pendingByTool.get(toolName);
|
|
5044
|
+
if (!pending || pending.length === 0)
|
|
5045
|
+
return null;
|
|
5046
|
+
const toolCallId = pending.shift() ?? null;
|
|
5047
|
+
if (pending.length === 0)
|
|
5048
|
+
this.pendingByTool.delete(toolName);
|
|
5049
|
+
return toolCallId;
|
|
5050
|
+
}
|
|
5051
|
+
}
|
|
5052
|
+
async function* mapFullStreamToTurnEvents(stream, opts) {
|
|
5053
|
+
const toolCallTracker = new StreamToolCallTracker;
|
|
5054
|
+
const textPhaseTracker = new StreamTextPhaseTracker;
|
|
5055
|
+
for await (const originalChunk of stream) {
|
|
5056
|
+
const prepared = toolCallTracker.prepare(originalChunk);
|
|
5057
|
+
const chunk = prepared.chunk;
|
|
5058
|
+
const route = textPhaseTracker.route(chunk);
|
|
5059
|
+
if (!prepared.suppressTurnEvent && route !== "skip" && shouldLogStreamChunk(chunk)) {
|
|
5060
|
+
opts.onChunk?.(chunk);
|
|
5061
|
+
}
|
|
5062
|
+
if (prepared.suppressTurnEvent || route === "skip")
|
|
5063
|
+
continue;
|
|
5064
|
+
const event = route === "reasoning" ? mapCommentaryChunkToTurnEvent(chunk) : mapStreamChunkToTurnEvent(chunk);
|
|
5065
|
+
if (event)
|
|
5066
|
+
yield event;
|
|
5067
|
+
}
|
|
5068
|
+
}
|
|
5069
|
+
|
|
5070
|
+
// src/llm-api/turn-request.ts
|
|
5071
|
+
import { stepCountIs } from "ai";
|
|
5072
|
+
|
|
5073
|
+
// src/llm-api/provider-options.ts
|
|
5074
|
+
var ANTHROPIC_BUDGET = {
|
|
5075
|
+
low: 4096,
|
|
5076
|
+
medium: 8192,
|
|
5077
|
+
high: 16384,
|
|
5078
|
+
xhigh: 32768
|
|
5079
|
+
};
|
|
5080
|
+
var GEMINI_BUDGET = {
|
|
5081
|
+
low: 4096,
|
|
5082
|
+
medium: 8192,
|
|
5083
|
+
high: 16384,
|
|
5084
|
+
xhigh: 24575
|
|
5085
|
+
};
|
|
5086
|
+
function clampEffort(effort, max) {
|
|
5087
|
+
const ORDER = ["low", "medium", "high", "xhigh"];
|
|
5088
|
+
const effortIdx = ORDER.indexOf(effort);
|
|
5089
|
+
const maxIdx = ORDER.indexOf(max);
|
|
5090
|
+
return ORDER[Math.min(effortIdx, maxIdx)];
|
|
5091
|
+
}
|
|
5092
|
+
function getAnthropicThinkingOptions(modelId, effort) {
|
|
5093
|
+
const isAdaptive = /^claude-3-7/.test(modelId) || /^claude-sonnet-4/.test(modelId) || /^claude-opus-4/.test(modelId);
|
|
5094
|
+
if (isAdaptive) {
|
|
5095
|
+
const isOpus = /^claude-opus-4/.test(modelId);
|
|
5096
|
+
const mapped = effort === "xhigh" ? isOpus ? "max" : "high" : effort;
|
|
5097
|
+
return { anthropic: { thinking: { type: "adaptive" }, effort: mapped } };
|
|
5098
|
+
}
|
|
5099
|
+
return {
|
|
5100
|
+
anthropic: {
|
|
5101
|
+
thinking: { type: "enabled", budgetTokens: ANTHROPIC_BUDGET[effort] },
|
|
5102
|
+
betas: ["interleaved-thinking-2025-05-14"]
|
|
5103
|
+
}
|
|
5104
|
+
};
|
|
5105
|
+
}
|
|
5106
|
+
function getOpenAIThinkingOptions(modelId, effort) {
|
|
5107
|
+
const supportsXhigh = /^gpt-5\.[2-9]/.test(modelId) || /^o4/.test(modelId);
|
|
5108
|
+
const clamped = supportsXhigh ? effort : clampEffort(effort, "high");
|
|
5109
|
+
return { openai: { reasoningEffort: clamped, reasoningSummary: "auto" } };
|
|
5110
|
+
}
|
|
5111
|
+
function getGeminiThinkingOptions(modelId, effort) {
|
|
5112
|
+
if (/^gemini-3/.test(modelId)) {
|
|
5113
|
+
return {
|
|
5114
|
+
google: {
|
|
5115
|
+
thinkingConfig: {
|
|
5116
|
+
includeThoughts: true,
|
|
5117
|
+
thinkingLevel: clampEffort(effort, "high")
|
|
5118
|
+
}
|
|
5119
|
+
}
|
|
5120
|
+
};
|
|
5121
|
+
}
|
|
5122
|
+
return {
|
|
5123
|
+
google: {
|
|
5124
|
+
thinkingConfig: {
|
|
5125
|
+
includeThoughts: true,
|
|
5126
|
+
thinkingBudget: GEMINI_BUDGET[effort]
|
|
5127
|
+
}
|
|
5128
|
+
}
|
|
5129
|
+
};
|
|
5130
|
+
}
|
|
5131
|
+
var THINKING_STRATEGIES = [
|
|
5132
|
+
{
|
|
5133
|
+
supports: isAnthropicModelFamily,
|
|
5134
|
+
build: getAnthropicThinkingOptions
|
|
5135
|
+
},
|
|
5136
|
+
{
|
|
5137
|
+
supports: isOpenAIReasoningModelFamily,
|
|
5138
|
+
build: getOpenAIThinkingOptions
|
|
5139
|
+
},
|
|
5140
|
+
{
|
|
5141
|
+
supports: isGeminiModelFamily,
|
|
5142
|
+
build: getGeminiThinkingOptions
|
|
5143
|
+
}
|
|
5144
|
+
];
|
|
5145
|
+
function getThinkingProviderOptions(modelString, effort) {
|
|
5146
|
+
if (!supportsThinking(modelString))
|
|
5147
|
+
return null;
|
|
5148
|
+
const { modelId } = parseModelString(modelString);
|
|
5149
|
+
for (const strategy of THINKING_STRATEGIES) {
|
|
5150
|
+
if (!strategy.supports(modelString))
|
|
5151
|
+
continue;
|
|
5152
|
+
return strategy.build(modelId, effort);
|
|
5153
|
+
}
|
|
5154
|
+
return null;
|
|
5155
|
+
}
|
|
5156
|
+
var CACHE_FAMILY_RULES = [
|
|
5157
|
+
[isAnthropicModelFamily, "anthropic"],
|
|
5158
|
+
[isGeminiModelFamily, "google"]
|
|
5159
|
+
];
|
|
5160
|
+
function getCacheFamily(modelString) {
|
|
5161
|
+
for (const [match, family] of CACHE_FAMILY_RULES) {
|
|
5162
|
+
if (match(modelString))
|
|
5163
|
+
return family;
|
|
5164
|
+
}
|
|
5165
|
+
return "none";
|
|
5166
|
+
}
|
|
5167
|
+
function getCachingProviderOptions(modelString, opts) {
|
|
5168
|
+
if (!opts.enabled)
|
|
5169
|
+
return null;
|
|
5170
|
+
const family = getCacheFamily(modelString);
|
|
5171
|
+
if (family === "google" && opts.googleCachedContent && opts.googleExplicitCachingCompatible !== false) {
|
|
5172
|
+
return { google: { cachedContent: opts.googleCachedContent } };
|
|
5173
|
+
}
|
|
5174
|
+
return null;
|
|
5175
|
+
}
|
|
5176
|
+
|
|
5177
|
+
// src/llm-api/turn-prepare-messages.ts
|
|
5178
|
+
function prepareTurnMessages(input) {
|
|
5179
|
+
const {
|
|
5180
|
+
messages,
|
|
5181
|
+
modelString,
|
|
5182
|
+
toolCount,
|
|
5183
|
+
systemPrompt,
|
|
5184
|
+
pruningMode,
|
|
5185
|
+
toolResultPayloadCapBytes,
|
|
5186
|
+
promptCachingEnabled
|
|
5187
|
+
} = input;
|
|
5188
|
+
const apiLogOn = isApiLogEnabled();
|
|
5189
|
+
const strippedRuntimeToolFields = stripToolRuntimeInputFields(messages);
|
|
5190
|
+
if (strippedRuntimeToolFields !== messages && apiLogOn) {
|
|
5191
|
+
logApiEvent("runtime tool input fields stripped", { modelString });
|
|
5192
|
+
}
|
|
5193
|
+
const geminiResult = sanitizeGeminiToolMessagesWithMetadata(strippedRuntimeToolFields, modelString, toolCount > 0);
|
|
5194
|
+
if (geminiResult.repaired && apiLogOn) {
|
|
5195
|
+
logApiEvent("gemini tool history repaired", {
|
|
5196
|
+
modelString,
|
|
5197
|
+
reason: geminiResult.reason,
|
|
5198
|
+
repairedFromIndex: geminiResult.repairedFromIndex,
|
|
5199
|
+
droppedMessageCount: geminiResult.droppedMessageCount,
|
|
5200
|
+
tailOnlyAffected: geminiResult.tailOnlyAffected
|
|
5201
|
+
});
|
|
5866
5202
|
}
|
|
5867
|
-
|
|
5868
|
-
|
|
5869
|
-
|
|
5203
|
+
const openaiStripped = stripOpenAIHistoryTransforms(geminiResult.messages, modelString);
|
|
5204
|
+
if (openaiStripped !== geminiResult.messages && apiLogOn) {
|
|
5205
|
+
logApiEvent("openai history transforms applied", { modelString });
|
|
5870
5206
|
}
|
|
5871
|
-
|
|
5872
|
-
|
|
5873
|
-
|
|
5874
|
-
this.pendingByTool.set(toolName, pending);
|
|
5207
|
+
const normalised = normalizeOpenAICompatibleToolCallInputs(openaiStripped, modelString);
|
|
5208
|
+
if (normalised !== openaiStripped && apiLogOn) {
|
|
5209
|
+
logApiEvent("openai-compatible tool input normalized", { modelString });
|
|
5875
5210
|
}
|
|
5876
|
-
|
|
5877
|
-
|
|
5211
|
+
const preStats = apiLogOn ? getMessageDiagnostics(normalised) : getMessageStats(normalised);
|
|
5212
|
+
if (apiLogOn)
|
|
5213
|
+
logApiEvent("turn context pre-prune", preStats);
|
|
5214
|
+
const pruned = applyContextPruning(normalised, pruningMode);
|
|
5215
|
+
const postStats = apiLogOn ? getMessageDiagnostics(pruned) : getMessageStats(pruned);
|
|
5216
|
+
if (apiLogOn)
|
|
5217
|
+
logApiEvent("turn context post-prune", postStats);
|
|
5218
|
+
const compacted = compactToolResultPayloads(pruned, toolResultPayloadCapBytes);
|
|
5219
|
+
if (compacted !== pruned && apiLogOn) {
|
|
5220
|
+
logApiEvent("turn context post-compaction", {
|
|
5221
|
+
capBytes: toolResultPayloadCapBytes,
|
|
5222
|
+
diagnostics: getMessageDiagnostics(compacted)
|
|
5223
|
+
});
|
|
5878
5224
|
}
|
|
5879
|
-
|
|
5880
|
-
|
|
5881
|
-
|
|
5882
|
-
|
|
5883
|
-
|
|
5884
|
-
|
|
5885
|
-
|
|
5225
|
+
let finalMessages = compacted;
|
|
5226
|
+
let finalSystemPrompt = systemPrompt;
|
|
5227
|
+
const cacheFamily = getCacheFamily(modelString);
|
|
5228
|
+
if (cacheFamily === "anthropic" && promptCachingEnabled) {
|
|
5229
|
+
const annotated = annotateAnthropicCacheBreakpoints(compacted, systemPrompt);
|
|
5230
|
+
finalMessages = annotated.messages;
|
|
5231
|
+
finalSystemPrompt = annotated.systemPrompt;
|
|
5232
|
+
if (apiLogOn) {
|
|
5233
|
+
logApiEvent("Anthropic prompt caching", annotated.diagnostics);
|
|
5886
5234
|
}
|
|
5887
|
-
this.deferredStartsByTool.set(toolName, count - 1);
|
|
5888
|
-
}
|
|
5889
|
-
consumeTracked(toolName, toolCallId) {
|
|
5890
|
-
const pending = this.pendingByTool.get(toolName);
|
|
5891
|
-
if (!pending || pending.length === 0)
|
|
5892
|
-
return;
|
|
5893
|
-
const idx = pending.indexOf(toolCallId);
|
|
5894
|
-
if (idx === -1)
|
|
5895
|
-
return;
|
|
5896
|
-
pending.splice(idx, 1);
|
|
5897
|
-
if (pending.length === 0)
|
|
5898
|
-
this.pendingByTool.delete(toolName);
|
|
5899
5235
|
}
|
|
5900
|
-
|
|
5901
|
-
const
|
|
5902
|
-
|
|
5903
|
-
|
|
5904
|
-
|
|
5905
|
-
|
|
5906
|
-
|
|
5907
|
-
|
|
5236
|
+
if (isAnthropicModelFamily(modelString) && isAnthropicOAuth()) {
|
|
5237
|
+
const ccIdentity = "You are Claude Code, Anthropic's official CLI for Claude.";
|
|
5238
|
+
const ccSystemMsg = {
|
|
5239
|
+
role: "system",
|
|
5240
|
+
content: ccIdentity,
|
|
5241
|
+
providerOptions: {
|
|
5242
|
+
anthropic: { cacheControl: { type: "ephemeral" } }
|
|
5243
|
+
}
|
|
5244
|
+
};
|
|
5245
|
+
if (finalSystemPrompt) {
|
|
5246
|
+
finalMessages = [ccSystemMsg, ...finalMessages];
|
|
5247
|
+
} else {
|
|
5248
|
+
const sysIdx = finalMessages.findIndex((m) => m.role === "system");
|
|
5249
|
+
if (sysIdx >= 0) {
|
|
5250
|
+
finalMessages = [
|
|
5251
|
+
...finalMessages.slice(0, sysIdx),
|
|
5252
|
+
ccSystemMsg,
|
|
5253
|
+
...finalMessages.slice(sysIdx)
|
|
5254
|
+
];
|
|
5255
|
+
} else {
|
|
5256
|
+
finalMessages = [ccSystemMsg, ...finalMessages];
|
|
5257
|
+
}
|
|
5258
|
+
}
|
|
5908
5259
|
}
|
|
5260
|
+
const wasPruned = (pruningMode === "balanced" || pruningMode === "aggressive") && (postStats.messageCount < preStats.messageCount || postStats.totalBytes < preStats.totalBytes);
|
|
5261
|
+
return {
|
|
5262
|
+
messages: finalMessages,
|
|
5263
|
+
systemPrompt: finalSystemPrompt,
|
|
5264
|
+
pruned: wasPruned,
|
|
5265
|
+
prePruneMessageCount: preStats.messageCount,
|
|
5266
|
+
prePruneTotalBytes: preStats.totalBytes,
|
|
5267
|
+
postPruneMessageCount: postStats.messageCount,
|
|
5268
|
+
postPruneTotalBytes: postStats.totalBytes
|
|
5269
|
+
};
|
|
5909
5270
|
}
|
|
5910
|
-
|
|
5911
|
-
|
|
5912
|
-
|
|
5913
|
-
|
|
5914
|
-
|
|
5915
|
-
|
|
5916
|
-
|
|
5917
|
-
|
|
5918
|
-
|
|
5919
|
-
|
|
5920
|
-
|
|
5921
|
-
continue;
|
|
5922
|
-
const event = route === "reasoning" ? mapCommentaryChunkToTurnEvent(chunk) : mapStreamChunkToTurnEvent(chunk);
|
|
5923
|
-
if (event)
|
|
5924
|
-
yield event;
|
|
5271
|
+
|
|
5272
|
+
// src/llm-api/turn-provider-options.ts
|
|
5273
|
+
function isRecord6(value) {
|
|
5274
|
+
return value !== null && typeof value === "object";
|
|
5275
|
+
}
|
|
5276
|
+
function mergeDeep(target, source) {
|
|
5277
|
+
const output = { ...target };
|
|
5278
|
+
for (const key in source) {
|
|
5279
|
+
const sVal = source[key];
|
|
5280
|
+
const tVal = target[key];
|
|
5281
|
+
output[key] = isRecord6(sVal) && isRecord6(tVal) ? { ...tVal, ...sVal } : sVal;
|
|
5925
5282
|
}
|
|
5283
|
+
return output;
|
|
5284
|
+
}
|
|
5285
|
+
function buildTurnProviderOptions(input) {
|
|
5286
|
+
const {
|
|
5287
|
+
modelString,
|
|
5288
|
+
thinkingEffort,
|
|
5289
|
+
promptCachingEnabled,
|
|
5290
|
+
openaiPromptCacheRetention,
|
|
5291
|
+
googleCachedContent,
|
|
5292
|
+
toolCount,
|
|
5293
|
+
hasSystemPrompt
|
|
5294
|
+
} = input;
|
|
5295
|
+
const thinkingOpts = thinkingEffort ? getThinkingProviderOptions(modelString, thinkingEffort) : null;
|
|
5296
|
+
const reasoningSummaryRequested = isRecord6(thinkingOpts) && isRecord6(thinkingOpts.openai) && typeof thinkingOpts.openai.reasoningSummary === "string";
|
|
5297
|
+
const cacheFamily = getCacheFamily(modelString);
|
|
5298
|
+
const cacheOpts = getCachingProviderOptions(modelString, {
|
|
5299
|
+
enabled: promptCachingEnabled,
|
|
5300
|
+
openaiRetention: openaiPromptCacheRetention,
|
|
5301
|
+
googleCachedContent,
|
|
5302
|
+
googleExplicitCachingCompatible: toolCount === 0 && !hasSystemPrompt
|
|
5303
|
+
});
|
|
5304
|
+
const baseProviderOpts = {
|
|
5305
|
+
...thinkingOpts ?? {},
|
|
5306
|
+
...isOpenAIGPT(modelString) ? {
|
|
5307
|
+
openai: {
|
|
5308
|
+
store: false,
|
|
5309
|
+
...isRecord6(thinkingOpts?.openai) ? thinkingOpts.openai : {}
|
|
5310
|
+
}
|
|
5311
|
+
} : {}
|
|
5312
|
+
};
|
|
5313
|
+
const providerOptions = cacheOpts ? mergeDeep(baseProviderOpts, cacheOpts) : baseProviderOpts;
|
|
5314
|
+
return {
|
|
5315
|
+
cacheFamily,
|
|
5316
|
+
thinkingOpts,
|
|
5317
|
+
cacheOpts,
|
|
5318
|
+
providerOptions,
|
|
5319
|
+
reasoningSummaryRequested
|
|
5320
|
+
};
|
|
5321
|
+
}
|
|
5322
|
+
|
|
5323
|
+
// src/llm-api/turn-request.ts
|
|
5324
|
+
function buildTurnPreparation(input) {
|
|
5325
|
+
const providerOptionsResult = buildTurnProviderOptions({
|
|
5326
|
+
modelString: input.modelString,
|
|
5327
|
+
thinkingEffort: input.thinkingEffort,
|
|
5328
|
+
promptCachingEnabled: input.promptCachingEnabled,
|
|
5329
|
+
openaiPromptCacheRetention: input.openaiPromptCacheRetention,
|
|
5330
|
+
googleCachedContent: input.googleCachedContent,
|
|
5331
|
+
toolCount: input.toolCount,
|
|
5332
|
+
hasSystemPrompt: Boolean(input.systemPrompt)
|
|
5333
|
+
});
|
|
5334
|
+
const prepared = prepareTurnMessages({
|
|
5335
|
+
messages: input.messages,
|
|
5336
|
+
modelString: input.modelString,
|
|
5337
|
+
toolCount: input.toolCount,
|
|
5338
|
+
systemPrompt: input.systemPrompt,
|
|
5339
|
+
pruningMode: input.pruningMode,
|
|
5340
|
+
toolResultPayloadCapBytes: input.toolResultPayloadCapBytes,
|
|
5341
|
+
promptCachingEnabled: input.promptCachingEnabled
|
|
5342
|
+
});
|
|
5343
|
+
return { providerOptionsResult, prepared };
|
|
5344
|
+
}
|
|
5345
|
+
function buildStreamTextRequest(input) {
|
|
5346
|
+
return {
|
|
5347
|
+
model: input.model,
|
|
5348
|
+
maxOutputTokens: 16384,
|
|
5349
|
+
messages: input.prepared.messages,
|
|
5350
|
+
tools: input.toolSet,
|
|
5351
|
+
stopWhen: stepCountIs(input.maxSteps),
|
|
5352
|
+
onStepFinish: input.onStepFinish,
|
|
5353
|
+
prepareStep: ({ stepNumber }) => {
|
|
5354
|
+
if (stepNumber >= input.maxSteps - 1) {
|
|
5355
|
+
return { activeTools: [] };
|
|
5356
|
+
}
|
|
5357
|
+
return;
|
|
5358
|
+
},
|
|
5359
|
+
...input.prepared.systemPrompt ? { system: input.prepared.systemPrompt } : {},
|
|
5360
|
+
...Object.keys(input.providerOptions).length > 0 ? {
|
|
5361
|
+
providerOptions: input.providerOptions
|
|
5362
|
+
} : {},
|
|
5363
|
+
...input.signal ? { abortSignal: input.signal } : {},
|
|
5364
|
+
onError: () => {},
|
|
5365
|
+
timeout: { chunkMs: 120000 }
|
|
5366
|
+
};
|
|
5926
5367
|
}
|
|
5927
5368
|
|
|
5928
5369
|
// src/llm-api/turn.ts
|
|
5929
|
-
var
|
|
5370
|
+
var MAX_STEPS = 50;
|
|
5930
5371
|
async function* runTurn(options) {
|
|
5931
|
-
if (options.modelString.startsWith("anthropic/") && isLoggedIn("anthropic")) {
|
|
5932
|
-
const token = await getAccessToken("anthropic");
|
|
5933
|
-
if (token) {
|
|
5934
|
-
yield* runTurnAnthropicOAuth({ ...options, token });
|
|
5935
|
-
return;
|
|
5936
|
-
}
|
|
5937
|
-
}
|
|
5938
5372
|
const {
|
|
5939
5373
|
model,
|
|
5940
5374
|
modelString,
|
|
@@ -6006,7 +5440,7 @@ async function* runTurn(options) {
|
|
|
6006
5440
|
onStepFinish: stepTracker.onStepFinish,
|
|
6007
5441
|
signal,
|
|
6008
5442
|
providerOptions: providerOptionsResult.providerOptions,
|
|
6009
|
-
maxSteps:
|
|
5443
|
+
maxSteps: MAX_STEPS
|
|
6010
5444
|
}));
|
|
6011
5445
|
result.response.catch(() => {});
|
|
6012
5446
|
for await (const event of mapFullStreamToTurnEvents(result.fullStream, {
|
|
@@ -6049,7 +5483,7 @@ async function* runTurn(options) {
|
|
|
6049
5483
|
}
|
|
6050
5484
|
|
|
6051
5485
|
// src/session/manager.ts
|
|
6052
|
-
import * as
|
|
5486
|
+
import * as c10 from "yoctocolors";
|
|
6053
5487
|
function newSession(model, cwd) {
|
|
6054
5488
|
const id = generateSessionId();
|
|
6055
5489
|
const row = createSession({ id, cwd, model });
|
|
@@ -6068,19 +5502,19 @@ function touchActiveSession(session) {
|
|
|
6068
5502
|
function printSessionList() {
|
|
6069
5503
|
const sessions = listSessions(20);
|
|
6070
5504
|
if (sessions.length === 0) {
|
|
6071
|
-
|
|
5505
|
+
writeln(c10.dim("No sessions found."));
|
|
6072
5506
|
return;
|
|
6073
5507
|
}
|
|
6074
|
-
|
|
6075
|
-
${
|
|
5508
|
+
writeln(`
|
|
5509
|
+
${c10.bold("Recent sessions:")}`);
|
|
6076
5510
|
for (const s of sessions) {
|
|
6077
5511
|
const date = new Date(s.updated_at).toLocaleString();
|
|
6078
5512
|
const cwd = tildePath(s.cwd);
|
|
6079
|
-
const title = s.title ||
|
|
6080
|
-
|
|
5513
|
+
const title = s.title || c10.dim("(untitled)");
|
|
5514
|
+
writeln(` ${c10.dim(s.id.padEnd(14))} ${title.padEnd(30)} ${c10.cyan(s.model.split("/").pop() ?? s.model).padEnd(20)} ${c10.dim(cwd)} ${c10.dim(date)}`);
|
|
6081
5515
|
}
|
|
6082
|
-
|
|
6083
|
-
${
|
|
5516
|
+
writeln(`
|
|
5517
|
+
${c10.dim("Use")} mc --resume <id> ${c10.dim("to continue a session.")}`);
|
|
6084
5518
|
}
|
|
6085
5519
|
function getMostRecentSession() {
|
|
6086
5520
|
const sessions = listSessions(1);
|
|
@@ -6310,7 +5744,7 @@ class SessionRunner {
|
|
|
6310
5744
|
}
|
|
6311
5745
|
this.session = resumed;
|
|
6312
5746
|
this.currentModel = this.session.model;
|
|
6313
|
-
this.reporter.info(`Resumed session ${this.session.id} (${
|
|
5747
|
+
this.reporter.info(`Resumed session ${this.session.id} (${c11.cyan(this.currentModel)})`);
|
|
6314
5748
|
} else {
|
|
6315
5749
|
this.session = newSession(this.currentModel, this.cwd);
|
|
6316
5750
|
}
|
|
@@ -6774,13 +6208,12 @@ ${input.command}`], {
|
|
|
6774
6208
|
reader.cancel().catch(() => {});
|
|
6775
6209
|
}
|
|
6776
6210
|
}, timeout);
|
|
6777
|
-
async function collectStream(stream
|
|
6211
|
+
async function collectStream(stream) {
|
|
6778
6212
|
const reader = stream.getReader();
|
|
6779
6213
|
readers.push(reader);
|
|
6780
6214
|
const chunks = [];
|
|
6781
6215
|
let totalBytes = 0;
|
|
6782
6216
|
let truncated = false;
|
|
6783
|
-
const decoder = new TextDecoder;
|
|
6784
6217
|
while (true) {
|
|
6785
6218
|
try {
|
|
6786
6219
|
const { done, value } = await reader.read();
|
|
@@ -6788,7 +6221,6 @@ ${input.command}`], {
|
|
|
6788
6221
|
break;
|
|
6789
6222
|
if (!value)
|
|
6790
6223
|
continue;
|
|
6791
|
-
onChunk?.(decoder.decode(value, { stream: true }));
|
|
6792
6224
|
if (totalBytes + value.length > MAX_OUTPUT_BYTES) {
|
|
6793
6225
|
chunks.push(value.slice(0, MAX_OUTPUT_BYTES - totalBytes));
|
|
6794
6226
|
truncated = true;
|
|
@@ -6808,28 +6240,11 @@ ${input.command}`], {
|
|
|
6808
6240
|
let stdout = "";
|
|
6809
6241
|
let stderr = "";
|
|
6810
6242
|
let exitCode = 1;
|
|
6811
|
-
const onOutput = input.onOutput;
|
|
6812
|
-
const hasOutputHandler = typeof onOutput === "function";
|
|
6813
|
-
let streamedAnyOutput = false;
|
|
6814
|
-
let streamedEndsWithNewline = true;
|
|
6815
|
-
const emitOutput = (chunk) => {
|
|
6816
|
-
if (!chunk || !hasOutputHandler)
|
|
6817
|
-
return;
|
|
6818
|
-
const handled = onOutput(chunk);
|
|
6819
|
-
if (handled === false)
|
|
6820
|
-
return;
|
|
6821
|
-
streamedAnyOutput = true;
|
|
6822
|
-
streamedEndsWithNewline = /[\r\n]$/.test(chunk);
|
|
6823
|
-
};
|
|
6824
6243
|
try {
|
|
6825
6244
|
[stdout, stderr] = await Promise.all([
|
|
6826
|
-
collectStream(proc.stdout
|
|
6827
|
-
collectStream(proc.stderr
|
|
6245
|
+
collectStream(proc.stdout),
|
|
6246
|
+
collectStream(proc.stderr)
|
|
6828
6247
|
]);
|
|
6829
|
-
if (streamedAnyOutput && !streamedEndsWithNewline) {
|
|
6830
|
-
emitOutput(`
|
|
6831
|
-
`);
|
|
6832
|
-
}
|
|
6833
6248
|
exitCode = await proc.exited;
|
|
6834
6249
|
} finally {
|
|
6835
6250
|
clearTimeout(timer);
|
|
@@ -6845,8 +6260,7 @@ ${input.command}`], {
|
|
|
6845
6260
|
stderr: stderr.trimEnd(),
|
|
6846
6261
|
exitCode,
|
|
6847
6262
|
success: exitCode === 0,
|
|
6848
|
-
timedOut
|
|
6849
|
-
streamedOutput: streamedAnyOutput
|
|
6263
|
+
timedOut
|
|
6850
6264
|
};
|
|
6851
6265
|
}
|
|
6852
6266
|
var shellTool = {
|
|
@@ -6919,22 +6333,9 @@ function withCwdDefault(tool, cwd) {
|
|
|
6919
6333
|
}
|
|
6920
6334
|
};
|
|
6921
6335
|
}
|
|
6922
|
-
function withShellOutput(tool, onOutput) {
|
|
6923
|
-
const originalExecute = tool.execute;
|
|
6924
|
-
return {
|
|
6925
|
-
...tool,
|
|
6926
|
-
execute: async (input) => {
|
|
6927
|
-
const patched = {
|
|
6928
|
-
...typeof input === "object" && input !== null ? input : {},
|
|
6929
|
-
onOutput
|
|
6930
|
-
};
|
|
6931
|
-
return originalExecute(patched);
|
|
6932
|
-
}
|
|
6933
|
-
};
|
|
6934
|
-
}
|
|
6935
6336
|
function buildToolSet2(opts) {
|
|
6936
|
-
const { cwd
|
|
6937
|
-
const shell =
|
|
6337
|
+
const { cwd } = opts;
|
|
6338
|
+
const shell = withCwdDefault(shellTool, cwd);
|
|
6938
6339
|
const tools = [
|
|
6939
6340
|
shell,
|
|
6940
6341
|
withCwdDefault(listSkillsTool, cwd),
|
|
@@ -6963,17 +6364,10 @@ async function initAgent(opts) {
|
|
|
6963
6364
|
const cwd = opts.cwd;
|
|
6964
6365
|
let currentModel = opts.model;
|
|
6965
6366
|
const { runSubagent, killAll } = createSubagentRunner(cwd, () => currentModel);
|
|
6966
|
-
let verboseOutputEnabled = opts.initialVerboseOutput;
|
|
6967
6367
|
const agents = loadAgents(cwd);
|
|
6968
6368
|
const tools = buildToolSet2({
|
|
6969
6369
|
cwd,
|
|
6970
6370
|
runSubagent,
|
|
6971
|
-
onShellOutput: (chunk) => {
|
|
6972
|
-
if (!verboseOutputEnabled)
|
|
6973
|
-
return false;
|
|
6974
|
-
opts.reporter.streamChunk(chunk);
|
|
6975
|
-
return true;
|
|
6976
|
-
},
|
|
6977
6371
|
availableAgents: subagentAgents(agents)
|
|
6978
6372
|
});
|
|
6979
6373
|
const mcpTools = [];
|
|
@@ -6997,7 +6391,7 @@ async function initAgent(opts) {
|
|
|
6997
6391
|
for (const row of listMcpServers()) {
|
|
6998
6392
|
try {
|
|
6999
6393
|
await connectAndAddMcp(row.name);
|
|
7000
|
-
opts.reporter.info(`MCP: connected ${
|
|
6394
|
+
opts.reporter.info(`MCP: connected ${c12.cyan(row.name)}`);
|
|
7001
6395
|
} catch (e) {
|
|
7002
6396
|
opts.reporter.error(`MCP: failed to connect ${row.name}: ${String(e)}`);
|
|
7003
6397
|
}
|
|
@@ -7062,7 +6456,6 @@ async function initAgent(opts) {
|
|
|
7062
6456
|
},
|
|
7063
6457
|
setVerboseOutput: (verbose) => {
|
|
7064
6458
|
runner.verboseOutput = verbose;
|
|
7065
|
-
verboseOutputEnabled = verbose;
|
|
7066
6459
|
setPreferredVerboseOutput(verbose);
|
|
7067
6460
|
},
|
|
7068
6461
|
get pruningMode() {
|
|
@@ -7120,12 +6513,12 @@ async function initAgent(opts) {
|
|
|
7120
6513
|
}
|
|
7121
6514
|
|
|
7122
6515
|
// src/cli/args.ts
|
|
7123
|
-
import * as
|
|
6516
|
+
import * as c13 from "yoctocolors";
|
|
7124
6517
|
function parseArgs(argv) {
|
|
7125
6518
|
const args = {
|
|
7126
6519
|
model: null,
|
|
7127
6520
|
sessionId: null,
|
|
7128
|
-
|
|
6521
|
+
list: false,
|
|
7129
6522
|
resumeLast: false,
|
|
7130
6523
|
prompt: null,
|
|
7131
6524
|
cwd: process.cwd(),
|
|
@@ -7152,7 +6545,7 @@ function parseArgs(argv) {
|
|
|
7152
6545
|
break;
|
|
7153
6546
|
case "--list":
|
|
7154
6547
|
case "-l":
|
|
7155
|
-
args.
|
|
6548
|
+
args.list = true;
|
|
7156
6549
|
break;
|
|
7157
6550
|
case "--cwd":
|
|
7158
6551
|
args.cwd = argv[++i] ?? process.cwd();
|
|
@@ -7184,11 +6577,11 @@ function parseArgs(argv) {
|
|
|
7184
6577
|
return args;
|
|
7185
6578
|
}
|
|
7186
6579
|
function printHelp() {
|
|
7187
|
-
|
|
6580
|
+
writeln(`${c13.bold("mini-coder")} \u2014 a small, fast CLI coding agent
|
|
7188
6581
|
`);
|
|
7189
|
-
|
|
6582
|
+
writeln(`${c13.bold("Usage:")} mc [options] [prompt]
|
|
7190
6583
|
`);
|
|
7191
|
-
|
|
6584
|
+
writeln(`${c13.bold("Options:")}`);
|
|
7192
6585
|
const opts = [
|
|
7193
6586
|
["-m, --model <id>", "Model to use (e.g. zen/claude-sonnet-4-6)"],
|
|
7194
6587
|
["-c, --continue", "Continue the most recent session"],
|
|
@@ -7198,10 +6591,10 @@ function printHelp() {
|
|
|
7198
6591
|
["-h, --help", "Show this help"]
|
|
7199
6592
|
];
|
|
7200
6593
|
for (const [flag, desc] of opts) {
|
|
7201
|
-
|
|
6594
|
+
writeln(` ${c13.cyan((flag ?? "").padEnd(22))} ${c13.dim(desc ?? "")}`);
|
|
7202
6595
|
}
|
|
7203
|
-
|
|
7204
|
-
${
|
|
6596
|
+
writeln(`
|
|
6597
|
+
${c13.bold("Provider env vars:")}`);
|
|
7205
6598
|
const envs = [
|
|
7206
6599
|
["OPENCODE_API_KEY", "OpenCode Zen (recommended)"],
|
|
7207
6600
|
["ANTHROPIC_API_KEY", "Anthropic direct"],
|
|
@@ -7212,22 +6605,22 @@ ${c14.bold("Provider env vars:")}`);
|
|
|
7212
6605
|
["OLLAMA_BASE_URL", "Ollama base URL (default: http://localhost:11434)"]
|
|
7213
6606
|
];
|
|
7214
6607
|
for (const [env, desc] of envs) {
|
|
7215
|
-
|
|
6608
|
+
writeln(` ${c13.yellow((env ?? "").padEnd(22))} ${c13.dim(desc ?? "")}`);
|
|
7216
6609
|
}
|
|
7217
|
-
|
|
7218
|
-
${
|
|
7219
|
-
|
|
7220
|
-
|
|
7221
|
-
|
|
7222
|
-
|
|
7223
|
-
|
|
6610
|
+
writeln(`
|
|
6611
|
+
${c13.bold("Examples:")}`);
|
|
6612
|
+
writeln(` mc ${c13.dim("# interactive session")}`);
|
|
6613
|
+
writeln(` mc "explain this codebase" ${c13.dim("# one-shot prompt then exit")}`);
|
|
6614
|
+
writeln(` mc -c ${c13.dim("# continue last session")}`);
|
|
6615
|
+
writeln(` mc -m ollama/llama3.2 ${c13.dim("# use local Ollama model")}`);
|
|
6616
|
+
writeln(` mc -l ${c13.dim("# list sessions")}`);
|
|
7224
6617
|
}
|
|
7225
6618
|
|
|
7226
6619
|
// src/cli/bootstrap.ts
|
|
7227
6620
|
import { existsSync as existsSync8, mkdirSync as mkdirSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
7228
6621
|
import { homedir as homedir8 } from "os";
|
|
7229
6622
|
import { join as join12 } from "path";
|
|
7230
|
-
import * as
|
|
6623
|
+
import * as c14 from "yoctocolors";
|
|
7231
6624
|
var REVIEW_COMMAND_CONTENT = `---
|
|
7232
6625
|
description: Review recent changes for correctness, code quality, and performance
|
|
7233
6626
|
context: fork
|
|
@@ -7251,7 +6644,7 @@ function bootstrapGlobalDefaults() {
|
|
|
7251
6644
|
if (!existsSync8(reviewPath)) {
|
|
7252
6645
|
mkdirSync4(commandsDir, { recursive: true });
|
|
7253
6646
|
writeFileSync3(reviewPath, REVIEW_COMMAND_CONTENT, "utf-8");
|
|
7254
|
-
|
|
6647
|
+
writeln(`${c14.green("\u2713")} created ${c14.dim("~/.agents/commands/review.md")} ${c14.dim("(edit it to customise your reviews)")}`);
|
|
7255
6648
|
}
|
|
7256
6649
|
}
|
|
7257
6650
|
|
|
@@ -7316,7 +6709,6 @@ class HeadlessReporter {
|
|
|
7316
6709
|
error(_msg, _hint) {}
|
|
7317
6710
|
warn(_msg) {}
|
|
7318
6711
|
writeText(_text) {}
|
|
7319
|
-
streamChunk(_text) {}
|
|
7320
6712
|
startSpinner(_label) {}
|
|
7321
6713
|
stopSpinner() {}
|
|
7322
6714
|
async renderTurn(events, _opts) {
|
|
@@ -7366,7 +6758,7 @@ class HeadlessReporter {
|
|
|
7366
6758
|
}
|
|
7367
6759
|
|
|
7368
6760
|
// src/cli/input-loop.ts
|
|
7369
|
-
import * as
|
|
6761
|
+
import * as c22 from "yoctocolors";
|
|
7370
6762
|
|
|
7371
6763
|
// src/cli/cli-helpers.ts
|
|
7372
6764
|
async function getGitBranch(cwd) {
|
|
@@ -7387,67 +6779,67 @@ async function getGitBranch(cwd) {
|
|
|
7387
6779
|
}
|
|
7388
6780
|
|
|
7389
6781
|
// src/cli/commands.ts
|
|
7390
|
-
import * as
|
|
6782
|
+
import * as c21 from "yoctocolors";
|
|
7391
6783
|
|
|
7392
6784
|
// src/cli/commands-agent.ts
|
|
7393
|
-
import * as
|
|
6785
|
+
import * as c15 from "yoctocolors";
|
|
7394
6786
|
function handleAgentCommand(ctx, args) {
|
|
7395
6787
|
const raw = args.trim();
|
|
7396
6788
|
const agents = loadAgents(ctx.cwd);
|
|
7397
6789
|
if (!raw) {
|
|
7398
6790
|
if (agents.size === 0) {
|
|
7399
|
-
|
|
7400
|
-
|
|
6791
|
+
writeln(c15.dim(" no agents found (~/.agents/agents/ or .agents/agents/)"));
|
|
6792
|
+
writeln(c15.dim(" /agent <name> to activate \xB7 /agent off to deactivate"));
|
|
7401
6793
|
return;
|
|
7402
6794
|
}
|
|
7403
|
-
|
|
7404
|
-
|
|
6795
|
+
writeln();
|
|
6796
|
+
writeln(c15.dim(" agents:"));
|
|
7405
6797
|
for (const agent2 of agents.values()) {
|
|
7406
|
-
const modeTag = agent2.mode ?
|
|
7407
|
-
const srcTag = agent2.source === "local" ?
|
|
7408
|
-
const active = ctx.activeAgent === agent2.name ?
|
|
7409
|
-
|
|
7410
|
-
}
|
|
7411
|
-
|
|
7412
|
-
|
|
7413
|
-
|
|
6798
|
+
const modeTag = agent2.mode ? c15.dim(` [${agent2.mode}]`) : "";
|
|
6799
|
+
const srcTag = agent2.source === "local" ? c15.dim(" (local)") : c15.dim(" (global)");
|
|
6800
|
+
const active = ctx.activeAgent === agent2.name ? c15.cyan(" \u25C0 active") : "";
|
|
6801
|
+
writeln(` ${c15.magenta(`@${agent2.name}`.padEnd(26))} ${c15.dim(agent2.description)}${modeTag}${srcTag}${active}`);
|
|
6802
|
+
}
|
|
6803
|
+
writeln();
|
|
6804
|
+
writeln(c15.dim(" /agent <name> to activate \xB7 /agent off to deactivate"));
|
|
6805
|
+
writeln();
|
|
7414
6806
|
return;
|
|
7415
6807
|
}
|
|
7416
6808
|
if (raw.toLowerCase() === "off" || raw.toLowerCase() === "none") {
|
|
7417
6809
|
ctx.setActiveAgent(null);
|
|
7418
|
-
|
|
6810
|
+
writeln(`${PREFIX.info} ${c15.dim("active agent cleared")}`);
|
|
7419
6811
|
return;
|
|
7420
6812
|
}
|
|
7421
6813
|
const agent = agents.get(raw);
|
|
7422
6814
|
if (!agent) {
|
|
7423
|
-
|
|
6815
|
+
writeln(`${PREFIX.error} agent ${c15.cyan(raw)} not found`);
|
|
7424
6816
|
return;
|
|
7425
6817
|
}
|
|
7426
6818
|
ctx.setActiveAgent(raw, agent.systemPrompt);
|
|
7427
|
-
|
|
6819
|
+
writeln(`${PREFIX.success} active agent \u2192 ${c15.cyan(raw)} ${c15.dim("(instructions appended to system prompt)")}`);
|
|
7428
6820
|
}
|
|
7429
6821
|
|
|
7430
6822
|
// src/cli/commands-config.ts
|
|
7431
|
-
import * as
|
|
6823
|
+
import * as c16 from "yoctocolors";
|
|
7432
6824
|
function handleBooleanToggleCommand(opts) {
|
|
7433
6825
|
const mode = opts.args.trim().toLowerCase();
|
|
7434
6826
|
if (!mode) {
|
|
7435
6827
|
const nextValue = !opts.current;
|
|
7436
6828
|
opts.set(nextValue);
|
|
7437
|
-
|
|
6829
|
+
writeln(`${PREFIX.success} ${opts.label} ${nextValue ? c16.green("on") : c16.dim("off")}`);
|
|
7438
6830
|
return;
|
|
7439
6831
|
}
|
|
7440
6832
|
if (mode === "on") {
|
|
7441
6833
|
opts.set(true);
|
|
7442
|
-
|
|
6834
|
+
writeln(`${PREFIX.success} ${opts.label} ${c16.green("on")}`);
|
|
7443
6835
|
return;
|
|
7444
6836
|
}
|
|
7445
6837
|
if (mode === "off") {
|
|
7446
6838
|
opts.set(false);
|
|
7447
|
-
|
|
6839
|
+
writeln(`${PREFIX.success} ${opts.label} ${c16.dim("off")}`);
|
|
7448
6840
|
return;
|
|
7449
6841
|
}
|
|
7450
|
-
|
|
6842
|
+
writeln(`${PREFIX.error} usage: ${opts.usage}`);
|
|
7451
6843
|
}
|
|
7452
6844
|
function handleReasoningCommand(ctx, args) {
|
|
7453
6845
|
handleBooleanToggleCommand({
|
|
@@ -7471,105 +6863,105 @@ function handleContextCommand(ctx, args) {
|
|
|
7471
6863
|
const [subcommand, value] = args.trim().split(/\s+/, 2);
|
|
7472
6864
|
if (!subcommand) {
|
|
7473
6865
|
const capText = ctx.toolResultPayloadCapBytes <= 0 ? "off" : `${Math.round(ctx.toolResultPayloadCapBytes / 1024)}KB (${ctx.toolResultPayloadCapBytes} bytes)`;
|
|
7474
|
-
|
|
7475
|
-
|
|
7476
|
-
|
|
6866
|
+
writeln(`${PREFIX.info} pruning=${c16.cyan(ctx.pruningMode)} tool-result-cap=${c16.cyan(capText)}`);
|
|
6867
|
+
writeln(c16.dim(" usage: /context prune <off|balanced|aggressive>"));
|
|
6868
|
+
writeln(c16.dim(" /context cap <off|bytes|kb>"));
|
|
7477
6869
|
return;
|
|
7478
6870
|
}
|
|
7479
6871
|
if (subcommand === "prune") {
|
|
7480
6872
|
if (value === "off" || value === "balanced" || value === "aggressive") {
|
|
7481
6873
|
ctx.setPruningMode(value);
|
|
7482
|
-
|
|
6874
|
+
writeln(`${PREFIX.success} context pruning \u2192 ${c16.cyan(value)}`);
|
|
7483
6875
|
return;
|
|
7484
6876
|
}
|
|
7485
|
-
|
|
6877
|
+
writeln(`${PREFIX.error} usage: /context prune <off|balanced|aggressive>`);
|
|
7486
6878
|
return;
|
|
7487
6879
|
}
|
|
7488
6880
|
if (subcommand !== "cap") {
|
|
7489
|
-
|
|
6881
|
+
writeln(`${PREFIX.error} usage: /context <prune|cap> ...`);
|
|
7490
6882
|
return;
|
|
7491
6883
|
}
|
|
7492
6884
|
if (!value) {
|
|
7493
|
-
|
|
6885
|
+
writeln(`${PREFIX.error} usage: /context cap <off|bytes|kb>`);
|
|
7494
6886
|
return;
|
|
7495
6887
|
}
|
|
7496
6888
|
if (value === "off") {
|
|
7497
6889
|
ctx.setToolResultPayloadCapBytes(0);
|
|
7498
|
-
|
|
6890
|
+
writeln(`${PREFIX.success} tool-result payload cap disabled`);
|
|
7499
6891
|
return;
|
|
7500
6892
|
}
|
|
7501
6893
|
const capMatch = value.match(/^(\d+)(kb)?$/i);
|
|
7502
6894
|
if (!capMatch) {
|
|
7503
|
-
|
|
6895
|
+
writeln(`${PREFIX.error} invalid cap: ${c16.cyan(value)}`);
|
|
7504
6896
|
return;
|
|
7505
6897
|
}
|
|
7506
6898
|
const base = Number.parseInt(capMatch[1] ?? "", 10);
|
|
7507
6899
|
const capBytes = (capMatch[2] ?? "").toLowerCase() === "kb" ? base * 1024 : base;
|
|
7508
6900
|
if (!Number.isFinite(capBytes) || capBytes < 0) {
|
|
7509
|
-
|
|
6901
|
+
writeln(`${PREFIX.error} invalid cap: ${c16.cyan(value)}`);
|
|
7510
6902
|
return;
|
|
7511
6903
|
}
|
|
7512
6904
|
ctx.setToolResultPayloadCapBytes(capBytes);
|
|
7513
|
-
|
|
6905
|
+
writeln(`${PREFIX.success} tool-result payload cap \u2192 ${c16.cyan(`${capBytes} bytes`)}`);
|
|
7514
6906
|
}
|
|
7515
6907
|
function handleCacheCommand(ctx, args) {
|
|
7516
6908
|
const [subcommand, value] = args.trim().split(/\s+/, 2);
|
|
7517
6909
|
if (!subcommand) {
|
|
7518
|
-
const geminiCache = ctx.googleCachedContent ?
|
|
7519
|
-
|
|
7520
|
-
|
|
7521
|
-
|
|
7522
|
-
|
|
6910
|
+
const geminiCache = ctx.googleCachedContent ? c16.cyan(ctx.googleCachedContent) : "off";
|
|
6911
|
+
writeln(`${PREFIX.info} prompt-caching=${ctx.promptCachingEnabled ? c16.green("on") : c16.dim("off")} openai-retention=${c16.cyan(ctx.openaiPromptCacheRetention)} gemini-cache=${geminiCache}`);
|
|
6912
|
+
writeln(c16.dim(" usage: /cache <on|off>"));
|
|
6913
|
+
writeln(c16.dim(" /cache openai <in_memory|24h>"));
|
|
6914
|
+
writeln(c16.dim(" /cache gemini <off|cachedContents/...>"));
|
|
7523
6915
|
return;
|
|
7524
6916
|
}
|
|
7525
6917
|
if (subcommand === "on" || subcommand === "off") {
|
|
7526
6918
|
ctx.setPromptCachingEnabled(subcommand === "on");
|
|
7527
|
-
|
|
6919
|
+
writeln(`${PREFIX.success} prompt caching \u2192 ${subcommand === "on" ? c16.green("on") : c16.dim("off")}`);
|
|
7528
6920
|
return;
|
|
7529
6921
|
}
|
|
7530
6922
|
if (subcommand === "openai") {
|
|
7531
6923
|
if (value === "in_memory" || value === "24h") {
|
|
7532
6924
|
ctx.setOpenAIPromptCacheRetention(value);
|
|
7533
|
-
|
|
6925
|
+
writeln(`${PREFIX.success} openai prompt cache retention \u2192 ${c16.cyan(value)}`);
|
|
7534
6926
|
return;
|
|
7535
6927
|
}
|
|
7536
|
-
|
|
6928
|
+
writeln(`${PREFIX.error} usage: /cache openai <in_memory|24h>`);
|
|
7537
6929
|
return;
|
|
7538
6930
|
}
|
|
7539
6931
|
if (subcommand !== "gemini") {
|
|
7540
|
-
|
|
6932
|
+
writeln(`${PREFIX.error} usage: /cache <on|off|openai|gemini> ...`);
|
|
7541
6933
|
return;
|
|
7542
6934
|
}
|
|
7543
6935
|
if (!value) {
|
|
7544
|
-
|
|
6936
|
+
writeln(`${PREFIX.error} usage: /cache gemini <off|cachedContents/...>`);
|
|
7545
6937
|
return;
|
|
7546
6938
|
}
|
|
7547
6939
|
if (value === "off") {
|
|
7548
6940
|
ctx.setGoogleCachedContent(null);
|
|
7549
|
-
|
|
6941
|
+
writeln(`${PREFIX.success} gemini cached content \u2192 ${c16.dim("off")}`);
|
|
7550
6942
|
return;
|
|
7551
6943
|
}
|
|
7552
6944
|
ctx.setGoogleCachedContent(value);
|
|
7553
|
-
|
|
6945
|
+
writeln(`${PREFIX.success} gemini cached content \u2192 ${c16.cyan(value)}`);
|
|
7554
6946
|
}
|
|
7555
6947
|
|
|
7556
6948
|
// src/cli/commands-help.ts
|
|
7557
|
-
import * as
|
|
6949
|
+
import * as c17 from "yoctocolors";
|
|
7558
6950
|
function renderEntries(entries) {
|
|
7559
6951
|
for (const [label, description] of entries) {
|
|
7560
|
-
|
|
6952
|
+
writeln(` ${c17.cyan(label.padEnd(28))} ${c17.dim(description)}`);
|
|
7561
6953
|
}
|
|
7562
6954
|
}
|
|
7563
6955
|
function renderHelpCommand(ctx, custom) {
|
|
7564
|
-
|
|
7565
|
-
|
|
6956
|
+
writeln();
|
|
6957
|
+
writeln(` ${c17.dim("session")}`);
|
|
7566
6958
|
renderEntries([
|
|
7567
6959
|
["/new", "start a fresh session"],
|
|
7568
6960
|
["/undo", "remove the last conversation turn"],
|
|
7569
6961
|
["/exit", "quit"]
|
|
7570
6962
|
]);
|
|
7571
|
-
|
|
7572
|
-
|
|
6963
|
+
writeln();
|
|
6964
|
+
writeln(` ${c17.dim("model + context")}`);
|
|
7573
6965
|
renderEntries([
|
|
7574
6966
|
["/model [id]", "list or switch models"],
|
|
7575
6967
|
["/reasoning [on|off]", "toggle reasoning display"],
|
|
@@ -7584,8 +6976,8 @@ function renderHelpCommand(ctx, custom) {
|
|
|
7584
6976
|
["/logout <provider>", "clear OAuth tokens"],
|
|
7585
6977
|
["/help", "show this help"]
|
|
7586
6978
|
]);
|
|
7587
|
-
|
|
7588
|
-
|
|
6979
|
+
writeln();
|
|
6980
|
+
writeln(` ${c17.dim("prompt")}`);
|
|
7589
6981
|
renderEntries([
|
|
7590
6982
|
["ask normally", "send a prompt to the current agent"],
|
|
7591
6983
|
["!cmd", "run a shell command and keep the result in context"],
|
|
@@ -7593,63 +6985,63 @@ function renderHelpCommand(ctx, custom) {
|
|
|
7593
6985
|
["@skill", "inject a skill into the prompt (Tab to complete)"]
|
|
7594
6986
|
]);
|
|
7595
6987
|
if (custom.size > 0) {
|
|
7596
|
-
|
|
7597
|
-
|
|
6988
|
+
writeln();
|
|
6989
|
+
writeln(` ${c17.dim("custom commands")}`);
|
|
7598
6990
|
for (const cmd of custom.values()) {
|
|
7599
|
-
const source = cmd.source === "local" ?
|
|
7600
|
-
|
|
6991
|
+
const source = cmd.source === "local" ? c17.dim("local") : c17.dim("global");
|
|
6992
|
+
writeln(` ${c17.green(`/${cmd.name}`.padEnd(28))} ${c17.dim(cmd.description)} ${c17.dim("\xB7")} ${source}`);
|
|
7601
6993
|
}
|
|
7602
6994
|
}
|
|
7603
6995
|
const agents = loadAgents(ctx.cwd);
|
|
7604
6996
|
if (agents.size > 0) {
|
|
7605
|
-
|
|
7606
|
-
|
|
6997
|
+
writeln();
|
|
6998
|
+
writeln(` ${c17.dim("agents")}`);
|
|
7607
6999
|
for (const agent of agents.values()) {
|
|
7608
|
-
const mode = agent.mode ?
|
|
7609
|
-
const source = agent.source === "local" ?
|
|
7610
|
-
|
|
7000
|
+
const mode = agent.mode ? c17.dim(agent.mode) : c17.dim("agent");
|
|
7001
|
+
const source = agent.source === "local" ? c17.dim("local") : c17.dim("global");
|
|
7002
|
+
writeln(` ${c17.magenta(agent.name.padEnd(28))} ${c17.dim(agent.description)} ${c17.dim("\xB7")} ${mode} ${c17.dim("\xB7")} ${source}`);
|
|
7611
7003
|
}
|
|
7612
7004
|
}
|
|
7613
7005
|
const skills = loadSkillsIndex(ctx.cwd);
|
|
7614
7006
|
if (skills.size > 0) {
|
|
7615
|
-
|
|
7616
|
-
|
|
7007
|
+
writeln();
|
|
7008
|
+
writeln(` ${c17.dim("skills")}`);
|
|
7617
7009
|
for (const skill of skills.values()) {
|
|
7618
|
-
const source = skill.source === "local" ?
|
|
7619
|
-
|
|
7010
|
+
const source = skill.source === "local" ? c17.dim("local") : c17.dim("global");
|
|
7011
|
+
writeln(` ${c17.yellow(`@${skill.name}`.padEnd(28))} ${c17.dim(skill.description)} ${c17.dim("\xB7")} ${source}`);
|
|
7620
7012
|
}
|
|
7621
7013
|
}
|
|
7622
|
-
|
|
7623
|
-
|
|
7624
|
-
|
|
7014
|
+
writeln();
|
|
7015
|
+
writeln(` ${c17.dim("keys")} ${c17.dim("esc")} cancel response ${c17.dim("\xB7")} ${c17.dim("ctrl+c / ctrl+d")} exit ${c17.dim("\xB7")} ${c17.dim("ctrl+r")} history search ${c17.dim("\xB7")} ${c17.dim("\u2191\u2193")} history`);
|
|
7016
|
+
writeln();
|
|
7625
7017
|
}
|
|
7626
7018
|
|
|
7627
7019
|
// src/cli/commands-login.ts
|
|
7628
|
-
import * as
|
|
7020
|
+
import * as c18 from "yoctocolors";
|
|
7629
7021
|
function renderLoginHelp() {
|
|
7630
|
-
|
|
7631
|
-
|
|
7632
|
-
|
|
7633
|
-
|
|
7634
|
-
|
|
7635
|
-
|
|
7636
|
-
|
|
7022
|
+
writeln();
|
|
7023
|
+
writeln(` ${c18.dim("usage:")}`);
|
|
7024
|
+
writeln(` /login ${c18.dim("show login status")}`);
|
|
7025
|
+
writeln(` /login <provider> ${c18.dim("login via OAuth")}`);
|
|
7026
|
+
writeln(` /logout <provider> ${c18.dim("clear saved tokens")}`);
|
|
7027
|
+
writeln();
|
|
7028
|
+
writeln(` ${c18.dim("providers:")}`);
|
|
7637
7029
|
for (const p of getOAuthProviders()) {
|
|
7638
|
-
const status = isLoggedIn(p.id) ?
|
|
7639
|
-
|
|
7030
|
+
const status = isLoggedIn(p.id) ? c18.green("logged in") : c18.dim("not logged in");
|
|
7031
|
+
writeln(` ${c18.cyan(p.id.padEnd(20))} ${p.name} ${c18.dim("\xB7")} ${status}`);
|
|
7640
7032
|
}
|
|
7641
|
-
|
|
7033
|
+
writeln();
|
|
7642
7034
|
}
|
|
7643
7035
|
function renderStatus() {
|
|
7644
7036
|
const loggedIn = listLoggedInProviders();
|
|
7645
7037
|
if (loggedIn.length === 0) {
|
|
7646
|
-
|
|
7038
|
+
writeln(`${PREFIX.info} ${c18.dim("no OAuth logins \u2014 use")} /login <provider>`);
|
|
7647
7039
|
return;
|
|
7648
7040
|
}
|
|
7649
7041
|
for (const id of loggedIn) {
|
|
7650
7042
|
const provider = getOAuthProvider(id);
|
|
7651
7043
|
const name = provider?.name ?? id;
|
|
7652
|
-
|
|
7044
|
+
writeln(`${PREFIX.success} ${c18.cyan(id)} ${c18.dim(name)}`);
|
|
7653
7045
|
}
|
|
7654
7046
|
}
|
|
7655
7047
|
async function handleLoginCommand(ctx, args) {
|
|
@@ -7664,13 +7056,13 @@ async function handleLoginCommand(ctx, args) {
|
|
|
7664
7056
|
}
|
|
7665
7057
|
const provider = getOAuthProvider(providerId);
|
|
7666
7058
|
if (!provider) {
|
|
7667
|
-
|
|
7059
|
+
writeln(`${PREFIX.error} unknown provider "${providerId}" \u2014 available: ${getOAuthProviders().map((p) => p.id).join(", ")}`);
|
|
7668
7060
|
return;
|
|
7669
7061
|
}
|
|
7670
7062
|
if (isLoggedIn(providerId)) {
|
|
7671
7063
|
const token = await getAccessToken(providerId);
|
|
7672
7064
|
if (token) {
|
|
7673
|
-
|
|
7065
|
+
writeln(`${PREFIX.success} already logged in to ${c18.cyan(provider.name)}`);
|
|
7674
7066
|
return;
|
|
7675
7067
|
}
|
|
7676
7068
|
}
|
|
@@ -7679,43 +7071,43 @@ async function handleLoginCommand(ctx, args) {
|
|
|
7679
7071
|
await login(providerId, {
|
|
7680
7072
|
onOpenUrl: (url, instructions) => {
|
|
7681
7073
|
ctx.stopSpinner();
|
|
7682
|
-
|
|
7683
|
-
|
|
7684
|
-
|
|
7685
|
-
|
|
7074
|
+
writeln(`${PREFIX.info} ${instructions}`);
|
|
7075
|
+
writeln();
|
|
7076
|
+
writeln(` ${c18.cyan(url)}`);
|
|
7077
|
+
writeln();
|
|
7686
7078
|
const open = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
7687
7079
|
Bun.spawn([open, url], { stdout: "ignore", stderr: "ignore" });
|
|
7688
7080
|
ctx.startSpinner("waiting for browser callback");
|
|
7689
7081
|
},
|
|
7690
7082
|
onProgress: (msg) => {
|
|
7691
7083
|
ctx.stopSpinner();
|
|
7692
|
-
|
|
7084
|
+
writeln(`${PREFIX.info} ${c18.dim(msg)}`);
|
|
7693
7085
|
ctx.startSpinner("exchanging tokens");
|
|
7694
7086
|
}
|
|
7695
7087
|
});
|
|
7696
7088
|
ctx.stopSpinner();
|
|
7697
|
-
|
|
7089
|
+
writeln(`${PREFIX.success} logged in to ${c18.cyan(provider.name)}`);
|
|
7698
7090
|
} catch (err) {
|
|
7699
7091
|
ctx.stopSpinner();
|
|
7700
|
-
|
|
7092
|
+
writeln(`${PREFIX.error} login failed: ${err.message}`);
|
|
7701
7093
|
}
|
|
7702
7094
|
}
|
|
7703
7095
|
function handleLogoutCommand(_ctx, args) {
|
|
7704
7096
|
const providerId = args.trim().toLowerCase();
|
|
7705
7097
|
if (!providerId) {
|
|
7706
|
-
|
|
7098
|
+
writeln(`${PREFIX.error} usage: /logout <provider>`);
|
|
7707
7099
|
return;
|
|
7708
7100
|
}
|
|
7709
7101
|
if (!isLoggedIn(providerId)) {
|
|
7710
|
-
|
|
7102
|
+
writeln(`${PREFIX.info} ${c18.dim("not logged in to")} ${providerId}`);
|
|
7711
7103
|
return;
|
|
7712
7104
|
}
|
|
7713
7105
|
logout(providerId);
|
|
7714
|
-
|
|
7106
|
+
writeln(`${PREFIX.success} logged out of ${c18.cyan(providerId)}`);
|
|
7715
7107
|
}
|
|
7716
7108
|
|
|
7717
7109
|
// src/cli/commands-mcp.ts
|
|
7718
|
-
import * as
|
|
7110
|
+
import * as c19 from "yoctocolors";
|
|
7719
7111
|
async function handleMcpCommand(ctx, args) {
|
|
7720
7112
|
const parts = args.trim().split(/\s+/);
|
|
7721
7113
|
const sub = parts[0] ?? "list";
|
|
@@ -7723,28 +7115,28 @@ async function handleMcpCommand(ctx, args) {
|
|
|
7723
7115
|
case "list": {
|
|
7724
7116
|
const servers = listMcpServers();
|
|
7725
7117
|
if (servers.length === 0) {
|
|
7726
|
-
|
|
7727
|
-
|
|
7118
|
+
writeln(c19.dim(" no MCP servers configured"));
|
|
7119
|
+
writeln(c19.dim(" /mcp add <name> http <url> \xB7 /mcp add <name> stdio <cmd> [args...]"));
|
|
7728
7120
|
return;
|
|
7729
7121
|
}
|
|
7730
|
-
|
|
7122
|
+
writeln();
|
|
7731
7123
|
for (const s of servers) {
|
|
7732
|
-
const detail = s.url ?
|
|
7733
|
-
|
|
7124
|
+
const detail = s.url ? c19.dim(` ${s.url}`) : s.command ? c19.dim(` ${s.command}`) : "";
|
|
7125
|
+
writeln(` ${c19.yellow("\u2699")} ${c19.bold(s.name)} ${c19.dim(s.transport)}${detail}`);
|
|
7734
7126
|
}
|
|
7735
7127
|
return;
|
|
7736
7128
|
}
|
|
7737
7129
|
case "add": {
|
|
7738
7130
|
const [, name, transport, ...rest] = parts;
|
|
7739
7131
|
if (!name || !transport || rest.length === 0) {
|
|
7740
|
-
|
|
7741
|
-
|
|
7132
|
+
writeln(`${PREFIX.error} usage: /mcp add <name> http <url>`);
|
|
7133
|
+
writeln(`${PREFIX.error} /mcp add <name> stdio <cmd> [args...]`);
|
|
7742
7134
|
return;
|
|
7743
7135
|
}
|
|
7744
7136
|
if (transport === "http") {
|
|
7745
7137
|
const url = rest[0];
|
|
7746
7138
|
if (!url) {
|
|
7747
|
-
|
|
7139
|
+
writeln(`${PREFIX.error} usage: /mcp add <name> http <url>`);
|
|
7748
7140
|
return;
|
|
7749
7141
|
}
|
|
7750
7142
|
upsertMcpServer({
|
|
@@ -7758,7 +7150,7 @@ async function handleMcpCommand(ctx, args) {
|
|
|
7758
7150
|
} else if (transport === "stdio") {
|
|
7759
7151
|
const [command, ...cmdArgs] = rest;
|
|
7760
7152
|
if (!command) {
|
|
7761
|
-
|
|
7153
|
+
writeln(`${PREFIX.error} usage: /mcp add <name> stdio <cmd> [args...]`);
|
|
7762
7154
|
return;
|
|
7763
7155
|
}
|
|
7764
7156
|
upsertMcpServer({
|
|
@@ -7770,14 +7162,14 @@ async function handleMcpCommand(ctx, args) {
|
|
|
7770
7162
|
env: null
|
|
7771
7163
|
});
|
|
7772
7164
|
} else {
|
|
7773
|
-
|
|
7165
|
+
writeln(`${PREFIX.error} unknown transport: ${transport} (use http or stdio)`);
|
|
7774
7166
|
return;
|
|
7775
7167
|
}
|
|
7776
7168
|
try {
|
|
7777
7169
|
await ctx.connectMcpServer(name);
|
|
7778
|
-
|
|
7170
|
+
writeln(`${PREFIX.success} mcp server ${c19.cyan(name)} added and connected`);
|
|
7779
7171
|
} catch (e) {
|
|
7780
|
-
|
|
7172
|
+
writeln(`${PREFIX.success} mcp server ${c19.cyan(name)} saved ${c19.dim(`(connection failed: ${String(e)})`)}`);
|
|
7781
7173
|
}
|
|
7782
7174
|
return;
|
|
7783
7175
|
}
|
|
@@ -7785,21 +7177,21 @@ async function handleMcpCommand(ctx, args) {
|
|
|
7785
7177
|
case "rm": {
|
|
7786
7178
|
const [, name] = parts;
|
|
7787
7179
|
if (!name) {
|
|
7788
|
-
|
|
7180
|
+
writeln(`${PREFIX.error} usage: /mcp remove <name>`);
|
|
7789
7181
|
return;
|
|
7790
7182
|
}
|
|
7791
7183
|
deleteMcpServer(name);
|
|
7792
|
-
|
|
7184
|
+
writeln(`${PREFIX.success} mcp server ${c19.cyan(name)} removed`);
|
|
7793
7185
|
return;
|
|
7794
7186
|
}
|
|
7795
7187
|
default:
|
|
7796
|
-
|
|
7797
|
-
|
|
7188
|
+
writeln(`${PREFIX.error} unknown: /mcp ${sub}`);
|
|
7189
|
+
writeln(c19.dim(" subcommands: list \xB7 add \xB7 remove"));
|
|
7798
7190
|
}
|
|
7799
7191
|
}
|
|
7800
7192
|
|
|
7801
7193
|
// src/cli/commands-model.ts
|
|
7802
|
-
import * as
|
|
7194
|
+
import * as c20 from "yoctocolors";
|
|
7803
7195
|
var THINKING_EFFORTS = ["low", "medium", "high", "xhigh"];
|
|
7804
7196
|
function parseThinkingEffort(value) {
|
|
7805
7197
|
return THINKING_EFFORTS.includes(value) ? value : null;
|
|
@@ -7818,21 +7210,21 @@ function renderModelUpdatedMessage(ctx, modelId, effortArg) {
|
|
|
7818
7210
|
if (effortArg) {
|
|
7819
7211
|
if (effortArg === "off") {
|
|
7820
7212
|
ctx.setThinkingEffort(null);
|
|
7821
|
-
|
|
7213
|
+
writeln(`${PREFIX.success} model \u2192 ${c20.cyan(modelId)} ${c20.dim("(thinking disabled)")}`);
|
|
7822
7214
|
return;
|
|
7823
7215
|
}
|
|
7824
7216
|
const effort = parseThinkingEffort(effortArg);
|
|
7825
7217
|
if (effort) {
|
|
7826
7218
|
ctx.setThinkingEffort(effort);
|
|
7827
|
-
|
|
7219
|
+
writeln(`${PREFIX.success} model \u2192 ${c20.cyan(modelId)} ${c20.dim(`(\u2726 ${effort})`)}`);
|
|
7828
7220
|
return;
|
|
7829
7221
|
}
|
|
7830
|
-
|
|
7831
|
-
|
|
7222
|
+
writeln(`${PREFIX.success} model \u2192 ${c20.cyan(modelId)}`);
|
|
7223
|
+
writeln(`${PREFIX.error} unknown effort level ${c20.cyan(effortArg)} (use low, medium, high, xhigh, off)`);
|
|
7832
7224
|
return;
|
|
7833
7225
|
}
|
|
7834
|
-
const effortTag = ctx.thinkingEffort ?
|
|
7835
|
-
|
|
7226
|
+
const effortTag = ctx.thinkingEffort ? c20.dim(` (\u2726 ${ctx.thinkingEffort})`) : "";
|
|
7227
|
+
writeln(`${PREFIX.success} model \u2192 ${c20.cyan(modelId)}${effortTag}`);
|
|
7836
7228
|
}
|
|
7837
7229
|
async function handleModelSet(ctx, args) {
|
|
7838
7230
|
const parts = args.trim().split(/\s+/).filter(Boolean);
|
|
@@ -7845,7 +7237,7 @@ async function handleModelSet(ctx, args) {
|
|
|
7845
7237
|
const snapshot = await fetchAvailableModels();
|
|
7846
7238
|
const match = findModelIdByAlias(idArg, snapshot.models.map((model) => model.id));
|
|
7847
7239
|
if (!match) {
|
|
7848
|
-
|
|
7240
|
+
writeln(`${PREFIX.error} unknown model ${c20.cyan(idArg)} ${c20.dim("\u2014 run /models for the full list")}`);
|
|
7849
7241
|
return;
|
|
7850
7242
|
}
|
|
7851
7243
|
modelId = match;
|
|
@@ -7856,30 +7248,30 @@ async function handleModelSet(ctx, args) {
|
|
|
7856
7248
|
function handleModelEffort(ctx, effortArg) {
|
|
7857
7249
|
if (effortArg === "off") {
|
|
7858
7250
|
ctx.setThinkingEffort(null);
|
|
7859
|
-
|
|
7251
|
+
writeln(`${PREFIX.success} thinking effort disabled`);
|
|
7860
7252
|
return;
|
|
7861
7253
|
}
|
|
7862
7254
|
const effort = parseThinkingEffort(effortArg);
|
|
7863
7255
|
if (!effort) {
|
|
7864
|
-
|
|
7256
|
+
writeln(`${PREFIX.error} usage: /model effort <low|medium|high|xhigh|off>`);
|
|
7865
7257
|
return;
|
|
7866
7258
|
}
|
|
7867
7259
|
ctx.setThinkingEffort(effort);
|
|
7868
|
-
|
|
7260
|
+
writeln(`${PREFIX.success} thinking effort \u2192 ${c20.cyan(effort)}`);
|
|
7869
7261
|
}
|
|
7870
7262
|
async function renderModelList(ctx) {
|
|
7871
7263
|
ctx.startSpinner("fetching models");
|
|
7872
7264
|
const snapshot = await fetchAvailableModels();
|
|
7873
7265
|
ctx.stopSpinner();
|
|
7874
7266
|
if (snapshot.models.length === 0) {
|
|
7875
|
-
|
|
7876
|
-
|
|
7267
|
+
writeln(`${PREFIX.error} No models found. Check your API keys or Ollama connection.`);
|
|
7268
|
+
writeln(c20.dim(" Set OPENCODE_API_KEY for Zen, or start Ollama for local models."));
|
|
7877
7269
|
return;
|
|
7878
7270
|
}
|
|
7879
7271
|
if (snapshot.stale) {
|
|
7880
7272
|
const lastSync = snapshot.lastSyncAt ? new Date(snapshot.lastSyncAt).toLocaleString() : "never";
|
|
7881
7273
|
const refreshTag = snapshot.refreshing ? " (refreshing in background)" : "";
|
|
7882
|
-
|
|
7274
|
+
writeln(c20.dim(` model metadata is stale (last sync: ${lastSync})${refreshTag}`));
|
|
7883
7275
|
}
|
|
7884
7276
|
const modelsByProvider = new Map;
|
|
7885
7277
|
for (const model of snapshot.models) {
|
|
@@ -7890,22 +7282,22 @@ async function renderModelList(ctx) {
|
|
|
7890
7282
|
modelsByProvider.set(model.provider, [model]);
|
|
7891
7283
|
}
|
|
7892
7284
|
}
|
|
7893
|
-
|
|
7285
|
+
writeln();
|
|
7894
7286
|
for (const [provider, providerModels] of modelsByProvider) {
|
|
7895
|
-
|
|
7287
|
+
writeln(c20.bold(` ${provider}`));
|
|
7896
7288
|
for (const model of providerModels) {
|
|
7897
7289
|
const isCurrent = ctx.currentModel === model.id;
|
|
7898
|
-
const freeTag = model.free ?
|
|
7899
|
-
const contextTag = model.context ?
|
|
7900
|
-
const effortTag = isCurrent && ctx.thinkingEffort ?
|
|
7901
|
-
const currentTag = isCurrent ?
|
|
7902
|
-
|
|
7903
|
-
|
|
7290
|
+
const freeTag = model.free ? c20.green(" free") : "";
|
|
7291
|
+
const contextTag = model.context ? c20.dim(` ${Math.round(model.context / 1000)}k`) : "";
|
|
7292
|
+
const effortTag = isCurrent && ctx.thinkingEffort ? c20.dim(` \u2726 ${ctx.thinkingEffort}`) : "";
|
|
7293
|
+
const currentTag = isCurrent ? c20.cyan(" \u25C0") : "";
|
|
7294
|
+
writeln(` ${c20.dim("\xB7")} ${model.displayName}${freeTag}${contextTag}${currentTag}${effortTag}`);
|
|
7295
|
+
writeln(` ${c20.dim(model.id)}`);
|
|
7904
7296
|
}
|
|
7905
7297
|
}
|
|
7906
|
-
|
|
7907
|
-
|
|
7908
|
-
|
|
7298
|
+
writeln();
|
|
7299
|
+
writeln(c20.dim(" /model <id> to switch \xB7 e.g. /model zen/claude-sonnet-4-6"));
|
|
7300
|
+
writeln(c20.dim(" /model effort <low|medium|high|xhigh|off> to set thinking effort"));
|
|
7909
7301
|
}
|
|
7910
7302
|
async function handleModelCommand(ctx, args) {
|
|
7911
7303
|
const parts = args.trim().split(/\s+/).filter(Boolean);
|
|
@@ -7926,9 +7318,9 @@ async function handleUndo(ctx) {
|
|
|
7926
7318
|
try {
|
|
7927
7319
|
const ok = await ctx.undoLastTurn();
|
|
7928
7320
|
if (ok) {
|
|
7929
|
-
|
|
7321
|
+
writeln(`${PREFIX.success} ${c21.dim("last conversation turn removed")}`);
|
|
7930
7322
|
} else {
|
|
7931
|
-
|
|
7323
|
+
writeln(`${PREFIX.info} ${c21.dim("nothing to undo")}`);
|
|
7932
7324
|
}
|
|
7933
7325
|
} finally {
|
|
7934
7326
|
ctx.stopSpinner();
|
|
@@ -7941,11 +7333,11 @@ function handleNew(ctx) {
|
|
|
7941
7333
|
}
|
|
7942
7334
|
async function handleCustomCommand(cmd, args, ctx) {
|
|
7943
7335
|
const prompt = await expandTemplate(cmd.template, args, ctx.cwd);
|
|
7944
|
-
const label =
|
|
7336
|
+
const label = c21.cyan(cmd.name);
|
|
7945
7337
|
const srcPath = cmd.source === "local" ? `.agents/commands/${cmd.name}.md` : `~/.agents/commands/${cmd.name}.md`;
|
|
7946
|
-
const src =
|
|
7947
|
-
|
|
7948
|
-
|
|
7338
|
+
const src = c21.dim(`[${srcPath}]`);
|
|
7339
|
+
writeln(`${PREFIX.info} ${label} ${src}`);
|
|
7340
|
+
writeln();
|
|
7949
7341
|
const fork = cmd.context === "fork" || cmd.subtask === true;
|
|
7950
7342
|
if (!fork) {
|
|
7951
7343
|
return { type: "inject-user-message", text: prompt };
|
|
@@ -7957,8 +7349,8 @@ async function handleCustomCommand(cmd, args, ctx) {
|
|
|
7957
7349
|
try {
|
|
7958
7350
|
ctx.startSpinner("subagent");
|
|
7959
7351
|
const output = await ctx.runSubagent(prompt, cmd.agent, cmd.model, abortController.signal);
|
|
7960
|
-
|
|
7961
|
-
|
|
7352
|
+
write(output.result);
|
|
7353
|
+
writeln();
|
|
7962
7354
|
return {
|
|
7963
7355
|
type: "inject-user-message",
|
|
7964
7356
|
text: `/${cmd.name} output:
|
|
@@ -7971,7 +7363,7 @@ ${output.result}
|
|
|
7971
7363
|
if (isAbortError(e)) {
|
|
7972
7364
|
return { type: "handled" };
|
|
7973
7365
|
}
|
|
7974
|
-
|
|
7366
|
+
writeln(`${PREFIX.error} /${cmd.name} failed: ${String(e)}`);
|
|
7975
7367
|
return { type: "handled" };
|
|
7976
7368
|
} finally {
|
|
7977
7369
|
stopWatcher();
|
|
@@ -8028,7 +7420,7 @@ async function handleCommand(command, args, ctx) {
|
|
|
8028
7420
|
case "q":
|
|
8029
7421
|
return { type: "exit" };
|
|
8030
7422
|
default: {
|
|
8031
|
-
|
|
7423
|
+
writeln(`${PREFIX.error} unknown: /${command} ${c21.dim("\u2014 /help for commands")}`);
|
|
8032
7424
|
return { type: "unknown", command };
|
|
8033
7425
|
}
|
|
8034
7426
|
}
|
|
@@ -8124,7 +7516,7 @@ async function runInputLoop(opts) {
|
|
|
8124
7516
|
}
|
|
8125
7517
|
switch (input.type) {
|
|
8126
7518
|
case "eof":
|
|
8127
|
-
reporter.writeText(
|
|
7519
|
+
reporter.writeText(c22.dim("Goodbye."));
|
|
8128
7520
|
return;
|
|
8129
7521
|
case "interrupt":
|
|
8130
7522
|
gitBranchCache.refreshInBackground();
|
|
@@ -8132,7 +7524,7 @@ async function runInputLoop(opts) {
|
|
|
8132
7524
|
case "command": {
|
|
8133
7525
|
const result = await handleCommand(input.command, input.args, cmdCtx);
|
|
8134
7526
|
if (result.type === "exit") {
|
|
8135
|
-
reporter.writeText(
|
|
7527
|
+
reporter.writeText(c22.dim("Goodbye."));
|
|
8136
7528
|
return;
|
|
8137
7529
|
}
|
|
8138
7530
|
if (result.type === "inject-user-message") {
|
|
@@ -8150,13 +7542,7 @@ async function runInputLoop(opts) {
|
|
|
8150
7542
|
const result = await runShellCommand({
|
|
8151
7543
|
command: input.command,
|
|
8152
7544
|
timeout: 30000,
|
|
8153
|
-
cwd
|
|
8154
|
-
...cmdCtx.verboseOutput ? {
|
|
8155
|
-
onOutput: (chunk) => {
|
|
8156
|
-
reporter.streamChunk(chunk);
|
|
8157
|
-
return true;
|
|
8158
|
-
}
|
|
8159
|
-
} : {}
|
|
7545
|
+
cwd
|
|
8160
7546
|
});
|
|
8161
7547
|
renderToolResult("shell", result, false, {
|
|
8162
7548
|
verboseOutput: cmdCtx.verboseOutput
|
|
@@ -8211,8 +7597,8 @@ async function resolvePromptInput(promptArg, opts) {
|
|
|
8211
7597
|
|
|
8212
7598
|
// src/cli/structured-output.ts
|
|
8213
7599
|
import { createTwoFilesPatch } from "diff";
|
|
8214
|
-
function writeJsonLine(
|
|
8215
|
-
|
|
7600
|
+
function writeJsonLine(write2, payload) {
|
|
7601
|
+
write2(`${JSON.stringify(payload)}
|
|
8216
7602
|
`);
|
|
8217
7603
|
}
|
|
8218
7604
|
|
|
@@ -8253,7 +7639,7 @@ async function main() {
|
|
|
8253
7639
|
printHelp();
|
|
8254
7640
|
process.exit(0);
|
|
8255
7641
|
}
|
|
8256
|
-
if (args.
|
|
7642
|
+
if (args.list) {
|
|
8257
7643
|
printSessionList();
|
|
8258
7644
|
process.exit(0);
|
|
8259
7645
|
}
|
|
@@ -8268,7 +7654,7 @@ async function main() {
|
|
|
8268
7654
|
if (last) {
|
|
8269
7655
|
sessionId = last.id;
|
|
8270
7656
|
} else {
|
|
8271
|
-
|
|
7657
|
+
writeln(c23.dim("No previous session found, starting fresh."));
|
|
8272
7658
|
}
|
|
8273
7659
|
} else if (args.sessionId) {
|
|
8274
7660
|
sessionId = args.sessionId;
|
|
@@ -8335,7 +7721,7 @@ async function main() {
|
|
|
8335
7721
|
const { text: resolvedText, images: refImages } = await resolveFileRefs(prompt, args.cwd);
|
|
8336
7722
|
await runner.processUserInput(resolvedText, refImages);
|
|
8337
7723
|
const { totalIn, totalOut } = runner.getStatusInfo();
|
|
8338
|
-
|
|
7724
|
+
writeln(`${G.info} ${c23.dim(`${totalIn.toLocaleString()} in / ${totalOut.toLocaleString()} out tokens`)}`);
|
|
8339
7725
|
return;
|
|
8340
7726
|
}
|
|
8341
7727
|
await runInputLoop({
|