mini-coder 0.0.22 → 0.0.23
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 +937 -1587
- package/package.json +2 -3
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;
|
|
@@ -1437,28 +1376,23 @@ function renderShellResult(result, opts) {
|
|
|
1437
1376
|
if (!r || typeof r.stdout !== "string" || typeof r.stderr !== "string") {
|
|
1438
1377
|
return false;
|
|
1439
1378
|
}
|
|
1440
|
-
const streamedOutput = r.streamedOutput === true;
|
|
1441
1379
|
const verboseOutput = opts?.verboseOutput === true;
|
|
1442
1380
|
const stdoutLines = countShellLines(r.stdout);
|
|
1443
1381
|
const stderrLines = countShellLines(r.stderr);
|
|
1444
1382
|
const stdoutSingleLine = getSingleShellLine(r.stdout);
|
|
1445
|
-
const badge = r.timedOut ?
|
|
1383
|
+
const badge = r.timedOut ? c4.yellow("timeout") : r.success ? c4.green("done") : c4.red("error");
|
|
1446
1384
|
const parts = buildShellSummaryParts({
|
|
1447
1385
|
exitCode: r.exitCode,
|
|
1448
1386
|
stdoutLines,
|
|
1449
1387
|
stderrLines,
|
|
1450
1388
|
stdoutSingleLine,
|
|
1451
|
-
streamedOutput,
|
|
1452
1389
|
verboseOutput
|
|
1453
1390
|
});
|
|
1454
|
-
|
|
1455
|
-
if (streamedOutput) {
|
|
1456
|
-
return true;
|
|
1457
|
-
}
|
|
1391
|
+
writeln(` ${badge} ${c4.dim(parts.join(" \xB7 "))}`);
|
|
1458
1392
|
writePreviewLines({
|
|
1459
1393
|
label: "stderr",
|
|
1460
1394
|
value: r.stderr,
|
|
1461
|
-
lineColor:
|
|
1395
|
+
lineColor: c4.red,
|
|
1462
1396
|
maxLines: verboseOutput ? Number.POSITIVE_INFINITY : 10
|
|
1463
1397
|
});
|
|
1464
1398
|
if (shouldPreviewShellStdout({
|
|
@@ -1470,7 +1404,7 @@ function renderShellResult(result, opts) {
|
|
|
1470
1404
|
writePreviewLines({
|
|
1471
1405
|
label: "stdout",
|
|
1472
1406
|
value: r.stdout,
|
|
1473
|
-
lineColor:
|
|
1407
|
+
lineColor: c4.dim,
|
|
1474
1408
|
maxLines: verboseOutput ? Number.POSITIVE_INFINITY : 20
|
|
1475
1409
|
});
|
|
1476
1410
|
}
|
|
@@ -1480,8 +1414,8 @@ function renderSubagentResult(result, _opts) {
|
|
|
1480
1414
|
const r = result;
|
|
1481
1415
|
if (!r || typeof r !== "object")
|
|
1482
1416
|
return false;
|
|
1483
|
-
const label = r.agentName ? ` ${
|
|
1484
|
-
|
|
1417
|
+
const label = r.agentName ? ` ${c4.dim(c4.cyan(`[@${r.agentName}]`))}` : "";
|
|
1418
|
+
writeln(` ${G.agent}${label} ${c4.dim(`subagent done (${r.inputTokens ?? 0}in / ${r.outputTokens ?? 0}out tokens)`)}`);
|
|
1485
1419
|
return true;
|
|
1486
1420
|
}
|
|
1487
1421
|
function buildSkillDescriptionPart(description, verboseOutput = false) {
|
|
@@ -1489,21 +1423,21 @@ function buildSkillDescriptionPart(description, verboseOutput = false) {
|
|
|
1489
1423
|
if (!trimmed)
|
|
1490
1424
|
return "";
|
|
1491
1425
|
if (verboseOutput)
|
|
1492
|
-
return ` ${
|
|
1493
|
-
return ` ${
|
|
1426
|
+
return ` ${c4.dim("\xB7")} ${c4.dim(trimmed)}`;
|
|
1427
|
+
return ` ${c4.dim("\xB7")} ${c4.dim(trimmed.length > 60 ? `${trimmed.slice(0, 57)}\u2026` : trimmed)}`;
|
|
1494
1428
|
}
|
|
1495
1429
|
function renderSkillSummaryLine(skill, opts) {
|
|
1496
1430
|
const name = skill.name ?? "(unknown)";
|
|
1497
1431
|
const source = skill.source ?? "unknown";
|
|
1498
|
-
const labelPrefix = opts?.label ? `${
|
|
1499
|
-
|
|
1432
|
+
const labelPrefix = opts?.label ? `${c4.dim(opts.label)} ` : "";
|
|
1433
|
+
writeln(` ${G.info} ${labelPrefix}${name} ${c4.dim("\xB7")} ${c4.dim(source)}${buildSkillDescriptionPart(skill.description, opts?.verboseOutput === true)}`);
|
|
1500
1434
|
}
|
|
1501
1435
|
function renderListSkillsResult(result, opts) {
|
|
1502
1436
|
const r = result;
|
|
1503
1437
|
if (!Array.isArray(r?.skills))
|
|
1504
1438
|
return false;
|
|
1505
1439
|
if (r.skills.length === 0) {
|
|
1506
|
-
|
|
1440
|
+
writeln(` ${G.info} ${c4.dim("no skills")}`);
|
|
1507
1441
|
return true;
|
|
1508
1442
|
}
|
|
1509
1443
|
const maxSkills = opts?.verboseOutput ? r.skills.length : 6;
|
|
@@ -1513,7 +1447,7 @@ function renderListSkillsResult(result, opts) {
|
|
|
1513
1447
|
});
|
|
1514
1448
|
}
|
|
1515
1449
|
if (r.skills.length > maxSkills) {
|
|
1516
|
-
|
|
1450
|
+
writeln(` ${c4.dim(`+${r.skills.length - maxSkills} more skills`)}`);
|
|
1517
1451
|
}
|
|
1518
1452
|
return true;
|
|
1519
1453
|
}
|
|
@@ -1522,7 +1456,7 @@ function renderReadSkillResult(result, _opts) {
|
|
|
1522
1456
|
if (!r || typeof r !== "object")
|
|
1523
1457
|
return false;
|
|
1524
1458
|
if (!r.skill) {
|
|
1525
|
-
|
|
1459
|
+
writeln(` ${G.info} ${c4.dim("skill")} ${c4.dim("(not found)")}`);
|
|
1526
1460
|
return true;
|
|
1527
1461
|
}
|
|
1528
1462
|
renderSkillSummaryLine(r.skill, {
|
|
@@ -1536,19 +1470,19 @@ function renderWebSearchResult(result, opts) {
|
|
|
1536
1470
|
if (!Array.isArray(r?.results))
|
|
1537
1471
|
return false;
|
|
1538
1472
|
if (r.results.length === 0) {
|
|
1539
|
-
|
|
1473
|
+
writeln(` ${G.info} ${c4.dim("no results")}`);
|
|
1540
1474
|
return true;
|
|
1541
1475
|
}
|
|
1542
1476
|
const maxResults = opts?.verboseOutput ? r.results.length : 5;
|
|
1543
1477
|
for (const item of r.results.slice(0, maxResults)) {
|
|
1544
1478
|
const title = (item.title?.trim() || item.url || "(untitled)").replace(/\s+/g, " ");
|
|
1545
|
-
const score = typeof item.score === "number" ?
|
|
1546
|
-
|
|
1479
|
+
const score = typeof item.score === "number" ? c4.dim(` (${item.score.toFixed(2)})`) : "";
|
|
1480
|
+
writeln(` ${c4.dim("\u2022")} ${title}${score}`);
|
|
1547
1481
|
if (item.url)
|
|
1548
|
-
|
|
1482
|
+
writeln(` ${c4.dim(item.url)}`);
|
|
1549
1483
|
}
|
|
1550
1484
|
if (r.results.length > maxResults) {
|
|
1551
|
-
|
|
1485
|
+
writeln(` ${c4.dim(` +${r.results.length - maxResults} more`)}`);
|
|
1552
1486
|
}
|
|
1553
1487
|
return true;
|
|
1554
1488
|
}
|
|
@@ -1557,23 +1491,23 @@ function renderWebContentResult(result, opts) {
|
|
|
1557
1491
|
if (!Array.isArray(r?.results))
|
|
1558
1492
|
return false;
|
|
1559
1493
|
if (r.results.length === 0) {
|
|
1560
|
-
|
|
1494
|
+
writeln(` ${G.info} ${c4.dim("no pages")}`);
|
|
1561
1495
|
return true;
|
|
1562
1496
|
}
|
|
1563
1497
|
const maxPages = opts?.verboseOutput ? r.results.length : 3;
|
|
1564
1498
|
for (const item of r.results.slice(0, maxPages)) {
|
|
1565
1499
|
const title = (item.title?.trim() || item.url || "(untitled)").replace(/\s+/g, " ");
|
|
1566
|
-
|
|
1500
|
+
writeln(` ${c4.dim("\u2022")} ${title}`);
|
|
1567
1501
|
if (item.url)
|
|
1568
|
-
|
|
1502
|
+
writeln(` ${c4.dim(item.url)}`);
|
|
1569
1503
|
const preview = (item.text ?? "").replace(/\s+/g, " ").trim();
|
|
1570
1504
|
if (preview) {
|
|
1571
1505
|
const trimmed = opts?.verboseOutput || preview.length <= 220 ? preview : `${preview.slice(0, 217)}\u2026`;
|
|
1572
|
-
|
|
1506
|
+
writeln(` ${c4.dim(trimmed)}`);
|
|
1573
1507
|
}
|
|
1574
1508
|
}
|
|
1575
1509
|
if (r.results.length > maxPages) {
|
|
1576
|
-
|
|
1510
|
+
writeln(` ${c4.dim(` +${r.results.length - maxPages} more`)}`);
|
|
1577
1511
|
}
|
|
1578
1512
|
return true;
|
|
1579
1513
|
}
|
|
@@ -1586,7 +1520,7 @@ function renderMcpResult(result, opts) {
|
|
|
1586
1520
|
const lines = block.text.split(`
|
|
1587
1521
|
`).slice(0, maxLines);
|
|
1588
1522
|
for (const line of lines)
|
|
1589
|
-
|
|
1523
|
+
writeln(` ${c4.dim("\u2502")} ${line}`);
|
|
1590
1524
|
}
|
|
1591
1525
|
}
|
|
1592
1526
|
return true;
|
|
@@ -1629,40 +1563,40 @@ function buildToolCallLine(name, args) {
|
|
|
1629
1563
|
const prompt = typeof a.prompt === "string" ? a.prompt : "";
|
|
1630
1564
|
const short = prompt.length > 60 ? `${prompt.slice(0, 57)}\u2026` : prompt;
|
|
1631
1565
|
const agentName = typeof a.agentName === "string" && a.agentName ? a.agentName : "";
|
|
1632
|
-
const label = agentName ? ` ${
|
|
1633
|
-
return `${G.agent}${label} ${
|
|
1566
|
+
const label = agentName ? ` ${c5.dim(c5.cyan(`[@${agentName}]`))}` : "";
|
|
1567
|
+
return `${G.agent}${label} ${c5.dim("\u2014")} ${short}`;
|
|
1634
1568
|
}
|
|
1635
1569
|
if (name === "shell") {
|
|
1636
1570
|
const cmd = String(a.command ?? "").trim();
|
|
1637
1571
|
if (!cmd)
|
|
1638
|
-
return `${G.run} ${
|
|
1572
|
+
return `${G.run} ${c5.dim("shell")}`;
|
|
1639
1573
|
const shortCmd = cmd.length > 72 ? `${cmd.slice(0, 69)}\u2026` : cmd;
|
|
1640
1574
|
return `${G.run} ${shortCmd}`;
|
|
1641
1575
|
}
|
|
1642
1576
|
if (name === "listSkills") {
|
|
1643
|
-
return `${G.search} ${
|
|
1577
|
+
return `${G.search} ${c5.dim("list skills")}`;
|
|
1644
1578
|
}
|
|
1645
1579
|
if (name === "readSkill") {
|
|
1646
1580
|
const skillName = typeof a.name === "string" ? a.name : "";
|
|
1647
|
-
return `${G.read} ${
|
|
1581
|
+
return `${G.read} ${c5.dim("read skill")}${skillName ? ` ${skillName}` : ""}`;
|
|
1648
1582
|
}
|
|
1649
1583
|
if (name.startsWith("mcp_")) {
|
|
1650
|
-
return `${G.mcp} ${
|
|
1584
|
+
return `${G.mcp} ${c5.dim(name)}`;
|
|
1651
1585
|
}
|
|
1652
|
-
return `${toolGlyph(name)} ${
|
|
1586
|
+
return `${toolGlyph(name)} ${c5.dim(name)}`;
|
|
1653
1587
|
}
|
|
1654
1588
|
function renderToolCall(toolName, args) {
|
|
1655
|
-
|
|
1589
|
+
writeln(` ${buildToolCallLine(toolName, args)}`);
|
|
1656
1590
|
}
|
|
1657
1591
|
function formatErrorBadge(result) {
|
|
1658
1592
|
const msg = typeof result === "string" ? result : result instanceof Error ? result.message : JSON.stringify(result);
|
|
1659
1593
|
const oneLiner = msg.split(`
|
|
1660
1594
|
`)[0] ?? msg;
|
|
1661
|
-
return `${G.err} ${
|
|
1595
|
+
return `${G.err} ${c5.red(oneLiner)}`;
|
|
1662
1596
|
}
|
|
1663
1597
|
function renderToolResult(toolName, result, isError, opts) {
|
|
1664
1598
|
if (isError) {
|
|
1665
|
-
|
|
1599
|
+
writeln(` ${formatErrorBadge(result)}`);
|
|
1666
1600
|
return;
|
|
1667
1601
|
}
|
|
1668
1602
|
if (renderToolResultByName(toolName, result, opts)) {
|
|
@@ -1670,10 +1604,10 @@ function renderToolResult(toolName, result, isError, opts) {
|
|
|
1670
1604
|
}
|
|
1671
1605
|
const text = JSON.stringify(result);
|
|
1672
1606
|
if (opts?.verboseOutput || text.length <= 120) {
|
|
1673
|
-
|
|
1607
|
+
writeln(` ${c5.dim(text)}`);
|
|
1674
1608
|
return;
|
|
1675
1609
|
}
|
|
1676
|
-
|
|
1610
|
+
writeln(` ${c5.dim(`${text.slice(0, 117)}\u2026`)}`);
|
|
1677
1611
|
}
|
|
1678
1612
|
|
|
1679
1613
|
// src/cli/stream-render.ts
|
|
@@ -1702,7 +1636,7 @@ async function renderTurn(events, spinner, opts) {
|
|
|
1702
1636
|
case "text-delta": {
|
|
1703
1637
|
liveReasoning.finish();
|
|
1704
1638
|
content.appendTextDelta(event.delta, renderedVisibleOutput);
|
|
1705
|
-
if (
|
|
1639
|
+
if (content.hasOpenContent())
|
|
1706
1640
|
renderedVisibleOutput = true;
|
|
1707
1641
|
break;
|
|
1708
1642
|
}
|
|
@@ -1713,7 +1647,7 @@ async function renderTurn(events, spinner, opts) {
|
|
|
1713
1647
|
if (showReasoning && delta) {
|
|
1714
1648
|
spinner.stop();
|
|
1715
1649
|
if (renderedVisibleOutput && !liveReasoning.isOpen())
|
|
1716
|
-
|
|
1650
|
+
writeln();
|
|
1717
1651
|
liveReasoning.append(delta);
|
|
1718
1652
|
renderedVisibleOutput = true;
|
|
1719
1653
|
}
|
|
@@ -1728,7 +1662,7 @@ async function renderTurn(events, spinner, opts) {
|
|
|
1728
1662
|
content.flushOpenContent();
|
|
1729
1663
|
spinner.stop();
|
|
1730
1664
|
if (renderedVisibleOutput)
|
|
1731
|
-
|
|
1665
|
+
writeln();
|
|
1732
1666
|
renderToolCall(event.toolName, event.args);
|
|
1733
1667
|
renderedVisibleOutput = true;
|
|
1734
1668
|
spinner.start(event.toolName);
|
|
@@ -1750,7 +1684,7 @@ async function renderTurn(events, spinner, opts) {
|
|
|
1750
1684
|
content.flushOpenContent();
|
|
1751
1685
|
spinner.stop();
|
|
1752
1686
|
const removedKb = (event.removedBytes / 1024).toFixed(1);
|
|
1753
|
-
|
|
1687
|
+
writeln(`${G.info} ${c6.dim("context pruned")} ${c6.dim(event.mode)} ${c6.dim(`\u2013${event.removedMessageCount} messages`)} ${c6.dim(`\u2013${removedKb} KB`)}`);
|
|
1754
1688
|
renderedVisibleOutput = true;
|
|
1755
1689
|
break;
|
|
1756
1690
|
}
|
|
@@ -1759,7 +1693,7 @@ async function renderTurn(events, spinner, opts) {
|
|
|
1759
1693
|
content.flushOpenContent();
|
|
1760
1694
|
spinner.stop();
|
|
1761
1695
|
if (!renderedVisibleOutput)
|
|
1762
|
-
|
|
1696
|
+
writeln();
|
|
1763
1697
|
inputTokens = event.inputTokens;
|
|
1764
1698
|
outputTokens = event.outputTokens;
|
|
1765
1699
|
contextTokens = event.contextTokens;
|
|
@@ -1791,7 +1725,7 @@ async function renderTurn(events, spinner, opts) {
|
|
|
1791
1725
|
|
|
1792
1726
|
// src/cli/output.ts
|
|
1793
1727
|
var HOME = homedir3();
|
|
1794
|
-
var PACKAGE_VERSION = "0.0.
|
|
1728
|
+
var PACKAGE_VERSION = "0.0.23";
|
|
1795
1729
|
function tildePath(p) {
|
|
1796
1730
|
return p.startsWith(HOME) ? `~${p.slice(HOME.length)}` : p;
|
|
1797
1731
|
}
|
|
@@ -1801,38 +1735,38 @@ function restoreTerminal() {
|
|
|
1801
1735
|
function registerTerminalCleanup() {
|
|
1802
1736
|
terminal.registerCleanup();
|
|
1803
1737
|
}
|
|
1804
|
-
function
|
|
1738
|
+
function writeln(text = "") {
|
|
1805
1739
|
terminal.stdoutWrite(`${text}
|
|
1806
1740
|
`);
|
|
1807
1741
|
}
|
|
1808
|
-
function
|
|
1742
|
+
function write(text) {
|
|
1809
1743
|
terminal.stdoutWrite(text);
|
|
1810
1744
|
}
|
|
1811
1745
|
function renderUserMessage(text) {
|
|
1812
1746
|
const lines = text.split(`
|
|
1813
1747
|
`);
|
|
1814
1748
|
if (lines.length === 0) {
|
|
1815
|
-
|
|
1749
|
+
writeln(`${G.prompt}`);
|
|
1816
1750
|
return;
|
|
1817
1751
|
}
|
|
1818
|
-
|
|
1752
|
+
writeln(`${G.prompt} ${lines[0] ?? ""}`);
|
|
1819
1753
|
for (const line of lines.slice(1)) {
|
|
1820
|
-
|
|
1754
|
+
writeln(` ${line}`);
|
|
1821
1755
|
}
|
|
1822
1756
|
}
|
|
1823
1757
|
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:
|
|
1758
|
+
prompt: c7.green("\u203A"),
|
|
1759
|
+
reply: c7.cyan("\u25C6"),
|
|
1760
|
+
search: c7.yellow("?"),
|
|
1761
|
+
read: c7.dim("\u2190"),
|
|
1762
|
+
write: c7.green("\u270E"),
|
|
1763
|
+
run: c7.dim("$"),
|
|
1764
|
+
agent: c7.cyan("\u21E2"),
|
|
1765
|
+
mcp: c7.yellow("\u2699"),
|
|
1766
|
+
ok: c7.green("\u2714"),
|
|
1767
|
+
err: c7.red("\u2716"),
|
|
1768
|
+
warn: c7.yellow("!"),
|
|
1769
|
+
info: c7.dim("\xB7")
|
|
1836
1770
|
};
|
|
1837
1771
|
var PREFIX = {
|
|
1838
1772
|
user: G.prompt,
|
|
@@ -1854,9 +1788,9 @@ class RenderedError extends Error {
|
|
|
1854
1788
|
function renderError(err, context = "render") {
|
|
1855
1789
|
logError(err, context);
|
|
1856
1790
|
const parsed = parseAppError(err);
|
|
1857
|
-
|
|
1791
|
+
writeln(`${G.err} ${c7.red(parsed.headline)}`);
|
|
1858
1792
|
if (parsed.hint) {
|
|
1859
|
-
|
|
1793
|
+
writeln(` ${c7.dim(parsed.hint)}`);
|
|
1860
1794
|
}
|
|
1861
1795
|
}
|
|
1862
1796
|
function discoverContextFiles(cwd) {
|
|
@@ -1876,11 +1810,11 @@ function discoverContextFiles(cwd) {
|
|
|
1876
1810
|
return found;
|
|
1877
1811
|
}
|
|
1878
1812
|
function renderBanner(model, cwd) {
|
|
1879
|
-
|
|
1813
|
+
writeln();
|
|
1880
1814
|
const title = PACKAGE_VERSION ? `mini-coder \xB7 v${PACKAGE_VERSION}` : "mini-coder";
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1815
|
+
writeln(` ${c7.cyan("mc")} ${c7.dim(title)}`);
|
|
1816
|
+
writeln(` ${c7.dim(model)} ${c7.dim("\xB7")} ${c7.dim(tildePath(cwd))}`);
|
|
1817
|
+
writeln(` ${c7.dim("/help for commands \xB7 esc cancel \xB7 ctrl+c/ctrl+d exit")}`);
|
|
1884
1818
|
const items = [];
|
|
1885
1819
|
const contextFiles = discoverContextFiles(cwd);
|
|
1886
1820
|
if (contextFiles.length > 0)
|
|
@@ -1895,27 +1829,19 @@ function renderBanner(model, cwd) {
|
|
|
1895
1829
|
if (commands.size > 0)
|
|
1896
1830
|
items.push(`${commands.size} custom cmd${commands.size > 1 ? "s" : ""}`);
|
|
1897
1831
|
if (items.length > 0) {
|
|
1898
|
-
|
|
1832
|
+
writeln(` ${c7.dim(items.join(" \xB7 "))}`);
|
|
1899
1833
|
}
|
|
1900
|
-
|
|
1834
|
+
writeln();
|
|
1901
1835
|
}
|
|
1902
1836
|
|
|
1903
1837
|
class CliReporter {
|
|
1904
1838
|
spinner = new Spinner;
|
|
1905
|
-
|
|
1906
|
-
haltSpinner() {
|
|
1839
|
+
info(msg) {
|
|
1907
1840
|
this.spinner.stop();
|
|
1841
|
+
writeln(`${G.info} ${c7.dim(msg)}`);
|
|
1908
1842
|
}
|
|
1909
|
-
|
|
1843
|
+
error(msg, hint) {
|
|
1910
1844
|
this.spinner.stop();
|
|
1911
|
-
this.liveOutput.finish();
|
|
1912
|
-
}
|
|
1913
|
-
info(msg) {
|
|
1914
|
-
this.haltSpinnerAndFlushLiveOutput();
|
|
1915
|
-
writeln2(`${G.info} ${c8.dim(msg)}`);
|
|
1916
|
-
}
|
|
1917
|
-
error(msg, hint) {
|
|
1918
|
-
this.haltSpinnerAndFlushLiveOutput();
|
|
1919
1845
|
if (typeof msg === "string") {
|
|
1920
1846
|
renderError(msg, hint);
|
|
1921
1847
|
} else {
|
|
@@ -1923,29 +1849,23 @@ class CliReporter {
|
|
|
1923
1849
|
}
|
|
1924
1850
|
}
|
|
1925
1851
|
warn(msg) {
|
|
1926
|
-
this.
|
|
1927
|
-
|
|
1852
|
+
this.spinner.stop();
|
|
1853
|
+
writeln(`${G.warn} ${msg}`);
|
|
1928
1854
|
}
|
|
1929
1855
|
writeText(text) {
|
|
1930
|
-
this.
|
|
1931
|
-
|
|
1932
|
-
}
|
|
1933
|
-
streamChunk(text) {
|
|
1934
|
-
this.haltSpinner();
|
|
1935
|
-
this.liveOutput.append(text);
|
|
1856
|
+
this.spinner.stop();
|
|
1857
|
+
writeln(text);
|
|
1936
1858
|
}
|
|
1937
1859
|
startSpinner(label) {
|
|
1938
1860
|
this.spinner.start(label);
|
|
1939
1861
|
}
|
|
1940
1862
|
stopSpinner() {
|
|
1941
|
-
this.
|
|
1863
|
+
this.spinner.stop();
|
|
1942
1864
|
}
|
|
1943
1865
|
async renderTurn(events, opts) {
|
|
1944
|
-
this.liveOutput.finish();
|
|
1945
1866
|
return renderTurn(events, this.spinner, opts);
|
|
1946
1867
|
}
|
|
1947
1868
|
renderStatusBar(data) {
|
|
1948
|
-
this.liveOutput.finish();
|
|
1949
1869
|
renderStatusBar(data);
|
|
1950
1870
|
}
|
|
1951
1871
|
restoreTerminal() {
|
|
@@ -1965,8 +1885,8 @@ function warnConventionConflicts(kind, scope, agentsNames, claudeNames) {
|
|
|
1965
1885
|
if (conflicts.length === 0)
|
|
1966
1886
|
return;
|
|
1967
1887
|
conflicts.sort((a, b) => a.localeCompare(b));
|
|
1968
|
-
const list = conflicts.map((n) =>
|
|
1969
|
-
|
|
1888
|
+
const list = conflicts.map((n) => c8.cyan(n)).join(c8.dim(", "));
|
|
1889
|
+
writeln(`${G.warn} conflicting ${kind} in ${scope} .agents and .claude: ${list} ${c8.dim("\u2014 using .agents version")}`);
|
|
1970
1890
|
}
|
|
1971
1891
|
|
|
1972
1892
|
// src/cli/frontmatter.ts
|
|
@@ -2627,10 +2547,10 @@ function setPreferredGoogleCachedContent(contentId) {
|
|
|
2627
2547
|
}
|
|
2628
2548
|
}
|
|
2629
2549
|
// src/agent/session-runner.ts
|
|
2630
|
-
import * as
|
|
2550
|
+
import * as c11 from "yoctocolors";
|
|
2631
2551
|
|
|
2632
2552
|
// src/cli/input.ts
|
|
2633
|
-
import * as
|
|
2553
|
+
import * as c9 from "yoctocolors";
|
|
2634
2554
|
|
|
2635
2555
|
// src/cli/input-buffer.ts
|
|
2636
2556
|
var PASTE_TOKEN_START = 57344;
|
|
@@ -3064,7 +2984,7 @@ function watchForCancel(abortController, options) {
|
|
|
3064
2984
|
process.stdin.on("data", onData);
|
|
3065
2985
|
return cleanup;
|
|
3066
2986
|
}
|
|
3067
|
-
var PROMPT =
|
|
2987
|
+
var PROMPT = c9.green("\u25B6 ");
|
|
3068
2988
|
var PROMPT_RAW_LEN = 2;
|
|
3069
2989
|
async function readline(opts) {
|
|
3070
2990
|
const cwd = opts.cwd ?? process.cwd();
|
|
@@ -3113,7 +3033,7 @@ async function readline(opts) {
|
|
|
3113
3033
|
process.stdout.write(`${CLEAR_LINE}${prompt}${display}${CSI}${PROMPT_RAW_LEN + displayCursor + 1}G`);
|
|
3114
3034
|
}
|
|
3115
3035
|
function renderSearchPrompt() {
|
|
3116
|
-
process.stdout.write(`${CLEAR_LINE}${
|
|
3036
|
+
process.stdout.write(`${CLEAR_LINE}${c9.cyan("search:")} ${searchQuery}\u2588`);
|
|
3117
3037
|
}
|
|
3118
3038
|
function applyHistory() {
|
|
3119
3039
|
if (histIdx < history.length) {
|
|
@@ -4565,10 +4485,6 @@ function normalizeUnknownError(error) {
|
|
|
4565
4485
|
return new Error(stringifyUnknown(error));
|
|
4566
4486
|
}
|
|
4567
4487
|
|
|
4568
|
-
// src/llm-api/turn-anthropic-oauth.ts
|
|
4569
|
-
import Anthropic from "@anthropic-ai/sdk";
|
|
4570
|
-
import { zodSchema } from "ai";
|
|
4571
|
-
|
|
4572
4488
|
// src/llm-api/turn-context.ts
|
|
4573
4489
|
import { pruneMessages } from "ai";
|
|
4574
4490
|
var DEFAULT_TOOL_RESULT_PAYLOAD_CAP_BYTES = 16 * 1024;
|
|
@@ -4769,994 +4685,184 @@ function annotateAnthropicCacheBreakpoints(turnMessages, systemPrompt) {
|
|
|
4769
4685
|
};
|
|
4770
4686
|
}
|
|
4771
4687
|
|
|
4772
|
-
// src/llm-api/turn-
|
|
4773
|
-
import {
|
|
4688
|
+
// src/llm-api/turn-execution.ts
|
|
4689
|
+
import { dynamicTool, jsonSchema } from "ai";
|
|
4774
4690
|
|
|
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)];
|
|
4691
|
+
// src/llm-api/turn-stream-events.ts
|
|
4692
|
+
function shouldLogStreamChunk(c10) {
|
|
4693
|
+
return c10.type !== "text-delta" && c10.type !== "reasoning" && c10.type !== "reasoning-delta";
|
|
4793
4694
|
}
|
|
4794
|
-
function
|
|
4795
|
-
|
|
4796
|
-
|
|
4797
|
-
|
|
4798
|
-
|
|
4799
|
-
return
|
|
4800
|
-
|
|
4801
|
-
|
|
4802
|
-
|
|
4803
|
-
|
|
4804
|
-
|
|
4695
|
+
function extractToolArgs(c10) {
|
|
4696
|
+
return c10.input ?? c10.args;
|
|
4697
|
+
}
|
|
4698
|
+
function hasRenderableToolArgs(args) {
|
|
4699
|
+
if (args === null || args === undefined)
|
|
4700
|
+
return false;
|
|
4701
|
+
if (typeof args === "string")
|
|
4702
|
+
return args.trim().length > 0;
|
|
4703
|
+
if (Array.isArray(args))
|
|
4704
|
+
return args.length > 0;
|
|
4705
|
+
if (typeof args === "object")
|
|
4706
|
+
return Object.keys(args).length > 0;
|
|
4707
|
+
return true;
|
|
4708
|
+
}
|
|
4709
|
+
function mapStreamChunkToTurnEvent(c10) {
|
|
4710
|
+
switch (c10.type) {
|
|
4711
|
+
case "text-delta": {
|
|
4712
|
+
const delta = typeof c10.text === "string" ? c10.text : "";
|
|
4713
|
+
return {
|
|
4714
|
+
type: "text-delta",
|
|
4715
|
+
delta
|
|
4716
|
+
};
|
|
4805
4717
|
}
|
|
4806
|
-
|
|
4718
|
+
case "reasoning-delta":
|
|
4719
|
+
case "reasoning": {
|
|
4720
|
+
const delta = getReasoningDeltaFromStreamChunk(c10);
|
|
4721
|
+
if (delta === null)
|
|
4722
|
+
return null;
|
|
4723
|
+
return {
|
|
4724
|
+
type: "reasoning-delta",
|
|
4725
|
+
delta
|
|
4726
|
+
};
|
|
4727
|
+
}
|
|
4728
|
+
case "tool-input-start": {
|
|
4729
|
+
const args = extractToolArgs(c10);
|
|
4730
|
+
const hasStableToolCallId = typeof c10.toolCallId === "string" && c10.toolCallId.trim().length > 0;
|
|
4731
|
+
if (hasStableToolCallId && !hasRenderableToolArgs(args))
|
|
4732
|
+
return null;
|
|
4733
|
+
return {
|
|
4734
|
+
type: "tool-call-start",
|
|
4735
|
+
toolCallId: String(c10.toolCallId ?? ""),
|
|
4736
|
+
toolName: String(c10.toolName ?? ""),
|
|
4737
|
+
args
|
|
4738
|
+
};
|
|
4739
|
+
}
|
|
4740
|
+
case "tool-call": {
|
|
4741
|
+
return {
|
|
4742
|
+
type: "tool-call-start",
|
|
4743
|
+
toolCallId: String(c10.toolCallId ?? ""),
|
|
4744
|
+
toolName: String(c10.toolName ?? ""),
|
|
4745
|
+
args: extractToolArgs(c10)
|
|
4746
|
+
};
|
|
4747
|
+
}
|
|
4748
|
+
case "tool-result": {
|
|
4749
|
+
return {
|
|
4750
|
+
type: "tool-result",
|
|
4751
|
+
toolCallId: String(c10.toolCallId ?? ""),
|
|
4752
|
+
toolName: String(c10.toolName ?? ""),
|
|
4753
|
+
result: "output" in c10 ? c10.output : ("result" in c10) ? c10.result : undefined,
|
|
4754
|
+
isError: "isError" in c10 ? Boolean(c10.isError) : false
|
|
4755
|
+
};
|
|
4756
|
+
}
|
|
4757
|
+
case "tool-error":
|
|
4758
|
+
return {
|
|
4759
|
+
type: "tool-result",
|
|
4760
|
+
toolCallId: String(c10.toolCallId ?? ""),
|
|
4761
|
+
toolName: String(c10.toolName ?? ""),
|
|
4762
|
+
result: c10.error ?? "Tool execution failed",
|
|
4763
|
+
isError: true
|
|
4764
|
+
};
|
|
4765
|
+
case "error": {
|
|
4766
|
+
throw normalizeUnknownError(c10.error);
|
|
4767
|
+
}
|
|
4768
|
+
default:
|
|
4769
|
+
return null;
|
|
4770
|
+
}
|
|
4807
4771
|
}
|
|
4808
|
-
|
|
4809
|
-
|
|
4810
|
-
|
|
4811
|
-
return
|
|
4772
|
+
|
|
4773
|
+
// src/llm-api/turn-execution.ts
|
|
4774
|
+
function isZodSchema(s) {
|
|
4775
|
+
return s !== null && typeof s === "object" && "_def" in s;
|
|
4812
4776
|
}
|
|
4813
|
-
function
|
|
4814
|
-
|
|
4815
|
-
|
|
4816
|
-
|
|
4817
|
-
|
|
4818
|
-
|
|
4819
|
-
|
|
4820
|
-
|
|
4777
|
+
function toCoreTool(def) {
|
|
4778
|
+
const schema = isZodSchema(def.schema) ? def.schema : jsonSchema(def.schema);
|
|
4779
|
+
return dynamicTool({
|
|
4780
|
+
description: def.description,
|
|
4781
|
+
inputSchema: schema,
|
|
4782
|
+
execute: async (input) => {
|
|
4783
|
+
try {
|
|
4784
|
+
return await def.execute(input);
|
|
4785
|
+
} catch (err) {
|
|
4786
|
+
throw normalizeUnknownError(err);
|
|
4821
4787
|
}
|
|
4822
|
-
}
|
|
4788
|
+
}
|
|
4789
|
+
});
|
|
4790
|
+
}
|
|
4791
|
+
function buildToolSet(tools) {
|
|
4792
|
+
const toolSet = {};
|
|
4793
|
+
for (const def of tools) {
|
|
4794
|
+
toolSet[def.name] = toCoreTool(def);
|
|
4823
4795
|
}
|
|
4796
|
+
return toolSet;
|
|
4797
|
+
}
|
|
4798
|
+
function createTurnStepTracker(opts) {
|
|
4799
|
+
let stepCount = 0;
|
|
4800
|
+
let inputTokens = 0;
|
|
4801
|
+
let outputTokens = 0;
|
|
4802
|
+
let contextTokens = 0;
|
|
4803
|
+
let partialMessages = [];
|
|
4824
4804
|
return {
|
|
4825
|
-
|
|
4826
|
-
|
|
4827
|
-
|
|
4828
|
-
|
|
4829
|
-
|
|
4830
|
-
|
|
4805
|
+
onStepFinish: (step) => {
|
|
4806
|
+
opts.onStepLog({
|
|
4807
|
+
stepNumber: stepCount + 1,
|
|
4808
|
+
finishReason: step.finishReason,
|
|
4809
|
+
usage: step.usage
|
|
4810
|
+
});
|
|
4811
|
+
inputTokens += step.usage?.inputTokens ?? 0;
|
|
4812
|
+
outputTokens += step.usage?.outputTokens ?? 0;
|
|
4813
|
+
contextTokens = step.usage?.inputTokens ?? contextTokens;
|
|
4814
|
+
stepCount += 1;
|
|
4815
|
+
const s = step;
|
|
4816
|
+
partialMessages = s.response?.messages ?? s.messages ?? partialMessages;
|
|
4817
|
+
},
|
|
4818
|
+
getState: () => ({
|
|
4819
|
+
stepCount,
|
|
4820
|
+
inputTokens,
|
|
4821
|
+
outputTokens,
|
|
4822
|
+
contextTokens,
|
|
4823
|
+
partialMessages
|
|
4824
|
+
})
|
|
4831
4825
|
};
|
|
4832
4826
|
}
|
|
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))
|
|
4827
|
+
var TOOL_RESULT_CHUNK_TYPES = new Set(["tool-result", "tool-error"]);
|
|
4828
|
+
function normalizeToolCallId(raw) {
|
|
4829
|
+
if (typeof raw !== "string")
|
|
4849
4830
|
return null;
|
|
4850
|
-
const
|
|
4851
|
-
|
|
4852
|
-
if (!strategy.supports(modelString))
|
|
4853
|
-
continue;
|
|
4854
|
-
return strategy.build(modelId, effort);
|
|
4855
|
-
}
|
|
4856
|
-
return null;
|
|
4831
|
+
const trimmed = raw.trim();
|
|
4832
|
+
return trimmed ? trimmed : null;
|
|
4857
4833
|
}
|
|
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";
|
|
4834
|
+
function normalizeToolName(raw) {
|
|
4835
|
+
if (typeof raw !== "string")
|
|
4836
|
+
return "tool";
|
|
4837
|
+
const trimmed = raw.trim();
|
|
4838
|
+
return trimmed || "tool";
|
|
4868
4839
|
}
|
|
4869
|
-
function
|
|
4870
|
-
|
|
4840
|
+
function isRecord5(value) {
|
|
4841
|
+
return value !== null && typeof value === "object";
|
|
4842
|
+
}
|
|
4843
|
+
function normalizeTextPartId(raw) {
|
|
4844
|
+
if (typeof raw !== "string")
|
|
4871
4845
|
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 "";
|
|
4846
|
+
const trimmed = raw.trim();
|
|
4847
|
+
return trimmed ? trimmed : null;
|
|
4848
|
+
}
|
|
4849
|
+
function getOpenAITextPhase2(chunk) {
|
|
4850
|
+
const providerData = isRecord5(chunk.providerMetadata) ? chunk.providerMetadata : isRecord5(chunk.providerOptions) ? chunk.providerOptions : null;
|
|
4851
|
+
if (!providerData)
|
|
4852
|
+
return null;
|
|
4853
|
+
const openai = providerData.openai;
|
|
4854
|
+
if (!isRecord5(openai))
|
|
4855
|
+
return null;
|
|
4856
|
+
return openai.phase === "commentary" || openai.phase === "final_answer" ? openai.phase : null;
|
|
4857
|
+
}
|
|
4858
|
+
function extractTextDelta(chunk) {
|
|
4859
|
+
if (typeof chunk.text === "string")
|
|
4860
|
+
return chunk.text;
|
|
4861
|
+
if (typeof chunk.textDelta === "string")
|
|
4862
|
+
return chunk.textDelta;
|
|
4863
|
+
if (typeof chunk.delta === "string")
|
|
4864
|
+
return chunk.delta;
|
|
4865
|
+
return "";
|
|
5760
4866
|
}
|
|
5761
4867
|
|
|
5762
4868
|
class StreamTextPhaseTracker {
|
|
@@ -5857,84 +4963,376 @@ class StreamToolCallTracker {
|
|
|
5857
4963
|
}
|
|
5858
4964
|
return { chunk, suppressTurnEvent: false };
|
|
5859
4965
|
}
|
|
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 };
|
|
4966
|
+
trackRenderableStart(chunk, toolName, existingToolCallId) {
|
|
4967
|
+
const toolCallId = existingToolCallId ?? this.nextSyntheticToolCallId();
|
|
4968
|
+
this.trackStart(toolName, toolCallId);
|
|
4969
|
+
if (toolCallId === chunk.toolCallId)
|
|
4970
|
+
return chunk;
|
|
4971
|
+
return { ...chunk, toolCallId };
|
|
4972
|
+
}
|
|
4973
|
+
nextSyntheticToolCallId() {
|
|
4974
|
+
this.syntheticCount += 1;
|
|
4975
|
+
return `synthetic-tool-call-${this.syntheticCount}`;
|
|
4976
|
+
}
|
|
4977
|
+
trackStart(toolName, toolCallId) {
|
|
4978
|
+
const pending = this.pendingByTool.get(toolName) ?? [];
|
|
4979
|
+
pending.push(toolCallId);
|
|
4980
|
+
this.pendingByTool.set(toolName, pending);
|
|
4981
|
+
}
|
|
4982
|
+
trackDeferredStart(toolName) {
|
|
4983
|
+
this.deferredStartsByTool.set(toolName, (this.deferredStartsByTool.get(toolName) ?? 0) + 1);
|
|
4984
|
+
}
|
|
4985
|
+
consumeDeferredStart(toolName) {
|
|
4986
|
+
const count = this.deferredStartsByTool.get(toolName) ?? 0;
|
|
4987
|
+
if (count <= 0)
|
|
4988
|
+
return;
|
|
4989
|
+
if (count === 1) {
|
|
4990
|
+
this.deferredStartsByTool.delete(toolName);
|
|
4991
|
+
return;
|
|
4992
|
+
}
|
|
4993
|
+
this.deferredStartsByTool.set(toolName, count - 1);
|
|
4994
|
+
}
|
|
4995
|
+
consumeTracked(toolName, toolCallId) {
|
|
4996
|
+
const pending = this.pendingByTool.get(toolName);
|
|
4997
|
+
if (!pending || pending.length === 0)
|
|
4998
|
+
return;
|
|
4999
|
+
const idx = pending.indexOf(toolCallId);
|
|
5000
|
+
if (idx === -1)
|
|
5001
|
+
return;
|
|
5002
|
+
pending.splice(idx, 1);
|
|
5003
|
+
if (pending.length === 0)
|
|
5004
|
+
this.pendingByTool.delete(toolName);
|
|
5005
|
+
}
|
|
5006
|
+
consumeNextTracked(toolName) {
|
|
5007
|
+
const pending = this.pendingByTool.get(toolName);
|
|
5008
|
+
if (!pending || pending.length === 0)
|
|
5009
|
+
return null;
|
|
5010
|
+
const toolCallId = pending.shift() ?? null;
|
|
5011
|
+
if (pending.length === 0)
|
|
5012
|
+
this.pendingByTool.delete(toolName);
|
|
5013
|
+
return toolCallId;
|
|
5014
|
+
}
|
|
5015
|
+
}
|
|
5016
|
+
async function* mapFullStreamToTurnEvents(stream, opts) {
|
|
5017
|
+
const toolCallTracker = new StreamToolCallTracker;
|
|
5018
|
+
const textPhaseTracker = new StreamTextPhaseTracker;
|
|
5019
|
+
for await (const originalChunk of stream) {
|
|
5020
|
+
const prepared = toolCallTracker.prepare(originalChunk);
|
|
5021
|
+
const chunk = prepared.chunk;
|
|
5022
|
+
const route = textPhaseTracker.route(chunk);
|
|
5023
|
+
if (!prepared.suppressTurnEvent && route !== "skip" && shouldLogStreamChunk(chunk)) {
|
|
5024
|
+
opts.onChunk?.(chunk);
|
|
5025
|
+
}
|
|
5026
|
+
if (prepared.suppressTurnEvent || route === "skip")
|
|
5027
|
+
continue;
|
|
5028
|
+
const event = route === "reasoning" ? mapCommentaryChunkToTurnEvent(chunk) : mapStreamChunkToTurnEvent(chunk);
|
|
5029
|
+
if (event)
|
|
5030
|
+
yield event;
|
|
5031
|
+
}
|
|
5032
|
+
}
|
|
5033
|
+
|
|
5034
|
+
// src/llm-api/turn-request.ts
|
|
5035
|
+
import { stepCountIs } from "ai";
|
|
5036
|
+
|
|
5037
|
+
// src/llm-api/provider-options.ts
|
|
5038
|
+
var ANTHROPIC_BUDGET = {
|
|
5039
|
+
low: 4096,
|
|
5040
|
+
medium: 8192,
|
|
5041
|
+
high: 16384,
|
|
5042
|
+
xhigh: 32768
|
|
5043
|
+
};
|
|
5044
|
+
var GEMINI_BUDGET = {
|
|
5045
|
+
low: 4096,
|
|
5046
|
+
medium: 8192,
|
|
5047
|
+
high: 16384,
|
|
5048
|
+
xhigh: 24575
|
|
5049
|
+
};
|
|
5050
|
+
function clampEffort(effort, max) {
|
|
5051
|
+
const ORDER = ["low", "medium", "high", "xhigh"];
|
|
5052
|
+
const effortIdx = ORDER.indexOf(effort);
|
|
5053
|
+
const maxIdx = ORDER.indexOf(max);
|
|
5054
|
+
return ORDER[Math.min(effortIdx, maxIdx)];
|
|
5055
|
+
}
|
|
5056
|
+
function getAnthropicThinkingOptions(modelId, effort) {
|
|
5057
|
+
const isAdaptive = /^claude-3-7/.test(modelId) || /^claude-sonnet-4/.test(modelId) || /^claude-opus-4/.test(modelId);
|
|
5058
|
+
if (isAdaptive) {
|
|
5059
|
+
const isOpus = /^claude-opus-4/.test(modelId);
|
|
5060
|
+
const mapped = effort === "xhigh" ? isOpus ? "max" : "high" : effort;
|
|
5061
|
+
return { anthropic: { thinking: { type: "adaptive" }, effort: mapped } };
|
|
5062
|
+
}
|
|
5063
|
+
return {
|
|
5064
|
+
anthropic: {
|
|
5065
|
+
thinking: { type: "enabled", budgetTokens: ANTHROPIC_BUDGET[effort] },
|
|
5066
|
+
betas: ["interleaved-thinking-2025-05-14"]
|
|
5067
|
+
}
|
|
5068
|
+
};
|
|
5069
|
+
}
|
|
5070
|
+
function getOpenAIThinkingOptions(modelId, effort) {
|
|
5071
|
+
const supportsXhigh = /^gpt-5\.[2-9]/.test(modelId) || /^o4/.test(modelId);
|
|
5072
|
+
const clamped = supportsXhigh ? effort : clampEffort(effort, "high");
|
|
5073
|
+
return { openai: { reasoningEffort: clamped, reasoningSummary: "auto" } };
|
|
5074
|
+
}
|
|
5075
|
+
function getGeminiThinkingOptions(modelId, effort) {
|
|
5076
|
+
if (/^gemini-3/.test(modelId)) {
|
|
5077
|
+
return {
|
|
5078
|
+
google: {
|
|
5079
|
+
thinkingConfig: {
|
|
5080
|
+
includeThoughts: true,
|
|
5081
|
+
thinkingLevel: clampEffort(effort, "high")
|
|
5082
|
+
}
|
|
5083
|
+
}
|
|
5084
|
+
};
|
|
5085
|
+
}
|
|
5086
|
+
return {
|
|
5087
|
+
google: {
|
|
5088
|
+
thinkingConfig: {
|
|
5089
|
+
includeThoughts: true,
|
|
5090
|
+
thinkingBudget: GEMINI_BUDGET[effort]
|
|
5091
|
+
}
|
|
5092
|
+
}
|
|
5093
|
+
};
|
|
5094
|
+
}
|
|
5095
|
+
var THINKING_STRATEGIES = [
|
|
5096
|
+
{
|
|
5097
|
+
supports: isAnthropicModelFamily,
|
|
5098
|
+
build: getAnthropicThinkingOptions
|
|
5099
|
+
},
|
|
5100
|
+
{
|
|
5101
|
+
supports: isOpenAIReasoningModelFamily,
|
|
5102
|
+
build: getOpenAIThinkingOptions
|
|
5103
|
+
},
|
|
5104
|
+
{
|
|
5105
|
+
supports: isGeminiModelFamily,
|
|
5106
|
+
build: getGeminiThinkingOptions
|
|
5107
|
+
}
|
|
5108
|
+
];
|
|
5109
|
+
function getThinkingProviderOptions(modelString, effort) {
|
|
5110
|
+
if (!supportsThinking(modelString))
|
|
5111
|
+
return null;
|
|
5112
|
+
const { modelId } = parseModelString(modelString);
|
|
5113
|
+
for (const strategy of THINKING_STRATEGIES) {
|
|
5114
|
+
if (!strategy.supports(modelString))
|
|
5115
|
+
continue;
|
|
5116
|
+
return strategy.build(modelId, effort);
|
|
5117
|
+
}
|
|
5118
|
+
return null;
|
|
5119
|
+
}
|
|
5120
|
+
var CACHE_FAMILY_RULES = [
|
|
5121
|
+
[isAnthropicModelFamily, "anthropic"],
|
|
5122
|
+
[isGeminiModelFamily, "google"]
|
|
5123
|
+
];
|
|
5124
|
+
function getCacheFamily(modelString) {
|
|
5125
|
+
for (const [match, family] of CACHE_FAMILY_RULES) {
|
|
5126
|
+
if (match(modelString))
|
|
5127
|
+
return family;
|
|
5128
|
+
}
|
|
5129
|
+
return "none";
|
|
5130
|
+
}
|
|
5131
|
+
function getCachingProviderOptions(modelString, opts) {
|
|
5132
|
+
if (!opts.enabled)
|
|
5133
|
+
return null;
|
|
5134
|
+
const family = getCacheFamily(modelString);
|
|
5135
|
+
if (family === "google" && opts.googleCachedContent && opts.googleExplicitCachingCompatible !== false) {
|
|
5136
|
+
return { google: { cachedContent: opts.googleCachedContent } };
|
|
5137
|
+
}
|
|
5138
|
+
return null;
|
|
5139
|
+
}
|
|
5140
|
+
|
|
5141
|
+
// src/llm-api/turn-prepare-messages.ts
|
|
5142
|
+
function prepareTurnMessages(input) {
|
|
5143
|
+
const {
|
|
5144
|
+
messages,
|
|
5145
|
+
modelString,
|
|
5146
|
+
toolCount,
|
|
5147
|
+
systemPrompt,
|
|
5148
|
+
pruningMode,
|
|
5149
|
+
toolResultPayloadCapBytes,
|
|
5150
|
+
promptCachingEnabled
|
|
5151
|
+
} = input;
|
|
5152
|
+
const apiLogOn = isApiLogEnabled();
|
|
5153
|
+
const strippedRuntimeToolFields = stripToolRuntimeInputFields(messages);
|
|
5154
|
+
if (strippedRuntimeToolFields !== messages && apiLogOn) {
|
|
5155
|
+
logApiEvent("runtime tool input fields stripped", { modelString });
|
|
5156
|
+
}
|
|
5157
|
+
const geminiResult = sanitizeGeminiToolMessagesWithMetadata(strippedRuntimeToolFields, modelString, toolCount > 0);
|
|
5158
|
+
if (geminiResult.repaired && apiLogOn) {
|
|
5159
|
+
logApiEvent("gemini tool history repaired", {
|
|
5160
|
+
modelString,
|
|
5161
|
+
reason: geminiResult.reason,
|
|
5162
|
+
repairedFromIndex: geminiResult.repairedFromIndex,
|
|
5163
|
+
droppedMessageCount: geminiResult.droppedMessageCount,
|
|
5164
|
+
tailOnlyAffected: geminiResult.tailOnlyAffected
|
|
5165
|
+
});
|
|
5866
5166
|
}
|
|
5867
|
-
|
|
5868
|
-
|
|
5869
|
-
|
|
5167
|
+
const openaiStripped = stripOpenAIHistoryTransforms(geminiResult.messages, modelString);
|
|
5168
|
+
if (openaiStripped !== geminiResult.messages && apiLogOn) {
|
|
5169
|
+
logApiEvent("openai history transforms applied", { modelString });
|
|
5870
5170
|
}
|
|
5871
|
-
|
|
5872
|
-
|
|
5873
|
-
|
|
5874
|
-
this.pendingByTool.set(toolName, pending);
|
|
5171
|
+
const normalised = normalizeOpenAICompatibleToolCallInputs(openaiStripped, modelString);
|
|
5172
|
+
if (normalised !== openaiStripped && apiLogOn) {
|
|
5173
|
+
logApiEvent("openai-compatible tool input normalized", { modelString });
|
|
5875
5174
|
}
|
|
5876
|
-
|
|
5877
|
-
|
|
5175
|
+
const preStats = apiLogOn ? getMessageDiagnostics(normalised) : getMessageStats(normalised);
|
|
5176
|
+
if (apiLogOn)
|
|
5177
|
+
logApiEvent("turn context pre-prune", preStats);
|
|
5178
|
+
const pruned = applyContextPruning(normalised, pruningMode);
|
|
5179
|
+
const postStats = apiLogOn ? getMessageDiagnostics(pruned) : getMessageStats(pruned);
|
|
5180
|
+
if (apiLogOn)
|
|
5181
|
+
logApiEvent("turn context post-prune", postStats);
|
|
5182
|
+
const compacted = compactToolResultPayloads(pruned, toolResultPayloadCapBytes);
|
|
5183
|
+
if (compacted !== pruned && apiLogOn) {
|
|
5184
|
+
logApiEvent("turn context post-compaction", {
|
|
5185
|
+
capBytes: toolResultPayloadCapBytes,
|
|
5186
|
+
diagnostics: getMessageDiagnostics(compacted)
|
|
5187
|
+
});
|
|
5878
5188
|
}
|
|
5879
|
-
|
|
5880
|
-
|
|
5881
|
-
|
|
5882
|
-
|
|
5883
|
-
|
|
5884
|
-
|
|
5885
|
-
|
|
5189
|
+
let finalMessages = compacted;
|
|
5190
|
+
let finalSystemPrompt = systemPrompt;
|
|
5191
|
+
const cacheFamily = getCacheFamily(modelString);
|
|
5192
|
+
if (cacheFamily === "anthropic" && promptCachingEnabled) {
|
|
5193
|
+
const annotated = annotateAnthropicCacheBreakpoints(compacted, systemPrompt);
|
|
5194
|
+
finalMessages = annotated.messages;
|
|
5195
|
+
finalSystemPrompt = annotated.systemPrompt;
|
|
5196
|
+
if (apiLogOn) {
|
|
5197
|
+
logApiEvent("Anthropic prompt caching", annotated.diagnostics);
|
|
5886
5198
|
}
|
|
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
5199
|
}
|
|
5900
|
-
|
|
5901
|
-
const
|
|
5902
|
-
|
|
5903
|
-
|
|
5904
|
-
|
|
5905
|
-
|
|
5906
|
-
|
|
5907
|
-
|
|
5200
|
+
if (isAnthropicModelFamily(modelString) && isAnthropicOAuth()) {
|
|
5201
|
+
const ccIdentity = "You are Claude Code, Anthropic's official CLI for Claude.";
|
|
5202
|
+
const ccSystemMsg = {
|
|
5203
|
+
role: "system",
|
|
5204
|
+
content: ccIdentity,
|
|
5205
|
+
providerOptions: {
|
|
5206
|
+
anthropic: { cacheControl: { type: "ephemeral" } }
|
|
5207
|
+
}
|
|
5208
|
+
};
|
|
5209
|
+
if (finalSystemPrompt) {
|
|
5210
|
+
finalMessages = [ccSystemMsg, ...finalMessages];
|
|
5211
|
+
} else {
|
|
5212
|
+
const sysIdx = finalMessages.findIndex((m) => m.role === "system");
|
|
5213
|
+
if (sysIdx >= 0) {
|
|
5214
|
+
finalMessages = [
|
|
5215
|
+
...finalMessages.slice(0, sysIdx),
|
|
5216
|
+
ccSystemMsg,
|
|
5217
|
+
...finalMessages.slice(sysIdx)
|
|
5218
|
+
];
|
|
5219
|
+
} else {
|
|
5220
|
+
finalMessages = [ccSystemMsg, ...finalMessages];
|
|
5221
|
+
}
|
|
5222
|
+
}
|
|
5908
5223
|
}
|
|
5224
|
+
const wasPruned = (pruningMode === "balanced" || pruningMode === "aggressive") && (postStats.messageCount < preStats.messageCount || postStats.totalBytes < preStats.totalBytes);
|
|
5225
|
+
return {
|
|
5226
|
+
messages: finalMessages,
|
|
5227
|
+
systemPrompt: finalSystemPrompt,
|
|
5228
|
+
pruned: wasPruned,
|
|
5229
|
+
prePruneMessageCount: preStats.messageCount,
|
|
5230
|
+
prePruneTotalBytes: preStats.totalBytes,
|
|
5231
|
+
postPruneMessageCount: postStats.messageCount,
|
|
5232
|
+
postPruneTotalBytes: postStats.totalBytes
|
|
5233
|
+
};
|
|
5909
5234
|
}
|
|
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;
|
|
5235
|
+
|
|
5236
|
+
// src/llm-api/turn-provider-options.ts
|
|
5237
|
+
function isRecord6(value) {
|
|
5238
|
+
return value !== null && typeof value === "object";
|
|
5239
|
+
}
|
|
5240
|
+
function mergeDeep(target, source) {
|
|
5241
|
+
const output = { ...target };
|
|
5242
|
+
for (const key in source) {
|
|
5243
|
+
const sVal = source[key];
|
|
5244
|
+
const tVal = target[key];
|
|
5245
|
+
output[key] = isRecord6(sVal) && isRecord6(tVal) ? { ...tVal, ...sVal } : sVal;
|
|
5925
5246
|
}
|
|
5247
|
+
return output;
|
|
5248
|
+
}
|
|
5249
|
+
function buildTurnProviderOptions(input) {
|
|
5250
|
+
const {
|
|
5251
|
+
modelString,
|
|
5252
|
+
thinkingEffort,
|
|
5253
|
+
promptCachingEnabled,
|
|
5254
|
+
openaiPromptCacheRetention,
|
|
5255
|
+
googleCachedContent,
|
|
5256
|
+
toolCount,
|
|
5257
|
+
hasSystemPrompt
|
|
5258
|
+
} = input;
|
|
5259
|
+
const thinkingOpts = thinkingEffort ? getThinkingProviderOptions(modelString, thinkingEffort) : null;
|
|
5260
|
+
const reasoningSummaryRequested = isRecord6(thinkingOpts) && isRecord6(thinkingOpts.openai) && typeof thinkingOpts.openai.reasoningSummary === "string";
|
|
5261
|
+
const cacheFamily = getCacheFamily(modelString);
|
|
5262
|
+
const cacheOpts = getCachingProviderOptions(modelString, {
|
|
5263
|
+
enabled: promptCachingEnabled,
|
|
5264
|
+
openaiRetention: openaiPromptCacheRetention,
|
|
5265
|
+
googleCachedContent,
|
|
5266
|
+
googleExplicitCachingCompatible: toolCount === 0 && !hasSystemPrompt
|
|
5267
|
+
});
|
|
5268
|
+
const baseProviderOpts = {
|
|
5269
|
+
...thinkingOpts ?? {},
|
|
5270
|
+
...isOpenAIGPT(modelString) ? {
|
|
5271
|
+
openai: {
|
|
5272
|
+
store: false,
|
|
5273
|
+
...isRecord6(thinkingOpts?.openai) ? thinkingOpts.openai : {}
|
|
5274
|
+
}
|
|
5275
|
+
} : {}
|
|
5276
|
+
};
|
|
5277
|
+
const providerOptions = cacheOpts ? mergeDeep(baseProviderOpts, cacheOpts) : baseProviderOpts;
|
|
5278
|
+
return {
|
|
5279
|
+
cacheFamily,
|
|
5280
|
+
thinkingOpts,
|
|
5281
|
+
cacheOpts,
|
|
5282
|
+
providerOptions,
|
|
5283
|
+
reasoningSummaryRequested
|
|
5284
|
+
};
|
|
5285
|
+
}
|
|
5286
|
+
|
|
5287
|
+
// src/llm-api/turn-request.ts
|
|
5288
|
+
function buildTurnPreparation(input) {
|
|
5289
|
+
const providerOptionsResult = buildTurnProviderOptions({
|
|
5290
|
+
modelString: input.modelString,
|
|
5291
|
+
thinkingEffort: input.thinkingEffort,
|
|
5292
|
+
promptCachingEnabled: input.promptCachingEnabled,
|
|
5293
|
+
openaiPromptCacheRetention: input.openaiPromptCacheRetention,
|
|
5294
|
+
googleCachedContent: input.googleCachedContent,
|
|
5295
|
+
toolCount: input.toolCount,
|
|
5296
|
+
hasSystemPrompt: Boolean(input.systemPrompt)
|
|
5297
|
+
});
|
|
5298
|
+
const prepared = prepareTurnMessages({
|
|
5299
|
+
messages: input.messages,
|
|
5300
|
+
modelString: input.modelString,
|
|
5301
|
+
toolCount: input.toolCount,
|
|
5302
|
+
systemPrompt: input.systemPrompt,
|
|
5303
|
+
pruningMode: input.pruningMode,
|
|
5304
|
+
toolResultPayloadCapBytes: input.toolResultPayloadCapBytes,
|
|
5305
|
+
promptCachingEnabled: input.promptCachingEnabled
|
|
5306
|
+
});
|
|
5307
|
+
return { providerOptionsResult, prepared };
|
|
5308
|
+
}
|
|
5309
|
+
function buildStreamTextRequest(input) {
|
|
5310
|
+
return {
|
|
5311
|
+
model: input.model,
|
|
5312
|
+
maxOutputTokens: 16384,
|
|
5313
|
+
messages: input.prepared.messages,
|
|
5314
|
+
tools: input.toolSet,
|
|
5315
|
+
stopWhen: stepCountIs(input.maxSteps),
|
|
5316
|
+
onStepFinish: input.onStepFinish,
|
|
5317
|
+
prepareStep: ({ stepNumber }) => {
|
|
5318
|
+
if (stepNumber >= input.maxSteps - 1) {
|
|
5319
|
+
return { activeTools: [] };
|
|
5320
|
+
}
|
|
5321
|
+
return;
|
|
5322
|
+
},
|
|
5323
|
+
...input.prepared.systemPrompt ? { system: input.prepared.systemPrompt } : {},
|
|
5324
|
+
...Object.keys(input.providerOptions).length > 0 ? {
|
|
5325
|
+
providerOptions: input.providerOptions
|
|
5326
|
+
} : {},
|
|
5327
|
+
...input.signal ? { abortSignal: input.signal } : {},
|
|
5328
|
+
onError: () => {},
|
|
5329
|
+
timeout: { chunkMs: 120000 }
|
|
5330
|
+
};
|
|
5926
5331
|
}
|
|
5927
5332
|
|
|
5928
5333
|
// src/llm-api/turn.ts
|
|
5929
|
-
var
|
|
5334
|
+
var MAX_STEPS = 50;
|
|
5930
5335
|
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
5336
|
const {
|
|
5939
5337
|
model,
|
|
5940
5338
|
modelString,
|
|
@@ -6006,7 +5404,7 @@ async function* runTurn(options) {
|
|
|
6006
5404
|
onStepFinish: stepTracker.onStepFinish,
|
|
6007
5405
|
signal,
|
|
6008
5406
|
providerOptions: providerOptionsResult.providerOptions,
|
|
6009
|
-
maxSteps:
|
|
5407
|
+
maxSteps: MAX_STEPS
|
|
6010
5408
|
}));
|
|
6011
5409
|
result.response.catch(() => {});
|
|
6012
5410
|
for await (const event of mapFullStreamToTurnEvents(result.fullStream, {
|
|
@@ -6049,7 +5447,7 @@ async function* runTurn(options) {
|
|
|
6049
5447
|
}
|
|
6050
5448
|
|
|
6051
5449
|
// src/session/manager.ts
|
|
6052
|
-
import * as
|
|
5450
|
+
import * as c10 from "yoctocolors";
|
|
6053
5451
|
function newSession(model, cwd) {
|
|
6054
5452
|
const id = generateSessionId();
|
|
6055
5453
|
const row = createSession({ id, cwd, model });
|
|
@@ -6068,19 +5466,19 @@ function touchActiveSession(session) {
|
|
|
6068
5466
|
function printSessionList() {
|
|
6069
5467
|
const sessions = listSessions(20);
|
|
6070
5468
|
if (sessions.length === 0) {
|
|
6071
|
-
|
|
5469
|
+
writeln(c10.dim("No sessions found."));
|
|
6072
5470
|
return;
|
|
6073
5471
|
}
|
|
6074
|
-
|
|
6075
|
-
${
|
|
5472
|
+
writeln(`
|
|
5473
|
+
${c10.bold("Recent sessions:")}`);
|
|
6076
5474
|
for (const s of sessions) {
|
|
6077
5475
|
const date = new Date(s.updated_at).toLocaleString();
|
|
6078
5476
|
const cwd = tildePath(s.cwd);
|
|
6079
|
-
const title = s.title ||
|
|
6080
|
-
|
|
5477
|
+
const title = s.title || c10.dim("(untitled)");
|
|
5478
|
+
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
5479
|
}
|
|
6082
|
-
|
|
6083
|
-
${
|
|
5480
|
+
writeln(`
|
|
5481
|
+
${c10.dim("Use")} mc --resume <id> ${c10.dim("to continue a session.")}`);
|
|
6084
5482
|
}
|
|
6085
5483
|
function getMostRecentSession() {
|
|
6086
5484
|
const sessions = listSessions(1);
|
|
@@ -6310,7 +5708,7 @@ class SessionRunner {
|
|
|
6310
5708
|
}
|
|
6311
5709
|
this.session = resumed;
|
|
6312
5710
|
this.currentModel = this.session.model;
|
|
6313
|
-
this.reporter.info(`Resumed session ${this.session.id} (${
|
|
5711
|
+
this.reporter.info(`Resumed session ${this.session.id} (${c11.cyan(this.currentModel)})`);
|
|
6314
5712
|
} else {
|
|
6315
5713
|
this.session = newSession(this.currentModel, this.cwd);
|
|
6316
5714
|
}
|
|
@@ -6774,13 +6172,12 @@ ${input.command}`], {
|
|
|
6774
6172
|
reader.cancel().catch(() => {});
|
|
6775
6173
|
}
|
|
6776
6174
|
}, timeout);
|
|
6777
|
-
async function collectStream(stream
|
|
6175
|
+
async function collectStream(stream) {
|
|
6778
6176
|
const reader = stream.getReader();
|
|
6779
6177
|
readers.push(reader);
|
|
6780
6178
|
const chunks = [];
|
|
6781
6179
|
let totalBytes = 0;
|
|
6782
6180
|
let truncated = false;
|
|
6783
|
-
const decoder = new TextDecoder;
|
|
6784
6181
|
while (true) {
|
|
6785
6182
|
try {
|
|
6786
6183
|
const { done, value } = await reader.read();
|
|
@@ -6788,7 +6185,6 @@ ${input.command}`], {
|
|
|
6788
6185
|
break;
|
|
6789
6186
|
if (!value)
|
|
6790
6187
|
continue;
|
|
6791
|
-
onChunk?.(decoder.decode(value, { stream: true }));
|
|
6792
6188
|
if (totalBytes + value.length > MAX_OUTPUT_BYTES) {
|
|
6793
6189
|
chunks.push(value.slice(0, MAX_OUTPUT_BYTES - totalBytes));
|
|
6794
6190
|
truncated = true;
|
|
@@ -6808,28 +6204,11 @@ ${input.command}`], {
|
|
|
6808
6204
|
let stdout = "";
|
|
6809
6205
|
let stderr = "";
|
|
6810
6206
|
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
6207
|
try {
|
|
6825
6208
|
[stdout, stderr] = await Promise.all([
|
|
6826
|
-
collectStream(proc.stdout
|
|
6827
|
-
collectStream(proc.stderr
|
|
6209
|
+
collectStream(proc.stdout),
|
|
6210
|
+
collectStream(proc.stderr)
|
|
6828
6211
|
]);
|
|
6829
|
-
if (streamedAnyOutput && !streamedEndsWithNewline) {
|
|
6830
|
-
emitOutput(`
|
|
6831
|
-
`);
|
|
6832
|
-
}
|
|
6833
6212
|
exitCode = await proc.exited;
|
|
6834
6213
|
} finally {
|
|
6835
6214
|
clearTimeout(timer);
|
|
@@ -6845,8 +6224,7 @@ ${input.command}`], {
|
|
|
6845
6224
|
stderr: stderr.trimEnd(),
|
|
6846
6225
|
exitCode,
|
|
6847
6226
|
success: exitCode === 0,
|
|
6848
|
-
timedOut
|
|
6849
|
-
streamedOutput: streamedAnyOutput
|
|
6227
|
+
timedOut
|
|
6850
6228
|
};
|
|
6851
6229
|
}
|
|
6852
6230
|
var shellTool = {
|
|
@@ -6919,22 +6297,9 @@ function withCwdDefault(tool, cwd) {
|
|
|
6919
6297
|
}
|
|
6920
6298
|
};
|
|
6921
6299
|
}
|
|
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
6300
|
function buildToolSet2(opts) {
|
|
6936
|
-
const { cwd
|
|
6937
|
-
const shell =
|
|
6301
|
+
const { cwd } = opts;
|
|
6302
|
+
const shell = withCwdDefault(shellTool, cwd);
|
|
6938
6303
|
const tools = [
|
|
6939
6304
|
shell,
|
|
6940
6305
|
withCwdDefault(listSkillsTool, cwd),
|
|
@@ -6963,17 +6328,10 @@ async function initAgent(opts) {
|
|
|
6963
6328
|
const cwd = opts.cwd;
|
|
6964
6329
|
let currentModel = opts.model;
|
|
6965
6330
|
const { runSubagent, killAll } = createSubagentRunner(cwd, () => currentModel);
|
|
6966
|
-
let verboseOutputEnabled = opts.initialVerboseOutput;
|
|
6967
6331
|
const agents = loadAgents(cwd);
|
|
6968
6332
|
const tools = buildToolSet2({
|
|
6969
6333
|
cwd,
|
|
6970
6334
|
runSubagent,
|
|
6971
|
-
onShellOutput: (chunk) => {
|
|
6972
|
-
if (!verboseOutputEnabled)
|
|
6973
|
-
return false;
|
|
6974
|
-
opts.reporter.streamChunk(chunk);
|
|
6975
|
-
return true;
|
|
6976
|
-
},
|
|
6977
6335
|
availableAgents: subagentAgents(agents)
|
|
6978
6336
|
});
|
|
6979
6337
|
const mcpTools = [];
|
|
@@ -6997,7 +6355,7 @@ async function initAgent(opts) {
|
|
|
6997
6355
|
for (const row of listMcpServers()) {
|
|
6998
6356
|
try {
|
|
6999
6357
|
await connectAndAddMcp(row.name);
|
|
7000
|
-
opts.reporter.info(`MCP: connected ${
|
|
6358
|
+
opts.reporter.info(`MCP: connected ${c12.cyan(row.name)}`);
|
|
7001
6359
|
} catch (e) {
|
|
7002
6360
|
opts.reporter.error(`MCP: failed to connect ${row.name}: ${String(e)}`);
|
|
7003
6361
|
}
|
|
@@ -7062,7 +6420,6 @@ async function initAgent(opts) {
|
|
|
7062
6420
|
},
|
|
7063
6421
|
setVerboseOutput: (verbose) => {
|
|
7064
6422
|
runner.verboseOutput = verbose;
|
|
7065
|
-
verboseOutputEnabled = verbose;
|
|
7066
6423
|
setPreferredVerboseOutput(verbose);
|
|
7067
6424
|
},
|
|
7068
6425
|
get pruningMode() {
|
|
@@ -7120,7 +6477,7 @@ async function initAgent(opts) {
|
|
|
7120
6477
|
}
|
|
7121
6478
|
|
|
7122
6479
|
// src/cli/args.ts
|
|
7123
|
-
import * as
|
|
6480
|
+
import * as c13 from "yoctocolors";
|
|
7124
6481
|
function parseArgs(argv) {
|
|
7125
6482
|
const args = {
|
|
7126
6483
|
model: null,
|
|
@@ -7184,11 +6541,11 @@ function parseArgs(argv) {
|
|
|
7184
6541
|
return args;
|
|
7185
6542
|
}
|
|
7186
6543
|
function printHelp() {
|
|
7187
|
-
|
|
6544
|
+
writeln(`${c13.bold("mini-coder")} \u2014 a small, fast CLI coding agent
|
|
7188
6545
|
`);
|
|
7189
|
-
|
|
6546
|
+
writeln(`${c13.bold("Usage:")} mc [options] [prompt]
|
|
7190
6547
|
`);
|
|
7191
|
-
|
|
6548
|
+
writeln(`${c13.bold("Options:")}`);
|
|
7192
6549
|
const opts = [
|
|
7193
6550
|
["-m, --model <id>", "Model to use (e.g. zen/claude-sonnet-4-6)"],
|
|
7194
6551
|
["-c, --continue", "Continue the most recent session"],
|
|
@@ -7198,10 +6555,10 @@ function printHelp() {
|
|
|
7198
6555
|
["-h, --help", "Show this help"]
|
|
7199
6556
|
];
|
|
7200
6557
|
for (const [flag, desc] of opts) {
|
|
7201
|
-
|
|
6558
|
+
writeln(` ${c13.cyan((flag ?? "").padEnd(22))} ${c13.dim(desc ?? "")}`);
|
|
7202
6559
|
}
|
|
7203
|
-
|
|
7204
|
-
${
|
|
6560
|
+
writeln(`
|
|
6561
|
+
${c13.bold("Provider env vars:")}`);
|
|
7205
6562
|
const envs = [
|
|
7206
6563
|
["OPENCODE_API_KEY", "OpenCode Zen (recommended)"],
|
|
7207
6564
|
["ANTHROPIC_API_KEY", "Anthropic direct"],
|
|
@@ -7212,22 +6569,22 @@ ${c14.bold("Provider env vars:")}`);
|
|
|
7212
6569
|
["OLLAMA_BASE_URL", "Ollama base URL (default: http://localhost:11434)"]
|
|
7213
6570
|
];
|
|
7214
6571
|
for (const [env, desc] of envs) {
|
|
7215
|
-
|
|
6572
|
+
writeln(` ${c13.yellow((env ?? "").padEnd(22))} ${c13.dim(desc ?? "")}`);
|
|
7216
6573
|
}
|
|
7217
|
-
|
|
7218
|
-
${
|
|
7219
|
-
|
|
7220
|
-
|
|
7221
|
-
|
|
7222
|
-
|
|
7223
|
-
|
|
6574
|
+
writeln(`
|
|
6575
|
+
${c13.bold("Examples:")}`);
|
|
6576
|
+
writeln(` mc ${c13.dim("# interactive session")}`);
|
|
6577
|
+
writeln(` mc "explain this codebase" ${c13.dim("# one-shot prompt then exit")}`);
|
|
6578
|
+
writeln(` mc -c ${c13.dim("# continue last session")}`);
|
|
6579
|
+
writeln(` mc -m ollama/llama3.2 ${c13.dim("# use local Ollama model")}`);
|
|
6580
|
+
writeln(` mc -l ${c13.dim("# list sessions")}`);
|
|
7224
6581
|
}
|
|
7225
6582
|
|
|
7226
6583
|
// src/cli/bootstrap.ts
|
|
7227
6584
|
import { existsSync as existsSync8, mkdirSync as mkdirSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
7228
6585
|
import { homedir as homedir8 } from "os";
|
|
7229
6586
|
import { join as join12 } from "path";
|
|
7230
|
-
import * as
|
|
6587
|
+
import * as c14 from "yoctocolors";
|
|
7231
6588
|
var REVIEW_COMMAND_CONTENT = `---
|
|
7232
6589
|
description: Review recent changes for correctness, code quality, and performance
|
|
7233
6590
|
context: fork
|
|
@@ -7251,7 +6608,7 @@ function bootstrapGlobalDefaults() {
|
|
|
7251
6608
|
if (!existsSync8(reviewPath)) {
|
|
7252
6609
|
mkdirSync4(commandsDir, { recursive: true });
|
|
7253
6610
|
writeFileSync3(reviewPath, REVIEW_COMMAND_CONTENT, "utf-8");
|
|
7254
|
-
|
|
6611
|
+
writeln(`${c14.green("\u2713")} created ${c14.dim("~/.agents/commands/review.md")} ${c14.dim("(edit it to customise your reviews)")}`);
|
|
7255
6612
|
}
|
|
7256
6613
|
}
|
|
7257
6614
|
|
|
@@ -7316,7 +6673,6 @@ class HeadlessReporter {
|
|
|
7316
6673
|
error(_msg, _hint) {}
|
|
7317
6674
|
warn(_msg) {}
|
|
7318
6675
|
writeText(_text) {}
|
|
7319
|
-
streamChunk(_text) {}
|
|
7320
6676
|
startSpinner(_label) {}
|
|
7321
6677
|
stopSpinner() {}
|
|
7322
6678
|
async renderTurn(events, _opts) {
|
|
@@ -7366,7 +6722,7 @@ class HeadlessReporter {
|
|
|
7366
6722
|
}
|
|
7367
6723
|
|
|
7368
6724
|
// src/cli/input-loop.ts
|
|
7369
|
-
import * as
|
|
6725
|
+
import * as c22 from "yoctocolors";
|
|
7370
6726
|
|
|
7371
6727
|
// src/cli/cli-helpers.ts
|
|
7372
6728
|
async function getGitBranch(cwd) {
|
|
@@ -7387,67 +6743,67 @@ async function getGitBranch(cwd) {
|
|
|
7387
6743
|
}
|
|
7388
6744
|
|
|
7389
6745
|
// src/cli/commands.ts
|
|
7390
|
-
import * as
|
|
6746
|
+
import * as c21 from "yoctocolors";
|
|
7391
6747
|
|
|
7392
6748
|
// src/cli/commands-agent.ts
|
|
7393
|
-
import * as
|
|
6749
|
+
import * as c15 from "yoctocolors";
|
|
7394
6750
|
function handleAgentCommand(ctx, args) {
|
|
7395
6751
|
const raw = args.trim();
|
|
7396
6752
|
const agents = loadAgents(ctx.cwd);
|
|
7397
6753
|
if (!raw) {
|
|
7398
6754
|
if (agents.size === 0) {
|
|
7399
|
-
|
|
7400
|
-
|
|
6755
|
+
writeln(c15.dim(" no agents found (~/.agents/agents/ or .agents/agents/)"));
|
|
6756
|
+
writeln(c15.dim(" /agent <name> to activate \xB7 /agent off to deactivate"));
|
|
7401
6757
|
return;
|
|
7402
6758
|
}
|
|
7403
|
-
|
|
7404
|
-
|
|
6759
|
+
writeln();
|
|
6760
|
+
writeln(c15.dim(" agents:"));
|
|
7405
6761
|
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
|
-
|
|
6762
|
+
const modeTag = agent2.mode ? c15.dim(` [${agent2.mode}]`) : "";
|
|
6763
|
+
const srcTag = agent2.source === "local" ? c15.dim(" (local)") : c15.dim(" (global)");
|
|
6764
|
+
const active = ctx.activeAgent === agent2.name ? c15.cyan(" \u25C0 active") : "";
|
|
6765
|
+
writeln(` ${c15.magenta(`@${agent2.name}`.padEnd(26))} ${c15.dim(agent2.description)}${modeTag}${srcTag}${active}`);
|
|
6766
|
+
}
|
|
6767
|
+
writeln();
|
|
6768
|
+
writeln(c15.dim(" /agent <name> to activate \xB7 /agent off to deactivate"));
|
|
6769
|
+
writeln();
|
|
7414
6770
|
return;
|
|
7415
6771
|
}
|
|
7416
6772
|
if (raw.toLowerCase() === "off" || raw.toLowerCase() === "none") {
|
|
7417
6773
|
ctx.setActiveAgent(null);
|
|
7418
|
-
|
|
6774
|
+
writeln(`${PREFIX.info} ${c15.dim("active agent cleared")}`);
|
|
7419
6775
|
return;
|
|
7420
6776
|
}
|
|
7421
6777
|
const agent = agents.get(raw);
|
|
7422
6778
|
if (!agent) {
|
|
7423
|
-
|
|
6779
|
+
writeln(`${PREFIX.error} agent ${c15.cyan(raw)} not found`);
|
|
7424
6780
|
return;
|
|
7425
6781
|
}
|
|
7426
6782
|
ctx.setActiveAgent(raw, agent.systemPrompt);
|
|
7427
|
-
|
|
6783
|
+
writeln(`${PREFIX.success} active agent \u2192 ${c15.cyan(raw)} ${c15.dim("(instructions appended to system prompt)")}`);
|
|
7428
6784
|
}
|
|
7429
6785
|
|
|
7430
6786
|
// src/cli/commands-config.ts
|
|
7431
|
-
import * as
|
|
6787
|
+
import * as c16 from "yoctocolors";
|
|
7432
6788
|
function handleBooleanToggleCommand(opts) {
|
|
7433
6789
|
const mode = opts.args.trim().toLowerCase();
|
|
7434
6790
|
if (!mode) {
|
|
7435
6791
|
const nextValue = !opts.current;
|
|
7436
6792
|
opts.set(nextValue);
|
|
7437
|
-
|
|
6793
|
+
writeln(`${PREFIX.success} ${opts.label} ${nextValue ? c16.green("on") : c16.dim("off")}`);
|
|
7438
6794
|
return;
|
|
7439
6795
|
}
|
|
7440
6796
|
if (mode === "on") {
|
|
7441
6797
|
opts.set(true);
|
|
7442
|
-
|
|
6798
|
+
writeln(`${PREFIX.success} ${opts.label} ${c16.green("on")}`);
|
|
7443
6799
|
return;
|
|
7444
6800
|
}
|
|
7445
6801
|
if (mode === "off") {
|
|
7446
6802
|
opts.set(false);
|
|
7447
|
-
|
|
6803
|
+
writeln(`${PREFIX.success} ${opts.label} ${c16.dim("off")}`);
|
|
7448
6804
|
return;
|
|
7449
6805
|
}
|
|
7450
|
-
|
|
6806
|
+
writeln(`${PREFIX.error} usage: ${opts.usage}`);
|
|
7451
6807
|
}
|
|
7452
6808
|
function handleReasoningCommand(ctx, args) {
|
|
7453
6809
|
handleBooleanToggleCommand({
|
|
@@ -7471,105 +6827,105 @@ function handleContextCommand(ctx, args) {
|
|
|
7471
6827
|
const [subcommand, value] = args.trim().split(/\s+/, 2);
|
|
7472
6828
|
if (!subcommand) {
|
|
7473
6829
|
const capText = ctx.toolResultPayloadCapBytes <= 0 ? "off" : `${Math.round(ctx.toolResultPayloadCapBytes / 1024)}KB (${ctx.toolResultPayloadCapBytes} bytes)`;
|
|
7474
|
-
|
|
7475
|
-
|
|
7476
|
-
|
|
6830
|
+
writeln(`${PREFIX.info} pruning=${c16.cyan(ctx.pruningMode)} tool-result-cap=${c16.cyan(capText)}`);
|
|
6831
|
+
writeln(c16.dim(" usage: /context prune <off|balanced|aggressive>"));
|
|
6832
|
+
writeln(c16.dim(" /context cap <off|bytes|kb>"));
|
|
7477
6833
|
return;
|
|
7478
6834
|
}
|
|
7479
6835
|
if (subcommand === "prune") {
|
|
7480
6836
|
if (value === "off" || value === "balanced" || value === "aggressive") {
|
|
7481
6837
|
ctx.setPruningMode(value);
|
|
7482
|
-
|
|
6838
|
+
writeln(`${PREFIX.success} context pruning \u2192 ${c16.cyan(value)}`);
|
|
7483
6839
|
return;
|
|
7484
6840
|
}
|
|
7485
|
-
|
|
6841
|
+
writeln(`${PREFIX.error} usage: /context prune <off|balanced|aggressive>`);
|
|
7486
6842
|
return;
|
|
7487
6843
|
}
|
|
7488
6844
|
if (subcommand !== "cap") {
|
|
7489
|
-
|
|
6845
|
+
writeln(`${PREFIX.error} usage: /context <prune|cap> ...`);
|
|
7490
6846
|
return;
|
|
7491
6847
|
}
|
|
7492
6848
|
if (!value) {
|
|
7493
|
-
|
|
6849
|
+
writeln(`${PREFIX.error} usage: /context cap <off|bytes|kb>`);
|
|
7494
6850
|
return;
|
|
7495
6851
|
}
|
|
7496
6852
|
if (value === "off") {
|
|
7497
6853
|
ctx.setToolResultPayloadCapBytes(0);
|
|
7498
|
-
|
|
6854
|
+
writeln(`${PREFIX.success} tool-result payload cap disabled`);
|
|
7499
6855
|
return;
|
|
7500
6856
|
}
|
|
7501
6857
|
const capMatch = value.match(/^(\d+)(kb)?$/i);
|
|
7502
6858
|
if (!capMatch) {
|
|
7503
|
-
|
|
6859
|
+
writeln(`${PREFIX.error} invalid cap: ${c16.cyan(value)}`);
|
|
7504
6860
|
return;
|
|
7505
6861
|
}
|
|
7506
6862
|
const base = Number.parseInt(capMatch[1] ?? "", 10);
|
|
7507
6863
|
const capBytes = (capMatch[2] ?? "").toLowerCase() === "kb" ? base * 1024 : base;
|
|
7508
6864
|
if (!Number.isFinite(capBytes) || capBytes < 0) {
|
|
7509
|
-
|
|
6865
|
+
writeln(`${PREFIX.error} invalid cap: ${c16.cyan(value)}`);
|
|
7510
6866
|
return;
|
|
7511
6867
|
}
|
|
7512
6868
|
ctx.setToolResultPayloadCapBytes(capBytes);
|
|
7513
|
-
|
|
6869
|
+
writeln(`${PREFIX.success} tool-result payload cap \u2192 ${c16.cyan(`${capBytes} bytes`)}`);
|
|
7514
6870
|
}
|
|
7515
6871
|
function handleCacheCommand(ctx, args) {
|
|
7516
6872
|
const [subcommand, value] = args.trim().split(/\s+/, 2);
|
|
7517
6873
|
if (!subcommand) {
|
|
7518
|
-
const geminiCache = ctx.googleCachedContent ?
|
|
7519
|
-
|
|
7520
|
-
|
|
7521
|
-
|
|
7522
|
-
|
|
6874
|
+
const geminiCache = ctx.googleCachedContent ? c16.cyan(ctx.googleCachedContent) : "off";
|
|
6875
|
+
writeln(`${PREFIX.info} prompt-caching=${ctx.promptCachingEnabled ? c16.green("on") : c16.dim("off")} openai-retention=${c16.cyan(ctx.openaiPromptCacheRetention)} gemini-cache=${geminiCache}`);
|
|
6876
|
+
writeln(c16.dim(" usage: /cache <on|off>"));
|
|
6877
|
+
writeln(c16.dim(" /cache openai <in_memory|24h>"));
|
|
6878
|
+
writeln(c16.dim(" /cache gemini <off|cachedContents/...>"));
|
|
7523
6879
|
return;
|
|
7524
6880
|
}
|
|
7525
6881
|
if (subcommand === "on" || subcommand === "off") {
|
|
7526
6882
|
ctx.setPromptCachingEnabled(subcommand === "on");
|
|
7527
|
-
|
|
6883
|
+
writeln(`${PREFIX.success} prompt caching \u2192 ${subcommand === "on" ? c16.green("on") : c16.dim("off")}`);
|
|
7528
6884
|
return;
|
|
7529
6885
|
}
|
|
7530
6886
|
if (subcommand === "openai") {
|
|
7531
6887
|
if (value === "in_memory" || value === "24h") {
|
|
7532
6888
|
ctx.setOpenAIPromptCacheRetention(value);
|
|
7533
|
-
|
|
6889
|
+
writeln(`${PREFIX.success} openai prompt cache retention \u2192 ${c16.cyan(value)}`);
|
|
7534
6890
|
return;
|
|
7535
6891
|
}
|
|
7536
|
-
|
|
6892
|
+
writeln(`${PREFIX.error} usage: /cache openai <in_memory|24h>`);
|
|
7537
6893
|
return;
|
|
7538
6894
|
}
|
|
7539
6895
|
if (subcommand !== "gemini") {
|
|
7540
|
-
|
|
6896
|
+
writeln(`${PREFIX.error} usage: /cache <on|off|openai|gemini> ...`);
|
|
7541
6897
|
return;
|
|
7542
6898
|
}
|
|
7543
6899
|
if (!value) {
|
|
7544
|
-
|
|
6900
|
+
writeln(`${PREFIX.error} usage: /cache gemini <off|cachedContents/...>`);
|
|
7545
6901
|
return;
|
|
7546
6902
|
}
|
|
7547
6903
|
if (value === "off") {
|
|
7548
6904
|
ctx.setGoogleCachedContent(null);
|
|
7549
|
-
|
|
6905
|
+
writeln(`${PREFIX.success} gemini cached content \u2192 ${c16.dim("off")}`);
|
|
7550
6906
|
return;
|
|
7551
6907
|
}
|
|
7552
6908
|
ctx.setGoogleCachedContent(value);
|
|
7553
|
-
|
|
6909
|
+
writeln(`${PREFIX.success} gemini cached content \u2192 ${c16.cyan(value)}`);
|
|
7554
6910
|
}
|
|
7555
6911
|
|
|
7556
6912
|
// src/cli/commands-help.ts
|
|
7557
|
-
import * as
|
|
6913
|
+
import * as c17 from "yoctocolors";
|
|
7558
6914
|
function renderEntries(entries) {
|
|
7559
6915
|
for (const [label, description] of entries) {
|
|
7560
|
-
|
|
6916
|
+
writeln(` ${c17.cyan(label.padEnd(28))} ${c17.dim(description)}`);
|
|
7561
6917
|
}
|
|
7562
6918
|
}
|
|
7563
6919
|
function renderHelpCommand(ctx, custom) {
|
|
7564
|
-
|
|
7565
|
-
|
|
6920
|
+
writeln();
|
|
6921
|
+
writeln(` ${c17.dim("session")}`);
|
|
7566
6922
|
renderEntries([
|
|
7567
6923
|
["/new", "start a fresh session"],
|
|
7568
6924
|
["/undo", "remove the last conversation turn"],
|
|
7569
6925
|
["/exit", "quit"]
|
|
7570
6926
|
]);
|
|
7571
|
-
|
|
7572
|
-
|
|
6927
|
+
writeln();
|
|
6928
|
+
writeln(` ${c17.dim("model + context")}`);
|
|
7573
6929
|
renderEntries([
|
|
7574
6930
|
["/model [id]", "list or switch models"],
|
|
7575
6931
|
["/reasoning [on|off]", "toggle reasoning display"],
|
|
@@ -7584,8 +6940,8 @@ function renderHelpCommand(ctx, custom) {
|
|
|
7584
6940
|
["/logout <provider>", "clear OAuth tokens"],
|
|
7585
6941
|
["/help", "show this help"]
|
|
7586
6942
|
]);
|
|
7587
|
-
|
|
7588
|
-
|
|
6943
|
+
writeln();
|
|
6944
|
+
writeln(` ${c17.dim("prompt")}`);
|
|
7589
6945
|
renderEntries([
|
|
7590
6946
|
["ask normally", "send a prompt to the current agent"],
|
|
7591
6947
|
["!cmd", "run a shell command and keep the result in context"],
|
|
@@ -7593,63 +6949,63 @@ function renderHelpCommand(ctx, custom) {
|
|
|
7593
6949
|
["@skill", "inject a skill into the prompt (Tab to complete)"]
|
|
7594
6950
|
]);
|
|
7595
6951
|
if (custom.size > 0) {
|
|
7596
|
-
|
|
7597
|
-
|
|
6952
|
+
writeln();
|
|
6953
|
+
writeln(` ${c17.dim("custom commands")}`);
|
|
7598
6954
|
for (const cmd of custom.values()) {
|
|
7599
|
-
const source = cmd.source === "local" ?
|
|
7600
|
-
|
|
6955
|
+
const source = cmd.source === "local" ? c17.dim("local") : c17.dim("global");
|
|
6956
|
+
writeln(` ${c17.green(`/${cmd.name}`.padEnd(28))} ${c17.dim(cmd.description)} ${c17.dim("\xB7")} ${source}`);
|
|
7601
6957
|
}
|
|
7602
6958
|
}
|
|
7603
6959
|
const agents = loadAgents(ctx.cwd);
|
|
7604
6960
|
if (agents.size > 0) {
|
|
7605
|
-
|
|
7606
|
-
|
|
6961
|
+
writeln();
|
|
6962
|
+
writeln(` ${c17.dim("agents")}`);
|
|
7607
6963
|
for (const agent of agents.values()) {
|
|
7608
|
-
const mode = agent.mode ?
|
|
7609
|
-
const source = agent.source === "local" ?
|
|
7610
|
-
|
|
6964
|
+
const mode = agent.mode ? c17.dim(agent.mode) : c17.dim("agent");
|
|
6965
|
+
const source = agent.source === "local" ? c17.dim("local") : c17.dim("global");
|
|
6966
|
+
writeln(` ${c17.magenta(agent.name.padEnd(28))} ${c17.dim(agent.description)} ${c17.dim("\xB7")} ${mode} ${c17.dim("\xB7")} ${source}`);
|
|
7611
6967
|
}
|
|
7612
6968
|
}
|
|
7613
6969
|
const skills = loadSkillsIndex(ctx.cwd);
|
|
7614
6970
|
if (skills.size > 0) {
|
|
7615
|
-
|
|
7616
|
-
|
|
6971
|
+
writeln();
|
|
6972
|
+
writeln(` ${c17.dim("skills")}`);
|
|
7617
6973
|
for (const skill of skills.values()) {
|
|
7618
|
-
const source = skill.source === "local" ?
|
|
7619
|
-
|
|
6974
|
+
const source = skill.source === "local" ? c17.dim("local") : c17.dim("global");
|
|
6975
|
+
writeln(` ${c17.yellow(`@${skill.name}`.padEnd(28))} ${c17.dim(skill.description)} ${c17.dim("\xB7")} ${source}`);
|
|
7620
6976
|
}
|
|
7621
6977
|
}
|
|
7622
|
-
|
|
7623
|
-
|
|
7624
|
-
|
|
6978
|
+
writeln();
|
|
6979
|
+
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`);
|
|
6980
|
+
writeln();
|
|
7625
6981
|
}
|
|
7626
6982
|
|
|
7627
6983
|
// src/cli/commands-login.ts
|
|
7628
|
-
import * as
|
|
6984
|
+
import * as c18 from "yoctocolors";
|
|
7629
6985
|
function renderLoginHelp() {
|
|
7630
|
-
|
|
7631
|
-
|
|
7632
|
-
|
|
7633
|
-
|
|
7634
|
-
|
|
7635
|
-
|
|
7636
|
-
|
|
6986
|
+
writeln();
|
|
6987
|
+
writeln(` ${c18.dim("usage:")}`);
|
|
6988
|
+
writeln(` /login ${c18.dim("show login status")}`);
|
|
6989
|
+
writeln(` /login <provider> ${c18.dim("login via OAuth")}`);
|
|
6990
|
+
writeln(` /logout <provider> ${c18.dim("clear saved tokens")}`);
|
|
6991
|
+
writeln();
|
|
6992
|
+
writeln(` ${c18.dim("providers:")}`);
|
|
7637
6993
|
for (const p of getOAuthProviders()) {
|
|
7638
|
-
const status = isLoggedIn(p.id) ?
|
|
7639
|
-
|
|
6994
|
+
const status = isLoggedIn(p.id) ? c18.green("logged in") : c18.dim("not logged in");
|
|
6995
|
+
writeln(` ${c18.cyan(p.id.padEnd(20))} ${p.name} ${c18.dim("\xB7")} ${status}`);
|
|
7640
6996
|
}
|
|
7641
|
-
|
|
6997
|
+
writeln();
|
|
7642
6998
|
}
|
|
7643
6999
|
function renderStatus() {
|
|
7644
7000
|
const loggedIn = listLoggedInProviders();
|
|
7645
7001
|
if (loggedIn.length === 0) {
|
|
7646
|
-
|
|
7002
|
+
writeln(`${PREFIX.info} ${c18.dim("no OAuth logins \u2014 use")} /login <provider>`);
|
|
7647
7003
|
return;
|
|
7648
7004
|
}
|
|
7649
7005
|
for (const id of loggedIn) {
|
|
7650
7006
|
const provider = getOAuthProvider(id);
|
|
7651
7007
|
const name = provider?.name ?? id;
|
|
7652
|
-
|
|
7008
|
+
writeln(`${PREFIX.success} ${c18.cyan(id)} ${c18.dim(name)}`);
|
|
7653
7009
|
}
|
|
7654
7010
|
}
|
|
7655
7011
|
async function handleLoginCommand(ctx, args) {
|
|
@@ -7664,13 +7020,13 @@ async function handleLoginCommand(ctx, args) {
|
|
|
7664
7020
|
}
|
|
7665
7021
|
const provider = getOAuthProvider(providerId);
|
|
7666
7022
|
if (!provider) {
|
|
7667
|
-
|
|
7023
|
+
writeln(`${PREFIX.error} unknown provider "${providerId}" \u2014 available: ${getOAuthProviders().map((p) => p.id).join(", ")}`);
|
|
7668
7024
|
return;
|
|
7669
7025
|
}
|
|
7670
7026
|
if (isLoggedIn(providerId)) {
|
|
7671
7027
|
const token = await getAccessToken(providerId);
|
|
7672
7028
|
if (token) {
|
|
7673
|
-
|
|
7029
|
+
writeln(`${PREFIX.success} already logged in to ${c18.cyan(provider.name)}`);
|
|
7674
7030
|
return;
|
|
7675
7031
|
}
|
|
7676
7032
|
}
|
|
@@ -7679,43 +7035,43 @@ async function handleLoginCommand(ctx, args) {
|
|
|
7679
7035
|
await login(providerId, {
|
|
7680
7036
|
onOpenUrl: (url, instructions) => {
|
|
7681
7037
|
ctx.stopSpinner();
|
|
7682
|
-
|
|
7683
|
-
|
|
7684
|
-
|
|
7685
|
-
|
|
7038
|
+
writeln(`${PREFIX.info} ${instructions}`);
|
|
7039
|
+
writeln();
|
|
7040
|
+
writeln(` ${c18.cyan(url)}`);
|
|
7041
|
+
writeln();
|
|
7686
7042
|
const open = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
7687
7043
|
Bun.spawn([open, url], { stdout: "ignore", stderr: "ignore" });
|
|
7688
7044
|
ctx.startSpinner("waiting for browser callback");
|
|
7689
7045
|
},
|
|
7690
7046
|
onProgress: (msg) => {
|
|
7691
7047
|
ctx.stopSpinner();
|
|
7692
|
-
|
|
7048
|
+
writeln(`${PREFIX.info} ${c18.dim(msg)}`);
|
|
7693
7049
|
ctx.startSpinner("exchanging tokens");
|
|
7694
7050
|
}
|
|
7695
7051
|
});
|
|
7696
7052
|
ctx.stopSpinner();
|
|
7697
|
-
|
|
7053
|
+
writeln(`${PREFIX.success} logged in to ${c18.cyan(provider.name)}`);
|
|
7698
7054
|
} catch (err) {
|
|
7699
7055
|
ctx.stopSpinner();
|
|
7700
|
-
|
|
7056
|
+
writeln(`${PREFIX.error} login failed: ${err.message}`);
|
|
7701
7057
|
}
|
|
7702
7058
|
}
|
|
7703
7059
|
function handleLogoutCommand(_ctx, args) {
|
|
7704
7060
|
const providerId = args.trim().toLowerCase();
|
|
7705
7061
|
if (!providerId) {
|
|
7706
|
-
|
|
7062
|
+
writeln(`${PREFIX.error} usage: /logout <provider>`);
|
|
7707
7063
|
return;
|
|
7708
7064
|
}
|
|
7709
7065
|
if (!isLoggedIn(providerId)) {
|
|
7710
|
-
|
|
7066
|
+
writeln(`${PREFIX.info} ${c18.dim("not logged in to")} ${providerId}`);
|
|
7711
7067
|
return;
|
|
7712
7068
|
}
|
|
7713
7069
|
logout(providerId);
|
|
7714
|
-
|
|
7070
|
+
writeln(`${PREFIX.success} logged out of ${c18.cyan(providerId)}`);
|
|
7715
7071
|
}
|
|
7716
7072
|
|
|
7717
7073
|
// src/cli/commands-mcp.ts
|
|
7718
|
-
import * as
|
|
7074
|
+
import * as c19 from "yoctocolors";
|
|
7719
7075
|
async function handleMcpCommand(ctx, args) {
|
|
7720
7076
|
const parts = args.trim().split(/\s+/);
|
|
7721
7077
|
const sub = parts[0] ?? "list";
|
|
@@ -7723,28 +7079,28 @@ async function handleMcpCommand(ctx, args) {
|
|
|
7723
7079
|
case "list": {
|
|
7724
7080
|
const servers = listMcpServers();
|
|
7725
7081
|
if (servers.length === 0) {
|
|
7726
|
-
|
|
7727
|
-
|
|
7082
|
+
writeln(c19.dim(" no MCP servers configured"));
|
|
7083
|
+
writeln(c19.dim(" /mcp add <name> http <url> \xB7 /mcp add <name> stdio <cmd> [args...]"));
|
|
7728
7084
|
return;
|
|
7729
7085
|
}
|
|
7730
|
-
|
|
7086
|
+
writeln();
|
|
7731
7087
|
for (const s of servers) {
|
|
7732
|
-
const detail = s.url ?
|
|
7733
|
-
|
|
7088
|
+
const detail = s.url ? c19.dim(` ${s.url}`) : s.command ? c19.dim(` ${s.command}`) : "";
|
|
7089
|
+
writeln(` ${c19.yellow("\u2699")} ${c19.bold(s.name)} ${c19.dim(s.transport)}${detail}`);
|
|
7734
7090
|
}
|
|
7735
7091
|
return;
|
|
7736
7092
|
}
|
|
7737
7093
|
case "add": {
|
|
7738
7094
|
const [, name, transport, ...rest] = parts;
|
|
7739
7095
|
if (!name || !transport || rest.length === 0) {
|
|
7740
|
-
|
|
7741
|
-
|
|
7096
|
+
writeln(`${PREFIX.error} usage: /mcp add <name> http <url>`);
|
|
7097
|
+
writeln(`${PREFIX.error} /mcp add <name> stdio <cmd> [args...]`);
|
|
7742
7098
|
return;
|
|
7743
7099
|
}
|
|
7744
7100
|
if (transport === "http") {
|
|
7745
7101
|
const url = rest[0];
|
|
7746
7102
|
if (!url) {
|
|
7747
|
-
|
|
7103
|
+
writeln(`${PREFIX.error} usage: /mcp add <name> http <url>`);
|
|
7748
7104
|
return;
|
|
7749
7105
|
}
|
|
7750
7106
|
upsertMcpServer({
|
|
@@ -7758,7 +7114,7 @@ async function handleMcpCommand(ctx, args) {
|
|
|
7758
7114
|
} else if (transport === "stdio") {
|
|
7759
7115
|
const [command, ...cmdArgs] = rest;
|
|
7760
7116
|
if (!command) {
|
|
7761
|
-
|
|
7117
|
+
writeln(`${PREFIX.error} usage: /mcp add <name> stdio <cmd> [args...]`);
|
|
7762
7118
|
return;
|
|
7763
7119
|
}
|
|
7764
7120
|
upsertMcpServer({
|
|
@@ -7770,14 +7126,14 @@ async function handleMcpCommand(ctx, args) {
|
|
|
7770
7126
|
env: null
|
|
7771
7127
|
});
|
|
7772
7128
|
} else {
|
|
7773
|
-
|
|
7129
|
+
writeln(`${PREFIX.error} unknown transport: ${transport} (use http or stdio)`);
|
|
7774
7130
|
return;
|
|
7775
7131
|
}
|
|
7776
7132
|
try {
|
|
7777
7133
|
await ctx.connectMcpServer(name);
|
|
7778
|
-
|
|
7134
|
+
writeln(`${PREFIX.success} mcp server ${c19.cyan(name)} added and connected`);
|
|
7779
7135
|
} catch (e) {
|
|
7780
|
-
|
|
7136
|
+
writeln(`${PREFIX.success} mcp server ${c19.cyan(name)} saved ${c19.dim(`(connection failed: ${String(e)})`)}`);
|
|
7781
7137
|
}
|
|
7782
7138
|
return;
|
|
7783
7139
|
}
|
|
@@ -7785,21 +7141,21 @@ async function handleMcpCommand(ctx, args) {
|
|
|
7785
7141
|
case "rm": {
|
|
7786
7142
|
const [, name] = parts;
|
|
7787
7143
|
if (!name) {
|
|
7788
|
-
|
|
7144
|
+
writeln(`${PREFIX.error} usage: /mcp remove <name>`);
|
|
7789
7145
|
return;
|
|
7790
7146
|
}
|
|
7791
7147
|
deleteMcpServer(name);
|
|
7792
|
-
|
|
7148
|
+
writeln(`${PREFIX.success} mcp server ${c19.cyan(name)} removed`);
|
|
7793
7149
|
return;
|
|
7794
7150
|
}
|
|
7795
7151
|
default:
|
|
7796
|
-
|
|
7797
|
-
|
|
7152
|
+
writeln(`${PREFIX.error} unknown: /mcp ${sub}`);
|
|
7153
|
+
writeln(c19.dim(" subcommands: list \xB7 add \xB7 remove"));
|
|
7798
7154
|
}
|
|
7799
7155
|
}
|
|
7800
7156
|
|
|
7801
7157
|
// src/cli/commands-model.ts
|
|
7802
|
-
import * as
|
|
7158
|
+
import * as c20 from "yoctocolors";
|
|
7803
7159
|
var THINKING_EFFORTS = ["low", "medium", "high", "xhigh"];
|
|
7804
7160
|
function parseThinkingEffort(value) {
|
|
7805
7161
|
return THINKING_EFFORTS.includes(value) ? value : null;
|
|
@@ -7818,21 +7174,21 @@ function renderModelUpdatedMessage(ctx, modelId, effortArg) {
|
|
|
7818
7174
|
if (effortArg) {
|
|
7819
7175
|
if (effortArg === "off") {
|
|
7820
7176
|
ctx.setThinkingEffort(null);
|
|
7821
|
-
|
|
7177
|
+
writeln(`${PREFIX.success} model \u2192 ${c20.cyan(modelId)} ${c20.dim("(thinking disabled)")}`);
|
|
7822
7178
|
return;
|
|
7823
7179
|
}
|
|
7824
7180
|
const effort = parseThinkingEffort(effortArg);
|
|
7825
7181
|
if (effort) {
|
|
7826
7182
|
ctx.setThinkingEffort(effort);
|
|
7827
|
-
|
|
7183
|
+
writeln(`${PREFIX.success} model \u2192 ${c20.cyan(modelId)} ${c20.dim(`(\u2726 ${effort})`)}`);
|
|
7828
7184
|
return;
|
|
7829
7185
|
}
|
|
7830
|
-
|
|
7831
|
-
|
|
7186
|
+
writeln(`${PREFIX.success} model \u2192 ${c20.cyan(modelId)}`);
|
|
7187
|
+
writeln(`${PREFIX.error} unknown effort level ${c20.cyan(effortArg)} (use low, medium, high, xhigh, off)`);
|
|
7832
7188
|
return;
|
|
7833
7189
|
}
|
|
7834
|
-
const effortTag = ctx.thinkingEffort ?
|
|
7835
|
-
|
|
7190
|
+
const effortTag = ctx.thinkingEffort ? c20.dim(` (\u2726 ${ctx.thinkingEffort})`) : "";
|
|
7191
|
+
writeln(`${PREFIX.success} model \u2192 ${c20.cyan(modelId)}${effortTag}`);
|
|
7836
7192
|
}
|
|
7837
7193
|
async function handleModelSet(ctx, args) {
|
|
7838
7194
|
const parts = args.trim().split(/\s+/).filter(Boolean);
|
|
@@ -7845,7 +7201,7 @@ async function handleModelSet(ctx, args) {
|
|
|
7845
7201
|
const snapshot = await fetchAvailableModels();
|
|
7846
7202
|
const match = findModelIdByAlias(idArg, snapshot.models.map((model) => model.id));
|
|
7847
7203
|
if (!match) {
|
|
7848
|
-
|
|
7204
|
+
writeln(`${PREFIX.error} unknown model ${c20.cyan(idArg)} ${c20.dim("\u2014 run /models for the full list")}`);
|
|
7849
7205
|
return;
|
|
7850
7206
|
}
|
|
7851
7207
|
modelId = match;
|
|
@@ -7856,30 +7212,30 @@ async function handleModelSet(ctx, args) {
|
|
|
7856
7212
|
function handleModelEffort(ctx, effortArg) {
|
|
7857
7213
|
if (effortArg === "off") {
|
|
7858
7214
|
ctx.setThinkingEffort(null);
|
|
7859
|
-
|
|
7215
|
+
writeln(`${PREFIX.success} thinking effort disabled`);
|
|
7860
7216
|
return;
|
|
7861
7217
|
}
|
|
7862
7218
|
const effort = parseThinkingEffort(effortArg);
|
|
7863
7219
|
if (!effort) {
|
|
7864
|
-
|
|
7220
|
+
writeln(`${PREFIX.error} usage: /model effort <low|medium|high|xhigh|off>`);
|
|
7865
7221
|
return;
|
|
7866
7222
|
}
|
|
7867
7223
|
ctx.setThinkingEffort(effort);
|
|
7868
|
-
|
|
7224
|
+
writeln(`${PREFIX.success} thinking effort \u2192 ${c20.cyan(effort)}`);
|
|
7869
7225
|
}
|
|
7870
7226
|
async function renderModelList(ctx) {
|
|
7871
7227
|
ctx.startSpinner("fetching models");
|
|
7872
7228
|
const snapshot = await fetchAvailableModels();
|
|
7873
7229
|
ctx.stopSpinner();
|
|
7874
7230
|
if (snapshot.models.length === 0) {
|
|
7875
|
-
|
|
7876
|
-
|
|
7231
|
+
writeln(`${PREFIX.error} No models found. Check your API keys or Ollama connection.`);
|
|
7232
|
+
writeln(c20.dim(" Set OPENCODE_API_KEY for Zen, or start Ollama for local models."));
|
|
7877
7233
|
return;
|
|
7878
7234
|
}
|
|
7879
7235
|
if (snapshot.stale) {
|
|
7880
7236
|
const lastSync = snapshot.lastSyncAt ? new Date(snapshot.lastSyncAt).toLocaleString() : "never";
|
|
7881
7237
|
const refreshTag = snapshot.refreshing ? " (refreshing in background)" : "";
|
|
7882
|
-
|
|
7238
|
+
writeln(c20.dim(` model metadata is stale (last sync: ${lastSync})${refreshTag}`));
|
|
7883
7239
|
}
|
|
7884
7240
|
const modelsByProvider = new Map;
|
|
7885
7241
|
for (const model of snapshot.models) {
|
|
@@ -7890,22 +7246,22 @@ async function renderModelList(ctx) {
|
|
|
7890
7246
|
modelsByProvider.set(model.provider, [model]);
|
|
7891
7247
|
}
|
|
7892
7248
|
}
|
|
7893
|
-
|
|
7249
|
+
writeln();
|
|
7894
7250
|
for (const [provider, providerModels] of modelsByProvider) {
|
|
7895
|
-
|
|
7251
|
+
writeln(c20.bold(` ${provider}`));
|
|
7896
7252
|
for (const model of providerModels) {
|
|
7897
7253
|
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
|
-
|
|
7254
|
+
const freeTag = model.free ? c20.green(" free") : "";
|
|
7255
|
+
const contextTag = model.context ? c20.dim(` ${Math.round(model.context / 1000)}k`) : "";
|
|
7256
|
+
const effortTag = isCurrent && ctx.thinkingEffort ? c20.dim(` \u2726 ${ctx.thinkingEffort}`) : "";
|
|
7257
|
+
const currentTag = isCurrent ? c20.cyan(" \u25C0") : "";
|
|
7258
|
+
writeln(` ${c20.dim("\xB7")} ${model.displayName}${freeTag}${contextTag}${currentTag}${effortTag}`);
|
|
7259
|
+
writeln(` ${c20.dim(model.id)}`);
|
|
7904
7260
|
}
|
|
7905
7261
|
}
|
|
7906
|
-
|
|
7907
|
-
|
|
7908
|
-
|
|
7262
|
+
writeln();
|
|
7263
|
+
writeln(c20.dim(" /model <id> to switch \xB7 e.g. /model zen/claude-sonnet-4-6"));
|
|
7264
|
+
writeln(c20.dim(" /model effort <low|medium|high|xhigh|off> to set thinking effort"));
|
|
7909
7265
|
}
|
|
7910
7266
|
async function handleModelCommand(ctx, args) {
|
|
7911
7267
|
const parts = args.trim().split(/\s+/).filter(Boolean);
|
|
@@ -7926,9 +7282,9 @@ async function handleUndo(ctx) {
|
|
|
7926
7282
|
try {
|
|
7927
7283
|
const ok = await ctx.undoLastTurn();
|
|
7928
7284
|
if (ok) {
|
|
7929
|
-
|
|
7285
|
+
writeln(`${PREFIX.success} ${c21.dim("last conversation turn removed")}`);
|
|
7930
7286
|
} else {
|
|
7931
|
-
|
|
7287
|
+
writeln(`${PREFIX.info} ${c21.dim("nothing to undo")}`);
|
|
7932
7288
|
}
|
|
7933
7289
|
} finally {
|
|
7934
7290
|
ctx.stopSpinner();
|
|
@@ -7941,11 +7297,11 @@ function handleNew(ctx) {
|
|
|
7941
7297
|
}
|
|
7942
7298
|
async function handleCustomCommand(cmd, args, ctx) {
|
|
7943
7299
|
const prompt = await expandTemplate(cmd.template, args, ctx.cwd);
|
|
7944
|
-
const label =
|
|
7300
|
+
const label = c21.cyan(cmd.name);
|
|
7945
7301
|
const srcPath = cmd.source === "local" ? `.agents/commands/${cmd.name}.md` : `~/.agents/commands/${cmd.name}.md`;
|
|
7946
|
-
const src =
|
|
7947
|
-
|
|
7948
|
-
|
|
7302
|
+
const src = c21.dim(`[${srcPath}]`);
|
|
7303
|
+
writeln(`${PREFIX.info} ${label} ${src}`);
|
|
7304
|
+
writeln();
|
|
7949
7305
|
const fork = cmd.context === "fork" || cmd.subtask === true;
|
|
7950
7306
|
if (!fork) {
|
|
7951
7307
|
return { type: "inject-user-message", text: prompt };
|
|
@@ -7957,8 +7313,8 @@ async function handleCustomCommand(cmd, args, ctx) {
|
|
|
7957
7313
|
try {
|
|
7958
7314
|
ctx.startSpinner("subagent");
|
|
7959
7315
|
const output = await ctx.runSubagent(prompt, cmd.agent, cmd.model, abortController.signal);
|
|
7960
|
-
|
|
7961
|
-
|
|
7316
|
+
write(output.result);
|
|
7317
|
+
writeln();
|
|
7962
7318
|
return {
|
|
7963
7319
|
type: "inject-user-message",
|
|
7964
7320
|
text: `/${cmd.name} output:
|
|
@@ -7971,7 +7327,7 @@ ${output.result}
|
|
|
7971
7327
|
if (isAbortError(e)) {
|
|
7972
7328
|
return { type: "handled" };
|
|
7973
7329
|
}
|
|
7974
|
-
|
|
7330
|
+
writeln(`${PREFIX.error} /${cmd.name} failed: ${String(e)}`);
|
|
7975
7331
|
return { type: "handled" };
|
|
7976
7332
|
} finally {
|
|
7977
7333
|
stopWatcher();
|
|
@@ -8028,7 +7384,7 @@ async function handleCommand(command, args, ctx) {
|
|
|
8028
7384
|
case "q":
|
|
8029
7385
|
return { type: "exit" };
|
|
8030
7386
|
default: {
|
|
8031
|
-
|
|
7387
|
+
writeln(`${PREFIX.error} unknown: /${command} ${c21.dim("\u2014 /help for commands")}`);
|
|
8032
7388
|
return { type: "unknown", command };
|
|
8033
7389
|
}
|
|
8034
7390
|
}
|
|
@@ -8124,7 +7480,7 @@ async function runInputLoop(opts) {
|
|
|
8124
7480
|
}
|
|
8125
7481
|
switch (input.type) {
|
|
8126
7482
|
case "eof":
|
|
8127
|
-
reporter.writeText(
|
|
7483
|
+
reporter.writeText(c22.dim("Goodbye."));
|
|
8128
7484
|
return;
|
|
8129
7485
|
case "interrupt":
|
|
8130
7486
|
gitBranchCache.refreshInBackground();
|
|
@@ -8132,7 +7488,7 @@ async function runInputLoop(opts) {
|
|
|
8132
7488
|
case "command": {
|
|
8133
7489
|
const result = await handleCommand(input.command, input.args, cmdCtx);
|
|
8134
7490
|
if (result.type === "exit") {
|
|
8135
|
-
reporter.writeText(
|
|
7491
|
+
reporter.writeText(c22.dim("Goodbye."));
|
|
8136
7492
|
return;
|
|
8137
7493
|
}
|
|
8138
7494
|
if (result.type === "inject-user-message") {
|
|
@@ -8150,13 +7506,7 @@ async function runInputLoop(opts) {
|
|
|
8150
7506
|
const result = await runShellCommand({
|
|
8151
7507
|
command: input.command,
|
|
8152
7508
|
timeout: 30000,
|
|
8153
|
-
cwd
|
|
8154
|
-
...cmdCtx.verboseOutput ? {
|
|
8155
|
-
onOutput: (chunk) => {
|
|
8156
|
-
reporter.streamChunk(chunk);
|
|
8157
|
-
return true;
|
|
8158
|
-
}
|
|
8159
|
-
} : {}
|
|
7509
|
+
cwd
|
|
8160
7510
|
});
|
|
8161
7511
|
renderToolResult("shell", result, false, {
|
|
8162
7512
|
verboseOutput: cmdCtx.verboseOutput
|
|
@@ -8211,8 +7561,8 @@ async function resolvePromptInput(promptArg, opts) {
|
|
|
8211
7561
|
|
|
8212
7562
|
// src/cli/structured-output.ts
|
|
8213
7563
|
import { createTwoFilesPatch } from "diff";
|
|
8214
|
-
function writeJsonLine(
|
|
8215
|
-
|
|
7564
|
+
function writeJsonLine(write2, payload) {
|
|
7565
|
+
write2(`${JSON.stringify(payload)}
|
|
8216
7566
|
`);
|
|
8217
7567
|
}
|
|
8218
7568
|
|
|
@@ -8268,7 +7618,7 @@ async function main() {
|
|
|
8268
7618
|
if (last) {
|
|
8269
7619
|
sessionId = last.id;
|
|
8270
7620
|
} else {
|
|
8271
|
-
|
|
7621
|
+
writeln(c23.dim("No previous session found, starting fresh."));
|
|
8272
7622
|
}
|
|
8273
7623
|
} else if (args.sessionId) {
|
|
8274
7624
|
sessionId = args.sessionId;
|
|
@@ -8335,7 +7685,7 @@ async function main() {
|
|
|
8335
7685
|
const { text: resolvedText, images: refImages } = await resolveFileRefs(prompt, args.cwd);
|
|
8336
7686
|
await runner.processUserInput(resolvedText, refImages);
|
|
8337
7687
|
const { totalIn, totalOut } = runner.getStatusInfo();
|
|
8338
|
-
|
|
7688
|
+
writeln(`${G.info} ${c23.dim(`${totalIn.toLocaleString()} in / ${totalOut.toLocaleString()} out tokens`)}`);
|
|
8339
7689
|
return;
|
|
8340
7690
|
}
|
|
8341
7691
|
await runInputLoop({
|