@oh-my-pi/pi-coding-agent 16.0.10 โ 16.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +57 -0
- package/dist/cli.js +3344 -3371
- package/dist/types/advisor/index.d.ts +1 -0
- package/dist/types/advisor/transcript-recorder.d.ts +52 -0
- package/dist/types/commit/agentic/agent.d.ts +1 -1
- package/dist/types/config/settings-schema.d.ts +14 -8
- package/dist/types/edit/file-snapshot-store.d.ts +1 -1
- package/dist/types/extensibility/extensions/types.d.ts +7 -0
- package/dist/types/modes/components/__tests__/skill-message.test.d.ts +1 -0
- package/dist/types/modes/components/agent-hub.d.ts +6 -1
- package/dist/types/modes/components/agent-transcript-viewer.d.ts +39 -0
- package/dist/types/modes/components/assistant-message.d.ts +8 -0
- package/dist/types/modes/components/cache-invalidation-marker.d.ts +34 -0
- package/dist/types/modes/components/chat-transcript-builder.d.ts +42 -0
- package/dist/types/modes/components/compaction-summary-message.d.ts +14 -1
- package/dist/types/modes/components/index.d.ts +0 -1
- package/dist/types/modes/components/message-frame.d.ts +6 -4
- package/dist/types/modes/controllers/command-controller.d.ts +3 -2
- package/dist/types/modes/interactive-mode.d.ts +4 -2
- package/dist/types/modes/theme/theme.d.ts +7 -1
- package/dist/types/modes/types.d.ts +9 -2
- package/dist/types/registry/agent-registry.d.ts +10 -3
- package/dist/types/sdk.d.ts +1 -1
- package/dist/types/session/agent-session.d.ts +20 -1
- package/dist/types/session/compact-modes.d.ts +60 -0
- package/dist/types/session/session-context.d.ts +7 -0
- package/dist/types/session/session-dump-format.d.ts +1 -0
- package/dist/types/session/streaming-output.d.ts +0 -2
- package/dist/types/session/tool-choice-queue.d.ts +14 -0
- package/dist/types/system-prompt.d.ts +3 -3
- package/dist/types/tools/__tests__/json-tree.test.d.ts +1 -0
- package/dist/types/tools/index.d.ts +4 -0
- package/dist/types/tools/resolve.d.ts +15 -5
- package/package.json +12 -12
- package/src/advisor/index.ts +1 -0
- package/src/advisor/transcript-recorder.ts +136 -0
- package/src/cli/stats-cli.ts +2 -11
- package/src/collab/host.ts +25 -13
- package/src/commit/agentic/agent.ts +2 -1
- package/src/commit/agentic/tools/git-file-diff.ts +2 -2
- package/src/commit/changelog/index.ts +1 -1
- package/src/commit/map-reduce/map-phase.ts +1 -1
- package/src/commit/map-reduce/utils.ts +1 -1
- package/src/config/settings-schema.ts +16 -9
- package/src/config/settings.ts +0 -6
- package/src/debug/log-viewer.ts +4 -4
- package/src/debug/raw-sse.ts +4 -4
- package/src/edit/file-snapshot-store.ts +1 -1
- package/src/edit/renderer.ts +9 -9
- package/src/eval/js/tool-bridge.ts +3 -2
- package/src/eval/py/prelude.py +3 -2
- package/src/export/html/tool-views.generated.js +28 -28
- package/src/extensibility/extensions/types.ts +7 -0
- package/src/hindsight/mental-models.ts +1 -1
- package/src/internal-urls/docs-index.generated.txt +1 -1
- package/src/internal-urls/history-protocol.ts +8 -3
- package/src/irc/bus.ts +8 -0
- package/src/lsp/index.ts +2 -2
- package/src/lsp/render.ts +7 -7
- package/src/main.ts +4 -1
- package/src/modes/acp/acp-agent.ts +63 -0
- package/src/modes/components/__tests__/skill-message.test.ts +92 -0
- package/src/modes/components/agent-dashboard.ts +1 -1
- package/src/modes/components/agent-hub.ts +97 -920
- package/src/modes/components/agent-transcript-viewer.ts +461 -0
- package/src/modes/components/assistant-message.ts +21 -0
- package/src/modes/components/cache-invalidation-marker.ts +84 -0
- package/src/modes/components/chat-transcript-builder.ts +476 -0
- package/src/modes/components/compaction-summary-message.ts +29 -1
- package/src/modes/components/custom-message.ts +4 -1
- package/src/modes/components/diff.ts +12 -35
- package/src/modes/components/dynamic-border.ts +1 -1
- package/src/modes/components/extensions/extension-dashboard.ts +1 -1
- package/src/modes/components/extensions/inspector-panel.ts +5 -5
- package/src/modes/components/hook-selector.ts +2 -2
- package/src/modes/components/index.ts +0 -1
- package/src/modes/components/message-frame.ts +10 -6
- package/src/modes/components/model-selector.ts +2 -2
- package/src/modes/components/overlay-box.ts +10 -9
- package/src/modes/components/skill-message.ts +39 -19
- package/src/modes/components/tiny-title-download-progress.ts +1 -1
- package/src/modes/components/welcome.ts +1 -1
- package/src/modes/controllers/command-controller.ts +12 -2
- package/src/modes/controllers/event-controller.ts +15 -1
- package/src/modes/controllers/input-controller.ts +8 -1
- package/src/modes/controllers/selector-controller.ts +11 -1
- package/src/modes/interactive-mode.ts +13 -3
- package/src/modes/theme/theme.ts +14 -0
- package/src/modes/types.ts +9 -2
- package/src/modes/utils/ui-helpers.ts +20 -2
- package/src/prompts/steering/user-interjection.md +3 -4
- package/src/prompts/tools/read.md +1 -1
- package/src/registry/agent-registry.ts +13 -4
- package/src/sdk.ts +9 -7
- package/src/session/agent-session.ts +182 -16
- package/src/session/compact-modes.ts +105 -0
- package/src/session/messages.ts +7 -9
- package/src/session/session-context.ts +54 -7
- package/src/session/session-dump-format.ts +4 -2
- package/src/session/session-history-format.ts +1 -1
- package/src/session/snapcompact-inline.ts +2 -2
- package/src/session/streaming-output.ts +5 -5
- package/src/session/tool-choice-queue.ts +59 -0
- package/src/slash-commands/builtin-registry.ts +16 -4
- package/src/system-prompt.ts +10 -9
- package/src/task/executor.ts +1 -1
- package/src/task/output-manager.ts +5 -0
- package/src/tools/__tests__/json-tree.test.ts +35 -0
- package/src/tools/approval.ts +1 -1
- package/src/tools/bash-interactive.ts +4 -4
- package/src/tools/bash.ts +0 -1
- package/src/tools/browser.ts +0 -1
- package/src/tools/eval.ts +1 -1
- package/src/tools/gh.ts +1 -1
- package/src/tools/index.ts +4 -0
- package/src/tools/irc.ts +1 -1
- package/src/tools/json-tree.ts +22 -5
- package/src/tools/read.ts +5 -6
- package/src/tools/resolve.ts +66 -41
- package/src/tui/output-block.ts +9 -9
- package/src/web/scrapers/firefox-addons.ts +1 -1
- package/src/web/scrapers/github.ts +1 -1
- package/src/web/scrapers/go-pkg.ts +2 -2
- package/src/web/scrapers/metacpan.ts +2 -2
- package/src/web/scrapers/nvd.ts +2 -2
- package/src/web/scrapers/ollama.ts +1 -1
- package/src/web/scrapers/opencorporates.ts +1 -1
- package/src/web/scrapers/pub-dev.ts +1 -1
- package/src/web/scrapers/repology.ts +1 -1
- package/src/web/scrapers/sourcegraph.ts +1 -1
- package/src/web/scrapers/terraform.ts +6 -6
- package/src/web/scrapers/wikidata.ts +2 -2
- package/src/workspace-tree.ts +1 -1
- package/dist/types/modes/components/branch-summary-message.d.ts +0 -13
- package/src/modes/components/branch-summary-message.ts +0 -46
|
@@ -107,7 +107,7 @@ export class InspectorPanel implements Component {
|
|
|
107
107
|
#renderFilePreview(raw: unknown, width: number): string[] {
|
|
108
108
|
const lines: string[] = [];
|
|
109
109
|
lines.push(theme.fg("muted", "Preview:"));
|
|
110
|
-
lines.push(theme.fg("dim", theme.
|
|
110
|
+
lines.push(theme.fg("dim", theme.boxRound.horizontal.repeat(Math.min(width - 2, 40))));
|
|
111
111
|
|
|
112
112
|
const content = this.#getContextFileContent(raw);
|
|
113
113
|
if (!content) {
|
|
@@ -165,7 +165,7 @@ export class InspectorPanel implements Component {
|
|
|
165
165
|
#renderToolArgs(raw: unknown, width: number): string[] {
|
|
166
166
|
const lines: string[] = [];
|
|
167
167
|
lines.push(theme.fg("muted", "Arguments:"));
|
|
168
|
-
lines.push(theme.fg("dim", theme.
|
|
168
|
+
lines.push(theme.fg("dim", theme.boxRound.horizontal.repeat(Math.min(width - 2, 40))));
|
|
169
169
|
|
|
170
170
|
try {
|
|
171
171
|
const tool = raw as any;
|
|
@@ -207,7 +207,7 @@ export class InspectorPanel implements Component {
|
|
|
207
207
|
#renderSkillContent(raw: unknown, width: number): string[] {
|
|
208
208
|
const lines: string[] = [];
|
|
209
209
|
lines.push(theme.fg("muted", "Instruction:"));
|
|
210
|
-
lines.push(theme.fg("dim", theme.
|
|
210
|
+
lines.push(theme.fg("dim", theme.boxRound.horizontal.repeat(Math.min(width - 2, 40))));
|
|
211
211
|
|
|
212
212
|
try {
|
|
213
213
|
const skill = raw as any;
|
|
@@ -236,7 +236,7 @@ export class InspectorPanel implements Component {
|
|
|
236
236
|
#renderMcpDetails(raw: unknown, width: number): string[] {
|
|
237
237
|
const lines: string[] = [];
|
|
238
238
|
lines.push(theme.fg("muted", "Connection:"));
|
|
239
|
-
lines.push(theme.fg("dim", theme.
|
|
239
|
+
lines.push(theme.fg("dim", theme.boxRound.horizontal.repeat(Math.min(width - 2, 40))));
|
|
240
240
|
|
|
241
241
|
try {
|
|
242
242
|
const mcp = raw as any;
|
|
@@ -275,7 +275,7 @@ export class InspectorPanel implements Component {
|
|
|
275
275
|
// Show trigger pattern if present
|
|
276
276
|
if (ext.trigger) {
|
|
277
277
|
lines.push(theme.fg("muted", "Trigger:"));
|
|
278
|
-
lines.push(theme.fg("dim", theme.
|
|
278
|
+
lines.push(theme.fg("dim", theme.boxRound.horizontal.repeat(Math.min(width - 2, 40))));
|
|
279
279
|
lines.push(` ${theme.fg("accent", ext.trigger)}`);
|
|
280
280
|
lines.push("");
|
|
281
281
|
}
|
|
@@ -123,7 +123,7 @@ class OutlinedList extends Container {
|
|
|
123
123
|
|
|
124
124
|
render(width: number): readonly string[] {
|
|
125
125
|
const borderColor = (text: string) => theme.fg("border", text);
|
|
126
|
-
const horizontal = borderColor(theme.
|
|
126
|
+
const horizontal = borderColor(theme.boxRound.horizontal.repeat(Math.max(1, width)));
|
|
127
127
|
const innerWidth = Math.max(1, width - 2);
|
|
128
128
|
const content: string[] = [];
|
|
129
129
|
for (const line of this.#lines) {
|
|
@@ -134,7 +134,7 @@ class OutlinedList extends Container {
|
|
|
134
134
|
const wrappedLine = `${indent}${wrappedBody}`;
|
|
135
135
|
const pad = Math.max(0, innerWidth - visibleWidth(wrappedLine));
|
|
136
136
|
content.push(
|
|
137
|
-
`${borderColor(theme.
|
|
137
|
+
`${borderColor(theme.boxRound.vertical)}${wrappedLine}${padding(pad)}${borderColor(theme.boxRound.vertical)}`,
|
|
138
138
|
);
|
|
139
139
|
}
|
|
140
140
|
}
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
export * from "./assistant-message";
|
|
3
3
|
export * from "./bash-execution";
|
|
4
4
|
export * from "./bordered-loader";
|
|
5
|
-
export * from "./branch-summary-message";
|
|
6
5
|
export * from "./compaction-summary-message";
|
|
7
6
|
export * from "./countdown-timer";
|
|
8
7
|
export * from "./custom-editor";
|
|
@@ -34,16 +34,18 @@ export interface RebuildFrameOptions<M extends FramedMessage> {
|
|
|
34
34
|
message: M;
|
|
35
35
|
box: Box;
|
|
36
36
|
expanded: boolean;
|
|
37
|
+
/** Icon glyph shown before the customType in the default header (e.g. a hook/extension icon). */
|
|
38
|
+
icon?: string;
|
|
37
39
|
/** Collapse the markdown body to this many lines when `expanded` is false. Omit to never collapse. */
|
|
38
40
|
collapseAfterLines?: number;
|
|
39
41
|
customRenderer?: FramedRenderer<M>;
|
|
40
42
|
}
|
|
41
43
|
|
|
42
44
|
/**
|
|
43
|
-
* Attempt the custom renderer; on failure or undefined return, populate
|
|
44
|
-
*
|
|
45
|
-
* undefined. When the custom renderer succeeds, return its Component
|
|
46
|
-
* caller can mount it and skip the default box.
|
|
45
|
+
* Attempt the custom renderer; on failure or undefined return, populate `box`
|
|
46
|
+
* with the default outlined card โ an `icon customType` header + markdown body โ
|
|
47
|
+
* and return undefined. When the custom renderer succeeds, return its Component
|
|
48
|
+
* so the caller can mount it and skip the default box.
|
|
47
49
|
*/
|
|
48
50
|
export function renderFramedMessage<M extends FramedMessage>(opts: RebuildFrameOptions<M>): Component | undefined {
|
|
49
51
|
if (opts.customRenderer) {
|
|
@@ -56,9 +58,11 @@ export function renderFramedMessage<M extends FramedMessage>(opts: RebuildFrameO
|
|
|
56
58
|
}
|
|
57
59
|
|
|
58
60
|
opts.box.clear();
|
|
61
|
+
// Match the skill card: a subtle rounded outline so injected messages read as cards.
|
|
62
|
+
opts.box.setBorder({ chars: theme.boxRound, color: t => theme.fg("borderMuted", t) });
|
|
59
63
|
|
|
60
|
-
const
|
|
61
|
-
opts.box.addChild(new Text(
|
|
64
|
+
const tag = opts.icon ? `${opts.icon} ${opts.message.customType}` : opts.message.customType;
|
|
65
|
+
opts.box.addChild(new Text(theme.fg("customMessageLabel", theme.bold(tag)), 0, 0));
|
|
62
66
|
opts.box.addChild(new Spacer(1));
|
|
63
67
|
|
|
64
68
|
let text: string;
|
|
@@ -1112,7 +1112,7 @@ export class ModelSelectorComponent extends Container {
|
|
|
1112
1112
|
const menuWidth = contentWidth + (needsScroll ? 1 : 0);
|
|
1113
1113
|
|
|
1114
1114
|
this.#menuContainer.addChild(new Spacer(1));
|
|
1115
|
-
this.#menuContainer.addChild(new Text(theme.fg("border", theme.
|
|
1115
|
+
this.#menuContainer.addChild(new Text(theme.fg("border", theme.boxRound.horizontal.repeat(menuWidth)), 0, 0));
|
|
1116
1116
|
if (showingThinking && this.#menuSelectedRole) {
|
|
1117
1117
|
this.#menuContainer.addChild(
|
|
1118
1118
|
new Text(
|
|
@@ -1152,7 +1152,7 @@ export class ModelSelectorComponent extends Container {
|
|
|
1152
1152
|
|
|
1153
1153
|
this.#menuContainer.addChild(new Spacer(1));
|
|
1154
1154
|
this.#menuContainer.addChild(new Text(theme.fg("dim", hintText), 0, 0));
|
|
1155
|
-
this.#menuContainer.addChild(new Text(theme.fg("border", theme.
|
|
1155
|
+
this.#menuContainer.addChild(new Text(theme.fg("border", theme.boxRound.horizontal.repeat(menuWidth)), 0, 0));
|
|
1156
1156
|
}
|
|
1157
1157
|
|
|
1158
1158
|
#getMenuVisibleCount(optionCount: number): number {
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shared box-drawing chrome for fullscreen overlays (the `/copy` picker, the
|
|
3
|
-
* plan-review overlay, โฆ). Every helper paints with `theme.
|
|
4
|
-
* the `border`/`accent` theme
|
|
3
|
+
* plan-review overlay, โฆ). Every helper paints with `theme.boxRound` glyphs
|
|
4
|
+
* (rounded corners, sharp tee/cross junctions) and the `border`/`accent` theme
|
|
5
|
+
* colors so all outlined overlays read identically.
|
|
5
6
|
*/
|
|
6
7
|
import { padding, truncateToWidth, visibleWidth } from "@oh-my-pi/pi-tui";
|
|
7
8
|
import { theme } from "../theme/theme";
|
|
@@ -23,7 +24,7 @@ function paint(s: string): string {
|
|
|
23
24
|
|
|
24
25
|
/** Top border with an optional accent-colored title inset into the rule. */
|
|
25
26
|
export function topBorder(width: number, title: string): string {
|
|
26
|
-
const box = theme.
|
|
27
|
+
const box = theme.boxRound;
|
|
27
28
|
const inner = Math.max(0, width - 2);
|
|
28
29
|
if (!title) return paint(box.topLeft + box.horizontal.repeat(inner) + box.topRight);
|
|
29
30
|
const shown = truncateToWidth(` ${title} `, Math.max(0, inner - 2));
|
|
@@ -37,18 +38,18 @@ export function topBorder(width: number, title: string): string {
|
|
|
37
38
|
|
|
38
39
|
/** A horizontal rule with left/right tees, splitting overlay sections. */
|
|
39
40
|
export function divider(width: number): string {
|
|
40
|
-
const box = theme.
|
|
41
|
+
const box = theme.boxRound;
|
|
41
42
|
return paint(box.teeRight + box.horizontal.repeat(Math.max(0, width - 2)) + box.teeLeft);
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
export function bottomBorder(width: number): string {
|
|
45
|
-
const box = theme.
|
|
46
|
+
const box = theme.boxRound;
|
|
46
47
|
return paint(box.bottomLeft + box.horizontal.repeat(Math.max(0, width - 2)) + box.bottomRight);
|
|
47
48
|
}
|
|
48
49
|
|
|
49
50
|
/** Wrap pre-styled content in vertical borders with single-column insets. */
|
|
50
51
|
export function row(content: string, width: number): string {
|
|
51
|
-
const box = theme.
|
|
52
|
+
const box = theme.boxRound;
|
|
52
53
|
return `${paint(box.vertical)} ${fit(content, Math.max(0, width - 4))} ${paint(box.vertical)}`;
|
|
53
54
|
}
|
|
54
55
|
|
|
@@ -70,7 +71,7 @@ export function splitBodyWidth(width: number, sidebarWidth: number): number {
|
|
|
70
71
|
|
|
71
72
|
/** Top border carrying the title, split by a `โฌ` over the column divider. */
|
|
72
73
|
export function topBorderSplit(width: number, title: string, sidebarWidth: number): string {
|
|
73
|
-
const box = theme.
|
|
74
|
+
const box = theme.boxRound;
|
|
74
75
|
const dividerCol = splitDividerCol(sidebarWidth);
|
|
75
76
|
const leftLen = Math.max(0, dividerCol - 1);
|
|
76
77
|
const rightLen = Math.max(0, width - 2 - dividerCol);
|
|
@@ -90,7 +91,7 @@ export function topBorderSplit(width: number, title: string, sidebarWidth: numbe
|
|
|
90
91
|
|
|
91
92
|
/** Section rule that closes the sidebar column with a `โด` over the divider. */
|
|
92
93
|
export function dividerSplit(width: number, sidebarWidth: number): string {
|
|
93
|
-
const box = theme.
|
|
94
|
+
const box = theme.boxRound;
|
|
94
95
|
const dividerCol = splitDividerCol(sidebarWidth);
|
|
95
96
|
const leftLen = Math.max(0, dividerCol - 1);
|
|
96
97
|
const rightLen = Math.max(0, width - 2 - dividerCol);
|
|
@@ -101,7 +102,7 @@ export function dividerSplit(width: number, sidebarWidth: number): string {
|
|
|
101
102
|
|
|
102
103
|
/** A two-column content row: `โ sidebar โ body โ`, each inset by one column. */
|
|
103
104
|
export function splitRow(sidebar: string, body: string, width: number, sidebarWidth: number): string {
|
|
104
|
-
const box = theme.
|
|
105
|
+
const box = theme.boxRound;
|
|
105
106
|
const bodyWidth = splitBodyWidth(width, sidebarWidth);
|
|
106
107
|
const bar = paint(box.vertical);
|
|
107
108
|
return `${bar} ${fit(sidebar, sidebarWidth)} ${bar} ${fit(body, bodyWidth)} ${bar}`;
|
|
@@ -3,6 +3,8 @@ import type { Component } from "@oh-my-pi/pi-tui";
|
|
|
3
3
|
import { Box, Container, Markdown, Spacer, Text } from "@oh-my-pi/pi-tui";
|
|
4
4
|
import { getMarkdownTheme, theme } from "../../modes/theme/theme";
|
|
5
5
|
import type { CustomMessage, SkillPromptDetails } from "../../session/messages";
|
|
6
|
+
import { shortenPath } from "../../tools/render-utils";
|
|
7
|
+
import { fileHyperlink } from "../../tui";
|
|
6
8
|
|
|
7
9
|
export class SkillMessageComponent extends Container {
|
|
8
10
|
#box: Box;
|
|
@@ -38,25 +40,26 @@ export class SkillMessageComponent extends Container {
|
|
|
38
40
|
this.removeChild(this.#box);
|
|
39
41
|
this.addChild(this.#box);
|
|
40
42
|
this.#box.clear();
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
this.#box.addChild(new Text(label, 0, 0));
|
|
44
|
-
this.#box.addChild(new Spacer(1));
|
|
43
|
+
// Re-read symbols every rebuild so a runtime theme/preset switch refreshes the outline.
|
|
44
|
+
this.#box.setBorder({ chars: theme.boxRound, color: t => theme.fg("borderMuted", t) });
|
|
45
45
|
|
|
46
46
|
const details = this.message.details;
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
);
|
|
47
|
+
const name = details?.name?.trim() || "unknown";
|
|
48
|
+
// Collapse args to one line: a stray newline/tab in user-supplied args would split the header.
|
|
49
|
+
const args = details?.args?.replace(/\s+/g, " ").trim() ?? "";
|
|
50
|
+
|
|
51
|
+
// Header: icon-tag + skill name, with the invocation args trailing dimmed.
|
|
52
|
+
const tag = theme.fg("customMessageLabel", theme.bold(`${theme.icon.extensionSkill} skill`));
|
|
53
|
+
let header = `${tag} ${theme.fg("customMessageText", theme.bold(name))}`;
|
|
54
|
+
if (args) {
|
|
55
|
+
header += ` ${theme.fg("dim", args)}`;
|
|
56
|
+
}
|
|
57
|
+
this.#box.addChild(new Text(header, 0, 0));
|
|
58
|
+
|
|
59
|
+
const meta = this.#metaLine(details);
|
|
60
|
+
if (meta) {
|
|
61
|
+
this.#box.addChild(new Text(meta, 0, 0));
|
|
62
|
+
}
|
|
60
63
|
|
|
61
64
|
if (!this.#expanded) {
|
|
62
65
|
return;
|
|
@@ -68,8 +71,7 @@ export class SkillMessageComponent extends Container {
|
|
|
68
71
|
}
|
|
69
72
|
|
|
70
73
|
this.#box.addChild(new Spacer(1));
|
|
71
|
-
|
|
72
|
-
this.#box.addChild(new Text(promptHeader, 0, 0));
|
|
74
|
+
this.#box.addChild(new Text(theme.fg("muted", "prompt"), 0, 0));
|
|
73
75
|
this.#box.addChild(new Spacer(1));
|
|
74
76
|
|
|
75
77
|
this.#contentComponent = new Markdown(text, 0, 0, getMarkdownTheme(), {
|
|
@@ -78,6 +80,24 @@ export class SkillMessageComponent extends Container {
|
|
|
78
80
|
this.#box.addChild(this.#contentComponent);
|
|
79
81
|
}
|
|
80
82
|
|
|
83
|
+
/** Sub-line under the header: home-shortened (clickable) accent path ยท muted prompt size. */
|
|
84
|
+
#metaLine(details: SkillPromptDetails | undefined): string | undefined {
|
|
85
|
+
const parts: string[] = [];
|
|
86
|
+
|
|
87
|
+
const filePath = details?.path;
|
|
88
|
+
if (filePath) {
|
|
89
|
+
parts.push(fileHyperlink(filePath, theme.fg("accent", shortenPath(filePath)), { line: 1 }));
|
|
90
|
+
}
|
|
91
|
+
if (typeof details?.lineCount === "number") {
|
|
92
|
+
parts.push(theme.fg("muted", `${details.lineCount} ${details.lineCount === 1 ? "line" : "lines"}`));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (parts.length === 0) {
|
|
96
|
+
return undefined;
|
|
97
|
+
}
|
|
98
|
+
return ` ${parts.join(theme.fg("muted", theme.sep.dot))}`;
|
|
99
|
+
}
|
|
100
|
+
|
|
81
101
|
#extractText(): string {
|
|
82
102
|
if (typeof this.message.content === "string") {
|
|
83
103
|
return this.message.content;
|
|
@@ -74,7 +74,7 @@ export class TinyTitleDownloadProgressComponent implements Component {
|
|
|
74
74
|
render(width: number): readonly string[] {
|
|
75
75
|
width = Math.max(1, width);
|
|
76
76
|
const spec = getTinyTitleModelSpec(this.#modelKey);
|
|
77
|
-
const border = theme.fg("border", theme.
|
|
77
|
+
const border = theme.fg("border", theme.boxRound.horizontal.repeat(width));
|
|
78
78
|
const status = statusLabel(this.#event);
|
|
79
79
|
const file = currentFile(this.#event);
|
|
80
80
|
const pct =
|
|
@@ -308,7 +308,7 @@ export class WelcomeComponent implements Component {
|
|
|
308
308
|
}
|
|
309
309
|
// Bottom border
|
|
310
310
|
if (showRightColumn) {
|
|
311
|
-
lines.push(bl + h.repeat(leftCol) + theme.fg("dim", theme.
|
|
311
|
+
lines.push(bl + h.repeat(leftCol) + theme.fg("dim", theme.boxRound.teeUp) + h.repeat(rightCol) + br);
|
|
312
312
|
} else {
|
|
313
313
|
lines.push(bl + h.repeat(leftCol) + br);
|
|
314
314
|
}
|
|
@@ -38,6 +38,7 @@ import { buildHotkeysMarkdown } from "../../modes/utils/hotkeys-markdown";
|
|
|
38
38
|
import { buildToolsMarkdown } from "../../modes/utils/tools-markdown";
|
|
39
39
|
import type { AsyncJobSnapshotItem } from "../../session/agent-session";
|
|
40
40
|
import type { AuthStorage, OAuthAccountIdentity } from "../../session/auth-storage";
|
|
41
|
+
import type { CompactMode } from "../../session/compact-modes";
|
|
41
42
|
import type { NewSessionOptions } from "../../session/session-entries";
|
|
42
43
|
import { formatShakeSummary, type ShakeMode, type ShakeResult } from "../../session/shake-types";
|
|
43
44
|
import { limitMatchesActiveAccount } from "../../slash-commands/helpers/active-oauth-account";
|
|
@@ -1034,6 +1035,7 @@ export class CommandController {
|
|
|
1034
1035
|
|
|
1035
1036
|
async handleCompactCommand(
|
|
1036
1037
|
customInstructions?: string,
|
|
1038
|
+
mode?: CompactMode,
|
|
1037
1039
|
beforeFlush?: (outcome: CompactionOutcome) => void | Promise<void>,
|
|
1038
1040
|
): Promise<CompactionOutcome> {
|
|
1039
1041
|
const entries = this.ctx.sessionManager.getEntries();
|
|
@@ -1044,7 +1046,7 @@ export class CommandController {
|
|
|
1044
1046
|
return "ok";
|
|
1045
1047
|
}
|
|
1046
1048
|
|
|
1047
|
-
return this.executeCompaction(customInstructions, false, beforeFlush);
|
|
1049
|
+
return this.executeCompaction(customInstructions, false, beforeFlush, mode);
|
|
1048
1050
|
}
|
|
1049
1051
|
|
|
1050
1052
|
/**
|
|
@@ -1090,6 +1092,7 @@ export class CommandController {
|
|
|
1090
1092
|
customInstructionsOrOptions?: string | CompactOptions,
|
|
1091
1093
|
isAuto = false,
|
|
1092
1094
|
beforeFlush?: (outcome: CompactionOutcome) => void | Promise<void>,
|
|
1095
|
+
mode?: CompactMode,
|
|
1093
1096
|
): Promise<CompactionOutcome> {
|
|
1094
1097
|
if (this.ctx.loadingAnimation) {
|
|
1095
1098
|
this.ctx.loadingAnimation.stop();
|
|
@@ -1111,10 +1114,17 @@ export class CommandController {
|
|
|
1111
1114
|
let outcome: CompactionOutcome = "ok";
|
|
1112
1115
|
try {
|
|
1113
1116
|
const instructions = typeof customInstructionsOrOptions === "string" ? customInstructionsOrOptions : undefined;
|
|
1114
|
-
const
|
|
1117
|
+
const baseOptions =
|
|
1115
1118
|
customInstructionsOrOptions && typeof customInstructionsOrOptions === "object"
|
|
1116
1119
|
? customInstructionsOrOptions
|
|
1117
1120
|
: undefined;
|
|
1121
|
+
// The slash path passes `mode` positionally; the extension path carries
|
|
1122
|
+
// it inside the options object. Either source wins over no mode.
|
|
1123
|
+
const effectiveMode = mode ?? baseOptions?.mode;
|
|
1124
|
+
const options =
|
|
1125
|
+
baseOptions || effectiveMode
|
|
1126
|
+
? { ...baseOptions, ...(effectiveMode ? { mode: effectiveMode } : {}) }
|
|
1127
|
+
: undefined;
|
|
1118
1128
|
await this.ctx.session.compact(instructions, options);
|
|
1119
1129
|
|
|
1120
1130
|
compactingLoader.stop();
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { INTENT_FIELD } from "@oh-my-pi/pi-agent-core";
|
|
2
1
|
import type { ImageContent } from "@oh-my-pi/pi-ai";
|
|
3
2
|
import { type Component, Loader, TERMINAL } from "@oh-my-pi/pi-tui";
|
|
3
|
+
import { INTENT_FIELD } from "@oh-my-pi/pi-wire";
|
|
4
4
|
import { extractTextContent } from "../../commit/utils";
|
|
5
5
|
import { settings } from "../../config/settings";
|
|
6
6
|
import { getFileSnapshotStore } from "../../edit/file-snapshot-store";
|
|
7
7
|
import { AssistantMessageComponent } from "../../modes/components/assistant-message";
|
|
8
|
+
import { detectCacheInvalidation } from "../../modes/components/cache-invalidation-marker";
|
|
8
9
|
import {
|
|
9
10
|
ReadToolGroupComponent,
|
|
10
11
|
readArgsHaveTarget,
|
|
@@ -659,6 +660,16 @@ export class EventController {
|
|
|
659
660
|
// waiting poll cannot be displaced anymore โ freeze it in place.
|
|
660
661
|
this.#resolveDisplaceablePoll();
|
|
661
662
|
}
|
|
663
|
+
// Surface a prompt-cache invalidation: if the previous turn cached a
|
|
664
|
+
// meaningful prefix and this request read none of it back, flag the turn.
|
|
665
|
+
const usage = event.message.usage;
|
|
666
|
+
if (usage.cacheRead + usage.cacheWrite + usage.input > 0) {
|
|
667
|
+
if (settings.get("display.cacheMissMarker")) {
|
|
668
|
+
const invalidation = detectCacheInvalidation(this.ctx.lastAssistantUsage, usage);
|
|
669
|
+
if (invalidation) this.ctx.streamingComponent.setCacheInvalidation(invalidation);
|
|
670
|
+
}
|
|
671
|
+
this.ctx.lastAssistantUsage = usage;
|
|
672
|
+
}
|
|
662
673
|
this.#lastAssistantComponent = this.ctx.streamingComponent;
|
|
663
674
|
this.#lastAssistantComponent.markTranscriptBlockFinalized();
|
|
664
675
|
if (settings.get("display.showTokenUsage")) {
|
|
@@ -969,12 +980,14 @@ export class EventController {
|
|
|
969
980
|
}
|
|
970
981
|
this.ctx.showWarning(event.errorMessage);
|
|
971
982
|
} else if (!event.skipped) {
|
|
983
|
+
this.ctx.lastAssistantUsage = undefined;
|
|
972
984
|
this.ctx.rebuildChatFromMessages();
|
|
973
985
|
this.ctx.statusLine.invalidate();
|
|
974
986
|
this.ctx.updateEditorTopBorder();
|
|
975
987
|
this.ctx.showStatus("Auto-shake completed");
|
|
976
988
|
}
|
|
977
989
|
} else if (event.result) {
|
|
990
|
+
this.ctx.lastAssistantUsage = undefined;
|
|
978
991
|
this.ctx.rebuildChatFromMessages();
|
|
979
992
|
this.ctx.statusLine.invalidate();
|
|
980
993
|
this.ctx.updateEditorTopBorder();
|
|
@@ -982,6 +995,7 @@ export class EventController {
|
|
|
982
995
|
this.ctx.showWarning(event.errorMessage);
|
|
983
996
|
} else if (isHandoffAction) {
|
|
984
997
|
this.ctx.chatContainer.clear();
|
|
998
|
+
this.ctx.lastAssistantUsage = undefined;
|
|
985
999
|
this.ctx.rebuildChatFromMessages();
|
|
986
1000
|
this.ctx.statusLine.invalidate();
|
|
987
1001
|
this.ctx.updateEditorTopBorder();
|
|
@@ -1529,7 +1529,6 @@ export class InputController {
|
|
|
1529
1529
|
for (const child of this.ctx.chatContainer.children) {
|
|
1530
1530
|
if (child instanceof AssistantMessageComponent) {
|
|
1531
1531
|
child.setHideThinkingBlock(this.ctx.hideThinkingBlock);
|
|
1532
|
-
child.invalidate();
|
|
1533
1532
|
}
|
|
1534
1533
|
}
|
|
1535
1534
|
|
|
@@ -1538,6 +1537,14 @@ export class InputController {
|
|
|
1538
1537
|
this.ctx.streamingComponent.updateContent(this.ctx.streamingMessage);
|
|
1539
1538
|
}
|
|
1540
1539
|
|
|
1540
|
+
// Every block now carries the new flag, but on ED3-risk terminals the
|
|
1541
|
+
// blocks that scrolled past the live region are frozen snapshots in
|
|
1542
|
+
// committed scrollback โ a plain repaint replays them stale, so scrolling
|
|
1543
|
+
// up still shows the old thinking expanded. resetDisplay() retires those
|
|
1544
|
+
// snapshots (it invalidates every block) and forces a full clear + replay
|
|
1545
|
+
// of the whole transcript, matching setToolsExpanded()'s redraw.
|
|
1546
|
+
this.ctx.ui.resetDisplay();
|
|
1547
|
+
|
|
1541
1548
|
this.ctx.showStatus(`Thinking blocks: ${this.ctx.hideThinkingBlock ? "hidden" : "visible"}`);
|
|
1542
1549
|
}
|
|
1543
1550
|
|
|
@@ -320,9 +320,19 @@ export class SelectorController {
|
|
|
320
320
|
for (const child of this.ctx.chatContainer.children) {
|
|
321
321
|
if (child instanceof AssistantMessageComponent) {
|
|
322
322
|
child.setHideThinkingBlock(value as boolean);
|
|
323
|
-
child.invalidate();
|
|
324
323
|
}
|
|
325
324
|
}
|
|
325
|
+
// Full clear + replay so blocks frozen in committed scrollback on
|
|
326
|
+
// ED3-risk terminals retire their stale snapshots too (see
|
|
327
|
+
// InputController.toggleThinkingBlockVisibility).
|
|
328
|
+
this.ctx.ui.resetDisplay();
|
|
329
|
+
break;
|
|
330
|
+
case "display.cacheMissMarker":
|
|
331
|
+
// Rebuild re-runs the usage-based detection under the new setting so
|
|
332
|
+
// markers appear/disappear; full reset retires any already committed
|
|
333
|
+
// to native scrollback (mirrors hideThinking).
|
|
334
|
+
this.ctx.rebuildChatFromMessages();
|
|
335
|
+
this.ctx.ui.resetDisplay();
|
|
326
336
|
break;
|
|
327
337
|
case "tui.tight":
|
|
328
338
|
setTuiTight(value as boolean);
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
ThinkingLevel,
|
|
13
13
|
} from "@oh-my-pi/pi-agent-core";
|
|
14
14
|
import type { CompactionOutcome } from "@oh-my-pi/pi-agent-core/compaction";
|
|
15
|
-
import type { AssistantMessage, ImageContent, Message, Model, UsageReport } from "@oh-my-pi/pi-ai";
|
|
15
|
+
import type { AssistantMessage, ImageContent, Message, Model, Usage, UsageReport } from "@oh-my-pi/pi-ai";
|
|
16
16
|
import { modelsAreEqual } from "@oh-my-pi/pi-catalog/models";
|
|
17
17
|
import type {
|
|
18
18
|
Component,
|
|
@@ -82,6 +82,7 @@ import planModeCompactInstructionsPrompt from "../prompts/system/plan-mode-compa
|
|
|
82
82
|
type: "text",
|
|
83
83
|
};
|
|
84
84
|
import type { AgentSession, AgentSessionEvent, ResolvedRoleModel } from "../session/agent-session";
|
|
85
|
+
import type { CompactMode } from "../session/compact-modes";
|
|
85
86
|
import { HistoryStorage } from "../session/history-storage";
|
|
86
87
|
import type { SessionContext } from "../session/session-context";
|
|
87
88
|
import { getRecentSessions } from "../session/session-listing";
|
|
@@ -411,6 +412,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
411
412
|
isPythonMode = false;
|
|
412
413
|
streamingComponent: AssistantMessageComponent | undefined = undefined;
|
|
413
414
|
streamingMessage: AssistantMessage | undefined = undefined;
|
|
415
|
+
lastAssistantUsage: Usage | undefined = undefined;
|
|
414
416
|
loadingAnimation: Loader | undefined = undefined;
|
|
415
417
|
autoCompactionLoader: Loader | undefined = undefined;
|
|
416
418
|
retryLoader: Loader | undefined = undefined;
|
|
@@ -511,6 +513,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
511
513
|
this.compactionQueuedMessages = [];
|
|
512
514
|
this.streamingComponent = undefined;
|
|
513
515
|
this.streamingMessage = undefined;
|
|
516
|
+
this.lastAssistantUsage = undefined;
|
|
514
517
|
this.pendingTools.clear();
|
|
515
518
|
}
|
|
516
519
|
readonly #uiHelpers: UiHelpers;
|
|
@@ -1857,6 +1860,9 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1857
1860
|
this.#planModePreviousTools = previousTools;
|
|
1858
1861
|
this.planModePlanFilePath = planFilePath;
|
|
1859
1862
|
this.planModeEnabled = true;
|
|
1863
|
+
// Suppress cache-miss marker on the next turn: plan mode changes the system
|
|
1864
|
+
// prompt, which predictably invalidates the cache.
|
|
1865
|
+
this.lastAssistantUsage = undefined;
|
|
1860
1866
|
|
|
1861
1867
|
await this.session.setActiveToolsByName(uniquePlanTools);
|
|
1862
1868
|
this.session.setPlanModeState({
|
|
@@ -1974,6 +1980,9 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1974
1980
|
this.session.setStandingResolveHandler?.(null);
|
|
1975
1981
|
this.session.setPlanModeState(undefined);
|
|
1976
1982
|
this.planModeEnabled = false;
|
|
1983
|
+
// Suppress cache-miss marker on the next turn: plan exit changes the system
|
|
1984
|
+
// prompt, which predictably invalidates the cache.
|
|
1985
|
+
this.lastAssistantUsage = undefined;
|
|
1977
1986
|
this.planModePaused = options?.paused ?? false;
|
|
1978
1987
|
this.planModePlanFilePath = undefined;
|
|
1979
1988
|
this.#planModePreviousTools = undefined;
|
|
@@ -2350,7 +2359,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
2350
2359
|
// the try/finally is idempotent and kept for the !compactBeforeExecute
|
|
2351
2360
|
// branch.
|
|
2352
2361
|
this.session.setPlanReferencePath(options.planFilePath);
|
|
2353
|
-
compactOutcome = await this.handleCompactCommand(compactionPrompt, outcome =>
|
|
2362
|
+
compactOutcome = await this.handleCompactCommand(compactionPrompt, undefined, outcome =>
|
|
2354
2363
|
this.#applyDeferredPlanModelTransition(outcome, options.executionModel),
|
|
2355
2364
|
);
|
|
2356
2365
|
}
|
|
@@ -3616,9 +3625,10 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
3616
3625
|
|
|
3617
3626
|
handleCompactCommand(
|
|
3618
3627
|
customInstructions?: string,
|
|
3628
|
+
mode?: CompactMode,
|
|
3619
3629
|
beforeFlush?: (outcome: CompactionOutcome) => void | Promise<void>,
|
|
3620
3630
|
): Promise<CompactionOutcome> {
|
|
3621
|
-
return this.#commandController.handleCompactCommand(customInstructions, beforeFlush);
|
|
3631
|
+
return this.#commandController.handleCompactCommand(customInstructions, mode, beforeFlush);
|
|
3622
3632
|
}
|
|
3623
3633
|
|
|
3624
3634
|
handleHandoffCommand(customInstructions?: string): Promise<void> {
|
package/src/modes/theme/theme.ts
CHANGED
|
@@ -111,6 +111,7 @@ export type SymbolKey =
|
|
|
111
111
|
| "icon.agents"
|
|
112
112
|
| "icon.job"
|
|
113
113
|
| "icon.cache"
|
|
114
|
+
| "icon.cacheMiss"
|
|
114
115
|
| "icon.input"
|
|
115
116
|
| "icon.output"
|
|
116
117
|
| "icon.host"
|
|
@@ -310,6 +311,7 @@ const UNICODE_SYMBOLS: SymbolMap = {
|
|
|
310
311
|
"icon.agents": "๐ฅ",
|
|
311
312
|
"icon.job": "โ",
|
|
312
313
|
"icon.cache": "๐พ",
|
|
314
|
+
"icon.cacheMiss": "โ",
|
|
313
315
|
"icon.input": "โคต",
|
|
314
316
|
"icon.output": "โคด",
|
|
315
317
|
"icon.host": "๐ฅ",
|
|
@@ -579,6 +581,8 @@ const NERD_SYMBOLS: SymbolMap = {
|
|
|
579
581
|
"icon.job": "\uf013",
|
|
580
582
|
// pick: ๏ | alt: ๏ ๏
|
|
581
583
|
"icon.cache": "\uf1c0",
|
|
584
|
+
// pick: (fa-ban) | alt: โ
|
|
585
|
+
"icon.cacheMiss": "\uf05e",
|
|
582
586
|
// pick: ๏ | alt: ๏ก โ
|
|
583
587
|
"icon.input": "\uf090",
|
|
584
588
|
// pick: ๏ | alt: ๏ก โ
|
|
@@ -810,6 +814,7 @@ const ASCII_SYMBOLS: SymbolMap = {
|
|
|
810
814
|
"icon.agents": "AG",
|
|
811
815
|
"icon.job": "bg",
|
|
812
816
|
"icon.cache": "cache",
|
|
817
|
+
"icon.cacheMiss": "!",
|
|
813
818
|
"icon.input": "in:",
|
|
814
819
|
"icon.output": "out:",
|
|
815
820
|
"icon.host": "host",
|
|
@@ -1711,6 +1716,14 @@ export class Theme {
|
|
|
1711
1716
|
bottomRight: this.#symbols["boxRound.bottomRight"],
|
|
1712
1717
|
horizontal: this.#symbols["boxRound.horizontal"],
|
|
1713
1718
|
vertical: this.#symbols["boxRound.vertical"],
|
|
1719
|
+
// Junctions have no rounded Unicode variant, so a rounded box reuses the
|
|
1720
|
+
// sharp tee/cross glyphs. Sourcing them from the boxSharp.* tokens keeps a
|
|
1721
|
+
// theme's `boxSharp.tee*` overrides effective for rounded-box dividers.
|
|
1722
|
+
cross: this.#symbols["boxSharp.cross"],
|
|
1723
|
+
teeDown: this.#symbols["boxSharp.teeDown"],
|
|
1724
|
+
teeUp: this.#symbols["boxSharp.teeUp"],
|
|
1725
|
+
teeRight: this.#symbols["boxSharp.teeRight"],
|
|
1726
|
+
teeLeft: this.#symbols["boxSharp.teeLeft"],
|
|
1714
1727
|
};
|
|
1715
1728
|
}
|
|
1716
1729
|
|
|
@@ -1770,6 +1783,7 @@ export class Theme {
|
|
|
1770
1783
|
agents: this.#symbols["icon.agents"],
|
|
1771
1784
|
job: this.#symbols["icon.job"],
|
|
1772
1785
|
cache: this.#symbols["icon.cache"],
|
|
1786
|
+
cacheMiss: this.#symbols["icon.cacheMiss"],
|
|
1773
1787
|
input: this.#symbols["icon.input"],
|
|
1774
1788
|
output: this.#symbols["icon.output"],
|
|
1775
1789
|
host: this.#symbols["icon.host"],
|
package/src/modes/types.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { AgentMessage } from "@oh-my-pi/pi-agent-core";
|
|
2
2
|
import type { CompactionOutcome } from "@oh-my-pi/pi-agent-core/compaction";
|
|
3
|
-
import type { AssistantMessage, ImageContent, Message, UsageReport } from "@oh-my-pi/pi-ai";
|
|
3
|
+
import type { AssistantMessage, ImageContent, Message, Usage, UsageReport } from "@oh-my-pi/pi-ai";
|
|
4
4
|
import type { Component, Container, EditorTheme, Loader, Spacer, Text, TUI } from "@oh-my-pi/pi-tui";
|
|
5
5
|
import type { CollabGuestLink } from "../collab/guest";
|
|
6
6
|
import type { CollabHost } from "../collab/host";
|
|
@@ -17,6 +17,7 @@ import type { CompactOptions } from "../extensibility/extensions/types";
|
|
|
17
17
|
import type { MCPManager } from "../mcp";
|
|
18
18
|
import type { PlanApprovalDetails } from "../plan-mode/approved-plan";
|
|
19
19
|
import type { AgentSession } from "../session/agent-session";
|
|
20
|
+
import type { CompactMode } from "../session/compact-modes";
|
|
20
21
|
import type { HistoryStorage } from "../session/history-storage";
|
|
21
22
|
import type { SessionContext } from "../session/session-context";
|
|
22
23
|
import type { SessionManager } from "../session/session-manager";
|
|
@@ -158,6 +159,12 @@ export interface InteractiveModeContext {
|
|
|
158
159
|
isPythonMode: boolean;
|
|
159
160
|
streamingComponent: AssistantMessageComponent | undefined;
|
|
160
161
|
streamingMessage: AssistantMessage | undefined;
|
|
162
|
+
/**
|
|
163
|
+
* Usage of the most recently rendered assistant turn, used to detect a
|
|
164
|
+
* prompt-cache invalidation on the next turn (cache footprint collapse).
|
|
165
|
+
* Reseeded by `renderSessionContext` on every rebuild/session switch.
|
|
166
|
+
*/
|
|
167
|
+
lastAssistantUsage: Usage | undefined;
|
|
161
168
|
loadingAnimation: Loader | undefined;
|
|
162
169
|
autoCompactionLoader: Loader | undefined;
|
|
163
170
|
retryLoader: Loader | undefined;
|
|
@@ -293,7 +300,7 @@ export interface InteractiveModeContext {
|
|
|
293
300
|
handlePythonCommand(code: string, excludeFromContext?: boolean): Promise<void>;
|
|
294
301
|
handleMCPCommand(text: string): Promise<void>;
|
|
295
302
|
handleSSHCommand(text: string): Promise<void>;
|
|
296
|
-
handleCompactCommand(customInstructions?: string): Promise<CompactionOutcome>;
|
|
303
|
+
handleCompactCommand(customInstructions?: string, mode?: CompactMode): Promise<CompactionOutcome>;
|
|
297
304
|
handleHandoffCommand(customInstructions?: string): Promise<void>;
|
|
298
305
|
handleShakeCommand(mode: ShakeMode): Promise<void>;
|
|
299
306
|
handleMoveCommand(targetPath: string): Promise<void>;
|