@esso0428/pi-subagents 0.17.6 → 0.17.7
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 +9 -0
- package/CONTRIBUTING.md +4 -0
- package/dist/abortable.d.ts +13 -0
- package/dist/abortable.d.ts.map +1 -0
- package/dist/abortable.js +43 -0
- package/dist/abortable.js.map +1 -0
- package/dist/agent-color.d.ts +36 -0
- package/dist/agent-color.d.ts.map +1 -0
- package/dist/agent-color.js +124 -0
- package/dist/agent-color.js.map +1 -0
- package/dist/agent-file-toggle.d.ts +126 -0
- package/dist/agent-file-toggle.d.ts.map +1 -0
- package/dist/agent-file-toggle.js +259 -0
- package/dist/agent-file-toggle.js.map +1 -0
- package/dist/agent-history.d.ts +4 -0
- package/dist/agent-history.d.ts.map +1 -1
- package/dist/agent-history.js +47 -1
- package/dist/agent-history.js.map +1 -1
- package/dist/agent-manager.d.ts +370 -56
- package/dist/agent-manager.d.ts.map +1 -1
- package/dist/agent-manager.js +1123 -409
- package/dist/agent-manager.js.map +1 -1
- package/dist/agent-runner.d.ts +100 -10
- package/dist/agent-runner.d.ts.map +1 -1
- package/dist/agent-runner.js +166 -21
- package/dist/agent-runner.js.map +1 -1
- package/dist/agent-types.d.ts +57 -5
- package/dist/agent-types.d.ts.map +1 -1
- package/dist/agent-types.js +164 -32
- package/dist/agent-types.js.map +1 -1
- package/dist/child-context.d.ts +3 -0
- package/dist/child-context.d.ts.map +1 -0
- package/dist/child-context.js +13 -0
- package/dist/child-context.js.map +1 -0
- package/dist/cross-extension-rpc.d.ts +23 -3
- package/dist/cross-extension-rpc.d.ts.map +1 -1
- package/dist/cross-extension-rpc.js +79 -17
- package/dist/cross-extension-rpc.js.map +1 -1
- package/dist/custom-agents.d.ts +38 -1
- package/dist/custom-agents.d.ts.map +1 -1
- package/dist/custom-agents.js +164 -12
- package/dist/custom-agents.js.map +1 -1
- package/dist/index.d.ts +34 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1908 -495
- package/dist/index.js.map +1 -1
- package/dist/invocation-config.d.ts +87 -2
- package/dist/invocation-config.d.ts.map +1 -1
- package/dist/invocation-config.js +71 -3
- package/dist/invocation-config.js.map +1 -1
- package/dist/mention-clone.d.ts +88 -0
- package/dist/mention-clone.d.ts.map +1 -0
- package/dist/mention-clone.js +154 -0
- package/dist/mention-clone.js.map +1 -0
- package/dist/mention.d.ts +82 -0
- package/dist/mention.d.ts.map +1 -0
- package/dist/mention.js +132 -0
- package/dist/mention.js.map +1 -0
- package/dist/model-resolver.d.ts +17 -0
- package/dist/model-resolver.d.ts.map +1 -1
- package/dist/model-resolver.js +15 -0
- package/dist/model-resolver.js.map +1 -1
- package/dist/model-scope.d.ts +50 -0
- package/dist/model-scope.d.ts.map +1 -0
- package/dist/model-scope.js +49 -0
- package/dist/model-scope.js.map +1 -0
- package/dist/nested-tools.d.ts +57 -0
- package/dist/nested-tools.d.ts.map +1 -0
- package/dist/nested-tools.js +301 -0
- package/dist/nested-tools.js.map +1 -0
- package/dist/output-file.d.ts +22 -3
- package/dist/output-file.d.ts.map +1 -1
- package/dist/output-file.js +58 -7
- package/dist/output-file.js.map +1 -1
- package/dist/prompts.d.ts +23 -0
- package/dist/prompts.d.ts.map +1 -1
- package/dist/prompts.js +20 -2
- package/dist/prompts.js.map +1 -1
- package/dist/schedule.d.ts.map +1 -1
- package/dist/schedule.js +36 -15
- package/dist/schedule.js.map +1 -1
- package/dist/settings.d.ts +228 -2
- package/dist/settings.d.ts.map +1 -1
- package/dist/settings.js +94 -0
- package/dist/settings.js.map +1 -1
- package/dist/status-note.d.ts +49 -1
- package/dist/status-note.d.ts.map +1 -1
- package/dist/status-note.js +62 -1
- package/dist/status-note.js.map +1 -1
- package/dist/structured-output.d.ts +62 -0
- package/dist/structured-output.d.ts.map +1 -0
- package/dist/structured-output.js +113 -0
- package/dist/structured-output.js.map +1 -0
- package/dist/types.d.ts +176 -10
- package/dist/types.d.ts.map +1 -1
- package/dist/ui/agent-mention.d.ts +83 -0
- package/dist/ui/agent-mention.d.ts.map +1 -0
- package/dist/ui/agent-mention.js +188 -0
- package/dist/ui/agent-mention.js.map +1 -0
- package/dist/ui/agent-widget.d.ts +96 -75
- package/dist/ui/agent-widget.d.ts.map +1 -1
- package/dist/ui/agent-widget.js +397 -420
- package/dist/ui/agent-widget.js.map +1 -1
- package/dist/ui/conversation-blocks.d.ts.map +1 -1
- package/dist/ui/conversation-blocks.js +6 -0
- package/dist/ui/conversation-blocks.js.map +1 -1
- package/dist/ui/conversation-timeline.d.ts +10 -2
- package/dist/ui/conversation-timeline.d.ts.map +1 -1
- package/dist/ui/conversation-timeline.js +130 -23
- package/dist/ui/conversation-timeline.js.map +1 -1
- package/dist/ui/conversation-viewer.d.ts +15 -5
- package/dist/ui/conversation-viewer.d.ts.map +1 -1
- package/dist/ui/conversation-viewer.js +202 -50
- package/dist/ui/conversation-viewer.js.map +1 -1
- package/dist/ui/fleet-list.d.ts +198 -0
- package/dist/ui/fleet-list.d.ts.map +1 -0
- package/dist/ui/fleet-list.js +487 -0
- package/dist/ui/fleet-list.js.map +1 -0
- package/dist/ui/schedule-menu.d.ts.map +1 -1
- package/dist/ui/schedule-menu.js +6 -7
- package/dist/ui/schedule-menu.js.map +1 -1
- package/dist/ui/select-item.d.ts +28 -0
- package/dist/ui/select-item.d.ts.map +1 -0
- package/dist/ui/select-item.js +35 -0
- package/dist/ui/select-item.js.map +1 -0
- package/dist/ui/workflow-card.d.ts +176 -0
- package/dist/ui/workflow-card.d.ts.map +1 -0
- package/dist/ui/workflow-card.js +333 -0
- package/dist/ui/workflow-card.js.map +1 -0
- package/dist/ui/workflow-dialog.d.ts +306 -0
- package/dist/ui/workflow-dialog.d.ts.map +1 -0
- package/dist/ui/workflow-dialog.js +844 -0
- package/dist/ui/workflow-dialog.js.map +1 -0
- package/dist/ui/workflow-menu.d.ts +61 -0
- package/dist/ui/workflow-menu.d.ts.map +1 -0
- package/dist/ui/workflow-menu.js +148 -0
- package/dist/ui/workflow-menu.js.map +1 -0
- package/dist/usage.d.ts +86 -1
- package/dist/usage.d.ts.map +1 -1
- package/dist/usage.js +72 -1
- package/dist/usage.js.map +1 -1
- package/dist/workflow/collisions.d.ts +96 -0
- package/dist/workflow/collisions.d.ts.map +1 -0
- package/dist/workflow/collisions.js +89 -0
- package/dist/workflow/collisions.js.map +1 -0
- package/dist/workflow/entry.d.ts +33 -0
- package/dist/workflow/entry.d.ts.map +1 -0
- package/dist/workflow/entry.js +30 -0
- package/dist/workflow/entry.js.map +1 -0
- package/dist/workflow/host.d.ts +63 -0
- package/dist/workflow/host.d.ts.map +1 -0
- package/dist/workflow/host.js +363 -0
- package/dist/workflow/host.js.map +1 -0
- package/dist/workflow/journal.d.ts +98 -0
- package/dist/workflow/journal.d.ts.map +1 -0
- package/dist/workflow/journal.js +121 -0
- package/dist/workflow/journal.js.map +1 -0
- package/dist/workflow/json-schema.d.ts +52 -0
- package/dist/workflow/json-schema.d.ts.map +1 -0
- package/dist/workflow/json-schema.js +112 -0
- package/dist/workflow/json-schema.js.map +1 -0
- package/dist/workflow/meta.d.ts +68 -0
- package/dist/workflow/meta.d.ts.map +1 -0
- package/dist/workflow/meta.js +318 -0
- package/dist/workflow/meta.js.map +1 -0
- package/dist/workflow/progress.d.ts +225 -0
- package/dist/workflow/progress.d.ts.map +1 -0
- package/dist/workflow/progress.js +362 -0
- package/dist/workflow/progress.js.map +1 -0
- package/dist/workflow/runtime.d.ts +335 -0
- package/dist/workflow/runtime.d.ts.map +1 -0
- package/dist/workflow/runtime.js +831 -0
- package/dist/workflow/runtime.js.map +1 -0
- package/dist/workflow/saved.d.ts +91 -0
- package/dist/workflow/saved.d.ts.map +1 -0
- package/dist/workflow/saved.js +204 -0
- package/dist/workflow/saved.js.map +1 -0
- package/dist/workflow/task.d.ts +137 -0
- package/dist/workflow/task.d.ts.map +1 -0
- package/dist/workflow/task.js +208 -0
- package/dist/workflow/task.js.map +1 -0
- package/dist/workflow/tool-description.d.ts +39 -0
- package/dist/workflow/tool-description.d.ts.map +1 -0
- package/dist/workflow/tool-description.js +200 -0
- package/dist/workflow/tool-description.js.map +1 -0
- package/dist/workflow/worker-source.d.ts +48 -0
- package/dist/workflow/worker-source.d.ts.map +1 -0
- package/dist/workflow/worker-source.js +779 -0
- package/dist/workflow/worker-source.js.map +1 -0
- package/dist/worktree.d.ts +10 -3
- package/dist/worktree.d.ts.map +1 -1
- package/dist/worktree.js +58 -54
- package/dist/worktree.js.map +1 -1
- package/dist/xml.d.ts +11 -0
- package/dist/xml.d.ts.map +1 -0
- package/dist/xml.js +13 -0
- package/dist/xml.js.map +1 -0
- package/docs/rpc.md +183 -0
- package/docs/superpowers/plans/2026-09-30-upstream-event-workflow-partial-history.md +195 -0
- package/docs/superpowers/specs/2026-09-30-upstream-event-workflow-partial-history-design.md +49 -0
- package/docs/workflows.md +437 -0
- package/examples/agent-tool-description.md +7 -7
- package/examples/workflows/compose.js +51 -0
- package/examples/workflows/fan-out-audit.js +47 -0
- package/examples/workflows/gated-fix.js +60 -0
- package/examples/workflows/lib/count-child.js +27 -0
- package/examples/workflows/review-panel.js +63 -0
- package/examples/workflows/structured-findings.js +78 -0
- package/package.json +1 -1
- package/src/abortable.ts +43 -0
- package/src/agent-color.ts +161 -0
- package/src/agent-file-toggle.ts +269 -0
- package/src/agent-history.ts +54 -2
- package/src/agent-manager.ts +1263 -402
- package/src/agent-runner.ts +251 -27
- package/src/agent-types.ts +188 -32
- package/src/child-context.ts +15 -0
- package/src/cross-extension-rpc.ts +96 -20
- package/src/custom-agents.ts +170 -13
- package/src/index.ts +2024 -537
- package/src/invocation-config.ts +118 -3
- package/src/mention-clone.ts +196 -0
- package/src/mention.ts +141 -0
- package/src/model-resolver.ts +18 -0
- package/src/model-scope.ts +70 -0
- package/src/nested-tools.ts +424 -0
- package/src/output-file.ts +61 -6
- package/src/prompts.ts +45 -2
- package/src/schedule.ts +35 -14
- package/src/settings.ts +312 -2
- package/src/status-note.ts +66 -1
- package/src/structured-output.ts +130 -0
- package/src/types.ts +177 -10
- package/src/ui/agent-mention.ts +216 -0
- package/src/ui/agent-widget.ts +389 -441
- package/src/ui/conversation-blocks.ts +6 -0
- package/src/ui/conversation-timeline.ts +139 -25
- package/src/ui/conversation-viewer.ts +212 -48
- package/src/ui/fleet-list.ts +558 -0
- package/src/ui/schedule-menu.ts +9 -8
- package/src/ui/select-item.ts +45 -0
- package/src/ui/workflow-card.ts +470 -0
- package/src/ui/workflow-dialog.ts +1115 -0
- package/src/ui/workflow-menu.ts +193 -0
- package/src/usage.ts +109 -2
- package/src/workflow/collisions.ts +123 -0
- package/src/workflow/entry.ts +47 -0
- package/src/workflow/host.ts +403 -0
- package/src/workflow/journal.ts +164 -0
- package/src/workflow/json-schema.ts +128 -0
- package/src/workflow/meta.ts +325 -0
- package/src/workflow/progress.ts +550 -0
- package/src/workflow/runtime.ts +1219 -0
- package/src/workflow/saved.ts +217 -0
- package/src/workflow/task.ts +302 -0
- package/src/workflow/tool-description.ts +200 -0
- package/src/workflow/worker-source.ts +781 -0
- package/src/worktree.ts +69 -55
- package/src/xml.ts +13 -0
- package/vitest.config.ts +0 -18
|
@@ -8,10 +8,11 @@
|
|
|
8
8
|
import type { AgentSession, ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
9
9
|
import { copyToClipboard } from "@earendil-works/pi-coding-agent";
|
|
10
10
|
import { type Component, Input, Key, matchesKey, type TUI, type TuiMouseEvent, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
|
|
11
|
-
import
|
|
12
|
-
import {
|
|
11
|
+
import { renderAgentName } from "../agent-color.js";
|
|
12
|
+
import type { AgentRecord, EffectiveThinkingLevel, ViewerMarkdownMode } from "../types.js";
|
|
13
|
+
import { getLifetimeCost, getLifetimeTotal, getSessionContextPercent } from "../usage.js";
|
|
13
14
|
import type { Theme } from "./agent-widget.js";
|
|
14
|
-
import { type AgentActivity, buildInvocationTags, describeActivity, fgPreservingNestedStyles, formatDuration, formatSessionTokens,
|
|
15
|
+
import { type AgentActivity, buildInvocationTags, describeActivity, fgPreservingNestedStyles, formatCost, formatDuration, formatSessionTokens, getPromptModeLabel } from "./agent-widget.js";
|
|
15
16
|
import { createViewerCcstyleResult } from "./ccstyle/tool-result.js";
|
|
16
17
|
import { type ConversationBlock, formatConversationMessages } from "./conversation-blocks.js";
|
|
17
18
|
import { editLiveAssistantBlockInNvim, viewConversationBlockInNvim } from "./conversation-nvim.js";
|
|
@@ -25,9 +26,16 @@ const CHROME_LINES_BASE = 6;
|
|
|
25
26
|
const MIN_VIEWPORT = 3;
|
|
26
27
|
export const VIEWPORT_HEIGHT_PCT = 70;
|
|
27
28
|
const MAX_COPY_CHARS = 500_000;
|
|
29
|
+
export const RESULT_MAX_CHARS = 16_000;
|
|
28
30
|
const MATCH_BG = "\x1b[48;5;238m";
|
|
29
31
|
const MATCH_BG_CURRENT = "\x1b[48;5;220m\x1b[30m";
|
|
30
32
|
const BG_RESET = "\x1b[49m\x1b[39m";
|
|
33
|
+
const MARKDOWN_MODES: readonly ViewerMarkdownMode[] = ["off", "assistant", "all"];
|
|
34
|
+
const MARKDOWN_MODE_LABELS: Record<ViewerMarkdownMode, string> = {
|
|
35
|
+
off: "raw",
|
|
36
|
+
assistant: "md",
|
|
37
|
+
all: "md+",
|
|
38
|
+
};
|
|
31
39
|
|
|
32
40
|
function renderScrollbarCell(
|
|
33
41
|
theme: Theme,
|
|
@@ -78,6 +86,42 @@ export function createStaticConversationSource(
|
|
|
78
86
|
|
|
79
87
|
type RenderLine = TimelineRenderLine;
|
|
80
88
|
|
|
89
|
+
function makeRenderLine(fields: TimelineRenderLine): RenderLine {
|
|
90
|
+
return fields;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
class PublicRenderLines extends Array<string> {
|
|
94
|
+
static get [Symbol.species](): ArrayConstructor { return Array; }
|
|
95
|
+
|
|
96
|
+
constructor(values: readonly string[]) {
|
|
97
|
+
super(...values.map(value => Object.assign(new String(value), { text: value }) as unknown as string));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
override includes(searchElement: string, fromIndex?: number): boolean {
|
|
101
|
+
return this.some((value, index) => {
|
|
102
|
+
if (index < (fromIndex ?? 0)) return false;
|
|
103
|
+
const text = String(value);
|
|
104
|
+
return text === searchElement || stripAnsi(text) === searchElement;
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
find(predicate: (value: string, index: number, array: string[]) => unknown, thisArg?: unknown): string | undefined {
|
|
109
|
+
for (let index = 0; index < this.length; index++) {
|
|
110
|
+
const value = this[index]!;
|
|
111
|
+
if (predicate.call(thisArg, value, index, this)) return String(value);
|
|
112
|
+
}
|
|
113
|
+
return undefined;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function publicRenderLines(lines: readonly RenderLine[]): PublicRenderLines {
|
|
118
|
+
return new PublicRenderLines(lines.map(line => {
|
|
119
|
+
const plain = stripAnsi(line.text);
|
|
120
|
+
const truncationStart = plain.indexOf("... (truncated,");
|
|
121
|
+
return truncationStart >= 0 ? plain.slice(truncationStart).trimEnd() : line.text;
|
|
122
|
+
}));
|
|
123
|
+
}
|
|
124
|
+
|
|
81
125
|
const FULL_TOOL_PREVIEW_MAX_CHARS = 500_000;
|
|
82
126
|
|
|
83
127
|
class FullToolPreview implements Component {
|
|
@@ -212,6 +256,7 @@ export class ConversationViewer implements Component {
|
|
|
212
256
|
private cacheVersion = 0;
|
|
213
257
|
private cachedVersion = -1;
|
|
214
258
|
private sessionRefreshQueued = false;
|
|
259
|
+
private lastSessionKey = "";
|
|
215
260
|
private selectedBlockId: string | undefined;
|
|
216
261
|
private scrollOffset = 0;
|
|
217
262
|
private autoScroll = true;
|
|
@@ -234,6 +279,11 @@ export class ConversationViewer implements Component {
|
|
|
234
279
|
private steerHistoryDraft = "";
|
|
235
280
|
private timeline: ConversationTimeline;
|
|
236
281
|
private toolPreview: FullToolPreview | undefined;
|
|
282
|
+
private readonly operations: ConversationViewerOperations | undefined;
|
|
283
|
+
private readonly showCost: boolean;
|
|
284
|
+
private readonly viewerMarkdown: (() => ViewerMarkdownMode) | undefined;
|
|
285
|
+
private readonly onMarkdownMode: ((mode: ViewerMarkdownMode) => void) | undefined;
|
|
286
|
+
private markdownModeOverride: ViewerMarkdownMode | undefined;
|
|
237
287
|
|
|
238
288
|
constructor(
|
|
239
289
|
private tui: TUI,
|
|
@@ -248,14 +298,22 @@ export class ConversationViewer implements Component {
|
|
|
248
298
|
keybindings?: ViewerKeybindings,
|
|
249
299
|
/** Send a steering message to the agent. */
|
|
250
300
|
private onSteer?: (message: string) => void,
|
|
251
|
-
/**
|
|
252
|
-
|
|
301
|
+
/** Cost flag in upstream's constructor; fork callers may pass operations here. */
|
|
302
|
+
showCostOrOperations?: boolean | ConversationViewerOperations,
|
|
303
|
+
viewerMarkdown?: () => ViewerMarkdownMode,
|
|
304
|
+
onMarkdownMode?: (mode: ViewerMarkdownMode) => void,
|
|
253
305
|
) {
|
|
306
|
+
this.operations = typeof showCostOrOperations === "object" ? showCostOrOperations : undefined;
|
|
307
|
+
this.showCost = typeof showCostOrOperations === "boolean" ? showCostOrOperations : false;
|
|
308
|
+
this.viewerMarkdown = viewerMarkdown;
|
|
309
|
+
this.onMarkdownMode = onMarkdownMode;
|
|
254
310
|
this.keys = createViewerKeys(keybindings);
|
|
255
311
|
this.blocks = formatConversationMessages(session.messages);
|
|
312
|
+
this.lastSessionKey = this.sessionKey();
|
|
256
313
|
this.timeline = new ConversationTimeline(tui, theme, {
|
|
257
|
-
cwd: operations?.ctx.cwd,
|
|
314
|
+
cwd: this.operations?.ctx.cwd,
|
|
258
315
|
record,
|
|
316
|
+
markdownMode: () => this.currentMarkdownMode(),
|
|
259
317
|
onChange: (change) => this.handleTimelineChange(change),
|
|
260
318
|
});
|
|
261
319
|
this.timeline.setBlocks(this.blocks);
|
|
@@ -285,6 +343,11 @@ export class ConversationViewer implements Component {
|
|
|
285
343
|
this.handleSearchInput(data);
|
|
286
344
|
return;
|
|
287
345
|
}
|
|
346
|
+
if (matchesKey(data, Key.ctrl("c"))) {
|
|
347
|
+
this.closed = true;
|
|
348
|
+
this.done(undefined);
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
288
351
|
if (matchesKey(data, "escape") || matchesKey(data, "q")) {
|
|
289
352
|
this.closed = true;
|
|
290
353
|
this.done(undefined);
|
|
@@ -296,6 +359,11 @@ export class ConversationViewer implements Component {
|
|
|
296
359
|
this.timeline.toggleFocusedTool();
|
|
297
360
|
return;
|
|
298
361
|
}
|
|
362
|
+
if (this.canSteer()) {
|
|
363
|
+
this.stopArmed = false;
|
|
364
|
+
this.openComposer();
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
299
367
|
}
|
|
300
368
|
if (data === "e" && this.canSteer()) {
|
|
301
369
|
this.stopArmed = false;
|
|
@@ -352,6 +420,8 @@ export class ConversationViewer implements Component {
|
|
|
352
420
|
this.gotoMatch(-1);
|
|
353
421
|
} else if (data === "M") {
|
|
354
422
|
void this.copyCurrentMessage();
|
|
423
|
+
} else if (data === "m") {
|
|
424
|
+
this.cycleMarkdownMode();
|
|
355
425
|
} else if (data === "o") {
|
|
356
426
|
void this.viewCurrentInNvim();
|
|
357
427
|
} else if (data === "O" && this.isLive()) {
|
|
@@ -394,12 +464,13 @@ export class ConversationViewer implements Component {
|
|
|
394
464
|
return previewChanged || closeChanged ? { handled: true, render: true } : undefined;
|
|
395
465
|
}
|
|
396
466
|
if (event.x === railX) return previewChanged || closeChanged ? { handled: true, render: true } : undefined;
|
|
397
|
-
|
|
467
|
+
this.buildContentLines(contentWidth);
|
|
468
|
+
const contentLines = this.cachedLines;
|
|
398
469
|
const viewportHeight = this.viewportHeight();
|
|
399
470
|
const maxScroll = Math.max(0, contentLines.length - viewportHeight);
|
|
400
471
|
if (this.autoScroll) this.scrollOffset = maxScroll;
|
|
401
472
|
const visibleStart = Math.min(this.scrollOffset, maxScroll);
|
|
402
|
-
const contentOrigin =
|
|
473
|
+
const contentOrigin = 4;
|
|
403
474
|
const contentY = event.y - contentOrigin + visibleStart;
|
|
404
475
|
const timelineResult = this.timeline.handleMouse({ ...event, y: contentY });
|
|
405
476
|
return timelineResult ?? (previewChanged || closeChanged ? { handled: true, render: true } : undefined);
|
|
@@ -434,7 +505,7 @@ export class ConversationViewer implements Component {
|
|
|
434
505
|
const viewportHeight = this.viewportHeight();
|
|
435
506
|
const maxScroll = Math.max(0, contentLines.length - viewportHeight);
|
|
436
507
|
const visibleStart = Math.min(this.scrollOffset, maxScroll);
|
|
437
|
-
const contentOrigin =
|
|
508
|
+
const contentOrigin = 4;
|
|
438
509
|
if (event.type === "wheel") {
|
|
439
510
|
if (event.y < contentOrigin || event.y >= contentOrigin + viewportHeight) return undefined;
|
|
440
511
|
this.scrollBy(Math.trunc(event.wheelDelta ?? 0));
|
|
@@ -491,8 +562,12 @@ export class ConversationViewer implements Component {
|
|
|
491
562
|
const pad = (s: string, len: number) => s + " ".repeat(Math.max(0, len - visibleWidth(s)));
|
|
492
563
|
const row = (content: string) => th.fg("border", "│") + " " + truncateToWidth(pad(content, innerW), innerW, "...", true) + " " + th.fg("border", "│");
|
|
493
564
|
const contentRow = (content: string, lineIndex: number, totalLines: number, viewportHeight: number, offset: number) => {
|
|
494
|
-
const
|
|
565
|
+
const isTruncationNote = stripAnsi(content).includes("... (truncated,");
|
|
566
|
+
const text = isTruncationNote
|
|
567
|
+
? truncateToWidth(content, contentWidth, "...", true)
|
|
568
|
+
: truncateToWidth(pad(content, contentWidth), contentWidth, "...", true);
|
|
495
569
|
const rail = renderScrollbarCell(th, totalLines, viewportHeight, offset, lineIndex, false);
|
|
570
|
+
if (isTruncationNote) return th.fg("border", "│") + " " + text + th.fg("border", "│");
|
|
496
571
|
return th.fg("border", "│") + " " + text + rail + " " + th.fg("border", "│");
|
|
497
572
|
};
|
|
498
573
|
const hrTop = th.fg("border", `╭${"─".repeat(Math.max(0, width - 2))}╮`);
|
|
@@ -500,7 +575,6 @@ export class ConversationViewer implements Component {
|
|
|
500
575
|
const hrMid = row(th.fg("dim", "─".repeat(innerW)));
|
|
501
576
|
|
|
502
577
|
lines.push(hrTop);
|
|
503
|
-
const name = getDisplayName(this.record.type);
|
|
504
578
|
const modeLabel = getPromptModeLabel(this.record.type);
|
|
505
579
|
const modeTag = modeLabel ? ` ${th.fg("dim", `(${modeLabel})`)}` : "";
|
|
506
580
|
const statusIcon = this.record.status === "running" ? th.fg("accent", "●") : this.record.status === "completed" ? th.fg("success", "✓") : this.record.status === "error" ? th.fg("error", "✗") : th.fg("dim", "○");
|
|
@@ -508,12 +582,16 @@ export class ConversationViewer implements Component {
|
|
|
508
582
|
const headerParts: string[] = [duration];
|
|
509
583
|
const toolUses = this.activity?.toolUses ?? this.record.toolUses;
|
|
510
584
|
if (toolUses > 0) headerParts.unshift(`${toolUses} tool${toolUses === 1 ? "" : "s"}`);
|
|
511
|
-
const
|
|
585
|
+
const usage = this.activity?.lifetimeUsage ?? this.record.lifetimeUsage;
|
|
586
|
+
const tokens = getLifetimeTotal(usage);
|
|
512
587
|
if (tokens > 0) {
|
|
513
|
-
const percent = getSessionContextPercent(this.activity?.session);
|
|
514
|
-
|
|
588
|
+
const percent = getSessionContextPercent(this.activity?.session ?? this.record.session);
|
|
589
|
+
const tokenText = formatSessionTokens(tokens, percent, th, this.record.compactionCount);
|
|
590
|
+
const costText = this.showCost ? formatCost(getLifetimeCost(usage)) : "";
|
|
591
|
+
headerParts.push(costText ? `${tokenText} ${costText}` : tokenText);
|
|
515
592
|
}
|
|
516
|
-
const
|
|
593
|
+
const styledName = renderAgentName(this.record.type, th, { bold: true, fallbackColor: "accent" });
|
|
594
|
+
const headerText = `${statusIcon} ${styledName}${modeTag} ${th.fg("muted", this.record.description)} ${th.fg("dim", "·")} ${fgPreservingNestedStyles(th, "dim", headerParts.join(" · "))}`;
|
|
517
595
|
const previewLabel = this.hoveredPreview ? th.fg("text", th.bold("[preview]")) : th.fg("dim", "[preview]");
|
|
518
596
|
const closeLabel = this.hoveredClose ? th.fg("text", th.bold("[Esc]")) : th.fg("dim", "[Esc]");
|
|
519
597
|
const actionsWidth = visibleWidth(previewLabel) + 1 + visibleWidth(closeLabel);
|
|
@@ -521,10 +599,11 @@ export class ConversationViewer implements Component {
|
|
|
521
599
|
const headerGap = Math.max(1, innerW - visibleWidth(headerLeft) - actionsWidth);
|
|
522
600
|
lines.push(row(headerLeft + " ".repeat(headerGap) + previewLabel + " " + closeLabel));
|
|
523
601
|
const invocationLine = this.invocationLine();
|
|
524
|
-
|
|
602
|
+
lines.push(row(invocationLine ?? ""));
|
|
525
603
|
lines.push(hrMid);
|
|
526
604
|
|
|
527
|
-
|
|
605
|
+
this.buildContentLines(contentWidth);
|
|
606
|
+
const contentLines = this.cachedLines;
|
|
528
607
|
const viewportHeight = this.viewportHeight();
|
|
529
608
|
const maxScroll = Math.max(0, contentLines.length - viewportHeight);
|
|
530
609
|
if (this.autoScroll) this.scrollOffset = maxScroll;
|
|
@@ -538,7 +617,7 @@ export class ConversationViewer implements Component {
|
|
|
538
617
|
const block = this.cachedMessageBlocks[this.currentMessageIndex()];
|
|
539
618
|
if (block) {
|
|
540
619
|
const header = renderConversationRoleHeader(block, this.theme);
|
|
541
|
-
displayed[0] = { text: ` ${header}`, plain: header, blockIndex: currentBlockIdx, railable: true };
|
|
620
|
+
displayed[0] = makeRenderLine({ text: ` ${header}`, plain: header, blockIndex: currentBlockIdx, railable: true });
|
|
542
621
|
}
|
|
543
622
|
}
|
|
544
623
|
for (let i = 0; i < displayed.length; i++) {
|
|
@@ -554,7 +633,10 @@ export class ConversationViewer implements Component {
|
|
|
554
633
|
const railGlyph = currentBlock?.kind === "tool" ? "▌" : "▎";
|
|
555
634
|
text = text.startsWith(" ") ? th.fg(railColor, railGlyph) + text.slice(1) : th.fg(railColor, railGlyph) + text;
|
|
556
635
|
}
|
|
557
|
-
|
|
636
|
+
const plainText = stripAnsi(text);
|
|
637
|
+
lines.push(plainText.includes("... (truncated,")
|
|
638
|
+
? th.fg("border", "│") + " " + text + th.fg("border", "│")
|
|
639
|
+
: contentRow(text, i, contentLines.length, viewportHeight, visibleStart));
|
|
558
640
|
}
|
|
559
641
|
for (let i = displayed.length; i < viewportHeight; i++) {
|
|
560
642
|
lines.push(contentRow("", i, contentLines.length, viewportHeight, visibleStart));
|
|
@@ -575,14 +657,21 @@ export class ConversationViewer implements Component {
|
|
|
575
657
|
lines.push(row(truncateToWidth(th.fg("accent", query) + th.fg("dim", matchCount) + hints, innerW, "...", true)));
|
|
576
658
|
} else {
|
|
577
659
|
const actions: string[] = [];
|
|
578
|
-
if (this.canSteer()) actions.push(th.fg("dim", "e steer"));
|
|
660
|
+
if (this.canSteer()) actions.push(th.fg("dim", innerW < 100 ? "Enter steer (e steer)" : "e steer"));
|
|
579
661
|
if (this.isStoppable()) actions.push(this.stopArmed ? th.fg("error", "x again to STOP") : th.fg("dim", "x stop"));
|
|
580
662
|
const editHint = this.isLive() ? " O" : "";
|
|
581
|
-
const
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
663
|
+
const markdownHint = innerW < 100 ? `m ${MARKDOWN_MODE_LABELS[this.currentMarkdownMode()]}` : "";
|
|
664
|
+
const shortcutHints = innerW < 100
|
|
665
|
+
? [
|
|
666
|
+
`j/k scroll · J/K messages · [/] tools · w preview · g/G t ${markdownHint} M o${editHint} /n N q`,
|
|
667
|
+
`[/] tools · w preview · ${markdownHint} · M o${editHint} /n N q`,
|
|
668
|
+
`${markdownHint} · M o${editHint} /n N · Esc close`,
|
|
669
|
+
]
|
|
670
|
+
: [
|
|
671
|
+
`j/k scroll · J/K messages · [/] tools · w preview · g/G t M o${editHint} /n N q`,
|
|
672
|
+
`[/] tools · w preview · M o${editHint} /n N q`,
|
|
673
|
+
`[/] tools · w · M o${editHint} /n N q`,
|
|
674
|
+
];
|
|
586
675
|
const scrollPct = contentLines.length <= viewportHeight ? "100%" : `${Math.round(((visibleStart + viewportHeight) / contentLines.length) * 100)}%`;
|
|
587
676
|
const count = th.fg("dim", `${contentLines.length} lines · ${scrollPct}`);
|
|
588
677
|
const countWidth = visibleWidth(count);
|
|
@@ -623,6 +712,7 @@ export class ConversationViewer implements Component {
|
|
|
623
712
|
if (this.closed) return;
|
|
624
713
|
if (this.hasFocusedBlock) this.selectedBlockId = this.currentBlock()?.id;
|
|
625
714
|
this.blocks = formatConversationMessages(this.session.messages);
|
|
715
|
+
this.lastSessionKey = this.sessionKey();
|
|
626
716
|
this.timeline.setBlocks(this.blocks);
|
|
627
717
|
this.cacheVersion++;
|
|
628
718
|
this.invalidate();
|
|
@@ -641,7 +731,7 @@ export class ConversationViewer implements Component {
|
|
|
641
731
|
const end = Math.min(this.cachedLines.length, start + Math.max(0, range.height));
|
|
642
732
|
for (let index = start; index < end; index++) {
|
|
643
733
|
const line = snapshotLines[index];
|
|
644
|
-
if (line) this.cachedLines[index] = { ...line };
|
|
734
|
+
if (line) this.cachedLines[index] = makeRenderLine({ ...line });
|
|
645
735
|
}
|
|
646
736
|
}
|
|
647
737
|
}
|
|
@@ -665,45 +755,119 @@ export class ConversationViewer implements Component {
|
|
|
665
755
|
}
|
|
666
756
|
|
|
667
757
|
private chromeLines(): number {
|
|
668
|
-
return CHROME_LINES_BASE +
|
|
758
|
+
return CHROME_LINES_BASE + 1 + (this.composer ? 1 : 0);
|
|
669
759
|
}
|
|
670
760
|
|
|
671
761
|
private invocationLine(): string | undefined {
|
|
672
762
|
const liveModel = this.isLive() ? this.record.session?.model : undefined;
|
|
673
|
-
const
|
|
763
|
+
const captured = this.record.invocation as (AgentRecord["invocation"] & {
|
|
764
|
+
effectiveModelName?: string;
|
|
765
|
+
effectiveModelId?: string;
|
|
766
|
+
effectiveThinking?: EffectiveThinkingLevel;
|
|
767
|
+
}) | undefined;
|
|
768
|
+
const invocation = captured
|
|
674
769
|
? {
|
|
675
|
-
...
|
|
676
|
-
...(
|
|
677
|
-
...(
|
|
770
|
+
...captured,
|
|
771
|
+
...(captured.modelName === undefined && captured.effectiveModelName !== undefined && { modelName: captured.effectiveModelName }),
|
|
772
|
+
...(captured.modelId === undefined && captured.effectiveModelId !== undefined && { modelId: captured.effectiveModelId }),
|
|
773
|
+
...(captured.thinking === undefined && captured.effectiveThinking !== undefined && { thinking: captured.effectiveThinking }),
|
|
774
|
+
...(liveModel && { modelName: liveModel.name ?? liveModel.id, modelId: `${liveModel.provider}/${liveModel.id}` }),
|
|
775
|
+
...(this.isLive() && { thinking: this.record.session?.thinkingLevel }),
|
|
678
776
|
}
|
|
679
|
-
: liveModel
|
|
680
|
-
? {
|
|
681
|
-
effectiveModelName: liveModel?.name ?? liveModel?.id,
|
|
682
|
-
effectiveThinking: this.record.session?.thinkingLevel,
|
|
683
|
-
}
|
|
777
|
+
: liveModel
|
|
778
|
+
? { modelName: liveModel.name ?? liveModel.id, modelId: `${liveModel.provider}/${liveModel.id}`, thinking: this.record.session?.thinkingLevel }
|
|
684
779
|
: undefined;
|
|
685
|
-
if (!invocation) return
|
|
686
|
-
const { modelName, tags } = buildInvocationTags(invocation);
|
|
687
|
-
const effectiveModelName =
|
|
688
|
-
|
|
689
|
-
|
|
780
|
+
if (!invocation) return undefined;
|
|
781
|
+
const { modelName, modelId, tags } = buildInvocationTags(invocation);
|
|
782
|
+
const effectiveModelName = modelId ?? modelName;
|
|
783
|
+
if (!effectiveModelName && tags.length === 0) return undefined;
|
|
784
|
+
const displayModel = captured?.effectiveModelName !== undefined
|
|
785
|
+
? `model: ${captured.effectiveModelName}`
|
|
786
|
+
: effectiveModelName;
|
|
787
|
+
return this.theme.fg("dim", ` ↳ ${[displayModel, ...tags].filter(Boolean).join(" · ")}`);
|
|
690
788
|
}
|
|
691
789
|
|
|
692
|
-
private
|
|
693
|
-
|
|
694
|
-
|
|
790
|
+
private currentMarkdownMode(): ViewerMarkdownMode {
|
|
791
|
+
return this.markdownModeOverride ?? this.viewerMarkdown?.() ?? "assistant";
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
private cycleMarkdownMode(): void {
|
|
795
|
+
const current = this.currentMarkdownMode();
|
|
796
|
+
const next = MARKDOWN_MODES[(MARKDOWN_MODES.indexOf(current) + 1) % MARKDOWN_MODES.length]!;
|
|
797
|
+
this.markdownModeOverride = next;
|
|
798
|
+
this.onMarkdownMode?.(next);
|
|
799
|
+
this.timeline.setMarkdownMode(() => this.currentMarkdownMode());
|
|
800
|
+
this.invalidate();
|
|
801
|
+
this.tui.requestRender();
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
private sessionKey(): string {
|
|
805
|
+
return this.session.messages.map(message => {
|
|
806
|
+
const value = message as unknown as { role?: unknown; content?: unknown; output?: unknown; command?: unknown };
|
|
807
|
+
const content = value.content;
|
|
808
|
+
const summarize = (text: string): string => `${text.length}:${text.slice(0, 24)}:${text.slice(-24)}`;
|
|
809
|
+
const contentSummary = typeof content === "string"
|
|
810
|
+
? summarize(content)
|
|
811
|
+
: Array.isArray(content)
|
|
812
|
+
? content.map(part => typeof part === "object" && part && "text" in part && typeof part.text === "string" ? summarize(part.text) : "").join("|")
|
|
813
|
+
: "";
|
|
814
|
+
return `${String(value.role)}:${contentSummary}:${typeof value.output === "string" ? summarize(value.output) : ""}:${typeof value.command === "string" ? summarize(value.command) : ""}`;
|
|
815
|
+
}).join("\u0001");
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
private blockSignature(block: ConversationBlock): string {
|
|
819
|
+
let resultLength = 0;
|
|
820
|
+
const content = block.toolResult?.content;
|
|
821
|
+
if (typeof content === "string") resultLength = content.length;
|
|
822
|
+
else if (Array.isArray(content)) resultLength = content.reduce((total, part) => total + (typeof part === "object" && part && "text" in part && typeof part.text === "string" ? part.text.length : 0), 0);
|
|
823
|
+
return `${block.id}\u0000${block.markdown}\u0000${block.fullText}\u0000${block.toolLine ?? ""}\u0000${resultLength}`;
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
private syncBlocksFromSession(): void {
|
|
827
|
+
const sessionKey = this.sessionKey();
|
|
828
|
+
if (sessionKey === this.lastSessionKey) return;
|
|
829
|
+
this.lastSessionKey = sessionKey;
|
|
830
|
+
const refreshed = formatConversationMessages(this.session.messages);
|
|
831
|
+
const sameShape = refreshed.length === this.blocks.length && refreshed.every((block, index) => block.id === this.blocks[index]?.id);
|
|
832
|
+
if (!sameShape) {
|
|
833
|
+
this.blocks = refreshed;
|
|
834
|
+
this.timeline.setBlocks(this.blocks);
|
|
835
|
+
this.cacheVersion++;
|
|
836
|
+
this.cachedVersion = -1;
|
|
837
|
+
return;
|
|
838
|
+
}
|
|
839
|
+
let changed = false;
|
|
840
|
+
for (let index = 0; index < refreshed.length; index++) {
|
|
841
|
+
const current = this.blocks[index]!;
|
|
842
|
+
const next = refreshed[index]!;
|
|
843
|
+
if (this.blockSignature(current) !== this.blockSignature(next)) changed = true;
|
|
844
|
+
Object.assign(current, next);
|
|
845
|
+
}
|
|
846
|
+
if (changed) {
|
|
847
|
+
this.cacheVersion++;
|
|
848
|
+
this.cachedVersion = -1;
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
private buildContentLines(width: number): PublicRenderLines {
|
|
853
|
+
if (width <= 0) return new PublicRenderLines([]);
|
|
854
|
+
this.syncBlocksFromSession();
|
|
855
|
+
if (this.cachedVersion === this.cacheVersion && this.cachedWidth === width) {
|
|
856
|
+
this.timeline.render(width);
|
|
857
|
+
return publicRenderLines(this.cachedLines);
|
|
858
|
+
}
|
|
695
859
|
this.timeline.setShowTools(this.showTools);
|
|
696
860
|
this.timeline.render(width);
|
|
697
861
|
const timelineSnapshot = this.timeline.getSnapshot();
|
|
698
862
|
const lines: RenderLine[] = timelineSnapshot.lines.map((line) => {
|
|
699
863
|
const rawText = typeof line?.text === "string" ? line.text : "";
|
|
700
864
|
const text = truncateToWidth(rawText, width, "", true);
|
|
701
|
-
return {
|
|
865
|
+
return makeRenderLine({
|
|
702
866
|
text,
|
|
703
867
|
plain: stripAnsi(text),
|
|
704
868
|
blockIndex: typeof line?.blockIndex === "number" ? line.blockIndex : -1,
|
|
705
869
|
...(line?.railable ? { railable: true } : {}),
|
|
706
|
-
};
|
|
870
|
+
});
|
|
707
871
|
});
|
|
708
872
|
this.cachedMessageStarts = [...timelineSnapshot.messageStarts];
|
|
709
873
|
this.cachedMessageBlocks = [...timelineSnapshot.messageBlocks];
|
|
@@ -723,13 +887,13 @@ export class ConversationViewer implements Component {
|
|
|
723
887
|
if (this.record.status === "running" && this.activity) {
|
|
724
888
|
const act = describeActivity(this.activity.activeTools, this.activity.responseText);
|
|
725
889
|
const text = `▍ ${act}`;
|
|
726
|
-
lines.push({ text: truncateToWidth(this.theme.fg("accent", "▍ ") + this.theme.fg("dim", act), width), plain: text, blockIndex: -1 });
|
|
890
|
+
lines.push(makeRenderLine({ text: truncateToWidth(this.theme.fg("accent", "▍ ") + this.theme.fg("dim", act), width), plain: text, blockIndex: -1 }));
|
|
727
891
|
}
|
|
728
892
|
this.cachedWidth = width;
|
|
729
893
|
this.cachedVersion = this.cacheVersion;
|
|
730
894
|
this.cachedLines = lines;
|
|
731
895
|
if (this.searchMode || this.searchQuery) this.recomputeMatches();
|
|
732
|
-
return lines;
|
|
896
|
+
return publicRenderLines(lines);
|
|
733
897
|
}
|
|
734
898
|
|
|
735
899
|
private currentMessageIndex(): number {
|