laohuang 0.8.0 → 0.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.js +1145 -415
- package/dist/bin.js.map +4 -4
- package/package.json +3 -1
package/dist/bin.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/main.ts
|
|
4
|
-
import { existsSync as
|
|
5
|
-
import { dirname as dirname4, join as
|
|
4
|
+
import { existsSync as existsSync8, readFileSync as readFileSync6 } from "node:fs";
|
|
5
|
+
import { dirname as dirname4, join as join7 } from "node:path";
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
7
7
|
|
|
8
8
|
// ../../packages/core/agent-runtime/dist/agent.js
|
|
@@ -62,8 +62,8 @@ var CancelToken = class {
|
|
|
62
62
|
continue;
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
|
-
for (const
|
|
66
|
-
|
|
65
|
+
for (const resolve3 of this.#waiters) {
|
|
66
|
+
resolve3();
|
|
67
67
|
}
|
|
68
68
|
this.#waiters.clear();
|
|
69
69
|
return true;
|
|
@@ -76,20 +76,20 @@ var CancelToken = class {
|
|
|
76
76
|
if (this.#cancelled) {
|
|
77
77
|
return Promise.resolve(true);
|
|
78
78
|
}
|
|
79
|
-
return new Promise((
|
|
79
|
+
return new Promise((resolve3) => {
|
|
80
80
|
let timer;
|
|
81
81
|
const onCancel = () => {
|
|
82
82
|
if (timer !== void 0) {
|
|
83
83
|
clearTimeout(timer);
|
|
84
84
|
}
|
|
85
85
|
this.#waiters.delete(onCancel);
|
|
86
|
-
|
|
86
|
+
resolve3(true);
|
|
87
87
|
};
|
|
88
88
|
this.#waiters.add(onCancel);
|
|
89
89
|
if (timeoutMs !== void 0) {
|
|
90
90
|
timer = setTimeout(() => {
|
|
91
91
|
this.#waiters.delete(onCancel);
|
|
92
|
-
|
|
92
|
+
resolve3(false);
|
|
93
93
|
}, timeoutMs);
|
|
94
94
|
}
|
|
95
95
|
});
|
|
@@ -149,6 +149,7 @@ var EventKind = {
|
|
|
149
149
|
AgentRepeatWarning: "agent.repeat_warning",
|
|
150
150
|
ToolStarted: "tool.started",
|
|
151
151
|
ToolOutputDelta: "tool.output_delta",
|
|
152
|
+
ToolOutputSnapshot: "tool.output_snapshot",
|
|
152
153
|
ToolFinished: "tool.finished",
|
|
153
154
|
SessionReady: "session.ready",
|
|
154
155
|
SessionStopped: "session.stopped",
|
|
@@ -222,7 +223,7 @@ var EventSpec = class {
|
|
|
222
223
|
this.payload_types = options.payload_types ?? {};
|
|
223
224
|
this.require_task_id = options.require_task_id ?? false;
|
|
224
225
|
this.require_correlation_id = options.require_correlation_id ?? false;
|
|
225
|
-
this.max_payload_chars = options.max_payload_chars
|
|
226
|
+
this.max_payload_chars = options.max_payload_chars === void 0 ? DEFAULT_MAX_PAYLOAD_CHARS : options.max_payload_chars;
|
|
226
227
|
this.validator = options.validator ?? null;
|
|
227
228
|
}
|
|
228
229
|
validate(event) {
|
|
@@ -254,7 +255,7 @@ var EventSpec = class {
|
|
|
254
255
|
} catch (error) {
|
|
255
256
|
throw new EventValidationError(`event ${event.kind} payload must be JSON-compatible`, { cause: error });
|
|
256
257
|
}
|
|
257
|
-
if (payloadSize > this.max_payload_chars) {
|
|
258
|
+
if (this.max_payload_chars !== null && payloadSize > this.max_payload_chars) {
|
|
258
259
|
throw new EventValidationError(`event ${event.kind} payload exceeds ${this.max_payload_chars} characters`);
|
|
259
260
|
}
|
|
260
261
|
if (this.validator !== null && this.validator(event) === false) {
|
|
@@ -414,7 +415,16 @@ var EVENT_SPECS = new Map([
|
|
|
414
415
|
payload_types: { stream: "string", text: "string" },
|
|
415
416
|
require_task_id: true,
|
|
416
417
|
require_correlation_id: true,
|
|
417
|
-
max_payload_chars:
|
|
418
|
+
max_payload_chars: null
|
|
419
|
+
}),
|
|
420
|
+
spec(EventKind.ToolOutputSnapshot, {
|
|
421
|
+
sources: [EventSource.Tool],
|
|
422
|
+
required_payload: ["stream", "text"],
|
|
423
|
+
payload_types: { stream: "string", text: "string", stream_sequence: "integer" },
|
|
424
|
+
require_task_id: true,
|
|
425
|
+
require_correlation_id: true,
|
|
426
|
+
// Producers own output budgets; JSON escaping must not reject valid previews.
|
|
427
|
+
max_payload_chars: null
|
|
418
428
|
}),
|
|
419
429
|
spec(EventKind.ToolFinished, {
|
|
420
430
|
sources: [EventSource.Tool],
|
|
@@ -422,7 +432,7 @@ var EVENT_SPECS = new Map([
|
|
|
422
432
|
payload_types: { status: "string" },
|
|
423
433
|
require_task_id: true,
|
|
424
434
|
require_correlation_id: true,
|
|
425
|
-
max_payload_chars:
|
|
435
|
+
max_payload_chars: null
|
|
426
436
|
}),
|
|
427
437
|
spec(EventKind.RoutingDecided, {
|
|
428
438
|
sources: [EventSource.Router],
|
|
@@ -528,6 +538,12 @@ function projectValue(value) {
|
|
|
528
538
|
}
|
|
529
539
|
return value;
|
|
530
540
|
}
|
|
541
|
+
function omitPartialOutputLine(value) {
|
|
542
|
+
if (value === "")
|
|
543
|
+
return value;
|
|
544
|
+
const newline = value.indexOf("\n");
|
|
545
|
+
return `[partial line omitted]${newline >= 0 ? value.slice(newline) : ""}`;
|
|
546
|
+
}
|
|
531
547
|
var EventProjector = class _EventProjector {
|
|
532
548
|
static AUDIENCES = /* @__PURE__ */ new Set([
|
|
533
549
|
"terminal",
|
|
@@ -538,6 +554,16 @@ var EventProjector = class _EventProjector {
|
|
|
538
554
|
if (!_EventProjector.AUDIENCES.has(audience)) {
|
|
539
555
|
throw new Error(`unknown event audience: ${audience}`);
|
|
540
556
|
}
|
|
557
|
+
const payload = { ...event.payload };
|
|
558
|
+
if (event.kind === EventKind.ToolOutputSnapshot && payload["start_mid_line"] === true && typeof payload["text"] === "string") {
|
|
559
|
+
payload["text"] = omitPartialOutputLine(payload["text"]);
|
|
560
|
+
} else if (event.kind === EventKind.ToolFinished) {
|
|
561
|
+
for (const stream of ["stdout", "stderr"]) {
|
|
562
|
+
if (payload[`${stream}_start_mid_line`] === true && typeof payload[stream] === "string") {
|
|
563
|
+
payload[stream] = omitPartialOutputLine(payload[stream]);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
}
|
|
541
567
|
return {
|
|
542
568
|
event_id: event.event_id,
|
|
543
569
|
kind: event.kind,
|
|
@@ -546,7 +572,7 @@ var EventProjector = class _EventProjector {
|
|
|
546
572
|
task_id: event.task_id,
|
|
547
573
|
correlation_id: event.correlation_id,
|
|
548
574
|
sequence: event.sequence,
|
|
549
|
-
payload: projectValue(
|
|
575
|
+
payload: projectValue(payload)
|
|
550
576
|
};
|
|
551
577
|
}
|
|
552
578
|
};
|
|
@@ -554,7 +580,8 @@ var COALESCIBLE_EVENTS = /* @__PURE__ */ new Set([
|
|
|
554
580
|
EventKind.ModelTextDelta,
|
|
555
581
|
EventKind.ModelReasoningDelta,
|
|
556
582
|
EventKind.ModelToolCallDelta,
|
|
557
|
-
EventKind.ToolOutputDelta
|
|
583
|
+
EventKind.ToolOutputDelta,
|
|
584
|
+
EventKind.ToolOutputSnapshot
|
|
558
585
|
]);
|
|
559
586
|
var MAX_COALESCED_TEXT_CHARS = 65536;
|
|
560
587
|
var MAILBOX_CRITICAL_HEADROOM = 64;
|
|
@@ -582,7 +609,7 @@ var SubscriberMailbox = class _SubscriberMailbox {
|
|
|
582
609
|
this.workerDone = this.run();
|
|
583
610
|
}
|
|
584
611
|
static merge(left, right) {
|
|
585
|
-
if (left.kind !== right.kind || left.task_id !== right.task_id || left.correlation_id !== right.correlation_id || !COALESCIBLE_EVENTS.has(left.kind)) {
|
|
612
|
+
if (left.kind !== right.kind || left.task_id !== right.task_id || left.correlation_id !== right.correlation_id || left.kind === EventKind.ToolOutputSnapshot || !COALESCIBLE_EVENTS.has(left.kind)) {
|
|
586
613
|
return null;
|
|
587
614
|
}
|
|
588
615
|
const leftText = left.payload["text"];
|
|
@@ -606,15 +633,23 @@ var SubscriberMailbox = class _SubscriberMailbox {
|
|
|
606
633
|
if (this.closed) {
|
|
607
634
|
return false;
|
|
608
635
|
}
|
|
636
|
+
if (event.kind === EventKind.ToolOutputSnapshot) {
|
|
637
|
+
const previous = this.items.findIndex((item) => item.kind === event.kind && item.task_id === event.task_id && item.correlation_id === event.correlation_id && item.payload.stream === event.payload.stream);
|
|
638
|
+
if (previous >= 0) {
|
|
639
|
+
const old = this.items.splice(previous, 1)[0];
|
|
640
|
+
this.unfinished--;
|
|
641
|
+
this.pendingGap += gapOf(old.payload);
|
|
642
|
+
}
|
|
643
|
+
}
|
|
609
644
|
if (this.items.length > 0) {
|
|
610
645
|
const last = this.items[this.items.length - 1];
|
|
611
646
|
const merged = _SubscriberMailbox.merge(last, event);
|
|
612
|
-
if (merged !== null && this.items.length >= this.maxItems - MAILBOX_CRITICAL_HEADROOM) {
|
|
647
|
+
if (merged !== null && event.kind !== EventKind.ToolOutputSnapshot && this.items.length >= this.maxItems - MAILBOX_CRITICAL_HEADROOM) {
|
|
613
648
|
this.items[this.items.length - 1] = this.withGapMarker(merged);
|
|
614
649
|
return true;
|
|
615
650
|
}
|
|
616
651
|
}
|
|
617
|
-
if (COALESCIBLE_EVENTS.has(event.kind) && this.items.length >= this.maxItems - MAILBOX_CRITICAL_HEADROOM) {
|
|
652
|
+
if (COALESCIBLE_EVENTS.has(event.kind) && event.kind !== EventKind.ToolOutputSnapshot && this.items.length >= this.maxItems - MAILBOX_CRITICAL_HEADROOM) {
|
|
618
653
|
this.recordDrop();
|
|
619
654
|
return false;
|
|
620
655
|
}
|
|
@@ -650,8 +685,8 @@ var SubscriberMailbox = class _SubscriberMailbox {
|
|
|
650
685
|
const deadline = timeoutMs === void 0 ? void 0 : Date.now() + timeoutMs;
|
|
651
686
|
while (this.unfinished > 0) {
|
|
652
687
|
if (deadline === void 0) {
|
|
653
|
-
await new Promise((
|
|
654
|
-
this.flushWaiters.push(
|
|
688
|
+
await new Promise((resolve3) => {
|
|
689
|
+
this.flushWaiters.push(resolve3);
|
|
655
690
|
});
|
|
656
691
|
continue;
|
|
657
692
|
}
|
|
@@ -659,12 +694,12 @@ var SubscriberMailbox = class _SubscriberMailbox {
|
|
|
659
694
|
if (remaining <= 0) {
|
|
660
695
|
return false;
|
|
661
696
|
}
|
|
662
|
-
const waiter = new Promise((
|
|
663
|
-
this.flushWaiters.push(
|
|
697
|
+
const waiter = new Promise((resolve3) => {
|
|
698
|
+
this.flushWaiters.push(resolve3);
|
|
664
699
|
});
|
|
665
700
|
let timer;
|
|
666
|
-
const timeout = new Promise((
|
|
667
|
-
timer = setTimeout(
|
|
701
|
+
const timeout = new Promise((resolve3) => {
|
|
702
|
+
timer = setTimeout(resolve3, remaining);
|
|
668
703
|
});
|
|
669
704
|
await Promise.race([waiter, timeout]);
|
|
670
705
|
clearTimeout(timer);
|
|
@@ -693,22 +728,22 @@ var SubscriberMailbox = class _SubscriberMailbox {
|
|
|
693
728
|
wake() {
|
|
694
729
|
const waiters = this.wakeWaiters;
|
|
695
730
|
this.wakeWaiters = [];
|
|
696
|
-
for (const
|
|
697
|
-
|
|
731
|
+
for (const resolve3 of waiters) {
|
|
732
|
+
resolve3();
|
|
698
733
|
}
|
|
699
734
|
}
|
|
700
735
|
notifyFlushWaiters() {
|
|
701
736
|
const waiters = this.flushWaiters;
|
|
702
737
|
this.flushWaiters = [];
|
|
703
|
-
for (const
|
|
704
|
-
|
|
738
|
+
for (const resolve3 of waiters) {
|
|
739
|
+
resolve3();
|
|
705
740
|
}
|
|
706
741
|
}
|
|
707
742
|
async run() {
|
|
708
743
|
for (; ; ) {
|
|
709
744
|
while (this.items.length === 0 && !this.closed) {
|
|
710
|
-
await new Promise((
|
|
711
|
-
this.wakeWaiters.push(
|
|
745
|
+
await new Promise((resolve3) => {
|
|
746
|
+
this.wakeWaiters.push(resolve3);
|
|
712
747
|
});
|
|
713
748
|
}
|
|
714
749
|
if (this.items.length === 0 && this.closed) {
|
|
@@ -780,6 +815,11 @@ var EventBus = class {
|
|
|
780
815
|
}
|
|
781
816
|
this.sequence += 1;
|
|
782
817
|
const published = { ...event, sequence: this.sequence };
|
|
818
|
+
if (event.kind === EventKind.ToolOutputSnapshot) {
|
|
819
|
+
const previous = this.buffer.findIndex((item) => item.kind === event.kind && item.task_id === event.task_id && item.correlation_id === event.correlation_id && item.payload.stream === event.payload.stream);
|
|
820
|
+
if (previous >= 0)
|
|
821
|
+
this.buffer.splice(previous, 1);
|
|
822
|
+
}
|
|
783
823
|
if (this.buffer.length >= this.maxBufferedEvents) {
|
|
784
824
|
this.buffer.shift();
|
|
785
825
|
}
|
|
@@ -851,8 +891,8 @@ var EventBus = class {
|
|
|
851
891
|
if (this.closed) {
|
|
852
892
|
return Promise.reject(new EventBusClosedError());
|
|
853
893
|
}
|
|
854
|
-
return new Promise((
|
|
855
|
-
const waiter = { resolve:
|
|
894
|
+
return new Promise((resolve3, reject) => {
|
|
895
|
+
const waiter = { resolve: resolve3, reject };
|
|
856
896
|
if (timeoutMs !== void 0) {
|
|
857
897
|
waiter.timer = setTimeout(() => {
|
|
858
898
|
const index = this.bufferWaiters.indexOf(waiter);
|
|
@@ -1190,7 +1230,7 @@ async function defaultSleep(delayMs, cancelToken) {
|
|
|
1190
1230
|
throw new ModelStreamCancelled(cancelToken?.reason || "cancelled");
|
|
1191
1231
|
}
|
|
1192
1232
|
if (cancelToken === null) {
|
|
1193
|
-
await new Promise((
|
|
1233
|
+
await new Promise((resolve3) => setTimeout(resolve3, delayMs));
|
|
1194
1234
|
}
|
|
1195
1235
|
}
|
|
1196
1236
|
function errorMessage(error) {
|
|
@@ -1229,20 +1269,20 @@ ${body}`;
|
|
|
1229
1269
|
].join("\n");
|
|
1230
1270
|
}
|
|
1231
1271
|
function runtimeFactLines(options) {
|
|
1232
|
-
const
|
|
1272
|
+
const lines2 = [];
|
|
1233
1273
|
if (hasText(options.cliName)) {
|
|
1234
|
-
|
|
1274
|
+
lines2.push(`- CLI: ${options.cliName}`);
|
|
1235
1275
|
}
|
|
1236
1276
|
if (hasText(options.cliVersion)) {
|
|
1237
|
-
|
|
1277
|
+
lines2.push(`- CLI version: ${options.cliVersion}`);
|
|
1238
1278
|
}
|
|
1239
1279
|
if (hasText(options.provider) && hasText(options.model)) {
|
|
1240
|
-
|
|
1280
|
+
lines2.push(`- Provider/model: ${options.provider}/${options.model}`);
|
|
1241
1281
|
}
|
|
1242
1282
|
if (hasText(options.promptCwd)) {
|
|
1243
|
-
|
|
1283
|
+
lines2.push(`- Current working directory: ${options.promptCwd}`);
|
|
1244
1284
|
}
|
|
1245
|
-
return
|
|
1285
|
+
return lines2;
|
|
1246
1286
|
}
|
|
1247
1287
|
function hasText(value) {
|
|
1248
1288
|
return value !== null && value !== void 0 && value.length > 0;
|
|
@@ -1701,14 +1741,6 @@ function optionalPositiveInteger(args, key) {
|
|
|
1701
1741
|
}
|
|
1702
1742
|
return value;
|
|
1703
1743
|
}
|
|
1704
|
-
function truncateText(text, maxOutputChars) {
|
|
1705
|
-
if (text.length <= maxOutputChars) {
|
|
1706
|
-
return text;
|
|
1707
|
-
}
|
|
1708
|
-
const omitted = text.length - maxOutputChars;
|
|
1709
|
-
return `${text.slice(0, maxOutputChars)}
|
|
1710
|
-
...[truncated ${omitted} chars]`;
|
|
1711
|
-
}
|
|
1712
1744
|
function errorMessage3(error) {
|
|
1713
1745
|
return error instanceof Error ? error.message : String(error);
|
|
1714
1746
|
}
|
|
@@ -2480,12 +2512,11 @@ function makeToolContext(context, toolCallId, cancelToken) {
|
|
|
2480
2512
|
cancellationReason: cancelToken?.reason || "cancelled",
|
|
2481
2513
|
publish: (kind, payload) => {
|
|
2482
2514
|
if (typeof context?.publish === "function") {
|
|
2483
|
-
context.publish(kind, {
|
|
2515
|
+
return context.publish(kind, {
|
|
2484
2516
|
source: EventSource.Tool,
|
|
2485
2517
|
correlation_id: toolCallId,
|
|
2486
2518
|
payload
|
|
2487
2519
|
});
|
|
2488
|
-
return;
|
|
2489
2520
|
}
|
|
2490
2521
|
const bus = context?.eventBus ?? null;
|
|
2491
2522
|
if (bus !== null) {
|
|
@@ -2534,14 +2565,14 @@ var ConversationHistory = class _ConversationHistory {
|
|
|
2534
2565
|
}
|
|
2535
2566
|
static fromReplay(replay, journal) {
|
|
2536
2567
|
const history = new _ConversationHistory(replay.items.filter((item) => item.kind === "entry"), journal);
|
|
2537
|
-
for (const
|
|
2568
|
+
for (const open2 of replay.openToolCalls) {
|
|
2538
2569
|
const entry = history.appendToolResults({
|
|
2539
2570
|
requestId: "recovered",
|
|
2540
2571
|
recovered: true,
|
|
2541
2572
|
messages: [{
|
|
2542
2573
|
role: "tool-result",
|
|
2543
|
-
toolCallId:
|
|
2544
|
-
toolName:
|
|
2574
|
+
toolCallId: open2.toolCallId,
|
|
2575
|
+
toolName: open2.toolName,
|
|
2545
2576
|
content: JSON.stringify({
|
|
2546
2577
|
ok: false,
|
|
2547
2578
|
status: "interrupted",
|
|
@@ -2554,7 +2585,7 @@ var ConversationHistory = class _ConversationHistory {
|
|
|
2554
2585
|
recordType: "error",
|
|
2555
2586
|
payload: {
|
|
2556
2587
|
type: "interrupted_tool_call",
|
|
2557
|
-
toolCallId:
|
|
2588
|
+
toolCallId: open2.toolCallId,
|
|
2558
2589
|
repairedEntryId: entry?.id ?? null
|
|
2559
2590
|
}
|
|
2560
2591
|
});
|
|
@@ -2901,26 +2932,26 @@ function selectCompactionPlan(input) {
|
|
|
2901
2932
|
};
|
|
2902
2933
|
}
|
|
2903
2934
|
function serializeConversation(entries) {
|
|
2904
|
-
const
|
|
2935
|
+
const lines2 = ["<conversation>"];
|
|
2905
2936
|
for (const entry of entries) {
|
|
2906
|
-
|
|
2937
|
+
lines2.push(`<entry id="${entry.id}" seq="${entry.seq}" type="${entry.entryType}">`);
|
|
2907
2938
|
if (entry.entryType === "assistant_message") {
|
|
2908
2939
|
for (const block of entry.payload.message.content) {
|
|
2909
2940
|
if (block.type === "tool-call") {
|
|
2910
|
-
|
|
2941
|
+
lines2.push(`[assistant tool-call ${block.call.id} ${block.call.name}] ${JSON.stringify(block.call.arguments)}`);
|
|
2911
2942
|
} else {
|
|
2912
|
-
|
|
2943
|
+
lines2.push(`[assistant ${block.type}] ${block.text}`);
|
|
2913
2944
|
}
|
|
2914
2945
|
}
|
|
2915
2946
|
} else if (entry.entryType === "tool_result") {
|
|
2916
|
-
|
|
2947
|
+
lines2.push(`[tool-result ${entry.payload.message.toolCallId} ${entry.payload.message.toolName}] ${entry.payload.message.content}`);
|
|
2917
2948
|
} else if (entry.entryType === "user_message" || entry.entryType === "reminder" || entry.entryType === "project_instructions") {
|
|
2918
|
-
|
|
2949
|
+
lines2.push(`[${entry.payload.message.role}] ${entry.payload.message.content}`);
|
|
2919
2950
|
}
|
|
2920
|
-
|
|
2951
|
+
lines2.push("</entry>");
|
|
2921
2952
|
}
|
|
2922
|
-
|
|
2923
|
-
return
|
|
2953
|
+
lines2.push("</conversation>");
|
|
2954
|
+
return lines2.join("\n");
|
|
2924
2955
|
}
|
|
2925
2956
|
function semanticUnits(entries) {
|
|
2926
2957
|
const units = [];
|
|
@@ -3673,8 +3704,8 @@ var SessionManager = class {
|
|
|
3673
3704
|
const visit = (directory) => {
|
|
3674
3705
|
for (const entry of readdirSync(directory)) {
|
|
3675
3706
|
const absolute = join2(directory, entry);
|
|
3676
|
-
const
|
|
3677
|
-
if (
|
|
3707
|
+
const stat2 = statSync2(absolute);
|
|
3708
|
+
if (stat2.isDirectory()) {
|
|
3678
3709
|
visit(absolute);
|
|
3679
3710
|
} else if (absolute.endsWith(".jsonl")) {
|
|
3680
3711
|
paths.push(absolute);
|
|
@@ -4679,31 +4710,106 @@ function errorMessage6(error) {
|
|
|
4679
4710
|
|
|
4680
4711
|
// ../../packages/storage/local-config/dist/config.js
|
|
4681
4712
|
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
4682
|
-
import { chmodSync, existsSync as
|
|
4683
|
-
import { homedir } from "node:os";
|
|
4713
|
+
import { chmodSync, existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as readFileSync3, renameSync, writeFileSync } from "node:fs";
|
|
4684
4714
|
import { basename as basename2, dirname, join as join3 } from "node:path";
|
|
4715
|
+
|
|
4716
|
+
// ../../packages/fs/local-paths/dist/index.js
|
|
4717
|
+
import { execFileSync } from "node:child_process";
|
|
4718
|
+
import { existsSync as existsSync4 } from "node:fs";
|
|
4719
|
+
import { homedir } from "node:os";
|
|
4720
|
+
import { posix, win32 } from "node:path";
|
|
4721
|
+
function envValue(env, name) {
|
|
4722
|
+
const key = Object.keys(env).find((key2) => key2.toLowerCase() === name.toLowerCase());
|
|
4723
|
+
return key === void 0 ? void 0 : env[key];
|
|
4724
|
+
}
|
|
4725
|
+
function convertVirtualPath(input, options) {
|
|
4726
|
+
if (options.convertShellPath)
|
|
4727
|
+
return options.convertShellPath(input);
|
|
4728
|
+
const candidates = [];
|
|
4729
|
+
const env = options.env ?? process.env;
|
|
4730
|
+
if (options.shellPath) {
|
|
4731
|
+
const directory = win32.dirname(options.shellPath());
|
|
4732
|
+
candidates.push(win32.join(directory, "cygpath.exe"), win32.resolve(directory, "../usr/bin/cygpath.exe"));
|
|
4733
|
+
} else {
|
|
4734
|
+
for (const name of ["ProgramFiles", "ProgramFiles(x86)"]) {
|
|
4735
|
+
const root = envValue(env, name);
|
|
4736
|
+
if (root && win32.isAbsolute(root))
|
|
4737
|
+
candidates.push(win32.join(root, "Git", "usr", "bin", "cygpath.exe"));
|
|
4738
|
+
}
|
|
4739
|
+
for (const entry of (envValue(env, "PATH") ?? "").split(";")) {
|
|
4740
|
+
const directory = entry.replace(/^"(.*)"$/, "$1");
|
|
4741
|
+
if (win32.isAbsolute(directory)) {
|
|
4742
|
+
candidates.push(win32.join(directory, "cygpath.exe"), win32.resolve(directory, "../usr/bin/cygpath.exe"));
|
|
4743
|
+
}
|
|
4744
|
+
}
|
|
4745
|
+
}
|
|
4746
|
+
const executable = candidates.find((candidate) => existsSync4(candidate));
|
|
4747
|
+
if (!executable)
|
|
4748
|
+
throw new Error(`Cannot map Git Bash path '${input}': cygpath.exe was not found. Use a Windows absolute path.`);
|
|
4749
|
+
return execFileSync(executable, ["-w", "--", input], {
|
|
4750
|
+
encoding: "utf8",
|
|
4751
|
+
windowsHide: true,
|
|
4752
|
+
timeout: 5e3
|
|
4753
|
+
}).replace(/\r?\n$/, "");
|
|
4754
|
+
}
|
|
4755
|
+
function getHomeDirectory(options = {}) {
|
|
4756
|
+
const windows = (options.platform ?? process.platform) === "win32";
|
|
4757
|
+
const env = options.env ?? process.env;
|
|
4758
|
+
const home = windows ? envValue(env, "USERPROFILE") || envValue(env, "HOME") || homedir() : env.HOME || homedir();
|
|
4759
|
+
return normalizeLocalPath(home, options, false);
|
|
4760
|
+
}
|
|
4761
|
+
function normalizeLocalPath(input, options = {}, expandHome = true) {
|
|
4762
|
+
if (input.includes("\0"))
|
|
4763
|
+
throw new Error("Paths cannot contain a NUL character");
|
|
4764
|
+
const windows = (options.platform ?? process.platform) === "win32";
|
|
4765
|
+
const paths = windows ? win32 : posix;
|
|
4766
|
+
if (expandHome && (input === "~" || input.startsWith("~/") || windows && input.startsWith("~\\"))) {
|
|
4767
|
+
return paths.join(getHomeDirectory(options), input.slice(2));
|
|
4768
|
+
}
|
|
4769
|
+
if (!windows)
|
|
4770
|
+
return input;
|
|
4771
|
+
if (input.startsWith("/") && !input.startsWith("//") && !input.includes("\\")) {
|
|
4772
|
+
const drive = /^\/(?:mnt\/|cygdrive\/)?([a-z])(?:\/(.*))?$/i.exec(input);
|
|
4773
|
+
if (drive)
|
|
4774
|
+
return `${drive[1].toUpperCase()}:\\${(drive[2] ?? "").replaceAll("/", "\\")}`;
|
|
4775
|
+
const converted = convertVirtualPath(input, options);
|
|
4776
|
+
if (!/^(?:[a-z]:[\\/]|\\\\)/i.test(converted)) {
|
|
4777
|
+
throw new Error(`Git Bash path did not resolve to a Windows absolute path: ${input}`);
|
|
4778
|
+
}
|
|
4779
|
+
return converted;
|
|
4780
|
+
}
|
|
4781
|
+
return input;
|
|
4782
|
+
}
|
|
4783
|
+
function resolveLocalPath(input, base = process.cwd(), options = {}) {
|
|
4784
|
+
const paths = (options.platform ?? process.platform) === "win32" ? win32 : posix;
|
|
4785
|
+
return paths.resolve(normalizeLocalPath(base, options), normalizeLocalPath(input, options));
|
|
4786
|
+
}
|
|
4787
|
+
function displayLocalPath(input, home, platform = process.platform) {
|
|
4788
|
+
if (!input || !home)
|
|
4789
|
+
return input;
|
|
4790
|
+
const paths = platform === "win32" ? win32 : posix;
|
|
4791
|
+
if (!paths.isAbsolute(input) || !paths.isAbsolute(home))
|
|
4792
|
+
return input;
|
|
4793
|
+
const relative = paths.relative(home, input);
|
|
4794
|
+
if (relative === "")
|
|
4795
|
+
return "~";
|
|
4796
|
+
return relative !== ".." && !relative.startsWith(`..${paths.sep}`) && !paths.isAbsolute(relative) ? `~/${relative.split(paths.sep).join("/")}` : input;
|
|
4797
|
+
}
|
|
4798
|
+
|
|
4799
|
+
// ../../packages/storage/local-config/dist/config.js
|
|
4685
4800
|
function isPlainObject(value) {
|
|
4686
4801
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4687
4802
|
}
|
|
4688
4803
|
function errorMessage7(error) {
|
|
4689
4804
|
return error instanceof Error ? error.message : String(error);
|
|
4690
4805
|
}
|
|
4691
|
-
function expandUser(input) {
|
|
4692
|
-
if (input === "~") {
|
|
4693
|
-
return homedir();
|
|
4694
|
-
}
|
|
4695
|
-
if (input.startsWith("~/")) {
|
|
4696
|
-
return join3(homedir(), input.slice(2));
|
|
4697
|
-
}
|
|
4698
|
-
return input;
|
|
4699
|
-
}
|
|
4700
4806
|
function defaultConfigPath(environ = process.env) {
|
|
4701
4807
|
const explicit = environ["LAOHUANG_CONFIG"];
|
|
4702
4808
|
if (explicit) {
|
|
4703
|
-
return
|
|
4809
|
+
return normalizeLocalPath(explicit, { env: environ });
|
|
4704
4810
|
}
|
|
4705
4811
|
const configHome = environ["XDG_CONFIG_HOME"];
|
|
4706
|
-
const root = configHome ?
|
|
4812
|
+
const root = configHome ? normalizeLocalPath(configHome, { env: environ }) : join3(getHomeDirectory({ env: environ }), ".config");
|
|
4707
4813
|
return join3(root, "laohuang", "config.json");
|
|
4708
4814
|
}
|
|
4709
4815
|
var ConfigManager = class {
|
|
@@ -4734,6 +4840,9 @@ var ConfigManager = class {
|
|
|
4734
4840
|
resolve(options = {}) {
|
|
4735
4841
|
return this.resolveSettings(options);
|
|
4736
4842
|
}
|
|
4843
|
+
getShellPath() {
|
|
4844
|
+
return this.readDocument({ optional: true })["shell_path"];
|
|
4845
|
+
}
|
|
4737
4846
|
resolveSettings(options = {}) {
|
|
4738
4847
|
const document = this.readDocument();
|
|
4739
4848
|
const environment = options.environ ?? process.env;
|
|
@@ -4778,7 +4887,7 @@ var ConfigManager = class {
|
|
|
4778
4887
|
});
|
|
4779
4888
|
}
|
|
4780
4889
|
readDocument(options = {}) {
|
|
4781
|
-
if (!
|
|
4890
|
+
if (!existsSync5(this.path)) {
|
|
4782
4891
|
if (options.optional) {
|
|
4783
4892
|
return {};
|
|
4784
4893
|
}
|
|
@@ -4807,6 +4916,10 @@ var ConfigManager = class {
|
|
|
4807
4916
|
}
|
|
4808
4917
|
};
|
|
4809
4918
|
function validateDocument(document) {
|
|
4919
|
+
const shellPath = document["shell_path"];
|
|
4920
|
+
if (shellPath !== void 0 && (typeof shellPath !== "string" || !shellPath.trim())) {
|
|
4921
|
+
throw new Error("Configuration shell_path must be a non-empty string");
|
|
4922
|
+
}
|
|
4810
4923
|
const version = document["version"] ?? 1;
|
|
4811
4924
|
if (version !== 1) {
|
|
4812
4925
|
throw new Error(`Unsupported configuration version: ${String(version)}`);
|
|
@@ -4843,7 +4956,7 @@ function validateDocument(document) {
|
|
|
4843
4956
|
|
|
4844
4957
|
// ../../packages/storage/local-config/dist/credentials.js
|
|
4845
4958
|
import { randomUUID as randomUUID7 } from "node:crypto";
|
|
4846
|
-
import { chmodSync as chmodSync2, existsSync as
|
|
4959
|
+
import { chmodSync as chmodSync2, existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync4, renameSync as renameSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
4847
4960
|
import { basename as basename3, dirname as dirname2, join as join4 } from "node:path";
|
|
4848
4961
|
function isPlainObject2(value) {
|
|
4849
4962
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -4968,7 +5081,7 @@ var CredentialStore = class {
|
|
|
4968
5081
|
await operation;
|
|
4969
5082
|
}
|
|
4970
5083
|
readDocument() {
|
|
4971
|
-
if (!
|
|
5084
|
+
if (!existsSync6(this.path)) {
|
|
4972
5085
|
return { version: 2, providers: {} };
|
|
4973
5086
|
}
|
|
4974
5087
|
let document;
|
|
@@ -4999,7 +5112,7 @@ var CredentialStore = class {
|
|
|
4999
5112
|
}
|
|
5000
5113
|
writeDocument(document) {
|
|
5001
5114
|
const parent = dirname2(this.path);
|
|
5002
|
-
const parentExisted =
|
|
5115
|
+
const parentExisted = existsSync6(parent);
|
|
5003
5116
|
mkdirSync3(parent, { recursive: true, mode: 448 });
|
|
5004
5117
|
if (!parentExisted) {
|
|
5005
5118
|
chmodSync2(parent, 448);
|
|
@@ -5024,7 +5137,7 @@ function normalizeStoredCredential(credential) {
|
|
|
5024
5137
|
|
|
5025
5138
|
// ../../packages/storage/local-config/dist/model-catalog-store.js
|
|
5026
5139
|
import { randomUUID as randomUUID8 } from "node:crypto";
|
|
5027
|
-
import { chmodSync as chmodSync3, existsSync as
|
|
5140
|
+
import { chmodSync as chmodSync3, existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as readFileSync5, renameSync as renameSync3, writeFileSync as writeFileSync3 } from "node:fs";
|
|
5028
5141
|
import { basename as basename4, dirname as dirname3, join as join5 } from "node:path";
|
|
5029
5142
|
function isPlainObject3(value) {
|
|
5030
5143
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -5056,7 +5169,7 @@ var ModelCatalogStore = class {
|
|
|
5056
5169
|
}
|
|
5057
5170
|
}
|
|
5058
5171
|
readDocument() {
|
|
5059
|
-
if (!
|
|
5172
|
+
if (!existsSync7(this.path)) {
|
|
5060
5173
|
return { version: 1, providers: {} };
|
|
5061
5174
|
}
|
|
5062
5175
|
let document;
|
|
@@ -5083,7 +5196,7 @@ var ModelCatalogStore = class {
|
|
|
5083
5196
|
}
|
|
5084
5197
|
writeDocument(document) {
|
|
5085
5198
|
const parent = dirname3(this.path);
|
|
5086
|
-
const parentExisted =
|
|
5199
|
+
const parentExisted = existsSync7(parent);
|
|
5087
5200
|
mkdirSync4(parent, { recursive: true, mode: 448 });
|
|
5088
5201
|
if (!parentExisted) {
|
|
5089
5202
|
chmodSync3(parent, 448);
|
|
@@ -6609,20 +6722,20 @@ var AgentSession = class {
|
|
|
6609
6722
|
if (timeoutMs !== void 0 && timeoutMs <= 0) {
|
|
6610
6723
|
return Promise.resolve(false);
|
|
6611
6724
|
}
|
|
6612
|
-
return new Promise((
|
|
6725
|
+
return new Promise((resolve3) => {
|
|
6613
6726
|
let timer;
|
|
6614
6727
|
const onIdle = () => {
|
|
6615
6728
|
if (timer !== void 0) {
|
|
6616
6729
|
clearTimeout(timer);
|
|
6617
6730
|
}
|
|
6618
6731
|
this.#idleWaiters.delete(onIdle);
|
|
6619
|
-
|
|
6732
|
+
resolve3(true);
|
|
6620
6733
|
};
|
|
6621
6734
|
this.#idleWaiters.add(onIdle);
|
|
6622
6735
|
if (timeoutMs !== void 0) {
|
|
6623
6736
|
timer = setTimeout(() => {
|
|
6624
6737
|
this.#idleWaiters.delete(onIdle);
|
|
6625
|
-
|
|
6738
|
+
resolve3(false);
|
|
6626
6739
|
}, timeoutMs);
|
|
6627
6740
|
timer.unref();
|
|
6628
6741
|
}
|
|
@@ -6936,8 +7049,8 @@ var AgentSession = class {
|
|
|
6936
7049
|
if (value) {
|
|
6937
7050
|
const waiters = [...this.#idleWaiters];
|
|
6938
7051
|
this.#idleWaiters.clear();
|
|
6939
|
-
for (const
|
|
6940
|
-
|
|
7052
|
+
for (const resolve3 of waiters) {
|
|
7053
|
+
resolve3();
|
|
6941
7054
|
}
|
|
6942
7055
|
}
|
|
6943
7056
|
}
|
|
@@ -7070,8 +7183,89 @@ function unavailableActionNotice(action) {
|
|
|
7070
7183
|
return "Cancellation is unavailable for this runtime.";
|
|
7071
7184
|
}
|
|
7072
7185
|
|
|
7186
|
+
// ../../packages/terminal/tui/dist/tui/native-console.js
|
|
7187
|
+
import { createRequire } from "node:module";
|
|
7188
|
+
var require2 = createRequire(import.meta.url);
|
|
7189
|
+
var native;
|
|
7190
|
+
var activeConsoles = [];
|
|
7191
|
+
function getWindowsConsole() {
|
|
7192
|
+
if (process.platform !== "win32")
|
|
7193
|
+
return null;
|
|
7194
|
+
if (native !== void 0)
|
|
7195
|
+
return native;
|
|
7196
|
+
const koffi = require2("koffi");
|
|
7197
|
+
const kernel = koffi.load("kernel32.dll");
|
|
7198
|
+
const user = koffi.load("user32.dll");
|
|
7199
|
+
const getHandle = kernel.func("__stdcall", "GetStdHandle", "void *", ["uint32_t"]);
|
|
7200
|
+
const getMode = kernel.func("__stdcall", "GetConsoleMode", "int", ["void *", "void *"]);
|
|
7201
|
+
const setMode = kernel.func("__stdcall", "SetConsoleMode", "int", ["void *", "uint32_t"]);
|
|
7202
|
+
const getKey = user.func("__stdcall", "GetAsyncKeyState", "int16_t", ["int"]);
|
|
7203
|
+
const getError = kernel.func("__stdcall", "GetLastError", "uint32_t", []);
|
|
7204
|
+
const handle = getHandle(4294967286);
|
|
7205
|
+
const buffer = Buffer.alloc(4);
|
|
7206
|
+
if (!getMode(handle, buffer))
|
|
7207
|
+
return native = null;
|
|
7208
|
+
native = {
|
|
7209
|
+
getMode: () => {
|
|
7210
|
+
if (!getMode(handle, buffer))
|
|
7211
|
+
throw new Error(`GetConsoleMode failed (${getError()})`);
|
|
7212
|
+
return buffer.readUInt32LE();
|
|
7213
|
+
},
|
|
7214
|
+
setMode: (mode) => {
|
|
7215
|
+
if (!setMode(handle, mode))
|
|
7216
|
+
throw new Error(`SetConsoleMode failed (${getError()})`);
|
|
7217
|
+
},
|
|
7218
|
+
shiftPressed: () => (getKey(16) & 32768) !== 0
|
|
7219
|
+
};
|
|
7220
|
+
return native;
|
|
7221
|
+
}
|
|
7222
|
+
function isLocalWindowsConsole() {
|
|
7223
|
+
return process.platform === "win32" && activeConsoles.length > 0 && !process.env.SSH_CONNECTION && !process.env.SSH_CLIENT && !process.env.SSH_TTY;
|
|
7224
|
+
}
|
|
7225
|
+
function isNativeShiftPressed() {
|
|
7226
|
+
return isLocalWindowsConsole() && (activeConsoles.at(-1)?.shiftPressed() ?? false);
|
|
7227
|
+
}
|
|
7228
|
+
function enterTerminalRawMode(input, console2 = input === process.stdin ? getWindowsConsole() : null) {
|
|
7229
|
+
if (!input.setRawMode)
|
|
7230
|
+
return () => {
|
|
7231
|
+
};
|
|
7232
|
+
const wasRaw = input.isRaw ?? false;
|
|
7233
|
+
const originalMode = console2?.getMode();
|
|
7234
|
+
let active = true;
|
|
7235
|
+
let registered = false;
|
|
7236
|
+
const restore = () => {
|
|
7237
|
+
if (!active)
|
|
7238
|
+
return;
|
|
7239
|
+
active = false;
|
|
7240
|
+
if (registered && console2)
|
|
7241
|
+
activeConsoles.splice(activeConsoles.lastIndexOf(console2), 1);
|
|
7242
|
+
try {
|
|
7243
|
+
input.setRawMode?.(wasRaw);
|
|
7244
|
+
} finally {
|
|
7245
|
+
if (originalMode !== void 0)
|
|
7246
|
+
console2?.setMode(originalMode);
|
|
7247
|
+
}
|
|
7248
|
+
};
|
|
7249
|
+
try {
|
|
7250
|
+
input.setRawMode(true);
|
|
7251
|
+
if (console2) {
|
|
7252
|
+
console2.setMode(console2.getMode() | 512);
|
|
7253
|
+
activeConsoles.push(console2);
|
|
7254
|
+
registered = true;
|
|
7255
|
+
}
|
|
7256
|
+
return restore;
|
|
7257
|
+
} catch (error) {
|
|
7258
|
+
try {
|
|
7259
|
+
restore();
|
|
7260
|
+
} catch (restoreError) {
|
|
7261
|
+
throw new AggregateError([error, restoreError], "Terminal setup and mode restoration failed");
|
|
7262
|
+
}
|
|
7263
|
+
throw error;
|
|
7264
|
+
}
|
|
7265
|
+
}
|
|
7266
|
+
|
|
7073
7267
|
// ../../packages/terminal/tui/dist/tui/screen.js
|
|
7074
|
-
var
|
|
7268
|
+
var MainScreenRenderer = class _MainScreenRenderer {
|
|
7075
7269
|
#terminal;
|
|
7076
7270
|
#previousLines = [];
|
|
7077
7271
|
#previousWidth = 0;
|
|
@@ -7084,7 +7278,7 @@ var PiMainScreenRenderer = class _PiMainScreenRenderer {
|
|
|
7084
7278
|
#closed = false;
|
|
7085
7279
|
constructor(terminal, env = process.env) {
|
|
7086
7280
|
this.#terminal = terminal;
|
|
7087
|
-
this.#clearOnShrink = env.
|
|
7281
|
+
this.#clearOnShrink = env.LAOHUANG_CLEAR_ON_SHRINK === "1";
|
|
7088
7282
|
}
|
|
7089
7283
|
render(frame) {
|
|
7090
7284
|
if (this.#closed) {
|
|
@@ -7094,9 +7288,9 @@ var PiMainScreenRenderer = class _PiMainScreenRenderer {
|
|
|
7094
7288
|
const width = Math.max(1, size.columns);
|
|
7095
7289
|
const height = Math.max(1, size.rows);
|
|
7096
7290
|
const newLines = frame.lines;
|
|
7097
|
-
_PiMainScreenRenderer.#validateLines(newLines, width);
|
|
7098
7291
|
const widthChanged = this.#previousWidth !== 0 && this.#previousWidth !== width;
|
|
7099
7292
|
const heightChanged = this.#previousHeight !== 0 && this.#previousHeight !== height;
|
|
7293
|
+
_MainScreenRenderer.#validateLines(newLines, width, widthChanged || heightChanged ? [] : this.#previousLines);
|
|
7100
7294
|
const previousBufferLength = this.#previousHeight > 0 ? this.#previousViewportTop + this.#previousHeight : height;
|
|
7101
7295
|
const prevViewportTop = heightChanged ? Math.max(0, previousBufferLength - height) : this.#previousViewportTop;
|
|
7102
7296
|
let viewportTop = prevViewportTop;
|
|
@@ -7144,7 +7338,7 @@ var PiMainScreenRenderer = class _PiMainScreenRenderer {
|
|
|
7144
7338
|
fullRender(true);
|
|
7145
7339
|
return;
|
|
7146
7340
|
}
|
|
7147
|
-
const span2 =
|
|
7341
|
+
const span2 = _MainScreenRenderer.#changedSpan(this.#previousLines, newLines);
|
|
7148
7342
|
if (span2 === null) {
|
|
7149
7343
|
const buffer2 = this.#positionHardwareCursor(frame, width, newLines.length);
|
|
7150
7344
|
this.#previousViewportTop = prevViewportTop;
|
|
@@ -7310,13 +7504,15 @@ var PiMainScreenRenderer = class _PiMainScreenRenderer {
|
|
|
7310
7504
|
this.#hardwareCursorRow = targetRow;
|
|
7311
7505
|
return buffer;
|
|
7312
7506
|
}
|
|
7313
|
-
#commitState(
|
|
7314
|
-
this.#previousLines = [...
|
|
7507
|
+
#commitState(lines2, width, height) {
|
|
7508
|
+
this.#previousLines = [...lines2];
|
|
7315
7509
|
this.#previousWidth = width;
|
|
7316
7510
|
this.#previousHeight = height;
|
|
7317
7511
|
}
|
|
7318
|
-
static #validateLines(
|
|
7319
|
-
|
|
7512
|
+
static #validateLines(lines2, width, previous) {
|
|
7513
|
+
lines2.forEach((line2, index) => {
|
|
7514
|
+
if (previous[index] === line2)
|
|
7515
|
+
return;
|
|
7320
7516
|
if (/[\r\n]/u.test(line2)) {
|
|
7321
7517
|
throw new Error(`rendered line ${index} contains a physical newline`);
|
|
7322
7518
|
}
|
|
@@ -7327,15 +7523,28 @@ var PiMainScreenRenderer = class _PiMainScreenRenderer {
|
|
|
7327
7523
|
});
|
|
7328
7524
|
}
|
|
7329
7525
|
};
|
|
7526
|
+
var WIDTH_CACHE_SIZE = 512;
|
|
7527
|
+
var WIDTH_CACHE_MAX_TEXT_LENGTH = 4096;
|
|
7528
|
+
var widthCache = /* @__PURE__ */ new Map();
|
|
7330
7529
|
function visibleWidth(text) {
|
|
7331
|
-
if (
|
|
7332
|
-
return
|
|
7333
|
-
|
|
7530
|
+
if (/^[\x20-\x7e]*$/u.test(text))
|
|
7531
|
+
return text.length;
|
|
7532
|
+
const cached = widthCache.get(text);
|
|
7533
|
+
if (cached !== void 0)
|
|
7534
|
+
return cached;
|
|
7334
7535
|
const stripped = stripTerminalControls(text).replace(/\t/g, " ");
|
|
7335
7536
|
let width = 0;
|
|
7336
7537
|
for (const cluster of graphemeClusters(stripped)) {
|
|
7337
7538
|
width += clusterWidth(cluster);
|
|
7338
7539
|
}
|
|
7540
|
+
if (text.length <= WIDTH_CACHE_MAX_TEXT_LENGTH) {
|
|
7541
|
+
if (widthCache.size >= WIDTH_CACHE_SIZE) {
|
|
7542
|
+
const oldest = widthCache.keys().next().value;
|
|
7543
|
+
if (oldest !== void 0)
|
|
7544
|
+
widthCache.delete(oldest);
|
|
7545
|
+
}
|
|
7546
|
+
widthCache.set(text, width);
|
|
7547
|
+
}
|
|
7339
7548
|
return width;
|
|
7340
7549
|
}
|
|
7341
7550
|
function truncateToWidth(text, width) {
|
|
@@ -7515,7 +7724,7 @@ function lineText(value) {
|
|
|
7515
7724
|
}
|
|
7516
7725
|
function wrapStyledSpans(spans, width) {
|
|
7517
7726
|
const targetWidth = Math.max(1, width);
|
|
7518
|
-
const
|
|
7727
|
+
const lines2 = [];
|
|
7519
7728
|
let current = [];
|
|
7520
7729
|
let currentWidth = 0;
|
|
7521
7730
|
let skipLineFeed = false;
|
|
@@ -7528,7 +7737,7 @@ function wrapStyledSpans(spans, width) {
|
|
|
7528
7737
|
current.push(span(text, style));
|
|
7529
7738
|
};
|
|
7530
7739
|
const finish = () => {
|
|
7531
|
-
|
|
7740
|
+
lines2.push(line(...current));
|
|
7532
7741
|
current = [];
|
|
7533
7742
|
currentWidth = 0;
|
|
7534
7743
|
};
|
|
@@ -7560,7 +7769,7 @@ function wrapStyledSpans(spans, width) {
|
|
|
7560
7769
|
}
|
|
7561
7770
|
}
|
|
7562
7771
|
finish();
|
|
7563
|
-
return
|
|
7772
|
+
return lines2;
|
|
7564
7773
|
}
|
|
7565
7774
|
function truncateStyledLine(value, width, ellipsis = "\u2026") {
|
|
7566
7775
|
if (width <= 0) {
|
|
@@ -7589,7 +7798,7 @@ function truncateStyledLine(value, width, ellipsis = "\u2026") {
|
|
|
7589
7798
|
if (!truncated) {
|
|
7590
7799
|
return line(...mergeAdjacent(result));
|
|
7591
7800
|
}
|
|
7592
|
-
const ellipsisWidth = charCellWidth(ellipsis);
|
|
7801
|
+
const ellipsisWidth = ellipsis === "" ? 0 : charCellWidth(ellipsis);
|
|
7593
7802
|
while (result.length > 0 && used + ellipsisWidth > width) {
|
|
7594
7803
|
const removed = result.pop();
|
|
7595
7804
|
used -= charCellWidth(removed.text);
|
|
@@ -7649,6 +7858,7 @@ var InputActionKind = {
|
|
|
7649
7858
|
CursorLeft: "cursor_left",
|
|
7650
7859
|
CursorRight: "cursor_right",
|
|
7651
7860
|
Backspace: "backspace",
|
|
7861
|
+
DeleteToLineStart: "delete_to_line_start",
|
|
7652
7862
|
Key: "key",
|
|
7653
7863
|
Dismiss: "dismiss",
|
|
7654
7864
|
Cancel: "cancel",
|
|
@@ -7697,6 +7907,8 @@ function toTuiInputEvent(input) {
|
|
|
7697
7907
|
return { type: "key", key: makeKeyInput("right") };
|
|
7698
7908
|
case InputActionKind.Backspace:
|
|
7699
7909
|
return { type: "key", key: makeKeyInput("backspace") };
|
|
7910
|
+
case InputActionKind.DeleteToLineStart:
|
|
7911
|
+
return { type: "key", key: makeKeyInput("ctrl_u", { ctrl: true }) };
|
|
7700
7912
|
case InputActionKind.Dismiss:
|
|
7701
7913
|
return { type: "key", key: makeKeyInput("escape") };
|
|
7702
7914
|
case InputActionKind.Cancel:
|
|
@@ -7829,13 +8041,15 @@ var ABANDONED_NEGOTIATION_TAIL_RE = /^\x1b\[\?[\d;]*([A-Za-z])$/;
|
|
|
7829
8041
|
var TerminalInputFilter = class {
|
|
7830
8042
|
pendingNegotiationPrefix = "";
|
|
7831
8043
|
isAppleTerminal;
|
|
8044
|
+
isWindowsConsole;
|
|
7832
8045
|
shiftPressed;
|
|
7833
8046
|
enableModifyOtherKeys;
|
|
7834
8047
|
disableModifyOtherKeys;
|
|
7835
8048
|
kittyProtocolActive = false;
|
|
7836
8049
|
constructor(options = {}) {
|
|
7837
8050
|
this.isAppleTerminal = options.isAppleTerminal ?? isAppleTerminalSession;
|
|
7838
|
-
this.
|
|
8051
|
+
this.isWindowsConsole = options.isWindowsConsole ?? isLocalWindowsConsole;
|
|
8052
|
+
this.shiftPressed = options.shiftPressed ?? isNativeShiftPressed;
|
|
7839
8053
|
this.enableModifyOtherKeys = options.enableModifyOtherKeys ?? (() => {
|
|
7840
8054
|
});
|
|
7841
8055
|
this.disableModifyOtherKeys = options.disableModifyOtherKeys ?? (() => {
|
|
@@ -7914,7 +8128,7 @@ var TerminalInputFilter = class {
|
|
|
7914
8128
|
return match[1];
|
|
7915
8129
|
}
|
|
7916
8130
|
normalizePlatformInput(sequence) {
|
|
7917
|
-
if (sequence === "\r" && this.isAppleTerminal() && this.shiftPressed()) {
|
|
8131
|
+
if (sequence === "\r" && (this.isAppleTerminal() || this.isWindowsConsole()) && this.shiftPressed()) {
|
|
7918
8132
|
return APPLE_TERMINAL_SHIFT_ENTER_SEQUENCE;
|
|
7919
8133
|
}
|
|
7920
8134
|
return sequence;
|
|
@@ -7937,6 +8151,7 @@ var CONTROL_ACTIONS = /* @__PURE__ */ new Map([
|
|
|
7937
8151
|
[4, InputActionKind.Eof],
|
|
7938
8152
|
[9, InputActionKind.Complete],
|
|
7939
8153
|
[8, InputActionKind.Backspace],
|
|
8154
|
+
[21, InputActionKind.DeleteToLineStart],
|
|
7940
8155
|
[127, InputActionKind.Backspace]
|
|
7941
8156
|
]);
|
|
7942
8157
|
var CONTROL_KEYS = /* @__PURE__ */ new Map([
|
|
@@ -8367,10 +8582,13 @@ function decodeSpecialEscapeAction(sequence) {
|
|
|
8367
8582
|
return kind !== void 0 ? inputAction(kind) : null;
|
|
8368
8583
|
}
|
|
8369
8584
|
function decodeModifiedControlKey(sequence) {
|
|
8370
|
-
const kitty = /^\x1b\[(13|57414|9)
|
|
8585
|
+
const kitty = /^\x1b\[(13|57414|9|117)(?::\d*)?(?::\d+)?;(\d+)(?::(\d+))?u$/.exec(sequence);
|
|
8371
8586
|
if (kitty !== null) {
|
|
8372
8587
|
const code = kitty[1];
|
|
8373
8588
|
const modifier2 = Number.parseInt(kitty[2], 10) - 1;
|
|
8589
|
+
if (code === "117" && matchesKittyModifiers(modifier2, 4) && (isKittyPressEvent(kitty[3]) || kitty[3] === "2")) {
|
|
8590
|
+
return inputAction(InputActionKind.DeleteToLineStart);
|
|
8591
|
+
}
|
|
8374
8592
|
if (!isKittyPressEvent(kitty[3])) {
|
|
8375
8593
|
return null;
|
|
8376
8594
|
}
|
|
@@ -8388,6 +8606,9 @@ function decodeModifiedControlKey(sequence) {
|
|
|
8388
8606
|
}
|
|
8389
8607
|
const modifier = Number.parseInt(modifyOtherKeys[1], 10) - 1;
|
|
8390
8608
|
const codepoint = Number.parseInt(modifyOtherKeys[2], 10);
|
|
8609
|
+
if (codepoint === 117 && modifier === 4) {
|
|
8610
|
+
return inputAction(InputActionKind.DeleteToLineStart);
|
|
8611
|
+
}
|
|
8391
8612
|
if (codepoint === 13 && modifier === 2) {
|
|
8392
8613
|
return inputAction(InputActionKind.Key, "", makeKeyInput("enter", { alt: true }));
|
|
8393
8614
|
}
|
|
@@ -8506,13 +8727,13 @@ var EditorState = class _EditorState {
|
|
|
8506
8727
|
const projection = this.#renderProjection(width, prompt, options.mask ?? false);
|
|
8507
8728
|
const promptStyle = options.styles?.prompt ?? ((text) => text);
|
|
8508
8729
|
const textStyle = options.styles?.text ?? ((text) => text);
|
|
8509
|
-
const
|
|
8730
|
+
const lines2 = projection.rows.map((row, index) => {
|
|
8510
8731
|
const promptText = index === 0 ? projection.prompt : " ".repeat(projection.promptWidth);
|
|
8511
8732
|
const renderedPrompt = index === 0 ? promptStyle(promptText) : promptText;
|
|
8512
8733
|
return renderedPrompt + textStyle(row);
|
|
8513
8734
|
});
|
|
8514
8735
|
return {
|
|
8515
|
-
lines,
|
|
8736
|
+
lines: lines2,
|
|
8516
8737
|
cursorRow: projection.cursorRow,
|
|
8517
8738
|
cursorColumn: projection.cursorColumn
|
|
8518
8739
|
};
|
|
@@ -8609,6 +8830,8 @@ var EditorState = class _EditorState {
|
|
|
8609
8830
|
this.clearCompletions();
|
|
8610
8831
|
this.text = this.text.slice(0, this.cursor - 1) + this.text.slice(this.cursor);
|
|
8611
8832
|
this.cursor -= 1;
|
|
8833
|
+
} else if (action.kind === InputActionKind.DeleteToLineStart) {
|
|
8834
|
+
this.deleteToLineStart();
|
|
8612
8835
|
} else if (action.kind === InputActionKind.CursorLeft) {
|
|
8613
8836
|
this.clearCompletions();
|
|
8614
8837
|
this.cursor = Math.max(0, this.cursor - 1);
|
|
@@ -8631,6 +8854,22 @@ var EditorState = class _EditorState {
|
|
|
8631
8854
|
this.cursor += text.length;
|
|
8632
8855
|
this.historyIndex = null;
|
|
8633
8856
|
}
|
|
8857
|
+
deleteToLineStart() {
|
|
8858
|
+
if (this.cursor === 0) {
|
|
8859
|
+
return;
|
|
8860
|
+
}
|
|
8861
|
+
const previousNewline = this.text.lastIndexOf("\n", this.cursor - 1);
|
|
8862
|
+
const lineStart = previousNewline + 1;
|
|
8863
|
+
this.clearCompletions();
|
|
8864
|
+
if (lineStart === this.cursor) {
|
|
8865
|
+
this.text = this.text.slice(0, previousNewline) + this.text.slice(this.cursor);
|
|
8866
|
+
this.cursor = previousNewline;
|
|
8867
|
+
} else {
|
|
8868
|
+
this.text = this.text.slice(0, lineStart) + this.text.slice(this.cursor);
|
|
8869
|
+
this.cursor = lineStart;
|
|
8870
|
+
}
|
|
8871
|
+
this.historyIndex = null;
|
|
8872
|
+
}
|
|
8634
8873
|
acceptCompletion() {
|
|
8635
8874
|
if (this.selectedCompletion === null) {
|
|
8636
8875
|
return;
|
|
@@ -8728,6 +8967,7 @@ var DEFAULT_KEYBINDINGS = [
|
|
|
8728
8967
|
{ context: "terminal", key: "ctrl+o", action: "toggle_tool_output" },
|
|
8729
8968
|
{ context: "terminal", key: "ctrl+l", action: "select_model" },
|
|
8730
8969
|
{ context: "terminal", key: "ctrl+t", action: "toggle_thinking" },
|
|
8970
|
+
{ context: "editor", key: "ctrl+u", action: "delete_to_line_start" },
|
|
8731
8971
|
{ context: "editor", key: "ctrl+s", action: "steer_now" },
|
|
8732
8972
|
{ context: "editor", key: "alt+enter", action: "submit_follow_up" },
|
|
8733
8973
|
{ context: "editor", key: "escape", action: "dismiss" },
|
|
@@ -8762,6 +9002,9 @@ function controlKeyId(character) {
|
|
|
8762
9002
|
if (character === "l") {
|
|
8763
9003
|
return "ctrl_l";
|
|
8764
9004
|
}
|
|
9005
|
+
if (character === "u") {
|
|
9006
|
+
return "ctrl_u";
|
|
9007
|
+
}
|
|
8765
9008
|
return "character";
|
|
8766
9009
|
}
|
|
8767
9010
|
function parseKey(value) {
|
|
@@ -8933,6 +9176,10 @@ var UIEventReducer = class _UIEventReducer {
|
|
|
8933
9176
|
payload
|
|
8934
9177
|
});
|
|
8935
9178
|
}
|
|
9179
|
+
if (kind === "ui.context_usage") {
|
|
9180
|
+
this.#updateContextUsage(payload);
|
|
9181
|
+
return createUpdate(kind, { payload });
|
|
9182
|
+
}
|
|
8936
9183
|
if (kind === "task.state_changed") {
|
|
8937
9184
|
const rawState = payload.state ?? "IDLE";
|
|
8938
9185
|
this.state.sessionState = String(unwrapValue(rawState)).toUpperCase();
|
|
@@ -9035,6 +9282,14 @@ var UIEventReducer = class _UIEventReducer {
|
|
|
9035
9282
|
}
|
|
9036
9283
|
return createUpdate(kind, { text, correlationId, stream });
|
|
9037
9284
|
}
|
|
9285
|
+
if (kind === "tool.output_snapshot") {
|
|
9286
|
+
const stream = String(payload.stream ?? "stdout");
|
|
9287
|
+
const text = String(payload.text ?? "");
|
|
9288
|
+
const tool = this.state.activeTools.get(correlationId);
|
|
9289
|
+
if (tool !== void 0 && (stream === "stdout" || stream === "stderr"))
|
|
9290
|
+
tool[stream] = text;
|
|
9291
|
+
return createUpdate(kind, { text, correlationId, stream, payload });
|
|
9292
|
+
}
|
|
9038
9293
|
if (kind === "tool.finished") {
|
|
9039
9294
|
const tool = this.state.activeTools.get(correlationId);
|
|
9040
9295
|
this.state.activeTools.delete(correlationId);
|
|
@@ -9094,11 +9349,13 @@ var LIFECYCLE_EVENTS = /* @__PURE__ */ new Set([
|
|
|
9094
9349
|
var HIGH_FREQUENCY_EVENTS = /* @__PURE__ */ new Set([
|
|
9095
9350
|
"model.reasoning_delta",
|
|
9096
9351
|
"model.tool_call_delta",
|
|
9097
|
-
"tool.output_delta"
|
|
9352
|
+
"tool.output_delta",
|
|
9353
|
+
"tool.output_snapshot"
|
|
9098
9354
|
]);
|
|
9099
9355
|
var TOOL_EVENTS = /* @__PURE__ */ new Set([
|
|
9100
9356
|
"tool.started",
|
|
9101
9357
|
"tool.output_delta",
|
|
9358
|
+
"tool.output_snapshot",
|
|
9102
9359
|
"tool.finished"
|
|
9103
9360
|
]);
|
|
9104
9361
|
var SENSITIVE_FIELDS2 = /* @__PURE__ */ new Set([
|
|
@@ -9129,6 +9386,26 @@ function asPayload(value) {
|
|
|
9129
9386
|
function redactToolText(value) {
|
|
9130
9387
|
return SENSITIVE_TEXT_PATTERNS2.reduce((text, pattern) => text.replace(pattern, "$1[REDACTED]"), value);
|
|
9131
9388
|
}
|
|
9389
|
+
function redactToolSnapshot(value, startMidLine = false) {
|
|
9390
|
+
const text = startMidLine ? omitPartialOutputLine(value) : value;
|
|
9391
|
+
return redactToolText(text);
|
|
9392
|
+
}
|
|
9393
|
+
function toolOutputNote(payload) {
|
|
9394
|
+
const notes = [];
|
|
9395
|
+
if (payload.truncated === true)
|
|
9396
|
+
notes.push("Output preview truncated.");
|
|
9397
|
+
if (isRecord3(payload.output_files)) {
|
|
9398
|
+
const label = payload.output_file_complete === true ? "Full output" : "Partial output";
|
|
9399
|
+
notes.push(`${label}: ${Object.values(payload.output_files).filter((value) => typeof value === "string").join(", ")}`);
|
|
9400
|
+
}
|
|
9401
|
+
if (payload.output_file_error)
|
|
9402
|
+
notes.push(String(payload.output_file_error));
|
|
9403
|
+
if (payload.output_complete === false)
|
|
9404
|
+
notes.push("Output collection incomplete.");
|
|
9405
|
+
if (payload.error)
|
|
9406
|
+
notes.push(String(payload.error));
|
|
9407
|
+
return redactToolText(notes.join("\n"));
|
|
9408
|
+
}
|
|
9132
9409
|
function redactToolValue(value) {
|
|
9133
9410
|
if (typeof value === "string")
|
|
9134
9411
|
return redactToolText(value);
|
|
@@ -9199,8 +9476,15 @@ var DisplayPolicy = class {
|
|
|
9199
9476
|
const text = kind === "tool.output_delta" ? this.#toolOutputRedactor.redact(correlationId, stream || "stdout", String(rawPayload.text ?? rawPayload.chunk ?? "")) : String(payload.text ?? payload.chunk ?? "");
|
|
9200
9477
|
if (kind === "tool.output_delta") {
|
|
9201
9478
|
payload = { ...payload, text };
|
|
9479
|
+
} else if (kind === "tool.output_snapshot") {
|
|
9480
|
+
payload = { ...payload, text: redactToolSnapshot(String(rawPayload.text ?? ""), rawPayload.start_mid_line === true) };
|
|
9202
9481
|
} else if (kind === "tool.finished") {
|
|
9203
9482
|
this.#toolOutputRedactor.clear(correlationId);
|
|
9483
|
+
for (const name of ["stdout", "stderr"]) {
|
|
9484
|
+
if (typeof rawPayload[name] === "string") {
|
|
9485
|
+
payload[name] = redactToolSnapshot(rawPayload[name], rawPayload[`${name}_start_mid_line`] === true);
|
|
9486
|
+
}
|
|
9487
|
+
}
|
|
9204
9488
|
}
|
|
9205
9489
|
const projected = [];
|
|
9206
9490
|
const dropped = droppedCount(payload);
|
|
@@ -9216,11 +9500,11 @@ var DisplayPolicy = class {
|
|
|
9216
9500
|
if (kind === "model.reasoning_delta" && !this.showReasoning) {
|
|
9217
9501
|
return projected;
|
|
9218
9502
|
}
|
|
9219
|
-
if (this.foldToolOutput && kind === "tool.output_delta" && (stream || "stdout") === "stdout") {
|
|
9503
|
+
if (this.foldToolOutput && (kind === "tool.output_delta" || kind === "tool.output_snapshot") && (stream || "stdout") === "stdout") {
|
|
9220
9504
|
return projected;
|
|
9221
9505
|
}
|
|
9222
9506
|
if (LIFECYCLE_EVENTS.has(kind) || kind.length > 0) {
|
|
9223
|
-
projected.push({ kind, correlationId, stream, text, payload });
|
|
9507
|
+
projected.push({ kind, correlationId, stream, text: String(payload.text ?? text), payload });
|
|
9224
9508
|
}
|
|
9225
9509
|
return projected;
|
|
9226
9510
|
}
|
|
@@ -9412,11 +9696,27 @@ var TranscriptStore = class {
|
|
|
9412
9696
|
}
|
|
9413
9697
|
return;
|
|
9414
9698
|
}
|
|
9699
|
+
if (kind === "tool.output_snapshot") {
|
|
9700
|
+
const item = this.#byCorrelation.get(`tool:${correlationId}`);
|
|
9701
|
+
if (item?.kind === "tool" && item.mutable && (update.stream === "stdout" || update.stream === "stderr")) {
|
|
9702
|
+
item[update.stream] = redactToolSnapshot(update.text, update.payload.start_mid_line === true);
|
|
9703
|
+
if (update.payload.truncated === true)
|
|
9704
|
+
item.outputNote = "Output preview truncated.";
|
|
9705
|
+
touchBlock(item);
|
|
9706
|
+
}
|
|
9707
|
+
return;
|
|
9708
|
+
}
|
|
9415
9709
|
if (kind === "tool.finished") {
|
|
9416
9710
|
const item = this.#getOrCreateTool(correlationId);
|
|
9417
9711
|
item.status = String(update.payload.status ?? "completed");
|
|
9418
9712
|
item.exitCode = typeof update.payload.exit_code === "number" ? Math.trunc(update.payload.exit_code) : null;
|
|
9419
9713
|
item.durationMs = typeof update.payload.duration_ms === "number" ? Math.trunc(update.payload.duration_ms) : null;
|
|
9714
|
+
for (const stream of ["stdout", "stderr"]) {
|
|
9715
|
+
const text = update.payload[stream];
|
|
9716
|
+
if (typeof text === "string")
|
|
9717
|
+
item[stream] = redactToolSnapshot(text, update.payload[`${stream}_start_mid_line`] === true);
|
|
9718
|
+
}
|
|
9719
|
+
item.outputNote = toolOutputNote(update.payload);
|
|
9420
9720
|
item.mutable = false;
|
|
9421
9721
|
touchBlock(item);
|
|
9422
9722
|
this.#toolOutputRedactor.clear(correlationId);
|
|
@@ -9480,6 +9780,23 @@ var TranscriptStore = class {
|
|
|
9480
9780
|
};
|
|
9481
9781
|
|
|
9482
9782
|
// ../../packages/terminal/tui/dist/tui/ansi-renderer.js
|
|
9783
|
+
var StyledLineCompiler = class {
|
|
9784
|
+
#cache = /* @__PURE__ */ new WeakMap();
|
|
9785
|
+
compile(values, width, theme) {
|
|
9786
|
+
return values.map((value) => {
|
|
9787
|
+
const cached = this.#cache.get(value);
|
|
9788
|
+
if (cached !== void 0 && cached.width === width && cached.theme === theme) {
|
|
9789
|
+
return cached.text;
|
|
9790
|
+
}
|
|
9791
|
+
const text = compileStyledLine(truncateStyledLine(value, width, ""), width, theme);
|
|
9792
|
+
this.#cache.set(value, { width, theme, text });
|
|
9793
|
+
return text;
|
|
9794
|
+
});
|
|
9795
|
+
}
|
|
9796
|
+
invalidate() {
|
|
9797
|
+
this.#cache = /* @__PURE__ */ new WeakMap();
|
|
9798
|
+
}
|
|
9799
|
+
};
|
|
9483
9800
|
function compileStyledLine(value, width, theme) {
|
|
9484
9801
|
const text = value.spans.map((item) => compileSpan(item, theme)).join("");
|
|
9485
9802
|
const renderedWidth = visibleWidth(text);
|
|
@@ -9540,7 +9857,7 @@ var CompletionPopup = class {
|
|
|
9540
9857
|
const description = item.description.replace(/[\r\n]+/gu, " ").trim();
|
|
9541
9858
|
const value = line(span(selected ? "\u203A " : " ", selected ? { foreground: "accent" } : void 0), span(item.value, selected ? { foreground: "accent" } : void 0), ...description ? [span(` ${description}`, { foreground: "muted" })] : []);
|
|
9542
9859
|
const boundedWidth = Math.max(1, width);
|
|
9543
|
-
return displayWidth2(lineText(value)) <= boundedWidth ? value : truncateStyledLine(value, boundedWidth
|
|
9860
|
+
return displayWidth2(lineText(value)) <= boundedWidth ? value : truncateStyledLine(value, boundedWidth, "");
|
|
9544
9861
|
}
|
|
9545
9862
|
};
|
|
9546
9863
|
function displayWidth2(value) {
|
|
@@ -9581,13 +9898,13 @@ var Composer = class {
|
|
|
9581
9898
|
mask: this.#mask
|
|
9582
9899
|
});
|
|
9583
9900
|
const borderStyle = { foreground: "dim" };
|
|
9584
|
-
const
|
|
9901
|
+
const lines2 = [
|
|
9585
9902
|
line(span("\u256D", borderStyle), span("\u2500".repeat(innerWidth), borderStyle), span("\u256E", borderStyle)),
|
|
9586
9903
|
...rendered.lines.map((value) => line(span("\u2502", borderStyle), ...padStyledLine(truncateStyledLine(value, innerWidth, ""), innerWidth).spans, span("\u2502", borderStyle))),
|
|
9587
9904
|
line(span("\u2570", borderStyle), span("\u2500".repeat(innerWidth), borderStyle), span("\u256F", borderStyle))
|
|
9588
9905
|
];
|
|
9589
9906
|
return {
|
|
9590
|
-
lines,
|
|
9907
|
+
lines: lines2,
|
|
9591
9908
|
cursor: {
|
|
9592
9909
|
row: rendered.cursorRow + 1,
|
|
9593
9910
|
column: rendered.cursorColumn + 1
|
|
@@ -9656,7 +9973,8 @@ function contextUsageLabel(tokens, contextWindow) {
|
|
|
9656
9973
|
return null;
|
|
9657
9974
|
}
|
|
9658
9975
|
const used = normalizedCount(tokens);
|
|
9659
|
-
const
|
|
9976
|
+
const ratio = Math.min(100, used / window * 100);
|
|
9977
|
+
const percent = ratio > 0 && ratio < 0.1 ? "<0.1" : formatScaled(ratio);
|
|
9660
9978
|
return `context: ${percent}% (${formatCompactCount(used)}/${formatCompactCount(window)})`;
|
|
9661
9979
|
}
|
|
9662
9980
|
function normalizedCount(value) {
|
|
@@ -9702,22 +10020,22 @@ function renderMarkdownStyledLines(text, width) {
|
|
|
9702
10020
|
return [];
|
|
9703
10021
|
}
|
|
9704
10022
|
const layoutWidth = Math.max(12, width);
|
|
9705
|
-
const
|
|
10023
|
+
const lines2 = [];
|
|
9706
10024
|
let previous = null;
|
|
9707
10025
|
for (const block of renderBlocks(clean, layoutWidth)) {
|
|
9708
10026
|
const leadingBlank = previous === null ? block.kind === "list" || block.kind === "table" || block.kind === "quote" : previous !== "hr";
|
|
9709
10027
|
if (leadingBlank) {
|
|
9710
|
-
|
|
10028
|
+
lines2.push(line());
|
|
9711
10029
|
}
|
|
9712
10030
|
for (const line2 of block.lines) {
|
|
9713
|
-
|
|
10031
|
+
lines2.push(truncateMarkdownLine(serializeLine(line2), width));
|
|
9714
10032
|
}
|
|
9715
10033
|
if (block.kind === "hr") {
|
|
9716
|
-
|
|
10034
|
+
lines2.push(line());
|
|
9717
10035
|
}
|
|
9718
10036
|
previous = block.kind;
|
|
9719
10037
|
}
|
|
9720
|
-
return
|
|
10038
|
+
return lines2;
|
|
9721
10039
|
}
|
|
9722
10040
|
function styleKey(style) {
|
|
9723
10041
|
return [
|
|
@@ -9729,7 +10047,7 @@ function styleKey(style) {
|
|
|
9729
10047
|
].join("|");
|
|
9730
10048
|
}
|
|
9731
10049
|
function renderBlocks(text, layoutWidth) {
|
|
9732
|
-
const
|
|
10050
|
+
const lines2 = text.replace(/\r\n/g, "\n").split("\n");
|
|
9733
10051
|
const blocks = [];
|
|
9734
10052
|
let paragraph = [];
|
|
9735
10053
|
let index = 0;
|
|
@@ -9743,8 +10061,8 @@ function renderBlocks(text, layoutWidth) {
|
|
|
9743
10061
|
});
|
|
9744
10062
|
}
|
|
9745
10063
|
};
|
|
9746
|
-
while (index <
|
|
9747
|
-
const line2 =
|
|
10064
|
+
while (index < lines2.length) {
|
|
10065
|
+
const line2 = lines2[index];
|
|
9748
10066
|
const trimmed = line2.trim();
|
|
9749
10067
|
if (trimmed.startsWith("```") || trimmed.startsWith("~~~")) {
|
|
9750
10068
|
flushParagraph();
|
|
@@ -9754,8 +10072,8 @@ function renderBlocks(text, layoutWidth) {
|
|
|
9754
10072
|
foreground: "code"
|
|
9755
10073
|
};
|
|
9756
10074
|
const codeLines = [];
|
|
9757
|
-
while (index <
|
|
9758
|
-
const body =
|
|
10075
|
+
while (index < lines2.length) {
|
|
10076
|
+
const body = lines2[index];
|
|
9759
10077
|
if (body.trim().startsWith(fence)) {
|
|
9760
10078
|
index += 1;
|
|
9761
10079
|
break;
|
|
@@ -9771,15 +10089,15 @@ function renderBlocks(text, layoutWidth) {
|
|
|
9771
10089
|
index += 1;
|
|
9772
10090
|
continue;
|
|
9773
10091
|
}
|
|
9774
|
-
if (trimmed.includes("|") && index + 1 <
|
|
10092
|
+
if (trimmed.includes("|") && index + 1 < lines2.length) {
|
|
9775
10093
|
const header = splitTableRow(trimmed);
|
|
9776
|
-
const delimiter = splitTableRow(
|
|
10094
|
+
const delimiter = splitTableRow(lines2[index + 1].trim());
|
|
9777
10095
|
if (header !== null && delimiter !== null && delimiter.length === header.length && delimiter.every((cell) => /^:?-+:?$/.test(cell))) {
|
|
9778
10096
|
flushParagraph();
|
|
9779
10097
|
index += 2;
|
|
9780
10098
|
const rows = [];
|
|
9781
|
-
while (index <
|
|
9782
|
-
const rowLine =
|
|
10099
|
+
while (index < lines2.length) {
|
|
10100
|
+
const rowLine = lines2[index].trim();
|
|
9783
10101
|
if (!rowLine) {
|
|
9784
10102
|
break;
|
|
9785
10103
|
}
|
|
@@ -9826,8 +10144,8 @@ function renderBlocks(text, layoutWidth) {
|
|
|
9826
10144
|
if (trimmed.startsWith(">")) {
|
|
9827
10145
|
flushParagraph();
|
|
9828
10146
|
const content = [];
|
|
9829
|
-
while (index <
|
|
9830
|
-
const match = /^>\s?(.*)$/.exec(
|
|
10147
|
+
while (index < lines2.length) {
|
|
10148
|
+
const match = /^>\s?(.*)$/.exec(lines2[index].trim());
|
|
9831
10149
|
if (!match) {
|
|
9832
10150
|
break;
|
|
9833
10151
|
}
|
|
@@ -9846,8 +10164,8 @@ function renderBlocks(text, layoutWidth) {
|
|
|
9846
10164
|
if (/^[-*+]\s+/.test(trimmed)) {
|
|
9847
10165
|
flushParagraph();
|
|
9848
10166
|
const items = [];
|
|
9849
|
-
while (index <
|
|
9850
|
-
const match = /^[-*+]\s+(.*)$/.exec(
|
|
10167
|
+
while (index < lines2.length) {
|
|
10168
|
+
const match = /^[-*+]\s+(.*)$/.exec(lines2[index].trim());
|
|
9851
10169
|
if (!match) {
|
|
9852
10170
|
break;
|
|
9853
10171
|
}
|
|
@@ -9866,8 +10184,8 @@ function renderBlocks(text, layoutWidth) {
|
|
|
9866
10184
|
const start = parseInt(numbered[1], 10);
|
|
9867
10185
|
const items = [parseInline(numbered[2], {})];
|
|
9868
10186
|
index += 1;
|
|
9869
|
-
while (index <
|
|
9870
|
-
const match = /^(\d+)[.)]\s+(.*)$/.exec(
|
|
10187
|
+
while (index < lines2.length) {
|
|
10188
|
+
const match = /^(\d+)[.)]\s+(.*)$/.exec(lines2[index].trim());
|
|
9871
10189
|
if (!match) {
|
|
9872
10190
|
break;
|
|
9873
10191
|
}
|
|
@@ -9889,16 +10207,16 @@ function renderBlocks(text, layoutWidth) {
|
|
|
9889
10207
|
}
|
|
9890
10208
|
function renderListItems(items, prefixFor, layoutWidth) {
|
|
9891
10209
|
const marker = { foreground: "accent" };
|
|
9892
|
-
const
|
|
10210
|
+
const lines2 = [];
|
|
9893
10211
|
for (let index = 0; index < items.length; index += 1) {
|
|
9894
10212
|
const prefix = prefixFor(index);
|
|
9895
10213
|
const wrapped = wrapSegments(items[index], Math.max(1, layoutWidth - prefix.length));
|
|
9896
10214
|
if (wrapped.length === 0) {
|
|
9897
|
-
|
|
10215
|
+
lines2.push([{ style: marker, text: prefix }]);
|
|
9898
10216
|
continue;
|
|
9899
10217
|
}
|
|
9900
10218
|
for (let row = 0; row < wrapped.length; row += 1) {
|
|
9901
|
-
|
|
10219
|
+
lines2.push([
|
|
9902
10220
|
{
|
|
9903
10221
|
style: marker,
|
|
9904
10222
|
text: row === 0 ? prefix : " ".repeat(prefix.length)
|
|
@@ -9907,7 +10225,7 @@ function renderListItems(items, prefixFor, layoutWidth) {
|
|
|
9907
10225
|
]);
|
|
9908
10226
|
}
|
|
9909
10227
|
}
|
|
9910
|
-
return
|
|
10228
|
+
return lines2;
|
|
9911
10229
|
}
|
|
9912
10230
|
function splitTableRow(line2) {
|
|
9913
10231
|
if (!line2.includes("|")) {
|
|
@@ -9955,15 +10273,15 @@ function renderTable(header, rows, layoutWidth) {
|
|
|
9955
10273
|
widths[widest] = widths[widest] - 1;
|
|
9956
10274
|
}
|
|
9957
10275
|
const inner = widths.reduce((total, w) => total + w, 0) + 2 * (columnCount - 1);
|
|
9958
|
-
const
|
|
9959
|
-
|
|
9960
|
-
|
|
9961
|
-
|
|
10276
|
+
const lines2 = [];
|
|
10277
|
+
lines2.push([{ style: borderStyle, text: " ".repeat(inner + 2) }]);
|
|
10278
|
+
lines2.push(...renderTableRows(headerCells, widths, borderStyle, headerStyle));
|
|
10279
|
+
lines2.push([{ style: borderStyle, text: ` ${"\u2500".repeat(inner)} ` }]);
|
|
9962
10280
|
for (const row of bodyCells) {
|
|
9963
|
-
|
|
10281
|
+
lines2.push(...renderTableRows(row, widths, borderStyle, {}));
|
|
9964
10282
|
}
|
|
9965
|
-
|
|
9966
|
-
return
|
|
10283
|
+
lines2.push([{ style: borderStyle, text: " ".repeat(inner + 2) }]);
|
|
10284
|
+
return lines2;
|
|
9967
10285
|
}
|
|
9968
10286
|
function renderTableRows(cells, widths, borderStyle, padStyle) {
|
|
9969
10287
|
const wrappedCells = cells.map((cell, i) => {
|
|
@@ -9971,7 +10289,7 @@ function renderTableRows(cells, widths, borderStyle, padStyle) {
|
|
|
9971
10289
|
return wrapped.length > 0 ? wrapped : [[]];
|
|
9972
10290
|
});
|
|
9973
10291
|
const height = Math.max(...wrappedCells.map((cell) => cell.length));
|
|
9974
|
-
const
|
|
10292
|
+
const lines2 = [];
|
|
9975
10293
|
for (let row = 0; row < height; row += 1) {
|
|
9976
10294
|
const line2 = [{ style: borderStyle, text: " " }];
|
|
9977
10295
|
for (let i = 0; i < cells.length; i += 1) {
|
|
@@ -9989,9 +10307,9 @@ function renderTableRows(cells, widths, borderStyle, padStyle) {
|
|
|
9989
10307
|
}
|
|
9990
10308
|
}
|
|
9991
10309
|
line2.push({ style: borderStyle, text: " " });
|
|
9992
|
-
|
|
10310
|
+
lines2.push(mergeSegments(line2));
|
|
9993
10311
|
}
|
|
9994
|
-
return
|
|
10312
|
+
return lines2;
|
|
9995
10313
|
}
|
|
9996
10314
|
var INLINE_PATTERNS = [
|
|
9997
10315
|
{
|
|
@@ -10146,12 +10464,12 @@ function wrapSegments(segments, width) {
|
|
|
10146
10464
|
if (current.length > 0) {
|
|
10147
10465
|
words.push(current);
|
|
10148
10466
|
}
|
|
10149
|
-
const
|
|
10467
|
+
const lines2 = [];
|
|
10150
10468
|
let line2 = [];
|
|
10151
10469
|
let lineWidth = 0;
|
|
10152
10470
|
let pendingSpace = null;
|
|
10153
10471
|
const flushLine = () => {
|
|
10154
|
-
|
|
10472
|
+
lines2.push(line2);
|
|
10155
10473
|
line2 = [];
|
|
10156
10474
|
lineWidth = 0;
|
|
10157
10475
|
pendingSpace = null;
|
|
@@ -10192,7 +10510,7 @@ function wrapSegments(segments, width) {
|
|
|
10192
10510
|
if (line2.length > 0) {
|
|
10193
10511
|
flushLine();
|
|
10194
10512
|
}
|
|
10195
|
-
return
|
|
10513
|
+
return lines2.map((physical) => regroup(physical));
|
|
10196
10514
|
}
|
|
10197
10515
|
function regroup(clusters) {
|
|
10198
10516
|
const segments = [];
|
|
@@ -10274,11 +10592,11 @@ var Text = class {
|
|
|
10274
10592
|
this.#cache = null;
|
|
10275
10593
|
}
|
|
10276
10594
|
};
|
|
10277
|
-
function applyTextPadding(
|
|
10595
|
+
function applyTextPadding(lines2, width, options) {
|
|
10278
10596
|
const targetWidth = Math.max(0, width);
|
|
10279
10597
|
const blank = () => line(span(" ".repeat(targetWidth), backgroundStyle(options.background)));
|
|
10280
10598
|
const horizontalPadding = span(" ".repeat(options.paddingX), backgroundStyle(options.background));
|
|
10281
|
-
const contentLines =
|
|
10599
|
+
const contentLines = lines2.map((value) => {
|
|
10282
10600
|
const contentSpans = options.background === void 0 ? value.spans : value.spans.map((item) => span(item.text, {
|
|
10283
10601
|
...item.style,
|
|
10284
10602
|
background: item.style?.background ?? options.background
|
|
@@ -10362,7 +10680,7 @@ var Box = class {
|
|
|
10362
10680
|
const width = Math.max(0, context.width);
|
|
10363
10681
|
const child = this.#child.render({ ...context, width: Math.max(1, width - this.#paddingX * 2) });
|
|
10364
10682
|
const blank = () => line(span(" ".repeat(width), backgroundStyle2(this.#background)));
|
|
10365
|
-
const
|
|
10683
|
+
const lines2 = child.lines.map((value) => this.#renderLine(value, width));
|
|
10366
10684
|
const cursor = child.cursor === void 0 ? void 0 : {
|
|
10367
10685
|
row: child.cursor.row + this.#paddingY,
|
|
10368
10686
|
column: child.cursor.column + this.#paddingX
|
|
@@ -10370,7 +10688,7 @@ var Box = class {
|
|
|
10370
10688
|
return {
|
|
10371
10689
|
lines: [
|
|
10372
10690
|
...Array.from({ length: this.#paddingY }, blank),
|
|
10373
|
-
...
|
|
10691
|
+
...lines2,
|
|
10374
10692
|
...Array.from({ length: this.#paddingY }, blank)
|
|
10375
10693
|
],
|
|
10376
10694
|
...cursor === void 0 ? {} : { cursor }
|
|
@@ -10420,11 +10738,11 @@ var ToolMessage = class {
|
|
|
10420
10738
|
options.exitCode === null ? "" : `exit ${options.exitCode}`,
|
|
10421
10739
|
options.durationMs === null ? "" : `${options.durationMs}ms`
|
|
10422
10740
|
].filter(Boolean).join(" \xB7 ");
|
|
10423
|
-
const output = options.expanded ? [stderr
|
|
10741
|
+
const output = options.expanded ? [stderr, stdout].filter(Boolean).map((text) => text.length > 1200 ? `\u2026${text.slice(-1200)}` : text) : [];
|
|
10424
10742
|
this.#content = new Box({
|
|
10425
10743
|
child: new Text({
|
|
10426
10744
|
spans: [...title, { text: `
|
|
10427
|
-
${[metadata, ...output].join("\n")}` }]
|
|
10745
|
+
${[metadata, ...output, options.outputNote && redactToolText(options.outputNote)].filter(Boolean).join("\n")}` }]
|
|
10428
10746
|
})
|
|
10429
10747
|
});
|
|
10430
10748
|
}
|
|
@@ -10447,12 +10765,12 @@ var UserMessage = class {
|
|
|
10447
10765
|
}
|
|
10448
10766
|
render(context) {
|
|
10449
10767
|
const width = Math.max(3, context.width);
|
|
10450
|
-
const
|
|
10768
|
+
const lines2 = wrapStyledSpans([
|
|
10451
10769
|
span("\u2728 ", { foreground: "accent" }),
|
|
10452
10770
|
span(this.#text)
|
|
10453
10771
|
], width);
|
|
10454
10772
|
return {
|
|
10455
|
-
lines:
|
|
10773
|
+
lines: lines2.map((value) => padStyledLine(value, width))
|
|
10456
10774
|
};
|
|
10457
10775
|
}
|
|
10458
10776
|
invalidate() {
|
|
@@ -10474,7 +10792,7 @@ var WelcomeMessage = class {
|
|
|
10474
10792
|
}
|
|
10475
10793
|
const innerWidth = width - 2;
|
|
10476
10794
|
const borderStyle = { foreground: "accent" };
|
|
10477
|
-
const
|
|
10795
|
+
const lines2 = [
|
|
10478
10796
|
line(span("\u256D", borderStyle), span("\u2500".repeat(innerWidth), borderStyle), span("\u256E", borderStyle)),
|
|
10479
10797
|
frameRow(innerWidth, borderStyle, [
|
|
10480
10798
|
span("H", { foreground: "accent", bold: true }),
|
|
@@ -10484,17 +10802,17 @@ var WelcomeMessage = class {
|
|
|
10484
10802
|
];
|
|
10485
10803
|
const [subtitle, ...meta] = this.#details;
|
|
10486
10804
|
if (subtitle !== void 0) {
|
|
10487
|
-
|
|
10805
|
+
lines2.push(frameRow(innerWidth, borderStyle, [
|
|
10488
10806
|
span(" "),
|
|
10489
10807
|
span(subtitle, { foreground: "dim" })
|
|
10490
10808
|
]));
|
|
10491
10809
|
}
|
|
10492
|
-
|
|
10810
|
+
lines2.push(frameRow(innerWidth, borderStyle, [span("")]));
|
|
10493
10811
|
for (const detail of meta) {
|
|
10494
|
-
|
|
10812
|
+
lines2.push(frameRow(innerWidth, borderStyle, metaSpans(detail)));
|
|
10495
10813
|
}
|
|
10496
|
-
|
|
10497
|
-
return { lines };
|
|
10814
|
+
lines2.push(line(span("\u2570", borderStyle), span("\u2500".repeat(innerWidth), borderStyle), span("\u256F", borderStyle)));
|
|
10815
|
+
return { lines: lines2 };
|
|
10498
10816
|
}
|
|
10499
10817
|
invalidate() {
|
|
10500
10818
|
}
|
|
@@ -10627,23 +10945,23 @@ var Transcript = class {
|
|
|
10627
10945
|
}
|
|
10628
10946
|
renderWithMetadata(context) {
|
|
10629
10947
|
const usableWidth = Math.max(12, context.width);
|
|
10630
|
-
const
|
|
10948
|
+
const lines2 = [];
|
|
10631
10949
|
let activeStart = null;
|
|
10632
10950
|
const seenKeys = /* @__PURE__ */ new Set();
|
|
10633
10951
|
for (const block of this.#blocks) {
|
|
10634
|
-
if (
|
|
10635
|
-
|
|
10952
|
+
if (lines2.length > 0) {
|
|
10953
|
+
lines2.push(plainLine(""));
|
|
10636
10954
|
}
|
|
10637
10955
|
if (block.mutable && activeStart === null)
|
|
10638
|
-
activeStart =
|
|
10956
|
+
activeStart = lines2.length;
|
|
10639
10957
|
seenKeys.add(cacheKey(block));
|
|
10640
|
-
|
|
10958
|
+
lines2.push(...this.#renderBlockCached(block, { ...context, width: usableWidth }));
|
|
10641
10959
|
}
|
|
10642
10960
|
for (const key of this.#cache.keys()) {
|
|
10643
10961
|
if (!seenKeys.has(key))
|
|
10644
10962
|
this.#cache.delete(key);
|
|
10645
10963
|
}
|
|
10646
|
-
return { lines, activeStart };
|
|
10964
|
+
return { lines: lines2, activeStart };
|
|
10647
10965
|
}
|
|
10648
10966
|
invalidate() {
|
|
10649
10967
|
this.#cache.clear();
|
|
@@ -10652,27 +10970,28 @@ var Transcript = class {
|
|
|
10652
10970
|
const key = cacheKey(block);
|
|
10653
10971
|
const signature = blockSignature(block);
|
|
10654
10972
|
const cached = this.#cache.get(key);
|
|
10655
|
-
if (cached !== void 0 && cached.width === context.width && cached.signature === signature) {
|
|
10973
|
+
if (cached !== void 0 && cached.block === block && cached.width === context.width && cached.signature === signature) {
|
|
10656
10974
|
return cached.lines;
|
|
10657
10975
|
}
|
|
10658
|
-
if (cached !== void 0 && cached.signature === signature) {
|
|
10659
|
-
const
|
|
10976
|
+
if (cached !== void 0 && cached.block === block && cached.signature === signature) {
|
|
10977
|
+
const lines3 = cached.component.render(context).lines;
|
|
10660
10978
|
this.#cache.set(key, {
|
|
10661
10979
|
...cached,
|
|
10662
10980
|
width: context.width,
|
|
10663
|
-
lines:
|
|
10981
|
+
lines: lines3
|
|
10664
10982
|
});
|
|
10665
|
-
return
|
|
10983
|
+
return lines3;
|
|
10666
10984
|
}
|
|
10667
10985
|
const component = this.#createBlockComponent(block);
|
|
10668
|
-
const
|
|
10986
|
+
const lines2 = component.render(context).lines;
|
|
10669
10987
|
this.#cache.set(key, {
|
|
10988
|
+
block,
|
|
10670
10989
|
component,
|
|
10671
10990
|
width: context.width,
|
|
10672
10991
|
signature,
|
|
10673
|
-
lines
|
|
10992
|
+
lines: lines2
|
|
10674
10993
|
});
|
|
10675
|
-
return
|
|
10994
|
+
return lines2;
|
|
10676
10995
|
}
|
|
10677
10996
|
#createBlockComponent(block) {
|
|
10678
10997
|
switch (block.kind) {
|
|
@@ -10744,7 +11063,7 @@ var TerminalTheme = class {
|
|
|
10744
11063
|
return `\x1B[${channel};2;${r};${g};${b}m`;
|
|
10745
11064
|
}
|
|
10746
11065
|
};
|
|
10747
|
-
var
|
|
11066
|
+
var DEFAULT_DARK_THEME = new TerminalTheme("dark", {
|
|
10748
11067
|
accent: "#8abeb7",
|
|
10749
11068
|
border: "#5f87ff",
|
|
10750
11069
|
border_muted: "#505050",
|
|
@@ -10765,7 +11084,7 @@ var PI_DARK = new TerminalTheme("dark", {
|
|
|
10765
11084
|
thinking: "#808080",
|
|
10766
11085
|
bash: "#b5bd68"
|
|
10767
11086
|
});
|
|
10768
|
-
var
|
|
11087
|
+
var DEFAULT_LIGHT_THEME = new TerminalTheme("light", {
|
|
10769
11088
|
accent: "#5a8080",
|
|
10770
11089
|
border: "#547da7",
|
|
10771
11090
|
border_muted: "#b0b0b0",
|
|
@@ -10789,10 +11108,10 @@ var PI_LIGHT = new TerminalTheme("light", {
|
|
|
10789
11108
|
function resolveTerminalTheme(name, env = process.env) {
|
|
10790
11109
|
const requested = (name ?? "auto").trim().toLowerCase() || "auto";
|
|
10791
11110
|
if (requested === "light") {
|
|
10792
|
-
return
|
|
11111
|
+
return DEFAULT_LIGHT_THEME;
|
|
10793
11112
|
}
|
|
10794
11113
|
if (requested === "dark") {
|
|
10795
|
-
return
|
|
11114
|
+
return DEFAULT_DARK_THEME;
|
|
10796
11115
|
}
|
|
10797
11116
|
if (requested !== "auto") {
|
|
10798
11117
|
throw new Error("terminal theme must be 'auto', 'dark', or 'light'");
|
|
@@ -10800,9 +11119,9 @@ function resolveTerminalTheme(name, env = process.env) {
|
|
|
10800
11119
|
const colorfgbg = env.COLORFGBG ?? "";
|
|
10801
11120
|
const background = colorfgbg ? colorfgbg.split(";").pop() : "";
|
|
10802
11121
|
if (/^\s*[+-]?\d+\s*$/.test(background)) {
|
|
10803
|
-
return parseInt(background, 10) >= 7 ?
|
|
11122
|
+
return parseInt(background, 10) >= 7 ? DEFAULT_LIGHT_THEME : DEFAULT_DARK_THEME;
|
|
10804
11123
|
}
|
|
10805
|
-
return
|
|
11124
|
+
return DEFAULT_DARK_THEME;
|
|
10806
11125
|
}
|
|
10807
11126
|
|
|
10808
11127
|
// ../../packages/terminal/tui/dist/tui/frame-builder.js
|
|
@@ -10815,6 +11134,11 @@ var FrameBuilder = class {
|
|
|
10815
11134
|
#effort;
|
|
10816
11135
|
#title;
|
|
10817
11136
|
#theme;
|
|
11137
|
+
#transcriptView;
|
|
11138
|
+
#lineCompiler = new StyledLineCompiler();
|
|
11139
|
+
#previousWidth = 0;
|
|
11140
|
+
#previousSources = [];
|
|
11141
|
+
#previousLines = [];
|
|
10818
11142
|
constructor(options) {
|
|
10819
11143
|
this.#state = options.state;
|
|
10820
11144
|
this.#transcript = options.transcript;
|
|
@@ -10824,14 +11148,23 @@ var FrameBuilder = class {
|
|
|
10824
11148
|
this.#effort = options.effort ?? null;
|
|
10825
11149
|
this.#title = options.title ?? "laoHuang";
|
|
10826
11150
|
this.#theme = options.theme ?? resolveTerminalTheme();
|
|
11151
|
+
this.#transcriptView = new Transcript({ blocks: this.#transcript.blocks() });
|
|
10827
11152
|
}
|
|
10828
11153
|
build(options) {
|
|
10829
11154
|
const terminalWidth = Math.max(1, options.width);
|
|
10830
11155
|
const width = Math.max(1, terminalWidth - 1);
|
|
10831
11156
|
const mainScreen = options.compiledMainScreen ?? this.#fallbackMainScreen(options, width);
|
|
10832
|
-
const
|
|
11157
|
+
const lines2 = mainScreen.lines.map((value, index) => {
|
|
11158
|
+
if (this.#previousWidth === width && this.#previousSources[index] === value) {
|
|
11159
|
+
return this.#previousLines[index];
|
|
11160
|
+
}
|
|
11161
|
+
return visibleWidth(value) <= width ? value : truncateToWidth(value, width);
|
|
11162
|
+
});
|
|
11163
|
+
this.#previousWidth = width;
|
|
11164
|
+
this.#previousSources = [...mainScreen.lines];
|
|
11165
|
+
this.#previousLines = lines2;
|
|
10833
11166
|
const cursor = {
|
|
10834
|
-
row: Math.max(0, Math.min(mainScreen.cursor.row, Math.max(0,
|
|
11167
|
+
row: Math.max(0, Math.min(mainScreen.cursor.row, Math.max(0, lines2.length - 1))),
|
|
10835
11168
|
col: Math.max(0, Math.min(mainScreen.cursor.column, width - 1))
|
|
10836
11169
|
};
|
|
10837
11170
|
return {
|
|
@@ -10840,8 +11173,8 @@ var FrameBuilder = class {
|
|
|
10840
11173
|
statusBar: this.statusBar(width),
|
|
10841
11174
|
cursor,
|
|
10842
11175
|
screen: {
|
|
10843
|
-
lines,
|
|
10844
|
-
activeStart: Math.max(0, Math.min(mainScreen.activeStart,
|
|
11176
|
+
lines: lines2,
|
|
11177
|
+
activeStart: Math.max(0, Math.min(mainScreen.activeStart, lines2.length)),
|
|
10845
11178
|
cursorRow: cursor.row,
|
|
10846
11179
|
cursorCol: cursor.col
|
|
10847
11180
|
}
|
|
@@ -10851,9 +11184,7 @@ var FrameBuilder = class {
|
|
|
10851
11184
|
return this.#status().render({ width: Math.max(1, width), theme: this.#theme }).lines.map(lineText).join("\n");
|
|
10852
11185
|
}
|
|
10853
11186
|
#fallbackMainScreen(options, width) {
|
|
10854
|
-
const transcript =
|
|
10855
|
-
blocks: this.#transcript.blocks()
|
|
10856
|
-
}).renderWithMetadata({ width, theme: this.#theme });
|
|
11187
|
+
const transcript = this.#transcriptView.renderWithMetadata({ width, theme: this.#theme });
|
|
10857
11188
|
const transcriptLines = transcript.lines;
|
|
10858
11189
|
const composer = new Composer({
|
|
10859
11190
|
editor: options.editor,
|
|
@@ -10866,9 +11197,9 @@ var FrameBuilder = class {
|
|
|
10866
11197
|
}).render({ width, theme: this.#theme });
|
|
10867
11198
|
const status = this.#status().render({ width, theme: this.#theme });
|
|
10868
11199
|
const cursor = composer.cursor ?? { row: Math.max(0, composer.lines.length - 1), column: 0 };
|
|
10869
|
-
const
|
|
11200
|
+
const lines2 = [...transcriptLines, ...composer.lines, ...completion.lines, ...status.lines];
|
|
10870
11201
|
return {
|
|
10871
|
-
lines:
|
|
11202
|
+
lines: this.#lineCompiler.compile(lines2, width, this.#theme),
|
|
10872
11203
|
cursor: {
|
|
10873
11204
|
row: transcriptLines.length + cursor.row,
|
|
10874
11205
|
column: cursor.column
|
|
@@ -10972,8 +11303,8 @@ var SearchInput = class {
|
|
|
10972
11303
|
const width = Math.max(1, context.width);
|
|
10973
11304
|
const contentWidth = Math.max(1, width - displayWidth4(PROMPT));
|
|
10974
11305
|
const displayText = this.#secret ? "\u2022".repeat(this.#editor.text.length) : this.#editor.text;
|
|
10975
|
-
const
|
|
10976
|
-
const structuredLines =
|
|
11306
|
+
const lines2 = renderLines(displayText || this.#placeholder, contentWidth, displayText.length === 0 && this.#placeholder.length > 0);
|
|
11307
|
+
const structuredLines = lines2.map((value, index) => truncateStyledLine(line(span(index === 0 ? PROMPT : " ".repeat(displayWidth4(PROMPT)), index === 0 ? { foreground: "accent" } : void 0), span(value, displayText.length === 0 && this.#placeholder.length > 0 ? { foreground: "muted" } : void 0)), width, ""));
|
|
10977
11308
|
const cursor = this.focused ? cursorMetadata(displayText.slice(0, this.#editor.cursor), contentWidth) : void 0;
|
|
10978
11309
|
return cursor === void 0 ? { lines: structuredLines } : {
|
|
10979
11310
|
lines: structuredLines,
|
|
@@ -11041,15 +11372,15 @@ function renderLines(value, width, placeholder) {
|
|
|
11041
11372
|
if (placeholder) {
|
|
11042
11373
|
return [value];
|
|
11043
11374
|
}
|
|
11044
|
-
const
|
|
11375
|
+
const lines2 = [];
|
|
11045
11376
|
for (const source of value.split("\n")) {
|
|
11046
|
-
|
|
11377
|
+
lines2.push(...wrapStyledSpans([span(source)], width).map((item) => item.spans.map((part) => part.text).join("")));
|
|
11047
11378
|
}
|
|
11048
11379
|
const last = value.split("\n").at(-1) ?? "";
|
|
11049
11380
|
if (value && !value.endsWith("\n") && displayWidth4(last) % width === 0) {
|
|
11050
|
-
|
|
11381
|
+
lines2.push("");
|
|
11051
11382
|
}
|
|
11052
|
-
return
|
|
11383
|
+
return lines2;
|
|
11053
11384
|
}
|
|
11054
11385
|
function cursorMetadata(before, width) {
|
|
11055
11386
|
const segments = before.split("\n");
|
|
@@ -11133,11 +11464,11 @@ var SelectList = class {
|
|
|
11133
11464
|
const start = Math.max(0, Math.min(this.#selectedIndex - Math.floor(this.#maxVisible / 2), this.#filteredItems.length - this.#maxVisible));
|
|
11134
11465
|
const end = Math.min(start + this.#maxVisible, this.#filteredItems.length);
|
|
11135
11466
|
const primaryColumnWidth = this.#primaryColumnWidth();
|
|
11136
|
-
const
|
|
11467
|
+
const lines2 = this.#filteredItems.slice(start, end).map((item, index) => this.#renderItem(item, start + index === this.#selectedIndex, context.width, primaryColumnWidth));
|
|
11137
11468
|
if (start > 0 || end < this.#filteredItems.length) {
|
|
11138
|
-
|
|
11469
|
+
lines2.push(truncateStyledLine(line(span(` (${this.#selectedIndex + 1}/${this.#filteredItems.length})`, { foreground: "muted" })), context.width, ""));
|
|
11139
11470
|
}
|
|
11140
|
-
return { lines };
|
|
11471
|
+
return { lines: lines2 };
|
|
11141
11472
|
}
|
|
11142
11473
|
handleInput(event) {
|
|
11143
11474
|
if (!this.focused || event.type !== "key") {
|
|
@@ -11171,14 +11502,14 @@ var SelectList = class {
|
|
|
11171
11502
|
const description = item.description === void 0 ? void 0 : normalizeDescription(item.description);
|
|
11172
11503
|
if (description && width > 40) {
|
|
11173
11504
|
const effectivePrimaryWidth = Math.max(1, Math.min(primaryColumnWidth, width - prefixWidth - 4));
|
|
11174
|
-
const label =
|
|
11505
|
+
const label = truncateText(displayValue(item), Math.max(1, effectivePrimaryWidth - PRIMARY_COLUMN_GAP));
|
|
11175
11506
|
const spacing = " ".repeat(Math.max(1, effectivePrimaryWidth - displayWidth5(label)));
|
|
11176
11507
|
const remainingWidth = width - prefixWidth - displayWidth5(label) - displayWidth5(spacing) - 2;
|
|
11177
11508
|
if (remainingWidth > MIN_DESCRIPTION_WIDTH) {
|
|
11178
|
-
return truncateStyledLine(line(prefix, span(label, selected ? { foreground: "accent" } : void 0), span(spacing), span(
|
|
11509
|
+
return truncateStyledLine(line(prefix, span(label, selected ? { foreground: "accent" } : void 0), span(spacing), span(truncateText(description, remainingWidth), { foreground: "muted" })), width, "");
|
|
11179
11510
|
}
|
|
11180
11511
|
}
|
|
11181
|
-
return truncateStyledLine(line(prefix, span(
|
|
11512
|
+
return truncateStyledLine(line(prefix, span(truncateText(displayValue(item), Math.max(1, width - prefixWidth - 2)), selected ? { foreground: "accent" } : void 0)), width, "");
|
|
11182
11513
|
}
|
|
11183
11514
|
#primaryColumnWidth() {
|
|
11184
11515
|
const widest = this.#filteredItems.reduce((value, item) => Math.max(value, displayWidth5(displayValue(item)) + PRIMARY_COLUMN_GAP), 0);
|
|
@@ -11191,7 +11522,7 @@ function displayValue(item) {
|
|
|
11191
11522
|
function normalizeDescription(value) {
|
|
11192
11523
|
return value.replace(/[\r\n]+/gu, " ").trim();
|
|
11193
11524
|
}
|
|
11194
|
-
function
|
|
11525
|
+
function truncateText(value, width) {
|
|
11195
11526
|
return lineText(truncateStyledLine(line(span(value)), width, ""));
|
|
11196
11527
|
}
|
|
11197
11528
|
function displayWidth5(value) {
|
|
@@ -11214,13 +11545,13 @@ var VStack = class {
|
|
|
11214
11545
|
}
|
|
11215
11546
|
}
|
|
11216
11547
|
render(context) {
|
|
11217
|
-
const
|
|
11548
|
+
const lines2 = [];
|
|
11218
11549
|
let cursor;
|
|
11219
11550
|
let rowOffset = 0;
|
|
11220
11551
|
for (const [index, child] of this.#children.entries()) {
|
|
11221
11552
|
if (index > 0) {
|
|
11222
11553
|
const gaps = Array.from({ length: this.#gap }, () => plainLine(""));
|
|
11223
|
-
|
|
11554
|
+
lines2.push(...gaps);
|
|
11224
11555
|
rowOffset += gaps.length;
|
|
11225
11556
|
}
|
|
11226
11557
|
const rendered = child.render(context);
|
|
@@ -11230,11 +11561,11 @@ var VStack = class {
|
|
|
11230
11561
|
column: rendered.cursor.column
|
|
11231
11562
|
};
|
|
11232
11563
|
}
|
|
11233
|
-
|
|
11564
|
+
lines2.push(...rendered.lines);
|
|
11234
11565
|
rowOffset += rendered.lines.length;
|
|
11235
11566
|
}
|
|
11236
11567
|
return {
|
|
11237
|
-
lines,
|
|
11568
|
+
lines: lines2,
|
|
11238
11569
|
...cursor === void 0 ? {} : { cursor }
|
|
11239
11570
|
};
|
|
11240
11571
|
}
|
|
@@ -11480,19 +11811,19 @@ var ViewHost = class {
|
|
|
11480
11811
|
this.#overlays = overlays;
|
|
11481
11812
|
}
|
|
11482
11813
|
openSelection(request) {
|
|
11483
|
-
return new Promise((
|
|
11814
|
+
return new Promise((resolve3) => {
|
|
11484
11815
|
const component = this.#selectionComponent(request);
|
|
11485
|
-
this.#open({ id: request.id, component, resolve:
|
|
11816
|
+
this.#open({ id: request.id, component, resolve: resolve3 }, "selector");
|
|
11486
11817
|
});
|
|
11487
11818
|
}
|
|
11488
11819
|
openPrompt(request) {
|
|
11489
|
-
return new Promise((
|
|
11820
|
+
return new Promise((resolve3) => {
|
|
11490
11821
|
const component = new AuthDialog({
|
|
11491
11822
|
request,
|
|
11492
11823
|
onSubmit: (value) => this.#close(request.id, value),
|
|
11493
11824
|
onCancel: () => this.#close(request.id, null)
|
|
11494
11825
|
});
|
|
11495
|
-
this.#open({ id: request.id, component, resolve:
|
|
11826
|
+
this.#open({ id: request.id, component, resolve: resolve3 }, "modal");
|
|
11496
11827
|
});
|
|
11497
11828
|
}
|
|
11498
11829
|
handleInput(event) {
|
|
@@ -11852,7 +12183,7 @@ var InteractiveTerminalLoop = class {
|
|
|
11852
12183
|
this.#disableModifyOtherKeys();
|
|
11853
12184
|
}
|
|
11854
12185
|
});
|
|
11855
|
-
this.#renderer = new
|
|
12186
|
+
this.#renderer = new MainScreenRenderer(driver);
|
|
11856
12187
|
}
|
|
11857
12188
|
get editor() {
|
|
11858
12189
|
return this.#editor;
|
|
@@ -11876,6 +12207,12 @@ var InteractiveTerminalLoop = class {
|
|
|
11876
12207
|
if (!this.#ui.shouldQueueDisplayEvent(event)) {
|
|
11877
12208
|
return;
|
|
11878
12209
|
}
|
|
12210
|
+
if (isRecord5(event) && event.kind === "tool.output_snapshot" && isRecord5(event.payload)) {
|
|
12211
|
+
const stream = event.payload.stream;
|
|
12212
|
+
const previous = this.#work.findIndex((item) => item.type === "event" && isRecord5(item.event) && item.event.kind === event.kind && item.event.correlation_id === event.correlation_id && isRecord5(item.event.payload) && item.event.payload.stream === stream);
|
|
12213
|
+
if (previous >= 0)
|
|
12214
|
+
this.#work.splice(previous, 1);
|
|
12215
|
+
}
|
|
11879
12216
|
if (this.#work.length >= _a.WORK_QUEUE_LIMIT - 128 && this.#ui.isHighFrequencyDisplayEvent(event)) {
|
|
11880
12217
|
this.#ui.recordDisplayDrop(event);
|
|
11881
12218
|
return;
|
|
@@ -11967,8 +12304,8 @@ var InteractiveTerminalLoop = class {
|
|
|
11967
12304
|
this.#wakeupEnabled = true;
|
|
11968
12305
|
try {
|
|
11969
12306
|
if (!this.#exitRequested) {
|
|
11970
|
-
await new Promise((
|
|
11971
|
-
this.#exitResolve =
|
|
12307
|
+
await new Promise((resolve3) => {
|
|
12308
|
+
this.#exitResolve = resolve3;
|
|
11972
12309
|
});
|
|
11973
12310
|
}
|
|
11974
12311
|
} finally {
|
|
@@ -12153,6 +12490,8 @@ var InteractiveTerminalLoop = class {
|
|
|
12153
12490
|
}
|
|
12154
12491
|
if (action === "editor_newline") {
|
|
12155
12492
|
this.#applyEditorAction(inputAction(InputActionKind.Newline));
|
|
12493
|
+
} else if (action === "delete_to_line_start") {
|
|
12494
|
+
this.#applyEditorAction(inputAction(InputActionKind.DeleteToLineStart));
|
|
12156
12495
|
} else if (action === "steer_now") {
|
|
12157
12496
|
this.#applySteerSubmit();
|
|
12158
12497
|
} else if (action === "submit_follow_up") {
|
|
@@ -12294,18 +12633,18 @@ var InteractiveTerminalLoop = class {
|
|
|
12294
12633
|
if (this.#closed || !this.#running) {
|
|
12295
12634
|
return Promise.resolve(null);
|
|
12296
12635
|
}
|
|
12297
|
-
return new Promise((
|
|
12636
|
+
return new Promise((resolve3) => {
|
|
12298
12637
|
if (type === "open_selection") {
|
|
12299
12638
|
this.#work.push({
|
|
12300
12639
|
type,
|
|
12301
12640
|
request,
|
|
12302
|
-
resolve:
|
|
12641
|
+
resolve: resolve3
|
|
12303
12642
|
});
|
|
12304
12643
|
} else {
|
|
12305
12644
|
this.#work.push({
|
|
12306
12645
|
type,
|
|
12307
12646
|
request,
|
|
12308
|
-
resolve:
|
|
12647
|
+
resolve: resolve3
|
|
12309
12648
|
});
|
|
12310
12649
|
}
|
|
12311
12650
|
this.#scheduleWakeup();
|
|
@@ -12352,6 +12691,7 @@ var TerminalUI = class {
|
|
|
12352
12691
|
#loop = null;
|
|
12353
12692
|
#transcript;
|
|
12354
12693
|
#transcriptView;
|
|
12694
|
+
#lineCompiler = new StyledLineCompiler();
|
|
12355
12695
|
#showReasoning = true;
|
|
12356
12696
|
#displayPolicy = new DisplayPolicy({
|
|
12357
12697
|
audience: "terminal",
|
|
@@ -12483,9 +12823,16 @@ var TerminalUI = class {
|
|
|
12483
12823
|
this.#sessionId = sessionId;
|
|
12484
12824
|
this.#loop?.requestRender();
|
|
12485
12825
|
}
|
|
12826
|
+
setContextUsage(tokens, contextWindow) {
|
|
12827
|
+
this.publishEvent({
|
|
12828
|
+
kind: "ui.context_usage",
|
|
12829
|
+
payload: { context_tokens: tokens, context_window: contextWindow }
|
|
12830
|
+
});
|
|
12831
|
+
}
|
|
12486
12832
|
replaceTranscript(items) {
|
|
12487
12833
|
this.#transcript.replace(items);
|
|
12488
12834
|
this.#transcriptView.invalidate();
|
|
12835
|
+
this.#lineCompiler.invalidate();
|
|
12489
12836
|
this.#loop?.requestRender();
|
|
12490
12837
|
}
|
|
12491
12838
|
setComposerText(text) {
|
|
@@ -12611,7 +12958,7 @@ var TerminalUI = class {
|
|
|
12611
12958
|
#buildHistoryFrameParts(width) {
|
|
12612
12959
|
const rendered = this.#transcriptView.renderWithMetadata({ width, theme: this.theme });
|
|
12613
12960
|
return {
|
|
12614
|
-
lines:
|
|
12961
|
+
lines: this.#lineCompiler.compile(rendered.lines, Math.max(12, width), this.theme),
|
|
12615
12962
|
activeStart: rendered.activeStart
|
|
12616
12963
|
};
|
|
12617
12964
|
}
|
|
@@ -12642,11 +12989,11 @@ var TerminalUI = class {
|
|
|
12642
12989
|
effort: this.effort
|
|
12643
12990
|
})
|
|
12644
12991
|
}).renderWithMetadata({ width: contentWidth, theme: this.theme });
|
|
12645
|
-
const
|
|
12992
|
+
const lines2 = this.#lineCompiler.compile(rendered.lines, contentWidth, this.theme);
|
|
12646
12993
|
return this.#frameBuilder.build({
|
|
12647
12994
|
...options,
|
|
12648
12995
|
compiledMainScreen: {
|
|
12649
|
-
lines,
|
|
12996
|
+
lines: lines2,
|
|
12650
12997
|
cursor: rendered.cursor ?? { row: 0, column: 0 },
|
|
12651
12998
|
activeStart: rendered.activeStart
|
|
12652
12999
|
}
|
|
@@ -12841,7 +13188,17 @@ var PlainEventSink = class {
|
|
|
12841
13188
|
if (text) {
|
|
12842
13189
|
this.outputFn(`[${correlationId || "unknown"}:${stream}] ${text}`);
|
|
12843
13190
|
}
|
|
13191
|
+
} else if (kind === "tool.output_snapshot") {
|
|
13192
|
+
return;
|
|
12844
13193
|
} else if (kind === "tool.finished") {
|
|
13194
|
+
for (const stream of ["stdout", "stderr"]) {
|
|
13195
|
+
if (typeof payload[stream] === "string" && payload[stream] !== "") {
|
|
13196
|
+
this.outputFn(`[${correlationId || "unknown"}:${stream}] ${payload[stream]}`);
|
|
13197
|
+
}
|
|
13198
|
+
}
|
|
13199
|
+
const note = toolOutputNote(payload);
|
|
13200
|
+
if (note)
|
|
13201
|
+
this.outputFn(note);
|
|
12845
13202
|
const status = String(payload.status ?? "completed");
|
|
12846
13203
|
this.outputFn(`[tool:${correlationId || "unknown"}] ${status}`);
|
|
12847
13204
|
} else if (kind === "task.failed") {
|
|
@@ -12866,7 +13223,7 @@ function escapeDebugCapture(data) {
|
|
|
12866
13223
|
return escaped;
|
|
12867
13224
|
}
|
|
12868
13225
|
var StdTerminalDriver = class {
|
|
12869
|
-
#
|
|
13226
|
+
#restoreRawMode = null;
|
|
12870
13227
|
/**
|
|
12871
13228
|
* Debug capture target from LAOHUANG_DEBUG_LOG: when set, every write is
|
|
12872
13229
|
* teed to this file with escape sequences made visible (for diagnosing
|
|
@@ -12884,8 +13241,7 @@ var StdTerminalDriver = class {
|
|
|
12884
13241
|
if (!process.stdin.isTTY) {
|
|
12885
13242
|
return;
|
|
12886
13243
|
}
|
|
12887
|
-
process.stdin
|
|
12888
|
-
this.#rawModeActive = true;
|
|
13244
|
+
this.#restoreRawMode ??= enterTerminalRawMode(process.stdin);
|
|
12889
13245
|
}
|
|
12890
13246
|
write(data) {
|
|
12891
13247
|
if (this.#debugLogPath !== null) {
|
|
@@ -12911,10 +13267,9 @@ var StdTerminalDriver = class {
|
|
|
12911
13267
|
};
|
|
12912
13268
|
}
|
|
12913
13269
|
restore() {
|
|
12914
|
-
|
|
12915
|
-
|
|
12916
|
-
|
|
12917
|
-
}
|
|
13270
|
+
const restore = this.#restoreRawMode;
|
|
13271
|
+
this.#restoreRawMode = null;
|
|
13272
|
+
restore?.();
|
|
12918
13273
|
}
|
|
12919
13274
|
};
|
|
12920
13275
|
|
|
@@ -12922,21 +13277,23 @@ var StdTerminalDriver = class {
|
|
|
12922
13277
|
import { promises as fs } from "node:fs";
|
|
12923
13278
|
import { realpathSync as realpathSync3 } from "node:fs";
|
|
12924
13279
|
import path3 from "node:path";
|
|
13280
|
+
var READ_MAX_BYTES = 50 * 1024;
|
|
13281
|
+
var READ_MAX_LINES = 2e3;
|
|
13282
|
+
var READ_MAX_LINE_CHARACTERS = 2e3;
|
|
12925
13283
|
var DEFAULT_IO = {
|
|
12926
13284
|
readFile: (target) => fs.readFile(target, "utf8"),
|
|
12927
13285
|
writeFile: (target, content) => fs.writeFile(target, content, "utf8")
|
|
12928
13286
|
};
|
|
12929
13287
|
var mutationLocks = /* @__PURE__ */ new Map();
|
|
12930
13288
|
function createFileToolDefinitions(options) {
|
|
12931
|
-
const root = resolveNonStrictSync(
|
|
13289
|
+
const root = resolveNonStrictSync(resolveLocalPath(options.projectRoot, process.cwd(), options.pathOptions));
|
|
12932
13290
|
const io = options.fileIo ?? DEFAULT_IO;
|
|
12933
|
-
const
|
|
12934
|
-
const resolvePath2 = (rawPath) => resolveNonStrict(path3.resolve(root, rawPath));
|
|
13291
|
+
const resolvePath = (rawPath) => resolveNonStrict(resolveLocalPath(rawPath, root, options.pathOptions));
|
|
12935
13292
|
return [
|
|
12936
13293
|
{
|
|
12937
13294
|
spec: {
|
|
12938
13295
|
name: "read",
|
|
12939
|
-
description: "Read a UTF-8 text file.",
|
|
13296
|
+
description: "Read a UTF-8 text file, up to 50 KiB of content, 2000 lines, and 2000 Unicode characters per line. has_more reports omitted content; next_offset resumes at the next unread line, or is null at EOF. truncated_line_numbers identifies shortened lines; use Bash to inspect their omitted content.",
|
|
12940
13297
|
parameters: {
|
|
12941
13298
|
type: "object",
|
|
12942
13299
|
properties: {
|
|
@@ -12951,8 +13308,9 @@ function createFileToolDefinitions(options) {
|
|
|
12951
13308
|
},
|
|
12952
13309
|
limit: {
|
|
12953
13310
|
type: "integer",
|
|
12954
|
-
description: "Maximum number of lines to return.",
|
|
12955
|
-
minimum: 1
|
|
13311
|
+
description: "Maximum number of lines to return (default and maximum: 2000).",
|
|
13312
|
+
minimum: 1,
|
|
13313
|
+
maximum: READ_MAX_LINES
|
|
12956
13314
|
}
|
|
12957
13315
|
},
|
|
12958
13316
|
required: ["path"],
|
|
@@ -12965,26 +13323,57 @@ function createFileToolDefinitions(options) {
|
|
|
12965
13323
|
]
|
|
12966
13324
|
},
|
|
12967
13325
|
execute: async (args, execution) => {
|
|
12968
|
-
const target = await
|
|
13326
|
+
const target = await resolvePath(stringArgument(args, "path"));
|
|
12969
13327
|
const offset = optionalPositiveInteger(args, "offset") ?? 1;
|
|
12970
|
-
const limit = optionalPositiveInteger(args, "limit");
|
|
13328
|
+
const limit = optionalPositiveInteger(args, "limit") ?? READ_MAX_LINES;
|
|
13329
|
+
if (limit > READ_MAX_LINES) {
|
|
13330
|
+
throw new Error(`limit must be less than or equal to ${READ_MAX_LINES}`);
|
|
13331
|
+
}
|
|
12971
13332
|
const content = await io.readFile(target);
|
|
12972
13333
|
if (execution.isCancelled()) {
|
|
12973
13334
|
return cancelledResult(execution);
|
|
12974
13335
|
}
|
|
12975
|
-
const
|
|
12976
|
-
const totalLines =
|
|
13336
|
+
const lines2 = content === "" ? [] : content.split(/(?<=\n)/);
|
|
13337
|
+
const totalLines = lines2.length;
|
|
12977
13338
|
if (offset > Math.max(totalLines, 1)) {
|
|
12978
13339
|
throw new Error(`offset ${offset} is out of range; the file has ${totalLines} lines`);
|
|
12979
13340
|
}
|
|
12980
|
-
const window =
|
|
13341
|
+
const window = [];
|
|
13342
|
+
const truncatedLineNumbers = [];
|
|
13343
|
+
let bytes = 0;
|
|
13344
|
+
for (const line2 of lines2.slice(offset - 1, offset - 1 + limit)) {
|
|
13345
|
+
const ending = line2.endsWith("\r\n") ? "\r\n" : line2.endsWith("\n") ? "\n" : "";
|
|
13346
|
+
const body = line2.slice(0, line2.length - ending.length);
|
|
13347
|
+
let end = 0;
|
|
13348
|
+
let characters = 0;
|
|
13349
|
+
for (const character of body) {
|
|
13350
|
+
if (characters === READ_MAX_LINE_CHARACTERS)
|
|
13351
|
+
break;
|
|
13352
|
+
end += character.length;
|
|
13353
|
+
characters += 1;
|
|
13354
|
+
}
|
|
13355
|
+
const rendered = body.slice(0, end) + ending;
|
|
13356
|
+
const lineBytes = Buffer.byteLength(rendered, "utf8");
|
|
13357
|
+
if (bytes + lineBytes > READ_MAX_BYTES)
|
|
13358
|
+
break;
|
|
13359
|
+
if (end < body.length)
|
|
13360
|
+
truncatedLineNumbers.push(offset + window.length);
|
|
13361
|
+
window.push(rendered);
|
|
13362
|
+
bytes += lineBytes;
|
|
13363
|
+
}
|
|
13364
|
+
const nextOffset = offset - 1 + window.length < totalLines ? offset + window.length : null;
|
|
12981
13365
|
return withTouchedPath({
|
|
12982
13366
|
ok: true,
|
|
12983
|
-
content:
|
|
13367
|
+
content: window.join(""),
|
|
12984
13368
|
offset,
|
|
12985
|
-
limit
|
|
13369
|
+
limit,
|
|
12986
13370
|
total_lines: totalLines,
|
|
12987
|
-
has_more:
|
|
13371
|
+
has_more: nextOffset !== null || truncatedLineNumbers.length > 0,
|
|
13372
|
+
next_offset: nextOffset,
|
|
13373
|
+
truncated_line_numbers: truncatedLineNumbers,
|
|
13374
|
+
...truncatedLineNumbers.length > 0 ? {
|
|
13375
|
+
note: "Listed lines were truncated to 2000 Unicode characters. Use Bash to inspect their omitted content; next_offset only continues to later lines."
|
|
13376
|
+
} : {}
|
|
12988
13377
|
}, target);
|
|
12989
13378
|
}
|
|
12990
13379
|
},
|
|
@@ -13012,7 +13401,7 @@ function createFileToolDefinitions(options) {
|
|
|
13012
13401
|
executionMode: "sequential",
|
|
13013
13402
|
execute: async (args, execution) => {
|
|
13014
13403
|
const rawPath = stringArgument(args, "path");
|
|
13015
|
-
const target = await
|
|
13404
|
+
const target = await resolvePath(rawPath);
|
|
13016
13405
|
return await withFileMutationLock(target, async () => {
|
|
13017
13406
|
if (execution.isCancelled()) {
|
|
13018
13407
|
return cancelledResult(execution);
|
|
@@ -13062,13 +13451,13 @@ function createFileToolDefinitions(options) {
|
|
|
13062
13451
|
]
|
|
13063
13452
|
},
|
|
13064
13453
|
executionMode: "sequential",
|
|
13065
|
-
execute: async (args, execution) => executeEdit(args, execution,
|
|
13454
|
+
execute: async (args, execution) => executeEdit(args, execution, resolvePath, io)
|
|
13066
13455
|
}
|
|
13067
13456
|
];
|
|
13068
13457
|
}
|
|
13069
|
-
async function executeEdit(args, execution,
|
|
13458
|
+
async function executeEdit(args, execution, resolvePath, io) {
|
|
13070
13459
|
const rawPath = stringArgument(args, "path");
|
|
13071
|
-
const target = await
|
|
13460
|
+
const target = await resolvePath(rawPath);
|
|
13072
13461
|
const edits = editsArgument(args);
|
|
13073
13462
|
return await withFileMutationLock(target, async () => {
|
|
13074
13463
|
if (execution.isCancelled()) {
|
|
@@ -13109,17 +13498,17 @@ async function executeEdit(args, execution, resolvePath2, io) {
|
|
|
13109
13498
|
async function withFileMutationLock(key, fn) {
|
|
13110
13499
|
const previous = mutationLocks.get(key) ?? Promise.resolve();
|
|
13111
13500
|
let release;
|
|
13112
|
-
const current = new Promise((
|
|
13113
|
-
release =
|
|
13501
|
+
const current = new Promise((resolve3) => {
|
|
13502
|
+
release = resolve3;
|
|
13114
13503
|
});
|
|
13115
|
-
const
|
|
13116
|
-
mutationLocks.set(key,
|
|
13504
|
+
const tail2 = previous.then(() => current);
|
|
13505
|
+
mutationLocks.set(key, tail2);
|
|
13117
13506
|
await previous;
|
|
13118
13507
|
try {
|
|
13119
13508
|
return await fn();
|
|
13120
13509
|
} finally {
|
|
13121
13510
|
release();
|
|
13122
|
-
if (mutationLocks.get(key) ===
|
|
13511
|
+
if (mutationLocks.get(key) === tail2) {
|
|
13123
13512
|
mutationLocks.delete(key);
|
|
13124
13513
|
}
|
|
13125
13514
|
}
|
|
@@ -13198,14 +13587,232 @@ import { promises as fs2 } from "node:fs";
|
|
|
13198
13587
|
import { realpathSync as realpathSync4 } from "node:fs";
|
|
13199
13588
|
import path4 from "node:path";
|
|
13200
13589
|
|
|
13590
|
+
// ../../packages/shell/bash-local/dist/bash-path.js
|
|
13591
|
+
import { accessSync, constants, statSync as statSync3 } from "node:fs";
|
|
13592
|
+
import { homedir as homedir2 } from "node:os";
|
|
13593
|
+
import { posix as posix2, win32 as win322 } from "node:path";
|
|
13594
|
+
function resolveBashPath(options = {}) {
|
|
13595
|
+
const windows = (options.platform ?? process.platform) === "win32";
|
|
13596
|
+
const paths = windows ? win322 : posix2;
|
|
13597
|
+
const env = options.env ?? process.env;
|
|
13598
|
+
const isExecutable = options.isExecutable ?? ((pathname) => {
|
|
13599
|
+
try {
|
|
13600
|
+
if (!statSync3(pathname).isFile())
|
|
13601
|
+
return false;
|
|
13602
|
+
accessSync(pathname, windows ? constants.F_OK : constants.X_OK);
|
|
13603
|
+
return true;
|
|
13604
|
+
} catch {
|
|
13605
|
+
return false;
|
|
13606
|
+
}
|
|
13607
|
+
});
|
|
13608
|
+
const environment = (name) => {
|
|
13609
|
+
const key = windows ? Object.keys(env).find((entry) => entry.toLowerCase() === name.toLowerCase()) : name;
|
|
13610
|
+
return key === void 0 ? void 0 : env[key];
|
|
13611
|
+
};
|
|
13612
|
+
const isLegacyWsl = (pathname) => windows && /[\\/]windows[\\/](?:system32|sysnative)[\\/]bash\.exe$/i.test(pathname);
|
|
13613
|
+
if (options.shellPath !== void 0) {
|
|
13614
|
+
let explicit = options.shellPath;
|
|
13615
|
+
if (explicit === "~" || explicit.startsWith("~/") || windows && explicit.startsWith("~\\")) {
|
|
13616
|
+
explicit = paths.join(options.homeDirectory ?? homedir2(), explicit.slice(2));
|
|
13617
|
+
}
|
|
13618
|
+
if (!paths.isAbsolute(explicit)) {
|
|
13619
|
+
throw new Error("shell_path must be an absolute Bash path (or start with ~/).");
|
|
13620
|
+
}
|
|
13621
|
+
if (isLegacyWsl(explicit)) {
|
|
13622
|
+
throw new Error("shell_path points to a legacy WSL launcher; select Git for Windows bash.exe instead.");
|
|
13623
|
+
}
|
|
13624
|
+
if (!isExecutable(explicit)) {
|
|
13625
|
+
throw new Error(`Configured Bash is missing or not executable: ${explicit}`);
|
|
13626
|
+
}
|
|
13627
|
+
return explicit;
|
|
13628
|
+
}
|
|
13629
|
+
const candidates = [];
|
|
13630
|
+
if (windows) {
|
|
13631
|
+
for (const name of ["ProgramFiles", "ProgramFiles(x86)"]) {
|
|
13632
|
+
const directory = environment(name);
|
|
13633
|
+
if (directory && paths.isAbsolute(directory)) {
|
|
13634
|
+
candidates.push(paths.join(directory, "Git", "bin", "bash.exe"));
|
|
13635
|
+
}
|
|
13636
|
+
}
|
|
13637
|
+
} else {
|
|
13638
|
+
candidates.push("/bin/bash");
|
|
13639
|
+
}
|
|
13640
|
+
for (const entry of (environment("PATH") ?? "").split(paths.delimiter)) {
|
|
13641
|
+
const directory = windows && entry.startsWith('"') && entry.endsWith('"') ? entry.slice(1, -1) : entry;
|
|
13642
|
+
if (paths.isAbsolute(directory)) {
|
|
13643
|
+
candidates.push(paths.join(directory, windows ? "bash.exe" : "bash"));
|
|
13644
|
+
}
|
|
13645
|
+
}
|
|
13646
|
+
for (const candidate of new Set(candidates)) {
|
|
13647
|
+
if (!isLegacyWsl(candidate) && isExecutable(candidate))
|
|
13648
|
+
return candidate;
|
|
13649
|
+
}
|
|
13650
|
+
throw new Error(windows ? "Bash not found. Install Git for Windows, add its bin directory to PATH, or set shell_path in config.json." : "Bash not found. Install Bash, add its directory to PATH, or set shell_path in config.json.");
|
|
13651
|
+
}
|
|
13652
|
+
|
|
13653
|
+
// ../../packages/shell/bash-local/dist/bash-output.js
|
|
13654
|
+
import { mkdir, mkdtemp, open, readdir, rm, stat, writeFile } from "node:fs/promises";
|
|
13655
|
+
import { homedir as homedir3 } from "node:os";
|
|
13656
|
+
import { join as join6, resolve as resolve2 } from "node:path";
|
|
13657
|
+
var BASH_OUTPUT_MAX_BYTES = 50 * 1024;
|
|
13658
|
+
var BASH_OUTPUT_MAX_LINES = 2e3;
|
|
13659
|
+
var OUTPUT_RETENTION_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
13660
|
+
var OUTPUT_FILE_MAX_BYTES = 64 * 1024 * 1024;
|
|
13661
|
+
function lines(text) {
|
|
13662
|
+
return text === "" ? 0 : text.split("\n").length;
|
|
13663
|
+
}
|
|
13664
|
+
function tail(value, maxBytes, maxLines) {
|
|
13665
|
+
if (maxBytes <= 0 || maxLines <= 0)
|
|
13666
|
+
return { text: "", startMidLine: value.text !== "" || value.startMidLine };
|
|
13667
|
+
const bytes = Buffer.from(value.text);
|
|
13668
|
+
let start = Math.max(0, bytes.length - maxBytes);
|
|
13669
|
+
while (start < bytes.length && (bytes[start] & 192) === 128)
|
|
13670
|
+
start++;
|
|
13671
|
+
let text = bytes.subarray(start).toString("utf8");
|
|
13672
|
+
let startMidLine = start > 0 ? bytes[start - 1] !== 10 : value.startMidLine;
|
|
13673
|
+
const parts = text.split("\n");
|
|
13674
|
+
if (parts.length > maxLines) {
|
|
13675
|
+
text = parts.slice(-maxLines).join("\n");
|
|
13676
|
+
startMidLine = false;
|
|
13677
|
+
}
|
|
13678
|
+
return { text, startMidLine };
|
|
13679
|
+
}
|
|
13680
|
+
var BashOutput = class {
|
|
13681
|
+
maxBytes;
|
|
13682
|
+
maxLines;
|
|
13683
|
+
#directory;
|
|
13684
|
+
#fileLimit;
|
|
13685
|
+
#tails = {
|
|
13686
|
+
stdout: { text: "", startMidLine: false },
|
|
13687
|
+
stderr: { text: "", startMidLine: false }
|
|
13688
|
+
};
|
|
13689
|
+
#totals = { stdout: 0, stderr: 0 };
|
|
13690
|
+
#handles = {};
|
|
13691
|
+
#pending = Promise.resolve();
|
|
13692
|
+
#archiveDirectory = null;
|
|
13693
|
+
#fileBytes = 0;
|
|
13694
|
+
#spillAttempted = false;
|
|
13695
|
+
files = null;
|
|
13696
|
+
fileError = null;
|
|
13697
|
+
revision = 0;
|
|
13698
|
+
constructor(options = {}) {
|
|
13699
|
+
this.maxBytes = options.maxOutputBytes ?? BASH_OUTPUT_MAX_BYTES;
|
|
13700
|
+
this.maxLines = options.maxOutputLines ?? BASH_OUTPUT_MAX_LINES;
|
|
13701
|
+
this.#fileLimit = options.maxOutputFileBytes ?? OUTPUT_FILE_MAX_BYTES;
|
|
13702
|
+
for (const limit of [this.maxBytes, this.maxLines, this.#fileLimit]) {
|
|
13703
|
+
if (!Number.isSafeInteger(limit) || limit < 1)
|
|
13704
|
+
throw new RangeError("Bash output limits must be positive integers");
|
|
13705
|
+
}
|
|
13706
|
+
this.#directory = resolve2(options.outputDirectory ?? join6(homedir3(), ".laohuang", "tool-output"));
|
|
13707
|
+
}
|
|
13708
|
+
append(stream, text) {
|
|
13709
|
+
const next = this.#pending.then(() => this.#append(stream, text));
|
|
13710
|
+
this.#pending = next.catch(() => {
|
|
13711
|
+
});
|
|
13712
|
+
return next;
|
|
13713
|
+
}
|
|
13714
|
+
async #append(stream, text) {
|
|
13715
|
+
if (!text)
|
|
13716
|
+
return;
|
|
13717
|
+
const combined = { ...this.#tails[stream], text: this.#tails[stream].text + text };
|
|
13718
|
+
const other = stream === "stdout" ? "stderr" : "stdout";
|
|
13719
|
+
if (!this.#spillAttempted && (Buffer.byteLength(combined.text) + Buffer.byteLength(this.#tails[other].text) > this.maxBytes || lines(combined.text) + lines(this.#tails[other].text) > this.maxLines)) {
|
|
13720
|
+
this.#spillAttempted = true;
|
|
13721
|
+
await this.#startArchive();
|
|
13722
|
+
for (const name of ["stdout", "stderr"])
|
|
13723
|
+
await this.#write(name, this.#tails[name].text);
|
|
13724
|
+
}
|
|
13725
|
+
await this.#write(stream, text);
|
|
13726
|
+
this.#totals[stream] += Buffer.byteLength(text);
|
|
13727
|
+
this.#tails[stream] = tail(combined, this.maxBytes, this.maxLines);
|
|
13728
|
+
this.revision++;
|
|
13729
|
+
}
|
|
13730
|
+
snapshot() {
|
|
13731
|
+
const reserved = tail(this.#tails.stderr, Math.floor(this.maxBytes / 2), Math.floor(this.maxLines / 2));
|
|
13732
|
+
const stdout = tail(this.#tails.stdout, this.maxBytes - Buffer.byteLength(reserved.text), this.maxLines - lines(reserved.text));
|
|
13733
|
+
const stderr = tail(this.#tails.stderr, this.maxBytes - Buffer.byteLength(stdout.text), this.maxLines - lines(stdout.text));
|
|
13734
|
+
return {
|
|
13735
|
+
stdout: { ...stdout, truncated: Buffer.byteLength(stdout.text) < this.#totals.stdout },
|
|
13736
|
+
stderr: { ...stderr, truncated: Buffer.byteLength(stderr.text) < this.#totals.stderr }
|
|
13737
|
+
};
|
|
13738
|
+
}
|
|
13739
|
+
async #startArchive() {
|
|
13740
|
+
try {
|
|
13741
|
+
await mkdir(this.#directory, { recursive: true, mode: 448 });
|
|
13742
|
+
await this.#removeExpired();
|
|
13743
|
+
this.#archiveDirectory = await mkdtemp(join6(this.#directory, "bash-"));
|
|
13744
|
+
const files = {
|
|
13745
|
+
stdout: join6(this.#archiveDirectory, "stdout.log"),
|
|
13746
|
+
stderr: join6(this.#archiveDirectory, "stderr.log")
|
|
13747
|
+
};
|
|
13748
|
+
for (const stream of ["stdout", "stderr"])
|
|
13749
|
+
this.#handles[stream] = await open(files[stream], "wx", 384);
|
|
13750
|
+
this.files = files;
|
|
13751
|
+
} catch (error) {
|
|
13752
|
+
this.fileError = `Cannot save Bash output: ${String(error)}`;
|
|
13753
|
+
}
|
|
13754
|
+
}
|
|
13755
|
+
async #write(stream, text) {
|
|
13756
|
+
const handle = this.#handles[stream];
|
|
13757
|
+
if (!handle || this.fileError !== null || text === "")
|
|
13758
|
+
return;
|
|
13759
|
+
try {
|
|
13760
|
+
const bytes = Buffer.from(text);
|
|
13761
|
+
const available = Math.max(0, this.#fileLimit - this.#fileBytes);
|
|
13762
|
+
let end = Math.min(bytes.length, available);
|
|
13763
|
+
while (end > 0 && end < bytes.length && (bytes[end] & 192) === 128)
|
|
13764
|
+
end--;
|
|
13765
|
+
if (end > 0)
|
|
13766
|
+
await handle.writeFile(bytes.subarray(0, end));
|
|
13767
|
+
this.#fileBytes += end;
|
|
13768
|
+
if (end < bytes.length)
|
|
13769
|
+
this.fileError = `Bash output file limit reached (${this.#fileLimit} bytes); files contain partial output`;
|
|
13770
|
+
} catch (error) {
|
|
13771
|
+
this.fileError = `Cannot save Bash output: ${String(error)}`;
|
|
13772
|
+
}
|
|
13773
|
+
}
|
|
13774
|
+
async close() {
|
|
13775
|
+
await this.#pending;
|
|
13776
|
+
for (const handle of Object.values(this.#handles)) {
|
|
13777
|
+
try {
|
|
13778
|
+
await handle.close();
|
|
13779
|
+
} catch (error) {
|
|
13780
|
+
this.fileError ??= `Cannot close Bash output: ${String(error)}`;
|
|
13781
|
+
}
|
|
13782
|
+
}
|
|
13783
|
+
if (this.#archiveDirectory !== null) {
|
|
13784
|
+
try {
|
|
13785
|
+
await writeFile(join6(this.#archiveDirectory, "completed"), "", { mode: 384 });
|
|
13786
|
+
} catch {
|
|
13787
|
+
}
|
|
13788
|
+
}
|
|
13789
|
+
}
|
|
13790
|
+
async #removeExpired() {
|
|
13791
|
+
for (const entry of await readdir(this.#directory, { withFileTypes: true })) {
|
|
13792
|
+
if (!entry.isDirectory() || !entry.name.startsWith("bash-"))
|
|
13793
|
+
continue;
|
|
13794
|
+
const directory = join6(this.#directory, entry.name);
|
|
13795
|
+
try {
|
|
13796
|
+
if (Date.now() - (await stat(join6(directory, "completed"))).mtimeMs > OUTPUT_RETENTION_MS) {
|
|
13797
|
+
await rm(directory, { recursive: true, force: true });
|
|
13798
|
+
}
|
|
13799
|
+
} catch {
|
|
13800
|
+
}
|
|
13801
|
+
}
|
|
13802
|
+
}
|
|
13803
|
+
};
|
|
13804
|
+
|
|
13201
13805
|
// ../../packages/shell/bash-local/dist/bash-runner.js
|
|
13202
|
-
import { spawn } from "node:child_process";
|
|
13806
|
+
import { execFile, spawn } from "node:child_process";
|
|
13203
13807
|
import { constants as osConstants } from "node:os";
|
|
13808
|
+
import { win32 as win323 } from "node:path";
|
|
13204
13809
|
import { StringDecoder } from "node:string_decoder";
|
|
13205
|
-
var FLUSH_INTERVAL_MS =
|
|
13206
|
-
var FLUSH_CHARS = 4096;
|
|
13810
|
+
var FLUSH_INTERVAL_MS = 100;
|
|
13207
13811
|
var TERMINATION_GRACE_MS = 2e3;
|
|
13208
13812
|
var WATCHDOG_INTERVAL_MS = 10;
|
|
13813
|
+
var POST_EXIT_IDLE_MS = 100;
|
|
13814
|
+
var TERMINATION_EXIT_GRACE_MS = 1e3;
|
|
13815
|
+
var TASKKILL_TIMEOUT_MS = 5e3;
|
|
13209
13816
|
var MODEL_API_KEY_ENV_NAMES = [
|
|
13210
13817
|
"OPENAI_API_KEY",
|
|
13211
13818
|
"DEEPSEEK_API_KEY",
|
|
@@ -13246,11 +13853,10 @@ var ToolExecutionContext = class {
|
|
|
13246
13853
|
if (sink == null)
|
|
13247
13854
|
return;
|
|
13248
13855
|
if (typeof sink === "function") {
|
|
13249
|
-
sink(kind, payload);
|
|
13250
|
-
return;
|
|
13856
|
+
return sink(kind, payload);
|
|
13251
13857
|
}
|
|
13252
13858
|
if (typeof sink.publish === "function") {
|
|
13253
|
-
sink.publish(kind, {
|
|
13859
|
+
return sink.publish(kind, {
|
|
13254
13860
|
source: "tool",
|
|
13255
13861
|
session_id: this.sessionId ?? "local",
|
|
13256
13862
|
task_id: this.taskId,
|
|
@@ -13268,6 +13874,12 @@ var BashResult = class {
|
|
|
13268
13874
|
error;
|
|
13269
13875
|
durationMs;
|
|
13270
13876
|
truncated;
|
|
13877
|
+
outputComplete;
|
|
13878
|
+
outputFiles;
|
|
13879
|
+
outputFileComplete;
|
|
13880
|
+
outputFileError;
|
|
13881
|
+
stdoutStartMidLine;
|
|
13882
|
+
stderrStartMidLine;
|
|
13271
13883
|
constructor(init) {
|
|
13272
13884
|
this.status = init.status;
|
|
13273
13885
|
this.stdout = init.stdout ?? "";
|
|
@@ -13276,6 +13888,12 @@ var BashResult = class {
|
|
|
13276
13888
|
this.error = init.error ?? null;
|
|
13277
13889
|
this.durationMs = init.durationMs ?? 0;
|
|
13278
13890
|
this.truncated = init.truncated ?? false;
|
|
13891
|
+
this.outputComplete = init.outputComplete ?? true;
|
|
13892
|
+
this.outputFiles = init.outputFiles ?? null;
|
|
13893
|
+
this.outputFileComplete = init.outputFileComplete ?? false;
|
|
13894
|
+
this.outputFileError = init.outputFileError ?? null;
|
|
13895
|
+
this.stdoutStartMidLine = init.stdoutStartMidLine ?? false;
|
|
13896
|
+
this.stderrStartMidLine = init.stderrStartMidLine ?? false;
|
|
13279
13897
|
}
|
|
13280
13898
|
get ok() {
|
|
13281
13899
|
return this.status === "completed" && this.exitCode === 0;
|
|
@@ -13288,66 +13906,21 @@ var BashResult = class {
|
|
|
13288
13906
|
stdout: this.stdout,
|
|
13289
13907
|
stderr: this.stderr,
|
|
13290
13908
|
duration_ms: this.durationMs,
|
|
13291
|
-
truncated: this.truncated
|
|
13909
|
+
truncated: this.truncated,
|
|
13910
|
+
output_complete: this.outputComplete,
|
|
13911
|
+
output_files: this.outputFiles,
|
|
13912
|
+
output_file_complete: this.outputFileComplete,
|
|
13913
|
+
stdout_start_mid_line: this.stdoutStartMidLine,
|
|
13914
|
+
stderr_start_mid_line: this.stderrStartMidLine
|
|
13292
13915
|
};
|
|
13916
|
+
if (this.outputFileError)
|
|
13917
|
+
result["output_file_error"] = this.outputFileError;
|
|
13293
13918
|
if (this.error) {
|
|
13294
13919
|
result["error"] = this.error;
|
|
13295
13920
|
}
|
|
13296
13921
|
return result;
|
|
13297
13922
|
}
|
|
13298
13923
|
};
|
|
13299
|
-
var BoundedOutput = class {
|
|
13300
|
-
limit;
|
|
13301
|
-
headLimit;
|
|
13302
|
-
tailLimit;
|
|
13303
|
-
totalChars = 0;
|
|
13304
|
-
whole = "";
|
|
13305
|
-
head = "";
|
|
13306
|
-
tail = "";
|
|
13307
|
-
isTruncated = false;
|
|
13308
|
-
constructor(limit) {
|
|
13309
|
-
this.limit = Math.max(0, Math.trunc(limit));
|
|
13310
|
-
this.headLimit = Math.trunc(this.limit * 0.4);
|
|
13311
|
-
this.tailLimit = this.limit - this.headLimit;
|
|
13312
|
-
}
|
|
13313
|
-
append(text) {
|
|
13314
|
-
if (!text)
|
|
13315
|
-
return;
|
|
13316
|
-
this.totalChars += text.length;
|
|
13317
|
-
if (this.limit === 0) {
|
|
13318
|
-
this.isTruncated = true;
|
|
13319
|
-
return;
|
|
13320
|
-
}
|
|
13321
|
-
if (!this.isTruncated && this.whole.length + text.length <= this.limit) {
|
|
13322
|
-
this.whole += text;
|
|
13323
|
-
return;
|
|
13324
|
-
}
|
|
13325
|
-
if (!this.isTruncated) {
|
|
13326
|
-
const combined = this.whole + text;
|
|
13327
|
-
this.head = combined.slice(0, this.headLimit);
|
|
13328
|
-
this.tail = this.tailLimit > 0 ? combined.slice(-this.tailLimit) : "";
|
|
13329
|
-
this.whole = "";
|
|
13330
|
-
this.isTruncated = true;
|
|
13331
|
-
return;
|
|
13332
|
-
}
|
|
13333
|
-
if (this.tailLimit > 0) {
|
|
13334
|
-
this.tail = (this.tail + text).slice(-this.tailLimit);
|
|
13335
|
-
}
|
|
13336
|
-
}
|
|
13337
|
-
render() {
|
|
13338
|
-
if (!this.isTruncated)
|
|
13339
|
-
return this.whole;
|
|
13340
|
-
const retained = this.head.length + this.tail.length;
|
|
13341
|
-
const omitted = Math.max(0, this.totalChars - retained);
|
|
13342
|
-
const marker = `
|
|
13343
|
-
...[truncated ${omitted} chars]...
|
|
13344
|
-
`;
|
|
13345
|
-
return this.head + marker + this.tail;
|
|
13346
|
-
}
|
|
13347
|
-
get truncated() {
|
|
13348
|
-
return this.isTruncated;
|
|
13349
|
-
}
|
|
13350
|
-
};
|
|
13351
13924
|
var TerminalSanitizer = class {
|
|
13352
13925
|
state = "normal";
|
|
13353
13926
|
feed(text) {
|
|
@@ -13402,15 +13975,31 @@ var TerminalSanitizer = class {
|
|
|
13402
13975
|
}
|
|
13403
13976
|
};
|
|
13404
13977
|
function sleep(ms) {
|
|
13405
|
-
return new Promise((
|
|
13978
|
+
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
13406
13979
|
}
|
|
13407
13980
|
function errnoCode(error) {
|
|
13408
13981
|
return error?.code;
|
|
13409
13982
|
}
|
|
13410
|
-
async function
|
|
13983
|
+
async function terminateProcessTree(child, graceMs = TERMINATION_GRACE_MS) {
|
|
13411
13984
|
const pid = child.pid;
|
|
13412
13985
|
if (pid === void 0)
|
|
13413
13986
|
return;
|
|
13987
|
+
if (process.platform === "win32") {
|
|
13988
|
+
const taskkill = win323.join(process.env.SystemRoot ?? "C:\\Windows", "System32", "taskkill.exe");
|
|
13989
|
+
await new Promise((resolve3, reject) => {
|
|
13990
|
+
execFile(taskkill, ["/F", "/T", "/PID", String(pid)], {
|
|
13991
|
+
windowsHide: true,
|
|
13992
|
+
timeout: TASKKILL_TIMEOUT_MS,
|
|
13993
|
+
killSignal: "SIGKILL"
|
|
13994
|
+
}, (error) => {
|
|
13995
|
+
if (error)
|
|
13996
|
+
reject(new Error(`taskkill failed: ${error.message}`));
|
|
13997
|
+
else
|
|
13998
|
+
resolve3();
|
|
13999
|
+
});
|
|
14000
|
+
});
|
|
14001
|
+
return;
|
|
14002
|
+
}
|
|
13414
14003
|
try {
|
|
13415
14004
|
process.kill(-pid, "SIGTERM");
|
|
13416
14005
|
} catch (error) {
|
|
@@ -13463,25 +14052,55 @@ function toExitCode(code, signal) {
|
|
|
13463
14052
|
async function runBash(command, options) {
|
|
13464
14053
|
const invokedAt = performance.now();
|
|
13465
14054
|
const execution = options.context ?? new ToolExecutionContext();
|
|
14055
|
+
const output = new BashOutput(options);
|
|
14056
|
+
let publicationError = null;
|
|
14057
|
+
const publish = async (kind, payload) => {
|
|
14058
|
+
try {
|
|
14059
|
+
await execution.publish(kind, payload);
|
|
14060
|
+
} catch (error) {
|
|
14061
|
+
publicationError ??= `Bash event publication failed: ${String(error)}`;
|
|
14062
|
+
}
|
|
14063
|
+
};
|
|
14064
|
+
const finish = async (result2) => {
|
|
14065
|
+
await publish("tool.finished", result2.asDict());
|
|
14066
|
+
if (publicationError !== null) {
|
|
14067
|
+
result2.status = "failed";
|
|
14068
|
+
if (!result2.error?.includes(publicationError)) {
|
|
14069
|
+
result2.error = [result2.error, publicationError].filter(Boolean).join("; ");
|
|
14070
|
+
}
|
|
14071
|
+
}
|
|
14072
|
+
return result2;
|
|
14073
|
+
};
|
|
13466
14074
|
if (execution.isCancelled()) {
|
|
13467
14075
|
const result2 = new BashResult({
|
|
13468
14076
|
status: "cancelled",
|
|
13469
14077
|
error: execution.cancellationReason,
|
|
13470
14078
|
durationMs: Math.trunc(performance.now() - invokedAt)
|
|
13471
14079
|
});
|
|
13472
|
-
|
|
13473
|
-
return result2;
|
|
14080
|
+
return await finish(result2);
|
|
13474
14081
|
}
|
|
13475
14082
|
const environment = cleanEnvironment(options.env ?? process.env);
|
|
13476
|
-
|
|
13477
|
-
|
|
13478
|
-
env: environment
|
|
13479
|
-
|
|
13480
|
-
|
|
13481
|
-
|
|
13482
|
-
|
|
13483
|
-
|
|
13484
|
-
|
|
14083
|
+
let child;
|
|
14084
|
+
try {
|
|
14085
|
+
const shellPath = resolveBashPath({ shellPath: options.shellPath, env: environment });
|
|
14086
|
+
child = spawn(shellPath, ["-lc", command], {
|
|
14087
|
+
cwd: options.cwd,
|
|
14088
|
+
env: environment,
|
|
14089
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
14090
|
+
detached: process.platform !== "win32",
|
|
14091
|
+
windowsHide: true
|
|
14092
|
+
});
|
|
14093
|
+
} catch (error) {
|
|
14094
|
+
const result2 = new BashResult({
|
|
14095
|
+
status: "spawn_failed",
|
|
14096
|
+
error: String(error),
|
|
14097
|
+
durationMs: Math.trunc(performance.now() - invokedAt)
|
|
14098
|
+
});
|
|
14099
|
+
return await finish(result2);
|
|
14100
|
+
}
|
|
14101
|
+
const spawnError = await new Promise((resolve3) => {
|
|
14102
|
+
child.once("spawn", () => resolve3(null));
|
|
14103
|
+
child.once("error", (error) => resolve3(error));
|
|
13485
14104
|
});
|
|
13486
14105
|
if (spawnError) {
|
|
13487
14106
|
const result2 = new BashResult({
|
|
@@ -13489,76 +14108,130 @@ async function runBash(command, options) {
|
|
|
13489
14108
|
error: String(spawnError),
|
|
13490
14109
|
durationMs: Math.trunc(performance.now() - invokedAt)
|
|
13491
14110
|
});
|
|
13492
|
-
|
|
13493
|
-
return result2;
|
|
14111
|
+
return await finish(result2);
|
|
13494
14112
|
}
|
|
13495
14113
|
child.on("error", () => {
|
|
13496
14114
|
});
|
|
13497
|
-
|
|
13498
|
-
|
|
13499
|
-
|
|
13500
|
-
|
|
13501
|
-
|
|
13502
|
-
|
|
13503
|
-
|
|
14115
|
+
const started = publish("tool.started", { name: "bash", arguments: { command } });
|
|
14116
|
+
let publishing = null;
|
|
14117
|
+
let publishedRevision = 0;
|
|
14118
|
+
let exited = false;
|
|
14119
|
+
let lastOutputAt = performance.now();
|
|
14120
|
+
let outputIncomplete = false;
|
|
14121
|
+
const ingesting = { stdout: false, stderr: false };
|
|
14122
|
+
const closeOutput = (force = false) => {
|
|
14123
|
+
for (const stream of ["stdout", "stderr"]) {
|
|
14124
|
+
const pipe = child[stream];
|
|
14125
|
+
if (!force && (ingesting[stream] || (pipe?.readableLength ?? 0) > 0))
|
|
14126
|
+
continue;
|
|
14127
|
+
if (pipe && !pipe.readableEnded && !pipe.destroyed) {
|
|
14128
|
+
outputIncomplete = true;
|
|
14129
|
+
pipe.destroy();
|
|
14130
|
+
}
|
|
14131
|
+
}
|
|
13504
14132
|
};
|
|
13505
|
-
const
|
|
13506
|
-
|
|
13507
|
-
const flush = (stream) => {
|
|
13508
|
-
const text = pending[stream];
|
|
13509
|
-
if (!text)
|
|
14133
|
+
const flush = () => {
|
|
14134
|
+
if (publishing !== null || publicationError !== null || publishedRevision === output.revision)
|
|
13510
14135
|
return;
|
|
13511
|
-
|
|
13512
|
-
|
|
13513
|
-
|
|
13514
|
-
|
|
13515
|
-
|
|
13516
|
-
stream,
|
|
13517
|
-
|
|
13518
|
-
|
|
14136
|
+
const snapshot2 = output.snapshot();
|
|
14137
|
+
publishedRevision = output.revision;
|
|
14138
|
+
const revision = publishedRevision;
|
|
14139
|
+
publishing = (async () => {
|
|
14140
|
+
await started;
|
|
14141
|
+
for (const stream of ["stdout", "stderr"]) {
|
|
14142
|
+
if (publicationError !== null)
|
|
14143
|
+
break;
|
|
14144
|
+
await publish("tool.output_snapshot", {
|
|
14145
|
+
name: "bash",
|
|
14146
|
+
stream,
|
|
14147
|
+
text: snapshot2[stream].text,
|
|
14148
|
+
stream_sequence: revision,
|
|
14149
|
+
truncated: snapshot2[stream].truncated,
|
|
14150
|
+
start_mid_line: snapshot2[stream].startMidLine
|
|
14151
|
+
});
|
|
14152
|
+
}
|
|
14153
|
+
})().finally(() => {
|
|
14154
|
+
publishing = null;
|
|
13519
14155
|
});
|
|
13520
14156
|
};
|
|
13521
14157
|
const readPipe = async (stream, pipe) => {
|
|
13522
14158
|
const decoder = new StringDecoder("utf8");
|
|
13523
14159
|
const sanitizer = new TerminalSanitizer();
|
|
13524
|
-
const ingest = (text) => {
|
|
13525
|
-
if (!text)
|
|
13526
|
-
return;
|
|
13527
|
-
pending[stream] += text;
|
|
13528
|
-
if (pending[stream].length >= FLUSH_CHARS)
|
|
13529
|
-
flush(stream);
|
|
13530
|
-
};
|
|
13531
14160
|
try {
|
|
13532
14161
|
for await (const chunk of pipe) {
|
|
13533
|
-
|
|
14162
|
+
lastOutputAt = performance.now();
|
|
14163
|
+
ingesting[stream] = true;
|
|
14164
|
+
try {
|
|
14165
|
+
await output.append(stream, sanitizer.feed(decoder.write(chunk)));
|
|
14166
|
+
} finally {
|
|
14167
|
+
ingesting[stream] = false;
|
|
14168
|
+
lastOutputAt = performance.now();
|
|
14169
|
+
}
|
|
13534
14170
|
}
|
|
13535
|
-
ingest(sanitizer.feed(decoder.end()));
|
|
13536
14171
|
} catch {
|
|
14172
|
+
outputIncomplete = true;
|
|
14173
|
+
} finally {
|
|
14174
|
+
await output.append(stream, sanitizer.feed(decoder.end()));
|
|
13537
14175
|
}
|
|
13538
14176
|
};
|
|
13539
14177
|
const stdoutDone = readPipe("stdout", child.stdout);
|
|
13540
14178
|
const stderrDone = readPipe("stderr", child.stderr);
|
|
13541
|
-
|
|
13542
|
-
|
|
14179
|
+
let resolveExit;
|
|
14180
|
+
const exitInfo = new Promise((resolve3) => {
|
|
14181
|
+
resolveExit = resolve3;
|
|
13543
14182
|
});
|
|
14183
|
+
const onExit = (code2, signal2) => {
|
|
14184
|
+
exited = true;
|
|
14185
|
+
lastOutputAt = performance.now();
|
|
14186
|
+
resolveExit({ code: code2, signal: signal2 });
|
|
14187
|
+
};
|
|
14188
|
+
child.once("exit", onExit);
|
|
13544
14189
|
let terminalStatus = null;
|
|
13545
14190
|
const startedAt = performance.now();
|
|
13546
14191
|
let lastFlush = startedAt;
|
|
13547
14192
|
const timeoutMs = options.timeout * 1e3;
|
|
14193
|
+
let termination = null;
|
|
14194
|
+
let terminationCompletedAt = null;
|
|
14195
|
+
let cleanupError = null;
|
|
14196
|
+
const terminate = () => {
|
|
14197
|
+
if (termination !== null)
|
|
14198
|
+
return;
|
|
14199
|
+
termination = terminateProcessTree(child).catch((error) => {
|
|
14200
|
+
cleanupError = error instanceof Error ? error.message : String(error);
|
|
14201
|
+
try {
|
|
14202
|
+
child.kill("SIGKILL");
|
|
14203
|
+
} catch {
|
|
14204
|
+
}
|
|
14205
|
+
}).finally(() => {
|
|
14206
|
+
terminationCompletedAt = performance.now();
|
|
14207
|
+
});
|
|
14208
|
+
};
|
|
13548
14209
|
const watchdog = setInterval(() => {
|
|
13549
14210
|
const now = performance.now();
|
|
13550
14211
|
if (terminalStatus === null) {
|
|
13551
|
-
if (
|
|
14212
|
+
if (publicationError !== null) {
|
|
14213
|
+
terminalStatus = "failed";
|
|
14214
|
+
terminate();
|
|
14215
|
+
} else if (execution.isCancelled()) {
|
|
13552
14216
|
terminalStatus = "cancelled";
|
|
13553
|
-
|
|
14217
|
+
terminate();
|
|
13554
14218
|
} else if (options.timeout >= 0 && now - startedAt >= timeoutMs) {
|
|
13555
14219
|
terminalStatus = "timed_out";
|
|
13556
|
-
|
|
14220
|
+
terminate();
|
|
14221
|
+
}
|
|
14222
|
+
}
|
|
14223
|
+
if (exited && now - lastOutputAt >= POST_EXIT_IDLE_MS)
|
|
14224
|
+
closeOutput();
|
|
14225
|
+
if (terminationCompletedAt !== null && now - terminationCompletedAt >= TERMINATION_EXIT_GRACE_MS) {
|
|
14226
|
+
closeOutput(true);
|
|
14227
|
+
if (!exited) {
|
|
14228
|
+
cleanupError ??= "Bash did not exit after process termination";
|
|
14229
|
+
child.unref();
|
|
14230
|
+
resolveExit({ code: null, signal: null });
|
|
13557
14231
|
}
|
|
13558
14232
|
}
|
|
13559
14233
|
if (now - lastFlush >= FLUSH_INTERVAL_MS) {
|
|
13560
|
-
flush(
|
|
13561
|
-
flush("stderr");
|
|
14234
|
+
flush();
|
|
13562
14235
|
lastFlush = now;
|
|
13563
14236
|
}
|
|
13564
14237
|
}, WATCHDOG_INTERVAL_MS);
|
|
@@ -13567,18 +14240,21 @@ async function runBash(command, options) {
|
|
|
13567
14240
|
await Promise.all([stdoutDone, stderrDone, exitInfo]);
|
|
13568
14241
|
} finally {
|
|
13569
14242
|
clearInterval(watchdog);
|
|
13570
|
-
|
|
13571
|
-
|
|
13572
|
-
|
|
13573
|
-
}
|
|
13574
|
-
|
|
13575
|
-
|
|
14243
|
+
child.removeListener("exit", onExit);
|
|
14244
|
+
await termination;
|
|
14245
|
+
await output.close();
|
|
14246
|
+
}
|
|
14247
|
+
await started;
|
|
14248
|
+
await publishing;
|
|
14249
|
+
flush();
|
|
14250
|
+
await publishing;
|
|
13576
14251
|
const { code, signal } = await exitInfo;
|
|
13577
14252
|
const exitCode = toExitCode(code, signal);
|
|
13578
|
-
const
|
|
13579
|
-
const
|
|
14253
|
+
const snapshot = output.snapshot();
|
|
14254
|
+
const stdout = snapshot.stdout.text;
|
|
14255
|
+
const stderr = snapshot.stderr.text;
|
|
13580
14256
|
const durationMs = Math.trunc(performance.now() - invokedAt);
|
|
13581
|
-
const truncated =
|
|
14257
|
+
const truncated = snapshot.stdout.truncated || snapshot.stderr.truncated;
|
|
13582
14258
|
let result;
|
|
13583
14259
|
if (terminalStatus === "cancelled") {
|
|
13584
14260
|
result = new BashResult({
|
|
@@ -13620,20 +14296,35 @@ async function runBash(command, options) {
|
|
|
13620
14296
|
truncated
|
|
13621
14297
|
});
|
|
13622
14298
|
}
|
|
13623
|
-
|
|
13624
|
-
|
|
14299
|
+
if (cleanupError !== null) {
|
|
14300
|
+
result.error = `${result.error ?? "Bash execution failed"}; Process cleanup failed: ${cleanupError}`;
|
|
14301
|
+
}
|
|
14302
|
+
result.outputComplete = !outputIncomplete;
|
|
14303
|
+
result.outputFiles = output.files;
|
|
14304
|
+
result.outputFileComplete = output.files !== null && output.fileError === null && !outputIncomplete;
|
|
14305
|
+
result.outputFileError = output.fileError;
|
|
14306
|
+
result.stdoutStartMidLine = snapshot.stdout.startMidLine;
|
|
14307
|
+
result.stderrStartMidLine = snapshot.stderr.startMidLine;
|
|
14308
|
+
if (publicationError !== null) {
|
|
14309
|
+
result.status = "failed";
|
|
14310
|
+
result.error = publicationError + (cleanupError === null ? "" : `; Process cleanup failed: ${cleanupError}`);
|
|
14311
|
+
}
|
|
14312
|
+
return await finish(result);
|
|
13625
14313
|
}
|
|
13626
14314
|
|
|
13627
14315
|
// ../../packages/shell/tool-bash/dist/tool-bash.js
|
|
13628
14316
|
function createBashToolDefinition(options) {
|
|
13629
|
-
const
|
|
14317
|
+
const pathOptions = {
|
|
14318
|
+
env: options.env ?? process.env,
|
|
14319
|
+
shellPath: () => resolveBashPath({ shellPath: options.shellPath, env: options.env })
|
|
14320
|
+
};
|
|
14321
|
+
const root = resolveNonStrictSync2(resolveLocalPath(options.projectRoot, process.cwd(), pathOptions));
|
|
13630
14322
|
const bashTimeoutSeconds = options.bashTimeoutSeconds ?? 120;
|
|
13631
|
-
const maxOutputChars = options.maxOutputChars ?? 2e4;
|
|
13632
14323
|
const runBash2 = options.runBash ?? defaultRunBash;
|
|
13633
14324
|
return {
|
|
13634
14325
|
spec: {
|
|
13635
14326
|
name: "bash",
|
|
13636
|
-
description: "Run a Bash command.",
|
|
14327
|
+
description: "Run a Bash command. Returns stdout and stderr, limited together to the latest 2000 lines or 50KiB by default. When truncated, output_files contains saved output paths; check output_file_complete before assuming the files are complete.",
|
|
13637
14328
|
parameters: {
|
|
13638
14329
|
type: "object",
|
|
13639
14330
|
properties: {
|
|
@@ -13659,39 +14350,52 @@ function createBashToolDefinition(options) {
|
|
|
13659
14350
|
"Supply a concise description of what the command does.",
|
|
13660
14351
|
"Use workdir instead of cd.",
|
|
13661
14352
|
"Each call runs in an independent shell; state does not persist between calls.",
|
|
13662
|
-
"On a non-zero exit, inspect the output before retrying."
|
|
14353
|
+
"On a non-zero exit, inspect the output before retrying.",
|
|
14354
|
+
"When output is truncated, use read or a targeted Bash search on output_files instead of repeating the command. Saved output expires after 7 days; files are capped at 64MiB per call."
|
|
13663
14355
|
]
|
|
13664
14356
|
},
|
|
13665
14357
|
execute: async (args, execution) => {
|
|
13666
14358
|
const command = stringArgument(args, "command");
|
|
13667
14359
|
stringArgument(args, "description");
|
|
13668
14360
|
const workdir = args["workdir"];
|
|
13669
|
-
const cwd = workdir === void 0 ? root : await
|
|
14361
|
+
const cwd = workdir === void 0 ? root : await resolveNonStrict2(resolveLocalPath(stringArgument(args, "workdir"), root, pathOptions));
|
|
13670
14362
|
const timeoutMs = optionalPositiveInteger(args, "timeoutMs");
|
|
13671
14363
|
return await runBash2(command, {
|
|
13672
14364
|
cwd,
|
|
14365
|
+
shellPath: options.shellPath,
|
|
13673
14366
|
timeoutSeconds: timeoutMs === void 0 ? bashTimeoutSeconds : timeoutMs / 1e3,
|
|
13674
|
-
|
|
14367
|
+
maxOutputBytes: options.maxOutputBytes,
|
|
14368
|
+
maxOutputLines: options.maxOutputLines,
|
|
14369
|
+
outputDirectory: options.outputDirectory,
|
|
14370
|
+
maxOutputFileBytes: options.maxOutputFileBytes,
|
|
13675
14371
|
context: execution,
|
|
13676
|
-
env: process.env
|
|
14372
|
+
env: options.env ?? process.env
|
|
13677
14373
|
});
|
|
13678
14374
|
}
|
|
13679
14375
|
};
|
|
13680
14376
|
}
|
|
13681
14377
|
async function defaultRunBash(command, options) {
|
|
13682
|
-
const
|
|
14378
|
+
const execution = options.context;
|
|
14379
|
+
const context = {
|
|
14380
|
+
isCancelled: () => execution.isCancelled(),
|
|
14381
|
+
get cancellationReason() {
|
|
14382
|
+
return execution.cancellationReason;
|
|
14383
|
+
},
|
|
14384
|
+
publish: (kind, payload) => execution.publish?.(kind, payload)
|
|
14385
|
+
};
|
|
13683
14386
|
const result = await runBash(command, {
|
|
13684
14387
|
cwd: options.cwd,
|
|
14388
|
+
shellPath: options.shellPath,
|
|
13685
14389
|
timeout: options.timeoutSeconds,
|
|
13686
|
-
|
|
14390
|
+
maxOutputBytes: options.maxOutputBytes,
|
|
14391
|
+
maxOutputLines: options.maxOutputLines,
|
|
14392
|
+
outputDirectory: options.outputDirectory,
|
|
14393
|
+
maxOutputFileBytes: options.maxOutputFileBytes,
|
|
13687
14394
|
context,
|
|
13688
14395
|
env: options.env
|
|
13689
14396
|
});
|
|
13690
14397
|
return result.asDict();
|
|
13691
14398
|
}
|
|
13692
|
-
async function resolvePath(root, rawPath) {
|
|
13693
|
-
return await resolveNonStrict2(path4.resolve(root, rawPath));
|
|
13694
|
-
}
|
|
13695
14399
|
async function resolveNonStrict2(pathname) {
|
|
13696
14400
|
const missing = [];
|
|
13697
14401
|
let current = pathname;
|
|
@@ -13963,23 +14667,13 @@ function sessionDisplayTitle(session) {
|
|
|
13963
14667
|
return lastUserText2 === "" ? "Untitled session" : lastUserText2;
|
|
13964
14668
|
}
|
|
13965
14669
|
function sessionDisplayDescription(session, options) {
|
|
13966
|
-
const displayPath =
|
|
14670
|
+
const displayPath = displayLocalPath(session.cwd ?? session.projectRoot ?? "", options.homeDirectory);
|
|
13967
14671
|
const updated = relativeTimeLabel(session.updatedAt, options.now);
|
|
13968
14672
|
return displayPath === "" ? updated : `${updated} ${displayPath}`;
|
|
13969
14673
|
}
|
|
13970
14674
|
function cleanSingleLine(value) {
|
|
13971
14675
|
return value?.replace(/\s+/g, " ").trim() ?? "";
|
|
13972
14676
|
}
|
|
13973
|
-
function displayPathFor(path5, homeDirectory) {
|
|
13974
|
-
if (path5 === "" || homeDirectory === null || homeDirectory === "") {
|
|
13975
|
-
return path5;
|
|
13976
|
-
}
|
|
13977
|
-
const home = homeDirectory.endsWith("/") ? homeDirectory.slice(0, -1) : homeDirectory;
|
|
13978
|
-
if (path5 === home) {
|
|
13979
|
-
return "~";
|
|
13980
|
-
}
|
|
13981
|
-
return path5.startsWith(`${home}/`) ? `~/${path5.slice(home.length + 1)}` : path5;
|
|
13982
|
-
}
|
|
13983
14677
|
function relativeTimeLabel(updatedAt, now) {
|
|
13984
14678
|
const updated = new Date(updatedAt);
|
|
13985
14679
|
const timestamp = updated.getTime();
|
|
@@ -15595,7 +16289,7 @@ function defaultSecretInputFn(prompt) {
|
|
|
15595
16289
|
}
|
|
15596
16290
|
const byte = Buffer.alloc(1);
|
|
15597
16291
|
let answer = "";
|
|
15598
|
-
process.stdin
|
|
16292
|
+
const restoreRawMode = enterTerminalRawMode(process.stdin);
|
|
15599
16293
|
try {
|
|
15600
16294
|
for (; ; ) {
|
|
15601
16295
|
const bytesRead = readSync2(0, byte, 0, 1, null);
|
|
@@ -15621,7 +16315,7 @@ function defaultSecretInputFn(prompt) {
|
|
|
15621
16315
|
answer += byte.toString("utf8", 0, 1);
|
|
15622
16316
|
}
|
|
15623
16317
|
} finally {
|
|
15624
|
-
|
|
16318
|
+
restoreRawMode();
|
|
15625
16319
|
}
|
|
15626
16320
|
process.stderr.write("\n");
|
|
15627
16321
|
return answer;
|
|
@@ -15790,8 +16484,8 @@ function startCoordinator(session, commandHandler, presenter, suggestCommand) {
|
|
|
15790
16484
|
const done = (async () => {
|
|
15791
16485
|
for (; ; ) {
|
|
15792
16486
|
if (queue.length === 0) {
|
|
15793
|
-
await new Promise((
|
|
15794
|
-
wake =
|
|
16487
|
+
await new Promise((resolve3) => {
|
|
16488
|
+
wake = resolve3;
|
|
15795
16489
|
});
|
|
15796
16490
|
wake = null;
|
|
15797
16491
|
}
|
|
@@ -15837,14 +16531,14 @@ function startCoordinator(session, commandHandler, presenter, suggestCommand) {
|
|
|
15837
16531
|
};
|
|
15838
16532
|
}
|
|
15839
16533
|
function delay(ms) {
|
|
15840
|
-
return new Promise((
|
|
15841
|
-
setTimeout(
|
|
16534
|
+
return new Promise((resolve3) => {
|
|
16535
|
+
setTimeout(resolve3, ms);
|
|
15842
16536
|
});
|
|
15843
16537
|
}
|
|
15844
16538
|
async function settlesWithin(promise, ms) {
|
|
15845
16539
|
let timer;
|
|
15846
|
-
const timeout = new Promise((
|
|
15847
|
-
timer = setTimeout(() =>
|
|
16540
|
+
const timeout = new Promise((resolve3) => {
|
|
16541
|
+
timer = setTimeout(() => resolve3(false), ms);
|
|
15848
16542
|
});
|
|
15849
16543
|
const settled = promise.then(
|
|
15850
16544
|
() => true,
|
|
@@ -16032,6 +16726,15 @@ function resetEmptySessionTranscript(terminalUi) {
|
|
|
16032
16726
|
terminalUi?.replaceTranscript([]);
|
|
16033
16727
|
terminalUi?.showWelcome();
|
|
16034
16728
|
}
|
|
16729
|
+
function refreshSessionContextUsage(terminalUi, input) {
|
|
16730
|
+
if (terminalUi === null) return;
|
|
16731
|
+
const context = new ContextBuilder().build(input);
|
|
16732
|
+
const estimator = new DefaultTokenEstimator();
|
|
16733
|
+
terminalUi.setContextUsage(
|
|
16734
|
+
estimator.estimateMessages(context.messages) + estimator.estimateTools(input.tools),
|
|
16735
|
+
input.contextWindow
|
|
16736
|
+
);
|
|
16737
|
+
}
|
|
16035
16738
|
async function main(argv, options = {}) {
|
|
16036
16739
|
let parsed;
|
|
16037
16740
|
try {
|
|
@@ -16070,10 +16773,10 @@ async function main(argv, options = {}) {
|
|
|
16070
16773
|
const configPath = options.configPath ?? defaultConfigPath(environ);
|
|
16071
16774
|
const manager = new ConfigManager(configPath);
|
|
16072
16775
|
const credentials = new CredentialStore(
|
|
16073
|
-
options.credentialsPath ??
|
|
16776
|
+
options.credentialsPath ?? join7(dirname4(configPath), "credentials.json")
|
|
16074
16777
|
);
|
|
16075
16778
|
const modelCatalogStore = new ModelCatalogStore(
|
|
16076
|
-
options.modelsPath ??
|
|
16779
|
+
options.modelsPath ?? join7(dirname4(configPath), "models.json")
|
|
16077
16780
|
);
|
|
16078
16781
|
const modelPlatform = await createPiAiPlatform({
|
|
16079
16782
|
credentials,
|
|
@@ -16186,12 +16889,20 @@ async function main(argv, options = {}) {
|
|
|
16186
16889
|
outputFn(`Verified: ${provider.verified ? "yes" : "no"}`);
|
|
16187
16890
|
outputFn(`Configuration: ${configPath}`);
|
|
16188
16891
|
outputFn(`Node: ${process.version}`);
|
|
16189
|
-
|
|
16190
|
-
|
|
16892
|
+
let bashAvailable = true;
|
|
16893
|
+
try {
|
|
16894
|
+
outputFn(`Bash: ${resolveBashPath({ shellPath: manager.getShellPath(), env: environ })}`);
|
|
16895
|
+
} catch (error) {
|
|
16896
|
+
bashAvailable = false;
|
|
16897
|
+
outputFn(`Bash: ${errorMessage11(error)}`);
|
|
16898
|
+
}
|
|
16899
|
+
return provider !== void 0 && auth.configured && refreshOk && model !== void 0 && bashAvailable ? 0 : 1;
|
|
16191
16900
|
}
|
|
16192
16901
|
let config;
|
|
16902
|
+
let shellPath;
|
|
16193
16903
|
try {
|
|
16194
|
-
|
|
16904
|
+
shellPath = manager.getShellPath();
|
|
16905
|
+
if (existsSync8(configPath)) {
|
|
16195
16906
|
config = manager.resolve({
|
|
16196
16907
|
environ,
|
|
16197
16908
|
profile: args.profile,
|
|
@@ -16273,8 +16984,11 @@ async function main(argv, options = {}) {
|
|
|
16273
16984
|
terminalUi.state.model = config.model;
|
|
16274
16985
|
}
|
|
16275
16986
|
const toolRegistry = new ToolRegistry([
|
|
16276
|
-
...createFileToolDefinitions({ projectRoot
|
|
16277
|
-
|
|
16987
|
+
...createFileToolDefinitions({ projectRoot, pathOptions: {
|
|
16988
|
+
env: environ,
|
|
16989
|
+
shellPath: () => resolveBashPath({ shellPath, env: environ })
|
|
16990
|
+
} }),
|
|
16991
|
+
createBashToolDefinition({ projectRoot, shellPath, env: environ })
|
|
16278
16992
|
]);
|
|
16279
16993
|
const activeConversationHistory = {
|
|
16280
16994
|
appendUser: (input) => {
|
|
@@ -16458,6 +17172,16 @@ async function main(argv, options = {}) {
|
|
|
16458
17172
|
input: presenterInput,
|
|
16459
17173
|
secretInput: presenterSecretInput
|
|
16460
17174
|
}) : new TerminalCommandPresenter(terminalUi);
|
|
17175
|
+
const refreshContextUsage = () => {
|
|
17176
|
+
refreshSessionContextUsage(terminalUi, {
|
|
17177
|
+
entries: sessionController.history?.entries() ?? [],
|
|
17178
|
+
currentProvider: config.provider,
|
|
17179
|
+
currentModel: config.model,
|
|
17180
|
+
tools: toolRegistry.definitions,
|
|
17181
|
+
contextWindow: selectedModel?.contextWindow ?? 0
|
|
17182
|
+
});
|
|
17183
|
+
};
|
|
17184
|
+
refreshContextUsage();
|
|
16461
17185
|
const refreshSessionView = () => {
|
|
16462
17186
|
const currentSessionId = sessionController.currentSessionId;
|
|
16463
17187
|
if (currentSessionId !== null) {
|
|
@@ -16479,6 +17203,7 @@ async function main(argv, options = {}) {
|
|
|
16479
17203
|
agent.messages = [system];
|
|
16480
17204
|
}
|
|
16481
17205
|
resetEmptySessionTranscript(terminalUi);
|
|
17206
|
+
refreshContextUsage();
|
|
16482
17207
|
return;
|
|
16483
17208
|
}
|
|
16484
17209
|
agent.messages = [...new ContextBuilder().build({
|
|
@@ -16487,6 +17212,7 @@ async function main(argv, options = {}) {
|
|
|
16487
17212
|
currentModel: config.model
|
|
16488
17213
|
}).messages];
|
|
16489
17214
|
terminalUi?.replaceTranscript(projectTranscript(entries));
|
|
17215
|
+
refreshContextUsage();
|
|
16490
17216
|
};
|
|
16491
17217
|
const commands = new SessionCommands({
|
|
16492
17218
|
agent,
|
|
@@ -16503,7 +17229,7 @@ async function main(argv, options = {}) {
|
|
|
16503
17229
|
sessionController,
|
|
16504
17230
|
onComposerText: (text) => terminalUi?.setComposerText(text),
|
|
16505
17231
|
onSessionChanged: refreshSessionView,
|
|
16506
|
-
homeDirectory: environ
|
|
17232
|
+
homeDirectory: getHomeDirectory({ env: environ }),
|
|
16507
17233
|
onModelSelected: (selection) => {
|
|
16508
17234
|
semanticClassifier.configure({
|
|
16509
17235
|
provider: selection.config.provider,
|
|
@@ -16526,6 +17252,7 @@ async function main(argv, options = {}) {
|
|
|
16526
17252
|
terminalUi.state.model = selection.config.model;
|
|
16527
17253
|
terminalUi.setRuntimeCapabilities({ reasoning: model?.reasoning ?? false });
|
|
16528
17254
|
}
|
|
17255
|
+
refreshContextUsage();
|
|
16529
17256
|
}
|
|
16530
17257
|
});
|
|
16531
17258
|
const unsubscribers = [];
|
|
@@ -16533,6 +17260,9 @@ async function main(argv, options = {}) {
|
|
|
16533
17260
|
unsubscribers.push(
|
|
16534
17261
|
runtime.eventBus.subscribe((event) => {
|
|
16535
17262
|
sessionSink.publishEvent(projector.project(event, "terminal"));
|
|
17263
|
+
if (event.session_id === runtime.sessionId && (event.kind === "task.completed" || event.kind === "task.cancelled" || event.kind === "task.failed")) {
|
|
17264
|
+
refreshContextUsage();
|
|
17265
|
+
}
|
|
16536
17266
|
})
|
|
16537
17267
|
);
|
|
16538
17268
|
if (terminalUi !== null) {
|
|
@@ -16596,7 +17326,7 @@ async function main(argv, options = {}) {
|
|
|
16596
17326
|
return cleanShutdown ? 0 : 1;
|
|
16597
17327
|
}
|
|
16598
17328
|
function defaultSessionsRoot(environ) {
|
|
16599
|
-
return
|
|
17329
|
+
return join7(getHomeDirectory({ env: environ }), ".laohuang", "sessions");
|
|
16600
17330
|
}
|
|
16601
17331
|
function defaultContextPolicy() {
|
|
16602
17332
|
return {
|