@aiwayds/dsh-opentui 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-host/index.js +410 -97
- package/package.json +1 -1
package/dist-host/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/host-entry.tsx
|
|
2
|
-
import { createComponent as _$
|
|
2
|
+
import { createComponent as _$createComponent7 } from "@opentui/solid";
|
|
3
3
|
import { render } from "@opentui/solid";
|
|
4
4
|
|
|
5
5
|
// ../runtime/src/engine.ts
|
|
@@ -91,6 +91,12 @@ var dict = {
|
|
|
91
91
|
"cmd.model.available": { "zh-CN": "可用模型", en: "available" },
|
|
92
92
|
"cmd.model.set": { "zh-CN": "已切换", en: "switched to" },
|
|
93
93
|
"cmd.model.next": { "zh-CN": "(下一条消息生效)", en: "(next message)" },
|
|
94
|
+
"picker.sessions.title": { "zh-CN": "会话(回车恢复 · esc 取消)", en: "sessions (enter resume · esc cancel)" },
|
|
95
|
+
"picker.effort.title": { "zh-CN": "{model} 思考档位", en: "{model} reasoning effort" },
|
|
96
|
+
"cmd.new.done": { "zh-CN": "已开新会话", en: "new session started" },
|
|
97
|
+
"cmd.sessions.empty": { "zh-CN": "没有可恢复的会话", en: "no stored sessions" },
|
|
98
|
+
"cmd.sessions.resumed": { "zh-CN": "已挂载会话(下条消息生效)", en: "session attached (applies to next message)" },
|
|
99
|
+
"ask.title": { "zh-CN": "模型提问", en: "model asks" },
|
|
94
100
|
"cmd.usage.summary": { "zh-CN": "累计", en: "total" },
|
|
95
101
|
"cmd.cleared": { "zh-CN": "会话已清空", en: "session cleared" },
|
|
96
102
|
"cmd.login.saved": { "zh-CN": "凭据 {ref} 已保存", en: "credential {ref} saved" },
|
|
@@ -123,14 +129,14 @@ function createI18n(locale = "zh-CN") {
|
|
|
123
129
|
|
|
124
130
|
// src/ShellHost.tsx
|
|
125
131
|
import { createTextNode as _$createTextNode5 } from "@opentui/solid";
|
|
126
|
-
import { effect as _$
|
|
127
|
-
import { insertNode as _$
|
|
132
|
+
import { effect as _$effect6 } from "@opentui/solid";
|
|
133
|
+
import { insertNode as _$insertNode6 } from "@opentui/solid";
|
|
128
134
|
import { memo as _$memo4 } from "@opentui/solid";
|
|
129
|
-
import { insert as _$
|
|
130
|
-
import { createComponent as _$
|
|
131
|
-
import { setProp as _$
|
|
132
|
-
import { createElement as _$
|
|
133
|
-
import { For as
|
|
135
|
+
import { insert as _$insert6 } from "@opentui/solid";
|
|
136
|
+
import { createComponent as _$createComponent6 } from "@opentui/solid";
|
|
137
|
+
import { setProp as _$setProp6 } from "@opentui/solid";
|
|
138
|
+
import { createElement as _$createElement6 } from "@opentui/solid";
|
|
139
|
+
import { For as For6, createMemo, createSignal, onCleanup, onMount } from "solid-js";
|
|
134
140
|
import { SyntaxStyle } from "@opentui/core";
|
|
135
141
|
import { useKeyboard, useRenderer } from "@opentui/solid";
|
|
136
142
|
// ../kit/src/theme.ts
|
|
@@ -290,6 +296,48 @@ function renderAssistant(text, theme) {
|
|
|
290
296
|
}
|
|
291
297
|
return out;
|
|
292
298
|
}
|
|
299
|
+
function itemsToLines(items, theme, t, spinnerFrame, _mdStyle) {
|
|
300
|
+
const out = [];
|
|
301
|
+
for (const it of items) {
|
|
302
|
+
if (it.kind === "user") {
|
|
303
|
+
out.push({
|
|
304
|
+
text: `❯ ${it.text}`,
|
|
305
|
+
fg: theme.tokens.userMark ?? theme.tokens.accent
|
|
306
|
+
});
|
|
307
|
+
} else if (it.kind === "assistant") {
|
|
308
|
+
out.push(...renderAssistant(it.text, theme));
|
|
309
|
+
if (it.final)
|
|
310
|
+
out.push({
|
|
311
|
+
text: ""
|
|
312
|
+
});
|
|
313
|
+
} else if (it.kind === "thinking") {
|
|
314
|
+
out.push({
|
|
315
|
+
text: `${spinnerFrame} ${t("agent.thinking")} (${it.chars}) ${it.tail.slice(-120)}`,
|
|
316
|
+
fg: theme.tokens.dim
|
|
317
|
+
});
|
|
318
|
+
} else if (it.kind === "tool") {
|
|
319
|
+
out.push({
|
|
320
|
+
text: ` ⚒ ${it.name} ${it.args.slice(0, 90)}`,
|
|
321
|
+
fg: theme.tokens.toolOk ?? theme.tokens.warn
|
|
322
|
+
});
|
|
323
|
+
out.push({
|
|
324
|
+
text: ` ⎿ ${it.ok ? t("agent.tool.ok") : t("agent.tool.fail")}: ${it.brief.slice(0, 110)}`,
|
|
325
|
+
fg: it.ok ? theme.tokens.dim : theme.tokens.toolFail ?? theme.tokens.danger
|
|
326
|
+
});
|
|
327
|
+
} else if (it.kind === "error") {
|
|
328
|
+
out.push({
|
|
329
|
+
text: `✗ ${t("agent.error")}: ${it.text}`,
|
|
330
|
+
fg: theme.tokens.danger
|
|
331
|
+
});
|
|
332
|
+
} else {
|
|
333
|
+
out.push({
|
|
334
|
+
text: `· ${it.text}`,
|
|
335
|
+
fg: theme.tokens.dim
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
return out;
|
|
340
|
+
}
|
|
293
341
|
function Transcript(props) {
|
|
294
342
|
const visibleItems = () => props.items.slice(-(props.maxItems ?? MAX_VISIBLE_ITEMS));
|
|
295
343
|
const itemNode = (it) => {
|
|
@@ -434,6 +482,66 @@ function Transcript(props) {
|
|
|
434
482
|
return _el$16;
|
|
435
483
|
})();
|
|
436
484
|
}
|
|
485
|
+
function TranscriptWindow(props) {
|
|
486
|
+
const offset = () => props.offset ?? 0;
|
|
487
|
+
if (offset() === 0) {
|
|
488
|
+
return _$createComponent2(Transcript, {
|
|
489
|
+
get items() {
|
|
490
|
+
return props.items;
|
|
491
|
+
},
|
|
492
|
+
get theme() {
|
|
493
|
+
return props.theme;
|
|
494
|
+
},
|
|
495
|
+
get t() {
|
|
496
|
+
return props.t;
|
|
497
|
+
},
|
|
498
|
+
get spinnerFrame() {
|
|
499
|
+
return props.spinnerFrame;
|
|
500
|
+
},
|
|
501
|
+
get maxItems() {
|
|
502
|
+
return props.maxItems;
|
|
503
|
+
},
|
|
504
|
+
get mdStyle() {
|
|
505
|
+
return props.mdStyle;
|
|
506
|
+
}
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
const windowLines = () => {
|
|
510
|
+
const visible = props.items.slice(-(props.maxItems ?? MAX_VISIBLE_ITEMS));
|
|
511
|
+
const all = itemsToLines(visible, props.theme, props.t, props.spinnerFrame, props.mdStyle);
|
|
512
|
+
const rows = props.viewportRows ?? 30;
|
|
513
|
+
const start = Math.max(0, all.length - rows - offset());
|
|
514
|
+
const end = Math.max(1, all.length - offset());
|
|
515
|
+
return all.slice(start, end);
|
|
516
|
+
};
|
|
517
|
+
return (() => {
|
|
518
|
+
var _el$18 = _$createElement2("box"), _el$19 = _$createElement2("text");
|
|
519
|
+
_$insertNode2(_el$18, _el$19);
|
|
520
|
+
_$setProp2(_el$18, "style", {
|
|
521
|
+
flexDirection: "column",
|
|
522
|
+
flexGrow: 1,
|
|
523
|
+
padding: 1
|
|
524
|
+
});
|
|
525
|
+
_$insert2(_el$18, _$createComponent2(For2, {
|
|
526
|
+
get each() {
|
|
527
|
+
return windowLines();
|
|
528
|
+
},
|
|
529
|
+
children: (l) => (() => {
|
|
530
|
+
var _el$20 = _$createElement2("text");
|
|
531
|
+
_$insert2(_el$20, () => l.text || " ");
|
|
532
|
+
_$effect2((_$p) => _$setProp2(_el$20, "style", l.fg ? {
|
|
533
|
+
fg: l.fg
|
|
534
|
+
} : {}, _$p));
|
|
535
|
+
return _el$20;
|
|
536
|
+
})()
|
|
537
|
+
}), _el$19);
|
|
538
|
+
_$insert2(_el$19, () => `↑ scrolled ${offset()} rows (PgDn = bottom)`);
|
|
539
|
+
_$effect2((_$p) => _$setProp2(_el$19, "style", {
|
|
540
|
+
fg: props.theme.tokens.dim
|
|
541
|
+
}, _$p));
|
|
542
|
+
return _el$18;
|
|
543
|
+
})();
|
|
544
|
+
}
|
|
437
545
|
function InputLine(props) {
|
|
438
546
|
const inputChunks = () => {
|
|
439
547
|
const s = props.value;
|
|
@@ -445,62 +553,62 @@ function InputLine(props) {
|
|
|
445
553
|
};
|
|
446
554
|
};
|
|
447
555
|
return (() => {
|
|
448
|
-
var _el$
|
|
449
|
-
_$insertNode2(_el$
|
|
450
|
-
_$insert2(_el$
|
|
556
|
+
var _el$21 = _$createElement2("box"), _el$22 = _$createElement2("text");
|
|
557
|
+
_$insertNode2(_el$21, _el$22);
|
|
558
|
+
_$insert2(_el$22, (() => {
|
|
451
559
|
var _c$ = _$memo(() => !!props.busy);
|
|
452
560
|
return () => _c$() ? `${props.spinnerFrame} ` : "❯ ";
|
|
453
561
|
})());
|
|
454
|
-
_$insert2(_el$
|
|
562
|
+
_$insert2(_el$21, (() => {
|
|
455
563
|
var _c$2 = _$memo(() => !!props.busy);
|
|
456
564
|
return () => _c$2() ? (() => {
|
|
457
|
-
var _el$
|
|
458
|
-
_$insert2(_el$
|
|
459
|
-
_$effect2((_$p) => _$setProp2(_el$
|
|
565
|
+
var _el$23 = _$createElement2("text");
|
|
566
|
+
_$insert2(_el$23, () => props.busyLabel);
|
|
567
|
+
_$effect2((_$p) => _$setProp2(_el$23, "style", {
|
|
460
568
|
fg: props.theme.tokens.dim
|
|
461
569
|
}, _$p));
|
|
462
|
-
return _el$
|
|
570
|
+
return _el$23;
|
|
463
571
|
})() : _$memo(() => props.value === "")() ? [(() => {
|
|
464
|
-
var _el$
|
|
465
|
-
_$insertNode2(_el$
|
|
466
|
-
_$effect2((_$p) => _$setProp2(_el$
|
|
572
|
+
var _el$24 = _$createElement2("text");
|
|
573
|
+
_$insertNode2(_el$24, _$createTextNode2(` `));
|
|
574
|
+
_$effect2((_$p) => _$setProp2(_el$24, "style", {
|
|
467
575
|
fg: props.theme.tokens.accent,
|
|
468
576
|
attributes: createTextAttributes({
|
|
469
577
|
inverse: true
|
|
470
578
|
})
|
|
471
579
|
}, _$p));
|
|
472
|
-
return _el$
|
|
580
|
+
return _el$24;
|
|
473
581
|
})(), (() => {
|
|
474
|
-
var _el$
|
|
475
|
-
_$insert2(_el$
|
|
476
|
-
_$effect2((_$p) => _$setProp2(_el$
|
|
582
|
+
var _el$26 = _$createElement2("text");
|
|
583
|
+
_$insert2(_el$26, () => props.placeholder);
|
|
584
|
+
_$effect2((_$p) => _$setProp2(_el$26, "style", {
|
|
477
585
|
fg: props.theme.tokens.dim
|
|
478
586
|
}, _$p));
|
|
479
|
-
return _el$
|
|
587
|
+
return _el$26;
|
|
480
588
|
})()] : [(() => {
|
|
481
|
-
var _el$
|
|
482
|
-
_$insert2(_el$
|
|
483
|
-
_$effect2((_$p) => _$setProp2(_el$
|
|
589
|
+
var _el$27 = _$createElement2("text");
|
|
590
|
+
_$insert2(_el$27, () => inputChunks().before);
|
|
591
|
+
_$effect2((_$p) => _$setProp2(_el$27, "style", {
|
|
484
592
|
fg: props.theme.tokens.text
|
|
485
593
|
}, _$p));
|
|
486
|
-
return _el$
|
|
594
|
+
return _el$27;
|
|
487
595
|
})(), (() => {
|
|
488
|
-
var _el$
|
|
489
|
-
_$insert2(_el$
|
|
490
|
-
_$effect2((_$p) => _$setProp2(_el$
|
|
596
|
+
var _el$28 = _$createElement2("text");
|
|
597
|
+
_$insert2(_el$28, () => inputChunks().at);
|
|
598
|
+
_$effect2((_$p) => _$setProp2(_el$28, "style", {
|
|
491
599
|
fg: props.theme.tokens.text,
|
|
492
600
|
attributes: createTextAttributes({
|
|
493
601
|
inverse: true
|
|
494
602
|
})
|
|
495
603
|
}, _$p));
|
|
496
|
-
return _el$
|
|
604
|
+
return _el$28;
|
|
497
605
|
})(), (() => {
|
|
498
|
-
var _el$
|
|
499
|
-
_$insert2(_el$
|
|
500
|
-
_$effect2((_$p) => _$setProp2(_el$
|
|
606
|
+
var _el$29 = _$createElement2("text");
|
|
607
|
+
_$insert2(_el$29, () => inputChunks().after);
|
|
608
|
+
_$effect2((_$p) => _$setProp2(_el$29, "style", {
|
|
501
609
|
fg: props.theme.tokens.text
|
|
502
610
|
}, _$p));
|
|
503
|
-
return _el$
|
|
611
|
+
return _el$29;
|
|
504
612
|
})()];
|
|
505
613
|
})(), null);
|
|
506
614
|
_$effect2((_p$) => {
|
|
@@ -511,39 +619,39 @@ function InputLine(props) {
|
|
|
511
619
|
}, _v$6 = {
|
|
512
620
|
fg: props.theme.tokens.accent
|
|
513
621
|
};
|
|
514
|
-
_v$5 !== _p$.e && (_p$.e = _$setProp2(_el$
|
|
515
|
-
_v$6 !== _p$.t && (_p$.t = _$setProp2(_el$
|
|
622
|
+
_v$5 !== _p$.e && (_p$.e = _$setProp2(_el$21, "style", _v$5, _p$.e));
|
|
623
|
+
_v$6 !== _p$.t && (_p$.t = _$setProp2(_el$22, "style", _v$6, _p$.t));
|
|
516
624
|
return _p$;
|
|
517
625
|
}, {
|
|
518
626
|
e: undefined,
|
|
519
627
|
t: undefined
|
|
520
628
|
});
|
|
521
|
-
return _el$
|
|
629
|
+
return _el$21;
|
|
522
630
|
})();
|
|
523
631
|
}
|
|
524
632
|
function FooterHints(props) {
|
|
525
633
|
return (() => {
|
|
526
|
-
var _el$
|
|
527
|
-
_$setProp2(_el$
|
|
634
|
+
var _el$30 = _$createElement2("box");
|
|
635
|
+
_$setProp2(_el$30, "style", {
|
|
528
636
|
flexDirection: "row",
|
|
529
637
|
paddingLeft: 2,
|
|
530
638
|
paddingRight: 2
|
|
531
639
|
});
|
|
532
|
-
_$insert2(_el$
|
|
640
|
+
_$insert2(_el$30, _$createComponent2(For2, {
|
|
533
641
|
each: footerHints,
|
|
534
642
|
children: (h, i) => (() => {
|
|
535
|
-
var _el$
|
|
536
|
-
_$insertNode2(_el$
|
|
537
|
-
_$insert2(_el$
|
|
538
|
-
_$insert2(_el$
|
|
539
|
-
_$insert2(_el$
|
|
540
|
-
_$effect2((_$p) => _$setProp2(_el$
|
|
643
|
+
var _el$31 = _$createElement2("text"), _el$32 = _$createTextNode2(` `);
|
|
644
|
+
_$insertNode2(_el$31, _el$32);
|
|
645
|
+
_$insert2(_el$31, () => i() > 0 ? " · " : "", _el$32);
|
|
646
|
+
_$insert2(_el$31, () => h.key, _el$32);
|
|
647
|
+
_$insert2(_el$31, () => h.label, null);
|
|
648
|
+
_$effect2((_$p) => _$setProp2(_el$31, "style", {
|
|
541
649
|
fg: props.theme.tokens.dim
|
|
542
650
|
}, _$p));
|
|
543
|
-
return _el$
|
|
651
|
+
return _el$31;
|
|
544
652
|
})()
|
|
545
653
|
}));
|
|
546
|
-
return _el$
|
|
654
|
+
return _el$30;
|
|
547
655
|
})();
|
|
548
656
|
}
|
|
549
657
|
// ../kit/src/overlays.tsx
|
|
@@ -813,6 +921,14 @@ function ThinkPanel(props) {
|
|
|
813
921
|
return _el$7;
|
|
814
922
|
})();
|
|
815
923
|
}
|
|
924
|
+
// ../kit/src/table.tsx
|
|
925
|
+
import { effect as _$effect5 } from "@opentui/solid";
|
|
926
|
+
import { insertNode as _$insertNode5 } from "@opentui/solid";
|
|
927
|
+
import { createComponent as _$createComponent5 } from "@opentui/solid";
|
|
928
|
+
import { insert as _$insert5 } from "@opentui/solid";
|
|
929
|
+
import { setProp as _$setProp5 } from "@opentui/solid";
|
|
930
|
+
import { createElement as _$createElement5 } from "@opentui/solid";
|
|
931
|
+
import { For as For5 } from "solid-js";
|
|
816
932
|
// src/ShellHost.tsx
|
|
817
933
|
var MAX_VISIBLE_ITEMS2 = 120;
|
|
818
934
|
var THEME_ORDER = ["catppuccin-mocha", "default"];
|
|
@@ -826,6 +942,12 @@ var COMMANDS = [{
|
|
|
826
942
|
}, {
|
|
827
943
|
name: "/usage",
|
|
828
944
|
desc: "show token usage summary"
|
|
945
|
+
}, {
|
|
946
|
+
name: "/new",
|
|
947
|
+
desc: "start a fresh session"
|
|
948
|
+
}, {
|
|
949
|
+
name: "/sessions",
|
|
950
|
+
desc: "list and resume stored sessions"
|
|
829
951
|
}, {
|
|
830
952
|
name: "/theme",
|
|
831
953
|
desc: "cycle theme"
|
|
@@ -866,6 +988,9 @@ function ShellHost(props) {
|
|
|
866
988
|
const [toolExpanded, setToolExpanded] = createSignal(-1);
|
|
867
989
|
const [picker, setPicker] = createSignal(null);
|
|
868
990
|
const [pickerSel, setPickerSel] = createSignal(0);
|
|
991
|
+
const [sessionItems, setSessionItems] = createSignal([]);
|
|
992
|
+
const [scrollOffset, setScrollOffset] = createSignal(0);
|
|
993
|
+
const [ask, setAsk] = createSignal(null);
|
|
869
994
|
let reasoningBuf = "";
|
|
870
995
|
const [lastReasoning, setLastReasoning] = createSignal("");
|
|
871
996
|
const initialTheme = process.env.DSH_OPENTUI_THEME ?? props.deps.themeName ?? "catppuccin-mocha";
|
|
@@ -878,6 +1003,17 @@ function ShellHost(props) {
|
|
|
878
1003
|
onCleanup(store.subscribe(() => setSnapshot(store.snapshot)));
|
|
879
1004
|
const t2 = setInterval(() => setSpin((s) => (s + 1) % 4), 150);
|
|
880
1005
|
onCleanup(() => clearInterval(t2));
|
|
1006
|
+
props.deps.session.registerAskProvider(async (questions) => {
|
|
1007
|
+
const answer = await new Promise((resolve) => {
|
|
1008
|
+
setAsk({
|
|
1009
|
+
questions,
|
|
1010
|
+
resolve
|
|
1011
|
+
});
|
|
1012
|
+
});
|
|
1013
|
+
if (answer === "declined")
|
|
1014
|
+
throw new Error("declined");
|
|
1015
|
+
return answer;
|
|
1016
|
+
});
|
|
881
1017
|
});
|
|
882
1018
|
const spinner = () => ["⠋", "⠙", "⠹", "⠸"][spin()];
|
|
883
1019
|
const filteredCommands = createMemo(() => {
|
|
@@ -891,7 +1027,7 @@ function ShellHost(props) {
|
|
|
891
1027
|
return n === 0 ? 0 : Math.min(menuSel(), n - 1);
|
|
892
1028
|
});
|
|
893
1029
|
const menuVisible = createMemo(() => !busy() && menuOpen() && filteredCommands().length > 0);
|
|
894
|
-
const
|
|
1030
|
+
const modelItems = createMemo(() => {
|
|
895
1031
|
const out = [];
|
|
896
1032
|
for (const p of props.deps.session.listProviders()) {
|
|
897
1033
|
for (const m of p.models) {
|
|
@@ -906,6 +1042,34 @@ function ShellHost(props) {
|
|
|
906
1042
|
}
|
|
907
1043
|
return out;
|
|
908
1044
|
});
|
|
1045
|
+
const EFFORTS = ["keep", "low", "medium", "high", "xhigh", "max"];
|
|
1046
|
+
const pickerData = createMemo(() => {
|
|
1047
|
+
const p = picker();
|
|
1048
|
+
if (!p)
|
|
1049
|
+
return {
|
|
1050
|
+
title: "",
|
|
1051
|
+
items: []
|
|
1052
|
+
};
|
|
1053
|
+
if (p.kind === "model")
|
|
1054
|
+
return {
|
|
1055
|
+
title: t("picker.model.title"),
|
|
1056
|
+
items: modelItems()
|
|
1057
|
+
};
|
|
1058
|
+
if (p.kind === "sessions")
|
|
1059
|
+
return {
|
|
1060
|
+
title: t("picker.sessions.title"),
|
|
1061
|
+
items: sessionItems()
|
|
1062
|
+
};
|
|
1063
|
+
return {
|
|
1064
|
+
title: t("picker.effort.title", {
|
|
1065
|
+
model: p.model
|
|
1066
|
+
}),
|
|
1067
|
+
items: EFFORTS.map((e) => ({
|
|
1068
|
+
id: e,
|
|
1069
|
+
label: e
|
|
1070
|
+
}))
|
|
1071
|
+
};
|
|
1072
|
+
});
|
|
909
1073
|
const toolEntries = createMemo(() => items().filter((it) => it.kind === "tool").map((it) => ({
|
|
910
1074
|
name: it.name,
|
|
911
1075
|
args: it.args,
|
|
@@ -1010,6 +1174,32 @@ function ShellHost(props) {
|
|
|
1010
1174
|
kind: "system",
|
|
1011
1175
|
text: `${t("cmd.usage.summary")}: ${u.turns} turns · prompt ${u.prompt} · completion ${u.completion} tokens`
|
|
1012
1176
|
});
|
|
1177
|
+
} else if (cmd === "new") {
|
|
1178
|
+
props.deps.session.newSession();
|
|
1179
|
+
setItems([]);
|
|
1180
|
+
setScrollOffset(0);
|
|
1181
|
+
push({
|
|
1182
|
+
kind: "system",
|
|
1183
|
+
text: t("cmd.new.done")
|
|
1184
|
+
});
|
|
1185
|
+
} else if (cmd === "sessions") {
|
|
1186
|
+
const list = await props.deps.session.listSessions();
|
|
1187
|
+
if (!list.length) {
|
|
1188
|
+
push({
|
|
1189
|
+
kind: "system",
|
|
1190
|
+
text: t("cmd.sessions.empty")
|
|
1191
|
+
});
|
|
1192
|
+
} else {
|
|
1193
|
+
setSessionItems(list.map((x) => ({
|
|
1194
|
+
id: x.id,
|
|
1195
|
+
label: x.title || x.id.slice(0, 8),
|
|
1196
|
+
detail: x.updatedAt ? String(x.updatedAt).slice(0, 19) : undefined
|
|
1197
|
+
})));
|
|
1198
|
+
setPickerSel(0);
|
|
1199
|
+
setPicker({
|
|
1200
|
+
kind: "sessions"
|
|
1201
|
+
});
|
|
1202
|
+
}
|
|
1013
1203
|
} else if (cmd === "theme") {
|
|
1014
1204
|
const i = THEME_ORDER.indexOf(themeName());
|
|
1015
1205
|
const next = THEME_ORDER[(i + 1) % THEME_ORDER.length];
|
|
@@ -1098,6 +1288,7 @@ function ShellHost(props) {
|
|
|
1098
1288
|
async function send(text) {
|
|
1099
1289
|
if (busy())
|
|
1100
1290
|
return;
|
|
1291
|
+
setScrollOffset(0);
|
|
1101
1292
|
push({
|
|
1102
1293
|
kind: "user",
|
|
1103
1294
|
text
|
|
@@ -1204,6 +1395,26 @@ function ShellHost(props) {
|
|
|
1204
1395
|
}
|
|
1205
1396
|
if (key.sequence === "\x03" || (key.name === "c" || key.name === "C") && key.ctrl)
|
|
1206
1397
|
process.exit(0);
|
|
1398
|
+
const pendingAsk = ask();
|
|
1399
|
+
if (pendingAsk) {
|
|
1400
|
+
const q0 = pendingAsk.questions[0];
|
|
1401
|
+
const opts = q0?.options ?? [];
|
|
1402
|
+
if (key.name === "up" || key.name === "down") {
|
|
1403
|
+
if (opts.length > 0)
|
|
1404
|
+
setPickerSel((v) => key.name === "up" ? (v - 1 + opts.length) % opts.length : (v + 1) % opts.length);
|
|
1405
|
+
} else if (key.name === "return") {
|
|
1406
|
+
const sel = opts[Math.min(pickerSel(), Math.max(0, opts.length - 1))];
|
|
1407
|
+
setAsk(null);
|
|
1408
|
+
pendingAsk.resolve(sel ? [{
|
|
1409
|
+
questionId: q0.id,
|
|
1410
|
+
selected: sel
|
|
1411
|
+
}] : "declined");
|
|
1412
|
+
} else if (key.name === "escape") {
|
|
1413
|
+
setAsk(null);
|
|
1414
|
+
pendingAsk.resolve("declined");
|
|
1415
|
+
}
|
|
1416
|
+
return;
|
|
1417
|
+
}
|
|
1207
1418
|
const ap = approval();
|
|
1208
1419
|
if (ap) {
|
|
1209
1420
|
if (key.sequence === "y" || key.name === "return") {
|
|
@@ -1217,25 +1428,38 @@ function ShellHost(props) {
|
|
|
1217
1428
|
}
|
|
1218
1429
|
return;
|
|
1219
1430
|
}
|
|
1220
|
-
|
|
1221
|
-
|
|
1431
|
+
const pk = picker();
|
|
1432
|
+
if (pk) {
|
|
1433
|
+
const n = pickerData().items.length;
|
|
1222
1434
|
if (key.name === "up" || key.name === "down") {
|
|
1223
1435
|
if (n > 0)
|
|
1224
1436
|
setPickerSel((s) => key.name === "up" ? (s - 1 + n) % n : (s + 1) % n);
|
|
1225
1437
|
} else if (key.name === "return") {
|
|
1226
|
-
const item = n > 0 ?
|
|
1438
|
+
const item = n > 0 ? pickerData().items[Math.min(pickerSel(), n - 1)] : undefined;
|
|
1227
1439
|
setPicker(null);
|
|
1228
1440
|
setPickerSel(0);
|
|
1229
|
-
if (item) {
|
|
1441
|
+
if (item && pk.kind === "model") {
|
|
1230
1442
|
const [provider, model] = item.id.split("/");
|
|
1231
|
-
if (provider && model)
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
text: `${t("cmd.model.set")} ${item.id} ${t("cmd.model.next")}`
|
|
1443
|
+
if (provider && model)
|
|
1444
|
+
setPicker({
|
|
1445
|
+
kind: "effort",
|
|
1446
|
+
provider,
|
|
1447
|
+
model
|
|
1237
1448
|
});
|
|
1238
|
-
|
|
1449
|
+
} else if (item && pk.kind === "effort") {
|
|
1450
|
+
const effort = item.id === "keep" ? undefined : item.id;
|
|
1451
|
+
props.deps.session.switchModel(pk.provider, pk.model, effort);
|
|
1452
|
+
setLabel(props.deps.session.label);
|
|
1453
|
+
push({
|
|
1454
|
+
kind: "system",
|
|
1455
|
+
text: `${t("cmd.model.set")} ${pk.provider}/${pk.model}${effort ? ` (${effort})` : ""} ${t("cmd.model.next")}`
|
|
1456
|
+
});
|
|
1457
|
+
} else if (item && pk.kind === "sessions") {
|
|
1458
|
+
props.deps.session.resumeSession(item.id);
|
|
1459
|
+
push({
|
|
1460
|
+
kind: "system",
|
|
1461
|
+
text: `${t("cmd.sessions.resumed")}: ${item.label}`
|
|
1462
|
+
});
|
|
1239
1463
|
}
|
|
1240
1464
|
} else if (key.name === "escape") {
|
|
1241
1465
|
setPicker(null);
|
|
@@ -1257,6 +1481,14 @@ function ShellHost(props) {
|
|
|
1257
1481
|
setToolExpanded((v) => v + 1 >= n ? -1 : v + 1);
|
|
1258
1482
|
return;
|
|
1259
1483
|
}
|
|
1484
|
+
if (key.name === "pageup") {
|
|
1485
|
+
setScrollOffset((o) => o + 10);
|
|
1486
|
+
return;
|
|
1487
|
+
}
|
|
1488
|
+
if (key.name === "pagedown") {
|
|
1489
|
+
setScrollOffset((o) => Math.max(0, o - 10));
|
|
1490
|
+
return;
|
|
1491
|
+
}
|
|
1260
1492
|
if (busy())
|
|
1261
1493
|
return;
|
|
1262
1494
|
if (menuVisible() && (key.name === "up" || key.name === "down")) {
|
|
@@ -1370,18 +1602,18 @@ function ShellHost(props) {
|
|
|
1370
1602
|
const widgets = () => snapshot().filter((d) => d.kind === "widget");
|
|
1371
1603
|
const th = () => theme();
|
|
1372
1604
|
return (() => {
|
|
1373
|
-
var _el$ = _$
|
|
1374
|
-
_$
|
|
1375
|
-
_$
|
|
1605
|
+
var _el$ = _$createElement6("box"), _el$2 = _$createElement6("box");
|
|
1606
|
+
_$insertNode6(_el$, _el$2);
|
|
1607
|
+
_$setProp6(_el$, "style", {
|
|
1376
1608
|
flexDirection: "column",
|
|
1377
1609
|
flexGrow: 1
|
|
1378
1610
|
});
|
|
1379
|
-
_$
|
|
1611
|
+
_$setProp6(_el$2, "style", {
|
|
1380
1612
|
flexDirection: "column",
|
|
1381
1613
|
flexGrow: 1,
|
|
1382
1614
|
padding: 1
|
|
1383
1615
|
});
|
|
1384
|
-
_$
|
|
1616
|
+
_$insert6(_el$2, _$createComponent6(TranscriptWindow, {
|
|
1385
1617
|
get items() {
|
|
1386
1618
|
return visibleItems();
|
|
1387
1619
|
},
|
|
@@ -1392,11 +1624,15 @@ function ShellHost(props) {
|
|
|
1392
1624
|
get spinnerFrame() {
|
|
1393
1625
|
return spinner();
|
|
1394
1626
|
},
|
|
1395
|
-
mdStyle
|
|
1627
|
+
mdStyle,
|
|
1628
|
+
get offset() {
|
|
1629
|
+
return scrollOffset();
|
|
1630
|
+
},
|
|
1631
|
+
viewportRows: 24
|
|
1396
1632
|
}));
|
|
1397
|
-
_$
|
|
1633
|
+
_$insert6(_el$, (() => {
|
|
1398
1634
|
var _c$ = _$memo4(() => !!showTool());
|
|
1399
|
-
return () => _c$() ? _$
|
|
1635
|
+
return () => _c$() ? _$createComponent6(ToolPanel, {
|
|
1400
1636
|
get entries() {
|
|
1401
1637
|
return toolEntries();
|
|
1402
1638
|
},
|
|
@@ -1408,9 +1644,9 @@ function ShellHost(props) {
|
|
|
1408
1644
|
}
|
|
1409
1645
|
}) : null;
|
|
1410
1646
|
})(), null);
|
|
1411
|
-
_$
|
|
1647
|
+
_$insert6(_el$, (() => {
|
|
1412
1648
|
var _c$2 = _$memo4(() => !!showThink());
|
|
1413
|
-
return () => _c$2() ? _$
|
|
1649
|
+
return () => _c$2() ? _$createComponent6(ThinkPanel, {
|
|
1414
1650
|
get text() {
|
|
1415
1651
|
return lastReasoning();
|
|
1416
1652
|
},
|
|
@@ -1419,11 +1655,11 @@ function ShellHost(props) {
|
|
|
1419
1655
|
}
|
|
1420
1656
|
}) : null;
|
|
1421
1657
|
})(), null);
|
|
1422
|
-
_$
|
|
1658
|
+
_$insert6(_el$, _$createComponent6(For6, {
|
|
1423
1659
|
get each() {
|
|
1424
1660
|
return _$memo4(() => !!approval())() ? [approval()] : [];
|
|
1425
1661
|
},
|
|
1426
|
-
children: (ap) => _$
|
|
1662
|
+
children: (ap) => _$createComponent6(ApprovalCard, {
|
|
1427
1663
|
get toolName() {
|
|
1428
1664
|
return ap.name;
|
|
1429
1665
|
},
|
|
@@ -1438,14 +1674,35 @@ function ShellHost(props) {
|
|
|
1438
1674
|
}
|
|
1439
1675
|
})
|
|
1440
1676
|
}), null);
|
|
1441
|
-
_$
|
|
1442
|
-
var _c$3 = _$memo4(() => !!
|
|
1443
|
-
return () => _c$3() ? _$
|
|
1677
|
+
_$insert6(_el$, (() => {
|
|
1678
|
+
var _c$3 = _$memo4(() => !!ask());
|
|
1679
|
+
return () => _c$3() ? _$createComponent6(Picker, {
|
|
1680
|
+
get title() {
|
|
1681
|
+
return `${t("ask.title")}: ${ask().questions[0]?.prompt ?? ""}`.slice(0, 70);
|
|
1682
|
+
},
|
|
1683
|
+
get items() {
|
|
1684
|
+
return (ask().questions[0]?.options ?? []).map((o) => ({
|
|
1685
|
+
id: o,
|
|
1686
|
+
label: o
|
|
1687
|
+
}));
|
|
1688
|
+
},
|
|
1689
|
+
get selected() {
|
|
1690
|
+
return pickerSel();
|
|
1691
|
+
},
|
|
1692
|
+
get theme() {
|
|
1693
|
+
return th();
|
|
1694
|
+
},
|
|
1695
|
+
maxRows: 8
|
|
1696
|
+
}) : null;
|
|
1697
|
+
})(), null);
|
|
1698
|
+
_$insert6(_el$, (() => {
|
|
1699
|
+
var _c$4 = _$memo4(() => !!picker());
|
|
1700
|
+
return () => _c$4() ? _$createComponent6(Picker, {
|
|
1444
1701
|
get title() {
|
|
1445
|
-
return
|
|
1702
|
+
return pickerData().title;
|
|
1446
1703
|
},
|
|
1447
1704
|
get items() {
|
|
1448
|
-
return
|
|
1705
|
+
return pickerData().items;
|
|
1449
1706
|
},
|
|
1450
1707
|
get selected() {
|
|
1451
1708
|
return pickerSel();
|
|
@@ -1456,29 +1713,29 @@ function ShellHost(props) {
|
|
|
1456
1713
|
maxRows: 8
|
|
1457
1714
|
}) : null;
|
|
1458
1715
|
})(), null);
|
|
1459
|
-
_$
|
|
1716
|
+
_$insert6(_el$, _$createComponent6(For6, {
|
|
1460
1717
|
get each() {
|
|
1461
1718
|
return menuVisible() ? [0] : [];
|
|
1462
1719
|
},
|
|
1463
1720
|
children: () => (() => {
|
|
1464
|
-
var _el$3 = _$
|
|
1465
|
-
_$
|
|
1721
|
+
var _el$3 = _$createElement6("box");
|
|
1722
|
+
_$insert6(_el$3, _$createComponent6(For6, {
|
|
1466
1723
|
get each() {
|
|
1467
1724
|
return filteredCommands();
|
|
1468
1725
|
},
|
|
1469
1726
|
children: (c, i) => (() => {
|
|
1470
|
-
var _el$4 = _$
|
|
1471
|
-
_$
|
|
1472
|
-
_$
|
|
1473
|
-
_$
|
|
1474
|
-
_$
|
|
1475
|
-
_$
|
|
1727
|
+
var _el$4 = _$createElement6("text"), _el$5 = _$createTextNode5(` `);
|
|
1728
|
+
_$insertNode6(_el$4, _el$5);
|
|
1729
|
+
_$insert6(_el$4, () => i() === clampedSel() ? "› " : " ", _el$5);
|
|
1730
|
+
_$insert6(_el$4, () => c.name, _el$5);
|
|
1731
|
+
_$insert6(_el$4, () => c.desc, null);
|
|
1732
|
+
_$effect6((_$p) => _$setProp6(_el$4, "style", {
|
|
1476
1733
|
fg: i() === clampedSel() ? th().tokens.accent : undefined
|
|
1477
1734
|
}, _$p));
|
|
1478
1735
|
return _el$4;
|
|
1479
1736
|
})()
|
|
1480
1737
|
}));
|
|
1481
|
-
_$
|
|
1738
|
+
_$effect6((_$p) => _$setProp6(_el$3, "style", {
|
|
1482
1739
|
border: true,
|
|
1483
1740
|
borderColor: th().tokens.border,
|
|
1484
1741
|
flexDirection: "column",
|
|
@@ -1487,7 +1744,7 @@ function ShellHost(props) {
|
|
|
1487
1744
|
return _el$3;
|
|
1488
1745
|
})()
|
|
1489
1746
|
}), null);
|
|
1490
|
-
_$
|
|
1747
|
+
_$insert6(_el$, _$createComponent6(InputLine, {
|
|
1491
1748
|
get value() {
|
|
1492
1749
|
return input();
|
|
1493
1750
|
},
|
|
@@ -1510,12 +1767,12 @@ function ShellHost(props) {
|
|
|
1510
1767
|
return th();
|
|
1511
1768
|
}
|
|
1512
1769
|
}), null);
|
|
1513
|
-
_$
|
|
1770
|
+
_$insert6(_el$, _$createComponent6(FooterHints, {
|
|
1514
1771
|
get theme() {
|
|
1515
1772
|
return th();
|
|
1516
1773
|
}
|
|
1517
1774
|
}), null);
|
|
1518
|
-
_$
|
|
1775
|
+
_$insert6(_el$, _$createComponent6(StatusBar, {
|
|
1519
1776
|
get widgets() {
|
|
1520
1777
|
return [{
|
|
1521
1778
|
kind: "widget",
|
|
@@ -1557,6 +1814,7 @@ class HostSession {
|
|
|
1557
1814
|
#running = false;
|
|
1558
1815
|
#toolCalls = new Map;
|
|
1559
1816
|
#override;
|
|
1817
|
+
#resumeId;
|
|
1560
1818
|
usage = { prompt: 0, completion: 0 };
|
|
1561
1819
|
constructor(ctx) {
|
|
1562
1820
|
this.#ctx = ctx;
|
|
@@ -1574,15 +1832,69 @@ class HostSession {
|
|
|
1574
1832
|
return [];
|
|
1575
1833
|
}
|
|
1576
1834
|
}
|
|
1577
|
-
switchModel(provider, model) {
|
|
1578
|
-
this.#override = { provider, model };
|
|
1835
|
+
switchModel(provider, model, reasoningEffort) {
|
|
1836
|
+
this.#override = { provider, model, ...reasoningEffort ? { reasoningEffort } : {} };
|
|
1579
1837
|
try {
|
|
1580
1838
|
this.#handle?.dispose?.();
|
|
1581
1839
|
} catch {}
|
|
1582
1840
|
this.#handle = undefined;
|
|
1583
1841
|
this.#sessionId = undefined;
|
|
1584
1842
|
this.#agentId = undefined;
|
|
1585
|
-
this.label = `${provider}/${model}`;
|
|
1843
|
+
this.label = `${provider}/${model}${reasoningEffort ? ` (${reasoningEffort})` : ""}`;
|
|
1844
|
+
}
|
|
1845
|
+
newSession() {
|
|
1846
|
+
try {
|
|
1847
|
+
this.#handle?.dispose?.();
|
|
1848
|
+
} catch {}
|
|
1849
|
+
this.#handle = undefined;
|
|
1850
|
+
this.#sessionId = undefined;
|
|
1851
|
+
this.#agentId = undefined;
|
|
1852
|
+
}
|
|
1853
|
+
resumeSession(id) {
|
|
1854
|
+
this.newSession();
|
|
1855
|
+
this.#resumeId = id;
|
|
1856
|
+
}
|
|
1857
|
+
async listSessions(limit = 12) {
|
|
1858
|
+
const persistence = this.#ctx.get?.("sessionPersistence");
|
|
1859
|
+
if (!persistence?.list)
|
|
1860
|
+
return [];
|
|
1861
|
+
try {
|
|
1862
|
+
const headers = await persistence.list();
|
|
1863
|
+
return headers.map((h) => ({
|
|
1864
|
+
id: String(h?.id ?? ""),
|
|
1865
|
+
title: String(h?.title ?? h?.firstPrompt ?? h?.name ?? "").slice(0, 40),
|
|
1866
|
+
updatedAt: h?.updatedAt ?? h?.lastEventAt ?? h?.mtime
|
|
1867
|
+
})).filter((s) => s.id).sort((a, b) => String(b.updatedAt ?? "").localeCompare(String(a.updatedAt ?? ""))).slice(0, limit);
|
|
1868
|
+
} catch {
|
|
1869
|
+
return [];
|
|
1870
|
+
}
|
|
1871
|
+
}
|
|
1872
|
+
registerAskProvider(onAsk) {
|
|
1873
|
+
this.#ctx.on("user-questions/request", async (request, next) => {
|
|
1874
|
+
const asking = request?.agent?.session?.id;
|
|
1875
|
+
if (this.#sessionId !== undefined && asking !== undefined && String(asking) !== this.#sessionId)
|
|
1876
|
+
return next();
|
|
1877
|
+
const questions = request?.questions ?? [];
|
|
1878
|
+
if (!questions.length)
|
|
1879
|
+
return next();
|
|
1880
|
+
const simplified = questions.map((q) => ({
|
|
1881
|
+
id: String(q?.id ?? ""),
|
|
1882
|
+
prompt: String(q?.prompt ?? q?.question ?? q?.header ?? ""),
|
|
1883
|
+
options: (q?.options ?? []).map((o) => String(o?.label ?? o?.value ?? o))
|
|
1884
|
+
}));
|
|
1885
|
+
let picks;
|
|
1886
|
+
try {
|
|
1887
|
+
picks = await onAsk(simplified);
|
|
1888
|
+
} catch {
|
|
1889
|
+
return { answers: simplified.map((q) => ({ id: q.id, selected: [], custom: "declined" })) };
|
|
1890
|
+
}
|
|
1891
|
+
return {
|
|
1892
|
+
answers: simplified.map((q) => {
|
|
1893
|
+
const pick = picks.find((p) => p.questionId === q.id);
|
|
1894
|
+
return pick ? { id: q.id, selected: [pick.selected] } : { id: q.id, selected: [], custom: "declined" };
|
|
1895
|
+
})
|
|
1896
|
+
};
|
|
1897
|
+
});
|
|
1586
1898
|
}
|
|
1587
1899
|
#credentials() {
|
|
1588
1900
|
try {
|
|
@@ -1612,7 +1924,8 @@ class HostSession {
|
|
|
1612
1924
|
async turn(userText, cb, _signal) {
|
|
1613
1925
|
this.#cb = cb;
|
|
1614
1926
|
if (!this.#handle) {
|
|
1615
|
-
this.#sessionId = randomUUID();
|
|
1927
|
+
this.#sessionId = this.#resumeId ?? randomUUID();
|
|
1928
|
+
this.#resumeId = undefined;
|
|
1616
1929
|
const sel = this.#override ?? this.#ctx.get?.("agentDefaultModel")?.currentSelection?.();
|
|
1617
1930
|
const agentOptions = sel ? { provider: sel.provider, model: sel.model, ...sel.reasoningEffort ? { reasoningEffort: sel.reasoningEffort } : {} } : {};
|
|
1618
1931
|
if (sel)
|
|
@@ -1737,7 +2050,7 @@ function apply(ctx, config) {
|
|
|
1737
2050
|
mountEngine({}).then(({
|
|
1738
2051
|
renderer
|
|
1739
2052
|
}) => {
|
|
1740
|
-
render(() => _$
|
|
2053
|
+
render(() => _$createComponent7(ShellHost, {
|
|
1741
2054
|
get deps() {
|
|
1742
2055
|
return {
|
|
1743
2056
|
store,
|