@selesai/code 0.13.26 → 0.13.27
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/CHANGELOG.md +74 -0
- package/dist/cli/args.js +1 -0
- package/dist/cli/session-picker.d.ts +1 -1
- package/dist/config.js +1 -1
- package/dist/core/agent-session-auto-handoff.test.js +2 -1
- package/dist/core/agent-session-length-continuation.test.js +2 -1
- package/dist/core/agent-session.d.ts +58 -7
- package/dist/core/agent-session.js +334 -167
- package/dist/core/bug-report-upload.d.ts +11 -0
- package/dist/core/bug-report-upload.js +21 -0
- package/dist/core/bug-report.d.ts +175 -0
- package/dist/core/bug-report.js +291 -0
- package/dist/core/cache-stats.js +12 -1
- package/dist/core/cache-warmer.d.ts +101 -0
- package/dist/core/cache-warmer.js +341 -0
- package/dist/core/compaction/branch-summarization.js +2 -2
- package/dist/core/compaction/compaction.d.ts +6 -5
- package/dist/core/compaction/compaction.js +49 -42
- package/dist/core/crash-log.d.ts +21 -0
- package/dist/core/crash-log.js +70 -0
- package/dist/core/experimental.d.ts +0 -4
- package/dist/core/experimental.js +0 -4
- package/dist/core/extensions/index.d.ts +1 -1
- package/dist/core/extensions/jiti-loader.d.ts +1 -0
- package/dist/core/extensions/jiti-loader.js +3 -0
- package/dist/core/extensions/jiti-static-loader.d.ts +1 -0
- package/dist/core/extensions/jiti-static-loader.js +3 -0
- package/dist/core/extensions/loader.js +39 -49
- package/dist/core/extensions/runner.d.ts +10 -7
- package/dist/core/extensions/runner.js +85 -67
- package/dist/core/extensions/types.d.ts +66 -52
- package/dist/core/extensions/virtual-modules.d.ts +2 -0
- package/dist/core/extensions/virtual-modules.js +39 -0
- package/dist/core/extensions/wrapper.js +1 -20
- package/dist/core/index.d.ts +2 -1
- package/dist/core/keybindings.d.ts +1 -1
- package/dist/core/keybindings.js +1 -1
- package/dist/core/messages.js +1 -0
- package/dist/core/model-config.d.ts +101 -20
- package/dist/core/model-config.js +25 -18
- package/dist/core/model-resolver.js +2 -1
- package/dist/core/model-runtime.js +5 -3
- package/dist/core/provider-composer.d.ts +7 -2
- package/dist/core/provider-composer.js +10 -5
- package/dist/core/radius.d.ts +3 -0
- package/dist/core/radius.js +6 -0
- package/dist/core/sdk.js +60 -38
- package/dist/core/session-export.d.ts +6 -2
- package/dist/core/session-export.js +14 -13
- package/dist/core/session-format.test.d.ts +1 -0
- package/dist/core/session-format.test.js +96 -0
- package/dist/core/session-manager.d.ts +29 -6
- package/dist/core/session-manager.js +159 -96
- package/dist/core/settings-manager.d.ts +22 -5
- package/dist/core/settings-manager.js +56 -23
- package/dist/core/slash-commands.js +1 -0
- package/dist/core/system-prompt.d.ts +49 -4
- package/dist/core/system-prompt.js +155 -102
- package/dist/core/system-prompt.test.js +61 -23
- package/dist/core/tools/bash.d.ts +2 -1
- package/dist/core/tools/bash.js +10 -4
- package/dist/core/tools/edit.js +1 -2
- package/dist/core/tools/read.js +1 -2
- package/dist/core/tools/renderers/bash.js +9 -1
- package/dist/core/tools/write.js +1 -2
- package/dist/core/usage-totals.d.ts +1 -1
- package/dist/core/usage-totals.js +5 -1
- package/dist/extensions/pi-web-agent/src/backends/factory.ts +14 -6
- package/dist/extensions/pi-web-agent/src/extension.ts +3 -1
- package/dist/extensions/pi-web-agent/src/orchestration/answer-synthesizer.ts +7 -3
- package/dist/extensions/pi-web-agent/src/orchestration/research-orchestrator.ts +3 -0
- package/dist/extensions/pi-web-agent/src/orchestration/research-types.ts +2 -0
- package/dist/extensions/pi-web-agent/src/presentation/explore-presentation.ts +4 -2
- package/dist/extensions/pi-web-agent/src/search/tokenin.ts +4 -1
- package/dist/extensions/pi-web-agent/src/tools/web-explore.ts +2 -1
- package/dist/extensions/pi-web-agent/src/types.ts +4 -0
- package/dist/index.d.ts +3 -2
- package/dist/main.js +13 -9
- package/dist/migrations.js +2 -2
- package/dist/modes/interactive/bug-report.d.ts +15 -0
- package/dist/modes/interactive/bug-report.js +205 -0
- package/dist/modes/interactive/chat-viewport.js +1 -1
- package/dist/modes/interactive/components/branch-summary-message.js +12 -5
- package/dist/modes/interactive/components/compaction-summary-message.js +12 -5
- package/dist/modes/interactive/components/custom-editor.d.ts +3 -3
- package/dist/modes/interactive/components/extension-editor.d.ts +4 -1
- package/dist/modes/interactive/components/extension-editor.js +7 -2
- package/dist/modes/interactive/components/extension-input.d.ts +2 -0
- package/dist/modes/interactive/components/extension-input.js +6 -0
- package/dist/modes/interactive/components/extension-selector.d.ts +1 -0
- package/dist/modes/interactive/components/extension-selector.js +4 -0
- package/dist/modes/interactive/components/footer.js +4 -1
- package/dist/modes/interactive/components/session-selector.d.ts +5 -5
- package/dist/modes/interactive/components/session-selector.js +74 -48
- package/dist/modes/interactive/components/settings-selector.d.ts +3 -1
- package/dist/modes/interactive/components/settings-selector.js +11 -0
- package/dist/modes/interactive/components/skill-invocation-message.js +11 -4
- package/dist/modes/interactive/components/status-indicator.d.ts +2 -2
- package/dist/modes/interactive/components/status-indicator.js +7 -7
- package/dist/modes/interactive/components/tool-execution.js +18 -9
- package/dist/modes/interactive/components/tree-selector.js +2 -0
- package/dist/modes/interactive/interactive-mode.d.ts +15 -2
- package/dist/modes/interactive/interactive-mode.js +257 -81
- package/dist/modes/interactive/session-share.d.ts +2 -0
- package/dist/modes/interactive/session-share.js +8 -4
- package/dist/modes/interactive/tui-renderer.js +2 -2
- package/dist/modes/rpc/rpc-mode.js +2 -2
- package/dist/utils/clipboard-command.d.ts +6 -0
- package/dist/utils/clipboard-command.js +44 -0
- package/dist/utils/clipboard-image.js +55 -89
- package/dist/utils/clipboard.d.ts +1 -1
- package/dist/utils/clipboard.js +118 -108
- package/dist/utils/wsl.d.ts +2 -0
- package/dist/utils/wsl.js +14 -0
- package/dist/utils/zip.d.ts +6 -0
- package/dist/utils/zip.js +59 -0
- package/package.json +9 -9
- package/dist/utils/clipboard-native.d.ts +0 -10
- package/dist/utils/clipboard-native.js +0 -19
|
@@ -78,7 +78,10 @@ export class FooterComponent {
|
|
|
78
78
|
const usageTotals = createUsageTotals();
|
|
79
79
|
let latestCacheHitRate;
|
|
80
80
|
for (const entry of this.session.sessionManager.getEntries()) {
|
|
81
|
-
if (entry.type === "
|
|
81
|
+
if (entry.type === "usage") {
|
|
82
|
+
addUsageToTotals(usageTotals, entry.usage);
|
|
83
|
+
}
|
|
84
|
+
else if (entry.type === "message" && entry.message.role === "assistant") {
|
|
82
85
|
addUsageToTotals(usageTotals, entry.message.usage);
|
|
83
86
|
const latestPromptTokens = entry.message.usage.input + entry.message.usage.cacheRead + entry.message.usage.cacheWrite;
|
|
84
87
|
latestCacheHitRate =
|
|
@@ -10,6 +10,7 @@ declare class SessionList implements Component, Focusable {
|
|
|
10
10
|
private allSessions;
|
|
11
11
|
private filteredSessions;
|
|
12
12
|
private selectedIndex;
|
|
13
|
+
private selectionTouched;
|
|
13
14
|
private searchInput;
|
|
14
15
|
private showCwd;
|
|
15
16
|
private sortMode;
|
|
@@ -46,7 +47,7 @@ declare class SessionList implements Component, Focusable {
|
|
|
46
47
|
private buildTreePrefix;
|
|
47
48
|
handleInput(keyData: string): void;
|
|
48
49
|
}
|
|
49
|
-
type SessionsLoader = (onProgress?: SessionListProgress) => Promise<SessionInfo[]>;
|
|
50
|
+
type SessionsLoader = (onProgress?: SessionListProgress, signal?: AbortSignal) => Promise<SessionInfo[]>;
|
|
50
51
|
/**
|
|
51
52
|
* Component that renders a session selector
|
|
52
53
|
*/
|
|
@@ -65,9 +66,8 @@ export declare class SessionSelectorComponent extends Container implements Focus
|
|
|
65
66
|
private allSessionsLoader;
|
|
66
67
|
private requestRender;
|
|
67
68
|
private renameSession?;
|
|
68
|
-
private
|
|
69
|
-
private
|
|
70
|
-
private allLoadSeq;
|
|
69
|
+
private currentLoad;
|
|
70
|
+
private allLoad;
|
|
71
71
|
private mode;
|
|
72
72
|
private renameInput;
|
|
73
73
|
private renameTargetPath;
|
|
@@ -80,7 +80,7 @@ export declare class SessionSelectorComponent extends Container implements Focus
|
|
|
80
80
|
showRenameHint?: boolean;
|
|
81
81
|
keybindings?: KeybindingsManager;
|
|
82
82
|
}, currentSessionFilePath?: string);
|
|
83
|
-
private
|
|
83
|
+
private cancelLoads;
|
|
84
84
|
private enterRenameMode;
|
|
85
85
|
private exitRenameMode;
|
|
86
86
|
private confirmRename;
|
|
@@ -233,6 +233,7 @@ class SessionList {
|
|
|
233
233
|
allSessions = [];
|
|
234
234
|
filteredSessions = [];
|
|
235
235
|
selectedIndex = 0;
|
|
236
|
+
selectionTouched = false;
|
|
236
237
|
searchInput;
|
|
237
238
|
showCwd = false;
|
|
238
239
|
sortMode = "threaded";
|
|
@@ -291,9 +292,18 @@ class SessionList {
|
|
|
291
292
|
this.filterSessions(this.searchInput.getValue());
|
|
292
293
|
}
|
|
293
294
|
setSessions(sessions, showCwd) {
|
|
295
|
+
const selectedPath = this.selectionTouched ? this.getSelectedSessionPath() : undefined;
|
|
294
296
|
this.allSessions = sessions;
|
|
295
297
|
this.showCwd = showCwd;
|
|
296
298
|
this.filterSessions(this.searchInput.getValue());
|
|
299
|
+
if (!this.selectionTouched) {
|
|
300
|
+
this.selectedIndex = 0;
|
|
301
|
+
}
|
|
302
|
+
else if (selectedPath) {
|
|
303
|
+
const selectedIndex = this.filteredSessions.findIndex((node) => node.session.path === selectedPath);
|
|
304
|
+
if (selectedIndex >= 0)
|
|
305
|
+
this.selectedIndex = selectedIndex;
|
|
306
|
+
}
|
|
297
307
|
}
|
|
298
308
|
filterSessions(query) {
|
|
299
309
|
const trimmed = query.trim();
|
|
@@ -499,6 +509,7 @@ class SessionList {
|
|
|
499
509
|
this.startDeleteConfirmationForSelectedSession();
|
|
500
510
|
return;
|
|
501
511
|
}
|
|
512
|
+
this.selectionTouched = true;
|
|
502
513
|
// Up arrow
|
|
503
514
|
if (kb.matches(keyData, "tui.select.up")) {
|
|
504
515
|
this.selectedIndex = Math.max(0, this.selectedIndex - 1);
|
|
@@ -600,9 +611,8 @@ export class SessionSelectorComponent extends Container {
|
|
|
600
611
|
allSessionsLoader;
|
|
601
612
|
requestRender;
|
|
602
613
|
renameSession;
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
allLoadSeq = 0;
|
|
614
|
+
currentLoad = null;
|
|
615
|
+
allLoad = null;
|
|
606
616
|
mode = "list";
|
|
607
617
|
renameInput = new Input();
|
|
608
618
|
renameTargetPath = null;
|
|
@@ -653,14 +663,17 @@ export class SessionSelectorComponent extends Container {
|
|
|
653
663
|
const clearStatusMessage = () => this.header.setStatusMessage(null);
|
|
654
664
|
this.sessionList.onSelect = (sessionPath) => {
|
|
655
665
|
clearStatusMessage();
|
|
666
|
+
this.cancelLoads();
|
|
656
667
|
onSelect(sessionPath);
|
|
657
668
|
};
|
|
658
669
|
this.sessionList.onCancel = () => {
|
|
659
670
|
clearStatusMessage();
|
|
671
|
+
this.cancelLoads();
|
|
660
672
|
onCancel();
|
|
661
673
|
};
|
|
662
674
|
this.sessionList.onExit = () => {
|
|
663
675
|
clearStatusMessage();
|
|
676
|
+
this.cancelLoads();
|
|
664
677
|
onExit();
|
|
665
678
|
};
|
|
666
679
|
this.sessionList.onToggleScope = () => this.toggleScope();
|
|
@@ -669,9 +682,7 @@ export class SessionSelectorComponent extends Container {
|
|
|
669
682
|
this.sessionList.onRenameSession = (sessionPath) => {
|
|
670
683
|
if (!renameSession)
|
|
671
684
|
return;
|
|
672
|
-
if (this.scope === "current"
|
|
673
|
-
return;
|
|
674
|
-
if (this.scope === "all" && this.allLoading)
|
|
685
|
+
if (this.scope === "current" ? this.currentLoad : this.allLoad)
|
|
675
686
|
return;
|
|
676
687
|
const sessions = this.scope === "all" ? (this.allSessions ?? []) : (this.currentSessions ?? []);
|
|
677
688
|
const session = sessions.find((s) => s.path === sessionPath);
|
|
@@ -714,10 +725,19 @@ export class SessionSelectorComponent extends Container {
|
|
|
714
725
|
this.requestRender();
|
|
715
726
|
};
|
|
716
727
|
// Start loading current sessions immediately
|
|
717
|
-
this.
|
|
728
|
+
void this.loadScope("current");
|
|
718
729
|
}
|
|
719
|
-
|
|
720
|
-
|
|
730
|
+
cancelLoads() {
|
|
731
|
+
if (this.currentLoad) {
|
|
732
|
+
this.currentLoad.abort();
|
|
733
|
+
this.currentLoad = null;
|
|
734
|
+
this.currentSessions = null;
|
|
735
|
+
}
|
|
736
|
+
if (this.allLoad) {
|
|
737
|
+
this.allLoad.abort();
|
|
738
|
+
this.allLoad = null;
|
|
739
|
+
this.allSessions = null;
|
|
740
|
+
}
|
|
721
741
|
}
|
|
722
742
|
enterRenameMode(sessionPath, currentName) {
|
|
723
743
|
this.mode = "rename";
|
|
@@ -762,64 +782,77 @@ export class SessionSelectorComponent extends Container {
|
|
|
762
782
|
this.exitRenameMode();
|
|
763
783
|
}
|
|
764
784
|
}
|
|
765
|
-
async loadScope(scope
|
|
785
|
+
async loadScope(scope) {
|
|
786
|
+
if (scope === "current" ? this.currentLoad : this.allLoad)
|
|
787
|
+
return;
|
|
766
788
|
const showCwd = scope === "all";
|
|
767
|
-
|
|
789
|
+
const controller = new AbortController();
|
|
768
790
|
if (scope === "current") {
|
|
769
|
-
this.
|
|
791
|
+
this.currentLoad = controller;
|
|
770
792
|
}
|
|
771
793
|
else {
|
|
772
|
-
this.
|
|
794
|
+
this.allLoad = controller;
|
|
773
795
|
}
|
|
774
|
-
const seq = scope === "all" ? ++this.allLoadSeq : undefined;
|
|
775
796
|
this.header.setScope(scope);
|
|
776
797
|
this.header.setLoading(true);
|
|
777
798
|
this.requestRender();
|
|
778
|
-
const
|
|
779
|
-
|
|
799
|
+
const isActive = () => (scope === "current" ? this.currentLoad : this.allLoad) === controller;
|
|
800
|
+
const onProgress = (loaded, total, partialSessions) => {
|
|
801
|
+
if (!isActive())
|
|
780
802
|
return;
|
|
781
|
-
if (
|
|
803
|
+
if (partialSessions) {
|
|
804
|
+
const sessions = [...partialSessions];
|
|
805
|
+
if (scope === "current") {
|
|
806
|
+
this.currentSessions = sessions;
|
|
807
|
+
}
|
|
808
|
+
else {
|
|
809
|
+
this.allSessions = sessions;
|
|
810
|
+
}
|
|
811
|
+
if (scope === this.scope)
|
|
812
|
+
this.sessionList.setSessions(sessions, showCwd);
|
|
813
|
+
}
|
|
814
|
+
if (scope !== this.scope)
|
|
782
815
|
return;
|
|
783
816
|
this.header.setProgress(loaded, total);
|
|
784
817
|
this.requestRender();
|
|
785
818
|
};
|
|
786
819
|
try {
|
|
787
820
|
const sessions = await (scope === "current"
|
|
788
|
-
? this.currentSessionsLoader(onProgress)
|
|
789
|
-
: this.allSessionsLoader(onProgress));
|
|
821
|
+
? this.currentSessionsLoader(onProgress, controller.signal)
|
|
822
|
+
: this.allSessionsLoader(onProgress, controller.signal));
|
|
823
|
+
if (!isActive())
|
|
824
|
+
return;
|
|
790
825
|
if (scope === "current") {
|
|
791
826
|
this.currentSessions = sessions;
|
|
792
|
-
this.
|
|
827
|
+
this.currentLoad = null;
|
|
793
828
|
}
|
|
794
829
|
else {
|
|
795
830
|
this.allSessions = sessions;
|
|
796
|
-
this.
|
|
831
|
+
this.allLoad = null;
|
|
797
832
|
}
|
|
798
833
|
if (scope !== this.scope)
|
|
799
834
|
return;
|
|
800
|
-
if (seq !== undefined && seq !== this.allLoadSeq)
|
|
801
|
-
return;
|
|
802
835
|
this.header.setLoading(false);
|
|
803
836
|
this.sessionList.setSessions(sessions, showCwd);
|
|
804
837
|
this.requestRender();
|
|
805
838
|
}
|
|
806
839
|
catch (err) {
|
|
840
|
+
if (!isActive())
|
|
841
|
+
return;
|
|
807
842
|
if (scope === "current") {
|
|
808
|
-
this.
|
|
843
|
+
this.currentLoad = null;
|
|
844
|
+
this.currentSessions = null;
|
|
809
845
|
}
|
|
810
846
|
else {
|
|
811
|
-
this.
|
|
847
|
+
this.allLoad = null;
|
|
848
|
+
this.allSessions = null;
|
|
812
849
|
}
|
|
813
850
|
if (scope !== this.scope)
|
|
814
851
|
return;
|
|
815
|
-
if (seq !== undefined && seq !== this.allLoadSeq)
|
|
816
|
-
return;
|
|
817
852
|
const message = err instanceof Error ? err.message : String(err);
|
|
818
853
|
this.header.setLoading(false);
|
|
819
854
|
this.header.setStatusMessage({ type: "error", message: `Failed to load sessions: ${message}` }, 4000);
|
|
820
|
-
|
|
821
|
-
this.sessionList.setSessions([], showCwd);
|
|
822
|
-
}
|
|
855
|
+
this.sessionList.setSessions([], showCwd);
|
|
823
856
|
this.requestRender();
|
|
824
857
|
}
|
|
825
858
|
}
|
|
@@ -837,28 +870,21 @@ export class SessionSelectorComponent extends Container {
|
|
|
837
870
|
this.requestRender();
|
|
838
871
|
}
|
|
839
872
|
async refreshSessionsAfterMutation() {
|
|
840
|
-
|
|
873
|
+
this.cancelLoads();
|
|
874
|
+
this.currentSessions = null;
|
|
875
|
+
this.allSessions = null;
|
|
876
|
+
await this.loadScope(this.scope);
|
|
841
877
|
}
|
|
842
878
|
toggleScope() {
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
if (this.allSessions !== null) {
|
|
847
|
-
this.header.setLoading(false);
|
|
848
|
-
this.sessionList.setSessions(this.allSessions, true);
|
|
849
|
-
this.requestRender();
|
|
850
|
-
return;
|
|
851
|
-
}
|
|
852
|
-
if (!this.allLoading) {
|
|
853
|
-
void this.loadScope("all", "toggle");
|
|
854
|
-
}
|
|
855
|
-
return;
|
|
856
|
-
}
|
|
857
|
-
this.scope = "current";
|
|
879
|
+
this.scope = this.scope === "current" ? "all" : "current";
|
|
880
|
+
const sessions = this.scope === "current" ? this.currentSessions : this.allSessions;
|
|
881
|
+
const loading = (this.scope === "current" ? this.currentLoad : this.allLoad) !== null;
|
|
858
882
|
this.header.setScope(this.scope);
|
|
859
|
-
this.header.setLoading(
|
|
860
|
-
this.sessionList.setSessions(
|
|
883
|
+
this.header.setLoading(loading);
|
|
884
|
+
this.sessionList.setSessions(sessions ?? [], this.scope === "all");
|
|
861
885
|
this.requestRender();
|
|
886
|
+
if (sessions === null && !loading)
|
|
887
|
+
void this.loadScope(this.scope);
|
|
862
888
|
}
|
|
863
889
|
getSessionList() {
|
|
864
890
|
return this.sessionList;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ThinkingLevel } from "@earendil-works/pi-agent-core";
|
|
2
2
|
import { type Model, type Transport } from "@earendil-works/pi-ai";
|
|
3
3
|
import { Container, type ScrollViewScrollbar, SettingsList } from "@earendil-works/pi-tui";
|
|
4
|
-
import type
|
|
4
|
+
import { type CacheWarmingMode, type DefaultProjectTrust, type FullscreenExitOutput, type TuiMode, type WarningSettings } from "../../../core/settings-manager.ts";
|
|
5
5
|
import { type TerminalTheme } from "../theme/theme.ts";
|
|
6
6
|
import type { ModelSelectorComponent } from "./model-selector.ts";
|
|
7
7
|
export interface SkillToggleItem {
|
|
@@ -27,6 +27,7 @@ export interface SettingsConfig {
|
|
|
27
27
|
followUpMode: "all" | "one-at-a-time";
|
|
28
28
|
transport: Transport;
|
|
29
29
|
httpIdleTimeoutMs: number;
|
|
30
|
+
cacheWarmingMode: CacheWarmingMode;
|
|
30
31
|
thinkingLevel: ThinkingLevel;
|
|
31
32
|
availableThinkingLevels: ThinkingLevel[];
|
|
32
33
|
defaultModel: string;
|
|
@@ -71,6 +72,7 @@ export interface SettingsCallbacks {
|
|
|
71
72
|
onFollowUpModeChange: (mode: "all" | "one-at-a-time") => void;
|
|
72
73
|
onTransportChange: (transport: Transport) => void;
|
|
73
74
|
onHttpIdleTimeoutMsChange: (timeoutMs: number) => void;
|
|
75
|
+
onCacheWarmingModeChange: (mode: CacheWarmingMode) => void;
|
|
74
76
|
onModelThinkingLevelChange: (provider: string, modelId: string, level: ThinkingLevel) => void;
|
|
75
77
|
onModelThinkingLevelRemove: (provider: string, modelId: string) => void;
|
|
76
78
|
onThinkingLevelChange: (level: ThinkingLevel) => void;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { getSupportedThinkingLevels } from "@earendil-works/pi-ai";
|
|
2
2
|
import { Container, getCapabilities, SelectList, SettingsList, Spacer, Text, } from "@earendil-works/pi-tui";
|
|
3
3
|
import { formatHttpIdleTimeoutMs, HTTP_IDLE_TIMEOUT_CHOICES } from "../../../core/http-dispatcher.js";
|
|
4
|
+
import { CACHE_WARMING_MODES, } from "../../../core/settings-manager.js";
|
|
4
5
|
import { getSelectListTheme, getSettingsListTheme, parseAutoThemeSetting, theme, } from "../theme/theme.js";
|
|
5
6
|
import { DynamicBorder } from "./dynamic-border.js";
|
|
6
7
|
import { keyDisplayText } from "./keybinding-hints.js";
|
|
@@ -390,6 +391,13 @@ export class SettingsSelectorComponent extends Container {
|
|
|
390
391
|
currentValue: formatHttpIdleTimeoutMs(config.httpIdleTimeoutMs),
|
|
391
392
|
values: HTTP_IDLE_TIMEOUT_CHOICES.map((choice) => choice.label),
|
|
392
393
|
},
|
|
394
|
+
{
|
|
395
|
+
id: "cache-warming-mode",
|
|
396
|
+
label: "Cache warming",
|
|
397
|
+
description: "off; streaming while the agent runs; idle also between runs while continuation stays profitable",
|
|
398
|
+
currentValue: config.cacheWarmingMode,
|
|
399
|
+
values: [...CACHE_WARMING_MODES],
|
|
400
|
+
},
|
|
393
401
|
{
|
|
394
402
|
id: "hide-thinking",
|
|
395
403
|
label: "Hide thinking",
|
|
@@ -757,6 +765,9 @@ export class SettingsSelectorComponent extends Container {
|
|
|
757
765
|
}
|
|
758
766
|
break;
|
|
759
767
|
}
|
|
768
|
+
case "cache-warming-mode":
|
|
769
|
+
callbacks.onCacheWarmingModeChange(newValue);
|
|
770
|
+
break;
|
|
760
771
|
case "hide-thinking":
|
|
761
772
|
callbacks.onHideThinkingBlockChange(newValue === "true");
|
|
762
773
|
break;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Box, Markdown, Text } from "@earendil-works/pi-tui";
|
|
1
|
+
import { Box, Container, Markdown, MouseRegion, Text } from "@earendil-works/pi-tui";
|
|
2
2
|
import { getMarkdownTheme, theme } from "../theme/theme.js";
|
|
3
3
|
import { keyText } from "./keybinding-hints.js";
|
|
4
4
|
/**
|
|
@@ -26,12 +26,13 @@ export class SkillInvocationMessageComponent extends Box {
|
|
|
26
26
|
}
|
|
27
27
|
updateDisplay() {
|
|
28
28
|
this.clear();
|
|
29
|
+
const content = new Container();
|
|
29
30
|
if (this.expanded) {
|
|
30
31
|
// Expanded: label + skill name header + full content
|
|
31
32
|
const label = theme.fg("customMessageLabel", `\x1b[1m[skill]\x1b[22m`);
|
|
32
|
-
|
|
33
|
+
content.addChild(new Text(label, 0, 0));
|
|
33
34
|
const header = `**${this.skillBlock.name}**\n\n`;
|
|
34
|
-
|
|
35
|
+
content.addChild(new Markdown(header + this.skillBlock.content, 0, 0, this.markdownTheme, {
|
|
35
36
|
color: (text) => theme.fg("customMessageText", text),
|
|
36
37
|
}));
|
|
37
38
|
}
|
|
@@ -40,7 +41,13 @@ export class SkillInvocationMessageComponent extends Box {
|
|
|
40
41
|
const line = theme.fg("customMessageLabel", `\x1b[1m[skill]\x1b[22m `) +
|
|
41
42
|
theme.fg("customMessageText", this.skillBlock.name) +
|
|
42
43
|
theme.fg("dim", ` (${keyText("app.tools.expand")} to expand)`);
|
|
43
|
-
|
|
44
|
+
content.addChild(new Text(line, 0, 0));
|
|
44
45
|
}
|
|
46
|
+
this.addChild(new MouseRegion(content, (event) => {
|
|
47
|
+
if (event.type !== "click" || event.button !== "left")
|
|
48
|
+
return undefined;
|
|
49
|
+
this.setExpanded(!this.expanded);
|
|
50
|
+
return { handled: true };
|
|
51
|
+
}));
|
|
45
52
|
}
|
|
46
53
|
}
|
|
@@ -4,12 +4,12 @@ export type StatusIndicatorKind = "working" | "retry" | "compaction" | "branchSu
|
|
|
4
4
|
export declare class StatusIndicator extends Loader {
|
|
5
5
|
readonly kind: StatusIndicatorKind;
|
|
6
6
|
constructor(kind: StatusIndicatorKind, ui: TUI, spinnerColorFn: (str: string) => string, messageColorFn: (str: string) => string, message: string, indicator?: WorkingIndicatorOptions);
|
|
7
|
+
renderInBorder(width: number): string;
|
|
8
|
+
renderSpinnerInBorder(width: number): string;
|
|
7
9
|
dispose(): void;
|
|
8
10
|
}
|
|
9
11
|
export declare class WorkingStatusIndicator extends StatusIndicator {
|
|
10
12
|
constructor(ui: TUI, message: string, indicator?: WorkingIndicatorOptions, colorFn?: (text: string) => string);
|
|
11
|
-
renderInBorder(width: number): string;
|
|
12
|
-
renderSpinnerInBorder(width: number): string;
|
|
13
13
|
}
|
|
14
14
|
export declare class ImageCaptioningStatusIndicator extends StatusIndicator {
|
|
15
15
|
constructor(ui: TUI);
|
|
@@ -8,6 +8,13 @@ export class StatusIndicator extends Loader {
|
|
|
8
8
|
super(ui, spinnerColorFn, messageColorFn, message, indicator);
|
|
9
9
|
this.kind = kind;
|
|
10
10
|
}
|
|
11
|
+
renderInBorder(width) {
|
|
12
|
+
const line = super.render(width + 2)[1] ?? "";
|
|
13
|
+
return truncateToWidth(line.startsWith(" ") ? line.slice(1).trimEnd() : line.trimEnd(), width, "");
|
|
14
|
+
}
|
|
15
|
+
renderSpinnerInBorder(width) {
|
|
16
|
+
return truncateToWidth(this.getRenderedIndicator(), width, "");
|
|
17
|
+
}
|
|
11
18
|
dispose() {
|
|
12
19
|
this.stop();
|
|
13
20
|
}
|
|
@@ -16,13 +23,6 @@ export class WorkingStatusIndicator extends StatusIndicator {
|
|
|
16
23
|
constructor(ui, message, indicator, colorFn) {
|
|
17
24
|
super("working", ui, colorFn ?? ((text) => theme.fg("accent", text)), colorFn ?? ((text) => theme.fg("muted", text)), message, indicator);
|
|
18
25
|
}
|
|
19
|
-
renderInBorder(width) {
|
|
20
|
-
const line = super.render(width + 2)[1] ?? "";
|
|
21
|
-
return truncateToWidth(line.startsWith(" ") ? line.slice(1).trimEnd() : line.trimEnd(), width, "");
|
|
22
|
-
}
|
|
23
|
-
renderSpinnerInBorder(width) {
|
|
24
|
-
return truncateToWidth(this.getRenderedIndicator(), width, "");
|
|
25
|
-
}
|
|
26
26
|
}
|
|
27
27
|
export class ImageCaptioningStatusIndicator extends StatusIndicator {
|
|
28
28
|
constructor(ui) {
|
|
@@ -143,17 +143,25 @@ export class ToolExecutionComponent extends Container {
|
|
|
143
143
|
const img = imageBlocks[i];
|
|
144
144
|
if (!img.data || !img.mimeType)
|
|
145
145
|
continue;
|
|
146
|
-
|
|
146
|
+
const sourceData = img.data;
|
|
147
|
+
const sourceMimeType = img.mimeType;
|
|
148
|
+
if (sourceMimeType === "image/png")
|
|
147
149
|
continue;
|
|
148
|
-
|
|
150
|
+
const cached = this.convertedImages.get(i);
|
|
151
|
+
if (cached?.sourceData === sourceData && cached.sourceMimeType === sourceMimeType)
|
|
149
152
|
continue;
|
|
150
153
|
const index = i;
|
|
151
|
-
convertToPng(
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
154
|
+
convertToPng(sourceData, sourceMimeType).then((converted) => {
|
|
155
|
+
const currentImage = this.result?.content.filter((content) => content.type === "image")[index];
|
|
156
|
+
if (!converted || currentImage?.data !== sourceData || currentImage.mimeType !== sourceMimeType)
|
|
157
|
+
return;
|
|
158
|
+
this.convertedImages.set(index, {
|
|
159
|
+
sourceData,
|
|
160
|
+
sourceMimeType,
|
|
161
|
+
...converted,
|
|
162
|
+
});
|
|
163
|
+
this.updateDisplay();
|
|
164
|
+
this.ui.requestRender();
|
|
157
165
|
});
|
|
158
166
|
}
|
|
159
167
|
}
|
|
@@ -291,7 +299,8 @@ export class ToolExecutionComponent extends Container {
|
|
|
291
299
|
for (let i = 0; i < imageBlocks.length; i++) {
|
|
292
300
|
const img = imageBlocks[i];
|
|
293
301
|
if (caps.images && this.showImages && img.data && img.mimeType) {
|
|
294
|
-
const
|
|
302
|
+
const cached = this.convertedImages.get(i);
|
|
303
|
+
const converted = cached?.sourceData === img.data && cached.sourceMimeType === img.mimeType ? cached : undefined;
|
|
295
304
|
const imageData = converted?.data ?? img.data;
|
|
296
305
|
const imageMimeType = converted?.mimeType ?? img.mimeType;
|
|
297
306
|
if (caps.images === "kitty" && imageMimeType !== "image/png")
|
|
@@ -239,6 +239,8 @@ class TreeList {
|
|
|
239
239
|
const searchTokens = this.searchQuery.toLowerCase().split(/\s+/).filter(Boolean);
|
|
240
240
|
this.filteredNodes = this.flatNodes.filter((flatNode) => {
|
|
241
241
|
const entry = flatNode.node.entry;
|
|
242
|
+
if (entry.type === "usage")
|
|
243
|
+
return false;
|
|
242
244
|
const isCurrentLeaf = entry.id === this.currentLeafId;
|
|
243
245
|
// Skip assistant messages with only tool calls (no text) unless error/aborted
|
|
244
246
|
// Always show current leaf so active position is visible
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* Interactive mode for the coding agent.
|
|
3
3
|
* Handles TUI rendering and user interaction, delegating business logic to AgentSession.
|
|
4
4
|
*/
|
|
5
|
-
import type
|
|
5
|
+
import { type ImageContent } from "@earendil-works/pi-ai/compat";
|
|
6
|
+
import { type Terminal } from "@earendil-works/pi-tui";
|
|
6
7
|
import { type AgentSessionRuntime } from "../../core/agent-session-runtime.ts";
|
|
7
8
|
import { SessionManager } from "../../core/session-manager.ts";
|
|
8
9
|
import type { FullscreenExitOutput, TuiMode } from "../../core/settings-manager.ts";
|
|
@@ -35,6 +36,8 @@ export interface InteractiveModeOptions {
|
|
|
35
36
|
tuiMode?: TuiMode;
|
|
36
37
|
/** Initial interactive theme setting for this invocation. */
|
|
37
38
|
initialThemeSetting?: string;
|
|
39
|
+
/** Terminal implementation. Defaults to the current process terminal. */
|
|
40
|
+
terminal?: Terminal;
|
|
38
41
|
}
|
|
39
42
|
export declare class InteractiveMode {
|
|
40
43
|
private runtimeHost;
|
|
@@ -96,6 +99,8 @@ export declare class InteractiveMode {
|
|
|
96
99
|
private retryEscapeHandler?;
|
|
97
100
|
private compactionQueuedMessages;
|
|
98
101
|
private shutdownRequested;
|
|
102
|
+
/** The `/bug` hint is shown at most once per session so error output stays readable. */
|
|
103
|
+
private bugReportHintShown;
|
|
99
104
|
private extensionSelector;
|
|
100
105
|
private extensionInput;
|
|
101
106
|
private extensionEditor;
|
|
@@ -175,6 +180,11 @@ export declare class InteractiveMode {
|
|
|
175
180
|
private applyRuntimeSettings;
|
|
176
181
|
private rebindCurrentSession;
|
|
177
182
|
private handleFatalRuntimeError;
|
|
183
|
+
/** Persist a crash so the next start can point the user at `/bug`. Returns false when nothing was written. */
|
|
184
|
+
private recordCrash;
|
|
185
|
+
private crashReportInstructions;
|
|
186
|
+
private suggestBugReport;
|
|
187
|
+
private maybeSuggestBugReport;
|
|
178
188
|
private renderCurrentSessionState;
|
|
179
189
|
/**
|
|
180
190
|
* Get a registered tool definition by name (for custom rendering).
|
|
@@ -297,12 +307,14 @@ export declare class InteractiveMode {
|
|
|
297
307
|
* @param options.populateHistory Add user messages to editor history
|
|
298
308
|
*/
|
|
299
309
|
private renderSessionEntries;
|
|
310
|
+
private addCacheWarmingUsage;
|
|
300
311
|
/**
|
|
301
312
|
* Render billing usage for a compaction or branch summary. The notice is derived
|
|
302
313
|
* from persisted summary usage and is not stored as a separate session entry.
|
|
303
314
|
*/
|
|
304
315
|
private addCompactionCostNotice;
|
|
305
|
-
private
|
|
316
|
+
private static countDroppedThinkingBlocks;
|
|
317
|
+
private maybeShowThinkingDropNotice;
|
|
306
318
|
/**
|
|
307
319
|
* Show a transcript notice when a completed assistant message paid for a
|
|
308
320
|
* significant cache miss. Only states observable facts: the miss itself,
|
|
@@ -420,6 +432,7 @@ export declare class InteractiveMode {
|
|
|
420
432
|
private getPathCommandArgument;
|
|
421
433
|
private handleImportCommand;
|
|
422
434
|
private handleShareCommand;
|
|
435
|
+
private handleBugCommand;
|
|
423
436
|
private handleCopyCommand;
|
|
424
437
|
private handleNameCommand;
|
|
425
438
|
private handleSessionCommand;
|