@esso0428/pi-subagents 0.17.15 → 0.17.17
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 +11 -0
- package/ROADMAP.md +87 -0
- package/docs/post-0.17.6-agents-roster-viewer-spec.md +42 -0
- package/docs/post-0.17.6-durable-history-spec.md +41 -0
- package/docs/post-0.17.6-feature-specs.md +50 -0
- package/docs/post-0.17.6-nested-agents-spec.md +38 -0
- package/docs/post-0.17.6-recovery-shutdown-spec.md +46 -0
- package/docs/post-0.17.6-ui-latency-baseline-spec.md +46 -0
- package/docs/post-0.17.6-workflow-rpc-lifecycle-spec.md +54 -0
- package/package.json +2 -3
- package/src/agent-history.ts +2 -54
- package/src/agent-manager.ts +406 -1288
- package/src/agent-runner.ts +27 -251
- package/src/agent-types.ts +32 -188
- package/src/cross-extension-rpc.ts +20 -96
- package/src/custom-agents.ts +13 -170
- package/src/index.ts +480 -1920
- package/src/invocation-config.ts +3 -118
- package/src/model-resolver.ts +0 -18
- package/src/output-file.ts +6 -61
- package/src/prompts.ts +2 -45
- package/src/schedule.ts +14 -35
- package/src/settings.ts +2 -301
- package/src/status-note.ts +1 -66
- package/src/types.ts +10 -177
- package/src/ui/agent-widget.ts +57 -278
- package/src/ui/conversation-blocks.ts +0 -6
- package/src/ui/conversation-timeline.ts +25 -139
- package/src/ui/conversation-viewer.ts +48 -212
- package/src/ui/schedule-menu.ts +8 -9
- package/src/usage.ts +2 -109
- package/src/worktree.ts +55 -69
- package/dist/abortable.d.ts +0 -13
- package/dist/abortable.d.ts.map +0 -1
- package/dist/abortable.js +0 -43
- package/dist/abortable.js.map +0 -1
- package/dist/agent-color.d.ts +0 -36
- package/dist/agent-color.d.ts.map +0 -1
- package/dist/agent-color.js +0 -124
- package/dist/agent-color.js.map +0 -1
- package/dist/agent-file-toggle.d.ts +0 -126
- package/dist/agent-file-toggle.d.ts.map +0 -1
- package/dist/agent-file-toggle.js +0 -259
- package/dist/agent-file-toggle.js.map +0 -1
- package/dist/agent-history-list.d.ts +0 -19
- package/dist/agent-history-list.d.ts.map +0 -1
- package/dist/agent-history-list.js +0 -69
- package/dist/agent-history-list.js.map +0 -1
- package/dist/agent-history.d.ts +0 -35
- package/dist/agent-history.d.ts.map +0 -1
- package/dist/agent-history.js +0 -188
- package/dist/agent-history.js.map +0 -1
- package/dist/agent-manager.d.ts +0 -503
- package/dist/agent-manager.d.ts.map +0 -1
- package/dist/agent-manager.js +0 -1572
- package/dist/agent-manager.js.map +0 -1
- package/dist/agent-recovery.d.ts +0 -37
- package/dist/agent-recovery.d.ts.map +0 -1
- package/dist/agent-recovery.js +0 -171
- package/dist/agent-recovery.js.map +0 -1
- package/dist/agent-runner.d.ts +0 -303
- package/dist/agent-runner.d.ts.map +0 -1
- package/dist/agent-runner.js +0 -1003
- package/dist/agent-runner.js.map +0 -1
- package/dist/agent-types.d.ts +0 -120
- package/dist/agent-types.d.ts.map +0 -1
- package/dist/agent-types.js +0 -301
- package/dist/agent-types.js.map +0 -1
- package/dist/child-context.d.ts +0 -3
- package/dist/child-context.d.ts.map +0 -1
- package/dist/child-context.js +0 -13
- package/dist/child-context.js.map +0 -1
- package/dist/context.d.ts +0 -13
- package/dist/context.d.ts.map +0 -1
- package/dist/context.js +0 -57
- package/dist/context.js.map +0 -1
- package/dist/cross-extension-rpc.d.ts +0 -67
- package/dist/cross-extension-rpc.d.ts.map +0 -1
- package/dist/cross-extension-rpc.js +0 -139
- package/dist/cross-extension-rpc.js.map +0 -1
- package/dist/custom-agents.d.ts +0 -55
- package/dist/custom-agents.d.ts.map +0 -1
- package/dist/custom-agents.js +0 -309
- package/dist/custom-agents.js.map +0 -1
- package/dist/default-agents.d.ts +0 -8
- package/dist/default-agents.d.ts.map +0 -1
- package/dist/default-agents.js +0 -123
- package/dist/default-agents.js.map +0 -1
- package/dist/enabled-models.d.ts +0 -50
- package/dist/enabled-models.d.ts.map +0 -1
- package/dist/enabled-models.js +0 -146
- package/dist/enabled-models.js.map +0 -1
- package/dist/env.d.ts +0 -7
- package/dist/env.d.ts.map +0 -1
- package/dist/env.js +0 -29
- package/dist/env.js.map +0 -1
- package/dist/group-join.d.ts +0 -33
- package/dist/group-join.d.ts.map +0 -1
- package/dist/group-join.js +0 -117
- package/dist/group-join.js.map +0 -1
- package/dist/index.d.ts +0 -51
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -3687
- package/dist/index.js.map +0 -1
- package/dist/invocation-config.d.ts +0 -108
- package/dist/invocation-config.d.ts.map +0 -1
- package/dist/invocation-config.js +0 -84
- package/dist/invocation-config.js.map +0 -1
- package/dist/memory.d.ts +0 -54
- package/dist/memory.d.ts.map +0 -1
- package/dist/memory.js +0 -166
- package/dist/memory.js.map +0 -1
- package/dist/mention-clone.d.ts +0 -88
- package/dist/mention-clone.d.ts.map +0 -1
- package/dist/mention-clone.js +0 -154
- package/dist/mention-clone.js.map +0 -1
- package/dist/mention.d.ts +0 -82
- package/dist/mention.d.ts.map +0 -1
- package/dist/mention.js +0 -132
- package/dist/mention.js.map +0 -1
- package/dist/model-resolver.d.ts +0 -37
- package/dist/model-resolver.d.ts.map +0 -1
- package/dist/model-resolver.js +0 -96
- package/dist/model-resolver.js.map +0 -1
- package/dist/model-scope.d.ts +0 -50
- package/dist/model-scope.d.ts.map +0 -1
- package/dist/model-scope.js +0 -49
- package/dist/model-scope.js.map +0 -1
- package/dist/nested-tools.d.ts +0 -57
- package/dist/nested-tools.d.ts.map +0 -1
- package/dist/nested-tools.js +0 -301
- package/dist/nested-tools.js.map +0 -1
- package/dist/nico-overrides.d.ts +0 -54
- package/dist/nico-overrides.d.ts.map +0 -1
- package/dist/nico-overrides.js +0 -170
- package/dist/nico-overrides.js.map +0 -1
- package/dist/output-file.d.ts +0 -44
- package/dist/output-file.d.ts.map +0 -1
- package/dist/output-file.js +0 -156
- package/dist/output-file.js.map +0 -1
- package/dist/prompts.d.ts +0 -56
- package/dist/prompts.d.ts.map +0 -1
- package/dist/prompts.js +0 -92
- package/dist/prompts.js.map +0 -1
- package/dist/schedule-store.d.ts +0 -39
- package/dist/schedule-store.d.ts.map +0 -1
- package/dist/schedule-store.js +0 -156
- package/dist/schedule-store.js.map +0 -1
- package/dist/schedule.d.ts +0 -110
- package/dist/schedule.d.ts.map +0 -1
- package/dist/schedule.js +0 -360
- package/dist/schedule.js.map +0 -1
- package/dist/settings.d.ts +0 -354
- package/dist/settings.d.ts.map +0 -1
- package/dist/settings.js +0 -247
- package/dist/settings.js.map +0 -1
- package/dist/skill-loader.d.ts +0 -25
- package/dist/skill-loader.d.ts.map +0 -1
- package/dist/skill-loader.js +0 -94
- package/dist/skill-loader.js.map +0 -1
- package/dist/status-note.d.ts +0 -62
- package/dist/status-note.d.ts.map +0 -1
- package/dist/status-note.js +0 -86
- package/dist/status-note.js.map +0 -1
- package/dist/structured-output.d.ts +0 -62
- package/dist/structured-output.d.ts.map +0 -1
- package/dist/structured-output.js +0 -113
- package/dist/structured-output.js.map +0 -1
- package/dist/types.d.ts +0 -372
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -6
- package/dist/types.js.map +0 -1
- package/dist/ui/agent-mention.d.ts +0 -83
- package/dist/ui/agent-mention.d.ts.map +0 -1
- package/dist/ui/agent-mention.js +0 -188
- package/dist/ui/agent-mention.js.map +0 -1
- package/dist/ui/agent-widget.d.ts +0 -241
- package/dist/ui/agent-widget.d.ts.map +0 -1
- package/dist/ui/agent-widget.js +0 -992
- package/dist/ui/agent-widget.js.map +0 -1
- package/dist/ui/ccstyle/diff/ansi-utils.d.ts +0 -11
- package/dist/ui/ccstyle/diff/ansi-utils.d.ts.map +0 -1
- package/dist/ui/ccstyle/diff/ansi-utils.js +0 -145
- package/dist/ui/ccstyle/diff/ansi-utils.js.map +0 -1
- package/dist/ui/ccstyle/diff/diff-presentation.d.ts +0 -12
- package/dist/ui/ccstyle/diff/diff-presentation.d.ts.map +0 -1
- package/dist/ui/ccstyle/diff/diff-presentation.js +0 -48
- package/dist/ui/ccstyle/diff/diff-presentation.js.map +0 -1
- package/dist/ui/ccstyle/diff/diff-renderer.d.ts +0 -46
- package/dist/ui/ccstyle/diff/diff-renderer.d.ts.map +0 -1
- package/dist/ui/ccstyle/diff/diff-renderer.js +0 -2049
- package/dist/ui/ccstyle/diff/diff-renderer.js.map +0 -1
- package/dist/ui/ccstyle/diff/line-width-safety.d.ts +0 -12
- package/dist/ui/ccstyle/diff/line-width-safety.d.ts.map +0 -1
- package/dist/ui/ccstyle/diff/line-width-safety.js +0 -58
- package/dist/ui/ccstyle/diff/line-width-safety.js.map +0 -1
- package/dist/ui/ccstyle/diff/render-utils.d.ts +0 -6
- package/dist/ui/ccstyle/diff/render-utils.d.ts.map +0 -1
- package/dist/ui/ccstyle/diff/render-utils.js +0 -25
- package/dist/ui/ccstyle/diff/render-utils.js.map +0 -1
- package/dist/ui/ccstyle/diff/shiki-highlight.d.ts +0 -19
- package/dist/ui/ccstyle/diff/shiki-highlight.d.ts.map +0 -1
- package/dist/ui/ccstyle/diff/shiki-highlight.js +0 -85
- package/dist/ui/ccstyle/diff/shiki-highlight.js.map +0 -1
- package/dist/ui/ccstyle/diff/types.d.ts +0 -22
- package/dist/ui/ccstyle/diff/types.d.ts.map +0 -1
- package/dist/ui/ccstyle/diff/types.js +0 -10
- package/dist/ui/ccstyle/diff/types.js.map +0 -1
- package/dist/ui/ccstyle/diff/write-display-utils.d.ts +0 -2
- package/dist/ui/ccstyle/diff/write-display-utils.d.ts.map +0 -1
- package/dist/ui/ccstyle/diff/write-display-utils.js +0 -12
- package/dist/ui/ccstyle/diff/write-display-utils.js.map +0 -1
- package/dist/ui/ccstyle/tool-renderer.d.ts +0 -17
- package/dist/ui/ccstyle/tool-renderer.d.ts.map +0 -1
- package/dist/ui/ccstyle/tool-renderer.js +0 -74
- package/dist/ui/ccstyle/tool-renderer.js.map +0 -1
- package/dist/ui/ccstyle/tool-result.d.ts +0 -44
- package/dist/ui/ccstyle/tool-result.d.ts.map +0 -1
- package/dist/ui/ccstyle/tool-result.js +0 -423
- package/dist/ui/ccstyle/tool-result.js.map +0 -1
- package/dist/ui/conversation-blocks.d.ts +0 -46
- package/dist/ui/conversation-blocks.d.ts.map +0 -1
- package/dist/ui/conversation-blocks.js +0 -313
- package/dist/ui/conversation-blocks.js.map +0 -1
- package/dist/ui/conversation-nvim.d.ts +0 -8
- package/dist/ui/conversation-nvim.d.ts.map +0 -1
- package/dist/ui/conversation-nvim.js +0 -117
- package/dist/ui/conversation-nvim.js.map +0 -1
- package/dist/ui/conversation-role.d.ts +0 -13
- package/dist/ui/conversation-role.d.ts.map +0 -1
- package/dist/ui/conversation-role.js +0 -39
- package/dist/ui/conversation-role.js.map +0 -1
- package/dist/ui/conversation-search.d.ts +0 -39
- package/dist/ui/conversation-search.d.ts.map +0 -1
- package/dist/ui/conversation-search.js +0 -124
- package/dist/ui/conversation-search.js.map +0 -1
- package/dist/ui/conversation-timeline.d.ts +0 -102
- package/dist/ui/conversation-timeline.d.ts.map +0 -1
- package/dist/ui/conversation-timeline.js +0 -555
- package/dist/ui/conversation-timeline.js.map +0 -1
- package/dist/ui/conversation-viewer.d.ts +0 -138
- package/dist/ui/conversation-viewer.d.ts.map +0 -1
- package/dist/ui/conversation-viewer.js +0 -1175
- package/dist/ui/conversation-viewer.js.map +0 -1
- package/dist/ui/schedule-menu.d.ts +0 -17
- package/dist/ui/schedule-menu.d.ts.map +0 -1
- package/dist/ui/schedule-menu.js +0 -95
- package/dist/ui/schedule-menu.js.map +0 -1
- package/dist/ui/select-item.d.ts +0 -28
- package/dist/ui/select-item.d.ts.map +0 -1
- package/dist/ui/select-item.js +0 -35
- package/dist/ui/select-item.js.map +0 -1
- package/dist/ui/viewer-keys.d.ts +0 -21
- package/dist/ui/viewer-keys.d.ts.map +0 -1
- package/dist/ui/viewer-keys.js +0 -18
- package/dist/ui/viewer-keys.js.map +0 -1
- package/dist/ui/workflow-card.d.ts +0 -176
- package/dist/ui/workflow-card.d.ts.map +0 -1
- package/dist/ui/workflow-card.js +0 -333
- package/dist/ui/workflow-card.js.map +0 -1
- package/dist/ui/workflow-dialog.d.ts +0 -306
- package/dist/ui/workflow-dialog.d.ts.map +0 -1
- package/dist/ui/workflow-dialog.js +0 -844
- package/dist/ui/workflow-dialog.js.map +0 -1
- package/dist/ui/workflow-menu.d.ts +0 -42
- package/dist/ui/workflow-menu.d.ts.map +0 -1
- package/dist/ui/workflow-menu.js +0 -127
- package/dist/ui/workflow-menu.js.map +0 -1
- package/dist/usage.d.ts +0 -136
- package/dist/usage.d.ts.map +0 -1
- package/dist/usage.js +0 -121
- package/dist/usage.js.map +0 -1
- package/dist/workflow/collisions.d.ts +0 -96
- package/dist/workflow/collisions.d.ts.map +0 -1
- package/dist/workflow/collisions.js +0 -89
- package/dist/workflow/collisions.js.map +0 -1
- package/dist/workflow/entry.d.ts +0 -33
- package/dist/workflow/entry.d.ts.map +0 -1
- package/dist/workflow/entry.js +0 -30
- package/dist/workflow/entry.js.map +0 -1
- package/dist/workflow/host.d.ts +0 -63
- package/dist/workflow/host.d.ts.map +0 -1
- package/dist/workflow/host.js +0 -363
- package/dist/workflow/host.js.map +0 -1
- package/dist/workflow/journal.d.ts +0 -98
- package/dist/workflow/journal.d.ts.map +0 -1
- package/dist/workflow/journal.js +0 -121
- package/dist/workflow/journal.js.map +0 -1
- package/dist/workflow/json-schema.d.ts +0 -52
- package/dist/workflow/json-schema.d.ts.map +0 -1
- package/dist/workflow/json-schema.js +0 -112
- package/dist/workflow/json-schema.js.map +0 -1
- package/dist/workflow/meta.d.ts +0 -68
- package/dist/workflow/meta.d.ts.map +0 -1
- package/dist/workflow/meta.js +0 -318
- package/dist/workflow/meta.js.map +0 -1
- package/dist/workflow/progress.d.ts +0 -225
- package/dist/workflow/progress.d.ts.map +0 -1
- package/dist/workflow/progress.js +0 -362
- package/dist/workflow/progress.js.map +0 -1
- package/dist/workflow/runtime.d.ts +0 -335
- package/dist/workflow/runtime.d.ts.map +0 -1
- package/dist/workflow/runtime.js +0 -831
- package/dist/workflow/runtime.js.map +0 -1
- package/dist/workflow/saved.d.ts +0 -91
- package/dist/workflow/saved.d.ts.map +0 -1
- package/dist/workflow/saved.js +0 -204
- package/dist/workflow/saved.js.map +0 -1
- package/dist/workflow/task.d.ts +0 -137
- package/dist/workflow/task.d.ts.map +0 -1
- package/dist/workflow/task.js +0 -208
- package/dist/workflow/task.js.map +0 -1
- package/dist/workflow/tool-description.d.ts +0 -39
- package/dist/workflow/tool-description.d.ts.map +0 -1
- package/dist/workflow/tool-description.js +0 -200
- package/dist/workflow/tool-description.js.map +0 -1
- package/dist/workflow/worker-source.d.ts +0 -48
- package/dist/workflow/worker-source.d.ts.map +0 -1
- package/dist/workflow/worker-source.js +0 -779
- package/dist/workflow/worker-source.js.map +0 -1
- package/dist/worktree.d.ts +0 -53
- package/dist/worktree.d.ts.map +0 -1
- package/dist/worktree.js +0 -165
- package/dist/worktree.js.map +0 -1
- package/dist/write-execution.d.ts +0 -42
- package/dist/write-execution.d.ts.map +0 -1
- package/dist/write-execution.js +0 -138
- package/dist/write-execution.js.map +0 -1
- package/dist/xml.d.ts +0 -11
- package/dist/xml.d.ts.map +0 -1
- package/dist/xml.js +0 -13
- package/dist/xml.js.map +0 -1
- package/src/abortable.ts +0 -43
- package/src/agent-color.ts +0 -161
- package/src/agent-file-toggle.ts +0 -269
- package/src/child-context.ts +0 -15
- package/src/mention-clone.ts +0 -196
- package/src/mention.ts +0 -141
- package/src/model-scope.ts +0 -70
- package/src/nested-tools.ts +0 -424
- package/src/structured-output.ts +0 -130
- package/src/ui/agent-mention.ts +0 -216
- package/src/ui/select-item.ts +0 -45
- package/src/ui/workflow-card.ts +0 -470
- package/src/ui/workflow-dialog.ts +0 -1115
- package/src/ui/workflow-menu.ts +0 -166
- package/src/workflow/collisions.ts +0 -123
- package/src/workflow/entry.ts +0 -47
- package/src/workflow/host.ts +0 -403
- package/src/workflow/journal.ts +0 -164
- package/src/workflow/json-schema.ts +0 -128
- package/src/workflow/meta.ts +0 -325
- package/src/workflow/progress.ts +0 -550
- package/src/workflow/runtime.ts +0 -1219
- package/src/workflow/saved.ts +0 -217
- package/src/workflow/task.ts +0 -302
- package/src/workflow/tool-description.ts +0 -200
- package/src/workflow/worker-source.ts +0 -781
- package/src/xml.ts +0 -13
package/src/ui/agent-widget.ts
CHANGED
|
@@ -6,11 +6,10 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { Editor, isKeyRelease, Key, matchesKey, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
|
|
9
|
-
import { renderAgentName } from "../agent-color.js";
|
|
10
9
|
import type { AgentManager } from "../agent-manager.js";
|
|
11
10
|
import { getConfig } from "../agent-types.js";
|
|
12
11
|
import type { AgentInvocation, AgentRecord, SubagentType, WidgetMode } from "../types.js";
|
|
13
|
-
import {
|
|
12
|
+
import { getLifetimeTotal, getSessionContextPercent, type LifetimeUsage, type SessionLike } from "../usage.js";
|
|
14
13
|
|
|
15
14
|
// ---- Constants ----
|
|
16
15
|
|
|
@@ -55,11 +54,10 @@ export type Theme = {
|
|
|
55
54
|
|
|
56
55
|
export type AgentWidgetOpenMode = "live" | "history";
|
|
57
56
|
export type AgentWidgetOpenCallback = (record: AgentRecord, mode: AgentWidgetOpenMode) => void | Promise<void>;
|
|
58
|
-
export
|
|
59
|
-
canOpenHistory
|
|
60
|
-
onOpen
|
|
61
|
-
|
|
62
|
-
}
|
|
57
|
+
export type AgentWidgetOptions = {
|
|
58
|
+
canOpenHistory: (record: AgentRecord) => boolean;
|
|
59
|
+
onOpen: AgentWidgetOpenCallback;
|
|
60
|
+
};
|
|
63
61
|
/** @deprecated Use AgentWidgetOpenMode. */
|
|
64
62
|
export type AgentOpenMode = AgentWidgetOpenMode;
|
|
65
63
|
/** @deprecated Use AgentWidgetOpenCallback. */
|
|
@@ -74,8 +72,8 @@ export type UICtx = {
|
|
|
74
72
|
content: undefined | ((tui: any, theme: Theme) => { render(): string[]; invalidate(): void }),
|
|
75
73
|
options?: { placement?: "aboveEditor" | "belowEditor" },
|
|
76
74
|
): void;
|
|
77
|
-
onTerminalInput
|
|
78
|
-
getEditorText
|
|
75
|
+
onTerminalInput(handler: (data: string) => { consume?: boolean; data?: string } | undefined): () => void;
|
|
76
|
+
getEditorText(): string;
|
|
79
77
|
};
|
|
80
78
|
|
|
81
79
|
/** Per-agent live activity state. */
|
|
@@ -113,8 +111,6 @@ export interface AgentDetails {
|
|
|
113
111
|
turnCount?: number;
|
|
114
112
|
/** Effective max turns (undefined = unlimited). */
|
|
115
113
|
maxTurns?: number;
|
|
116
|
-
/** Estimated cost in USD; 0 when the model has no pricing data. */
|
|
117
|
-
cost?: number;
|
|
118
114
|
agentId?: string;
|
|
119
115
|
error?: string;
|
|
120
116
|
}
|
|
@@ -180,16 +176,6 @@ export function formatDuration(startedAt: number, completedAt?: number): string
|
|
|
180
176
|
return `${formatMs(Date.now() - startedAt)} (running)`;
|
|
181
177
|
}
|
|
182
178
|
|
|
183
|
-
/** Format an estimated model cost without implying billing precision. */
|
|
184
|
-
export function formatCost(cost: number): string {
|
|
185
|
-
if (!(cost > 0)) return "";
|
|
186
|
-
if (cost < 0.0001) return "<$0.0001";
|
|
187
|
-
if (cost >= 1) return `~$${cost.toFixed(2)}`;
|
|
188
|
-
const rounded = Number(cost.toFixed(4));
|
|
189
|
-
const decimals = (String(rounded).split(".")[1] ?? "").length;
|
|
190
|
-
return `~$${rounded.toFixed(Math.max(2, decimals))}`;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
179
|
/** Get display name for any agent type (built-in or custom). */
|
|
194
180
|
export function getDisplayName(type: SubagentType): string {
|
|
195
181
|
return getConfig(type).displayName;
|
|
@@ -204,23 +190,17 @@ export function getPromptModeLabel(type: SubagentType): string | undefined {
|
|
|
204
190
|
/** Mode label is not included — callers add it where they want it. */
|
|
205
191
|
export function buildInvocationTags(
|
|
206
192
|
invocation: AgentInvocation | undefined,
|
|
207
|
-
): { modelName?: string;
|
|
193
|
+
): { modelName?: string; tags: string[] } {
|
|
208
194
|
const tags: string[] = [];
|
|
209
195
|
if (!invocation) return { tags };
|
|
210
|
-
const
|
|
211
|
-
value && requested && requested !== value ? `${value} (asked ${requested})` : value;
|
|
212
|
-
const thinking = asked(invocation.thinking, invocation.requestedThinking);
|
|
196
|
+
const thinking = invocation.effectiveThinking ?? invocation.thinking;
|
|
213
197
|
if (thinking) tags.push(`thinking: ${thinking}`);
|
|
214
198
|
if (invocation.isolated) tags.push("isolated");
|
|
215
199
|
if (invocation.isolation === "worktree") tags.push("worktree");
|
|
216
200
|
if (invocation.inheritContext) tags.push("inherit context");
|
|
217
201
|
if (invocation.runInBackground) tags.push("background");
|
|
218
202
|
if (invocation.maxTurns != null) tags.push(`max turns: ${invocation.maxTurns}`);
|
|
219
|
-
return {
|
|
220
|
-
modelName: asked(invocation.modelName, invocation.requestedModel),
|
|
221
|
-
modelId: asked(invocation.modelId, invocation.requestedModel),
|
|
222
|
-
tags,
|
|
223
|
-
};
|
|
203
|
+
return { modelName: invocation.modelName, tags };
|
|
224
204
|
}
|
|
225
205
|
|
|
226
206
|
/** Normalize and truncate text so it can never add physical widget rows. */
|
|
@@ -274,14 +254,6 @@ export class AgentWidget {
|
|
|
274
254
|
/** First logical row currently represented by the bounded viewport. */
|
|
275
255
|
private viewportStart = 0;
|
|
276
256
|
|
|
277
|
-
/** Cached records and roster. Rebuilt by update(), never by an arrow press. */
|
|
278
|
-
private cachedAgents: AgentRecord[] = [];
|
|
279
|
-
private cachedRoster: AgentRecord[] = [];
|
|
280
|
-
private rosterCacheKey: string | undefined;
|
|
281
|
-
private rosterCacheInitialized = false;
|
|
282
|
-
/** Terminal history capability is a potentially filesystem-backed lookup. */
|
|
283
|
-
private historyOpenabilityCache = new Map<string, { key: string; value: boolean }>();
|
|
284
|
-
|
|
285
257
|
/** Whether the widget callback is currently registered with the TUI. */
|
|
286
258
|
private widgetRegistered = false;
|
|
287
259
|
/** Cached TUI reference from widget factory callback, used for requestRender(). */
|
|
@@ -290,27 +262,17 @@ export class AgentWidget {
|
|
|
290
262
|
private lastStatusText: string | undefined;
|
|
291
263
|
/** Snapshot of the state used for the last widget registration/render request. */
|
|
292
264
|
private lastRenderKey: string | undefined;
|
|
293
|
-
private readonly openOptions: AgentWidgetOptions;
|
|
294
|
-
private readonly showCost: () => boolean;
|
|
295
|
-
private readonly showModel: () => boolean;
|
|
296
265
|
|
|
297
266
|
constructor(
|
|
298
267
|
private manager: AgentManager,
|
|
299
268
|
private agentActivity: Map<string, AgentActivity>,
|
|
300
269
|
/** Read live at render time. Selects which agents the widget shows. */
|
|
301
270
|
private mode: () => WidgetMode = () => "all",
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
this.openOptions = {};
|
|
308
|
-
} else {
|
|
309
|
-
this.showCost = showCostOrOptions.showCost ?? (() => false);
|
|
310
|
-
this.openOptions = showCostOrOptions;
|
|
311
|
-
}
|
|
312
|
-
this.showModel = showModel;
|
|
313
|
-
}
|
|
271
|
+
private options: AgentWidgetOptions = {
|
|
272
|
+
canOpenHistory: (record) => record.session !== undefined || record.completedAt !== undefined,
|
|
273
|
+
onOpen: () => {},
|
|
274
|
+
},
|
|
275
|
+
) {}
|
|
314
276
|
|
|
315
277
|
/**
|
|
316
278
|
* Agents eligible for the widget, per the current `WidgetMode`:
|
|
@@ -323,7 +285,7 @@ export class AgentWidget {
|
|
|
323
285
|
* only proven-foreground runs drop out — nothing else silently vanishes.
|
|
324
286
|
* - `all`: every agent.
|
|
325
287
|
*/
|
|
326
|
-
private
|
|
288
|
+
private widgetAgents() {
|
|
327
289
|
const all = this.manager.listAgents();
|
|
328
290
|
switch (this.mode()) {
|
|
329
291
|
case "off": return [];
|
|
@@ -332,80 +294,6 @@ export class AgentWidget {
|
|
|
332
294
|
}
|
|
333
295
|
}
|
|
334
296
|
|
|
335
|
-
/** Records currently visible to the widget; safe for render and input paths. */
|
|
336
|
-
private widgetAgents(): AgentRecord[] {
|
|
337
|
-
return this.rosterCacheInitialized ? this.cachedAgents : this.widgetAgentsFromManager();
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
private canOpenHistory(record: AgentRecord): boolean {
|
|
341
|
-
return this.openOptions.canOpenHistory?.(record) ?? true;
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
private historyOpenabilityKey(record: AgentRecord): string {
|
|
345
|
-
return JSON.stringify([
|
|
346
|
-
record.status,
|
|
347
|
-
record.completedAt ?? null,
|
|
348
|
-
record.transcriptPath ?? null,
|
|
349
|
-
record.session !== undefined,
|
|
350
|
-
]);
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
/**
|
|
354
|
-
* Cache terminal history capability by the record state that can affect it.
|
|
355
|
-
* The supplied predicate may call `existsSync`, so it must not run from a
|
|
356
|
-
* timer refresh, render-key calculation, or arrow-navigation path more than
|
|
357
|
-
* once for the same terminal record state.
|
|
358
|
-
*/
|
|
359
|
-
private cachedCanOpenHistory(record: AgentRecord): boolean {
|
|
360
|
-
if (record.status === "running" || record.status === "queued") return true;
|
|
361
|
-
|
|
362
|
-
const key = this.historyOpenabilityKey(record);
|
|
363
|
-
const cached = this.historyOpenabilityCache.get(record.id);
|
|
364
|
-
if (cached?.key === key) return cached.value;
|
|
365
|
-
|
|
366
|
-
const value = this.canOpenHistory(record);
|
|
367
|
-
this.historyOpenabilityCache.set(record.id, { key, value });
|
|
368
|
-
return value;
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
/** Refresh the manager snapshot and openability cache at lifecycle/update time. */
|
|
372
|
-
private refreshRoster(): AgentRecord[] {
|
|
373
|
-
const agents = this.widgetAgentsFromManager();
|
|
374
|
-
const key = JSON.stringify({
|
|
375
|
-
mode: this.mode(),
|
|
376
|
-
agents: agents.map(record => [
|
|
377
|
-
record.id,
|
|
378
|
-
record.type,
|
|
379
|
-
record.description,
|
|
380
|
-
record.status,
|
|
381
|
-
record.completedAt ?? null,
|
|
382
|
-
record.startedAt,
|
|
383
|
-
record.toolUses,
|
|
384
|
-
record.error ?? null,
|
|
385
|
-
record.transcriptPath ?? null,
|
|
386
|
-
record.session !== undefined,
|
|
387
|
-
record.isBackground,
|
|
388
|
-
record.status !== "running" && record.status !== "queued"
|
|
389
|
-
? this.cachedCanOpenHistory(record)
|
|
390
|
-
: undefined,
|
|
391
|
-
]),
|
|
392
|
-
});
|
|
393
|
-
if (this.rosterCacheInitialized && key === this.rosterCacheKey) return this.cachedRoster;
|
|
394
|
-
|
|
395
|
-
const finished = agents.filter(record =>
|
|
396
|
-
record.status !== "running" && record.status !== "queued"
|
|
397
|
-
&& record.completedAt !== undefined
|
|
398
|
-
&& this.cachedCanOpenHistory(record),
|
|
399
|
-
);
|
|
400
|
-
const running = agents.filter(record => record.status === "running");
|
|
401
|
-
const queued = agents.filter(record => record.status === "queued");
|
|
402
|
-
this.cachedAgents = agents;
|
|
403
|
-
this.cachedRoster = [...running, ...queued, ...finished];
|
|
404
|
-
this.rosterCacheKey = key;
|
|
405
|
-
this.rosterCacheInitialized = true;
|
|
406
|
-
return this.cachedRoster;
|
|
407
|
-
}
|
|
408
|
-
|
|
409
297
|
/** Set the UI context (grabbed from first tool execution). */
|
|
410
298
|
setUICtx(ctx: UICtx): boolean {
|
|
411
299
|
if (ctx === this.uiCtx) return false;
|
|
@@ -423,11 +311,6 @@ export class AgentWidget {
|
|
|
423
311
|
this.selectedAgentId = undefined;
|
|
424
312
|
this.selectedRosterIndex = 0;
|
|
425
313
|
this.viewportStart = 0;
|
|
426
|
-
this.cachedAgents = [];
|
|
427
|
-
this.cachedRoster = [];
|
|
428
|
-
this.rosterCacheKey = undefined;
|
|
429
|
-
this.rosterCacheInitialized = false;
|
|
430
|
-
this.historyOpenabilityCache.clear();
|
|
431
314
|
// Print/RPC tests and lightweight embedders may provide only the widget
|
|
432
315
|
// surface; real interactive contexts always implement this hook.
|
|
433
316
|
if (typeof ctx.onTerminalInput === "function") {
|
|
@@ -437,8 +320,8 @@ export class AgentWidget {
|
|
|
437
320
|
}
|
|
438
321
|
|
|
439
322
|
/** Request a render on the currently registered TUI without touching input. */
|
|
440
|
-
requestUiRefresh(force =
|
|
441
|
-
if (!this.tui
|
|
323
|
+
requestUiRefresh(force = true): boolean {
|
|
324
|
+
if (!this.tui) return false;
|
|
442
325
|
this.tui.requestRender(force);
|
|
443
326
|
return true;
|
|
444
327
|
}
|
|
@@ -450,9 +333,7 @@ export class AgentWidget {
|
|
|
450
333
|
|
|
451
334
|
/** Keep the spinner/elapsed-time timer alive only while a visible agent runs. */
|
|
452
335
|
ensureTimer() {
|
|
453
|
-
if (!this.uiCtx) return;
|
|
454
|
-
const roster = this.rosterCacheInitialized ? this.cachedRoster : this.refreshRoster();
|
|
455
|
-
if (!roster.some(a => a.status === "running")) return;
|
|
336
|
+
if (!this.uiCtx || !this.widgetAgents().some(a => a.status === "running")) return;
|
|
456
337
|
if (!this.widgetInterval) {
|
|
457
338
|
this.widgetInterval = setInterval(() => this.update(true), 250);
|
|
458
339
|
}
|
|
@@ -473,9 +354,6 @@ export class AgentWidget {
|
|
|
473
354
|
*/
|
|
474
355
|
markFinished(_agentId: string) {}
|
|
475
356
|
|
|
476
|
-
/** Retained for lifecycle call sites; terminal rows are record-driven now. */
|
|
477
|
-
markRunning(_agentId: string) {}
|
|
478
|
-
|
|
479
357
|
/**
|
|
480
358
|
* Records represented by selectable rows in the above-editor widget.
|
|
481
359
|
*
|
|
@@ -484,7 +362,15 @@ export class AgentWidget {
|
|
|
484
362
|
* panel exposes currently useful work before terminal history.
|
|
485
363
|
*/
|
|
486
364
|
private roster(): AgentRecord[] {
|
|
487
|
-
|
|
365
|
+
const agents = this.widgetAgents();
|
|
366
|
+
const finished = agents.filter(record =>
|
|
367
|
+
record.status !== "running" && record.status !== "queued"
|
|
368
|
+
&& record.completedAt !== undefined
|
|
369
|
+
&& this.options.canOpenHistory(record),
|
|
370
|
+
);
|
|
371
|
+
const running = agents.filter(record => record.status === "running");
|
|
372
|
+
const queued = agents.filter(record => record.status === "queued");
|
|
373
|
+
return [...running, ...queued, ...finished];
|
|
488
374
|
}
|
|
489
375
|
|
|
490
376
|
/** True when pi's prompt editor owns the keyboard. */
|
|
@@ -504,7 +390,7 @@ export class AgentWidget {
|
|
|
504
390
|
// roster) clear this state explicitly instead of treating every exit as a
|
|
505
391
|
// reset.
|
|
506
392
|
this.navigationActive = false;
|
|
507
|
-
this.
|
|
393
|
+
this.update();
|
|
508
394
|
}
|
|
509
395
|
|
|
510
396
|
/** Resume navigation from the retained row, or select the first row. */
|
|
@@ -522,7 +408,7 @@ export class AgentWidget {
|
|
|
522
408
|
this.selectedRosterIndex = fallbackIndex;
|
|
523
409
|
this.viewportStart = Math.min(this.viewportStart, Math.max(0, records.length - 1));
|
|
524
410
|
}
|
|
525
|
-
this.
|
|
411
|
+
this.update();
|
|
526
412
|
}
|
|
527
413
|
|
|
528
414
|
/** Move the selected row, activating only from an empty focused editor. */
|
|
@@ -545,7 +431,7 @@ export class AgentWidget {
|
|
|
545
431
|
const nextIndex = Math.max(0, Math.min(records.length - 1, currentIndex + direction));
|
|
546
432
|
this.selectedAgentId = records[nextIndex].id;
|
|
547
433
|
this.selectedRosterIndex = nextIndex;
|
|
548
|
-
this.
|
|
434
|
+
this.update();
|
|
549
435
|
return true;
|
|
550
436
|
}
|
|
551
437
|
|
|
@@ -554,7 +440,7 @@ export class AgentWidget {
|
|
|
554
440
|
this.deactivate();
|
|
555
441
|
if (!record) return;
|
|
556
442
|
const mode: AgentWidgetOpenMode = record.status === "running" || record.status === "queued" ? "live" : "history";
|
|
557
|
-
void this.
|
|
443
|
+
void this.options.onOpen(record, mode);
|
|
558
444
|
}
|
|
559
445
|
|
|
560
446
|
/** Handle terminal input before it reaches the focused prompt editor. */
|
|
@@ -592,7 +478,8 @@ export class AgentWidget {
|
|
|
592
478
|
}
|
|
593
479
|
|
|
594
480
|
/** Render a finished agent line. */
|
|
595
|
-
private renderFinishedLine(a: { id: string; type: SubagentType; status: string; description: string; toolUses: number; startedAt: number; completedAt?: number; error?: string
|
|
481
|
+
private renderFinishedLine(a: { id: string; type: SubagentType; status: string; description: string; toolUses: number; startedAt: number; completedAt?: number; error?: string }, theme: Theme): string {
|
|
482
|
+
const name = getDisplayName(a.type);
|
|
596
483
|
const modeLabel = getPromptModeLabel(a.type);
|
|
597
484
|
const duration = formatMs((a.completedAt ?? Date.now()) - a.startedAt);
|
|
598
485
|
|
|
@@ -621,13 +508,10 @@ export class AgentWidget {
|
|
|
621
508
|
const activity = this.agentActivity.get(a.id);
|
|
622
509
|
if (activity) parts.push(formatTurns(activity.turnCount, activity.maxTurns));
|
|
623
510
|
if (a.toolUses > 0) parts.push(`${a.toolUses} tool use${a.toolUses === 1 ? "" : "s"}`);
|
|
624
|
-
const costText = this.showCost() && a.lifetimeUsage ? formatCost(getLifetimeCost(a.lifetimeUsage)) : "";
|
|
625
|
-
if (costText) parts.push(costText);
|
|
626
511
|
parts.push(duration);
|
|
627
512
|
|
|
628
513
|
const modeTag = modeLabel ? ` ${theme.fg("dim", `(${modeLabel})`)}` : "";
|
|
629
|
-
|
|
630
|
-
return `${icon} ${renderAgentName(a.type, theme, { fallbackColor: "dim" })}${modeTag} ${theme.fg("dim", description)} ${theme.fg("dim", "·")} ${theme.fg("dim", parts.join(" · "))}${statusText}`;
|
|
514
|
+
return `${icon} ${theme.fg("dim", name)}${modeTag} ${theme.fg("dim", truncateLine(a.description))} ${theme.fg("dim", "·")} ${theme.fg("dim", parts.join(" · "))}${statusText}`;
|
|
631
515
|
}
|
|
632
516
|
|
|
633
517
|
/**
|
|
@@ -635,10 +519,13 @@ export class AgentWidget {
|
|
|
635
519
|
* reading live state each time instead of capturing it in a closure.
|
|
636
520
|
*/
|
|
637
521
|
private renderWidget(tui: any, theme: Theme): string[] {
|
|
638
|
-
const
|
|
639
|
-
const running =
|
|
640
|
-
const queued =
|
|
641
|
-
const finished =
|
|
522
|
+
const allAgents = this.widgetAgents();
|
|
523
|
+
const running = allAgents.filter(a => a.status === "running");
|
|
524
|
+
const queued = allAgents.filter(a => a.status === "queued");
|
|
525
|
+
const finished = allAgents.filter(a =>
|
|
526
|
+
a.status !== "running" && a.status !== "queued" && a.completedAt !== undefined
|
|
527
|
+
&& this.options.canOpenHistory(a),
|
|
528
|
+
);
|
|
642
529
|
|
|
643
530
|
const selectedId = this.navigationActive ? this.selectedAgentId : undefined;
|
|
644
531
|
const hasActive = running.length > 0 || queued.length > 0;
|
|
@@ -658,9 +545,7 @@ export class AgentWidget {
|
|
|
658
545
|
// Build sections separately for overflow-aware assembly.
|
|
659
546
|
// Each running agent = 2 lines (header + activity), finished = 1 line, queued = 1 line.
|
|
660
547
|
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
const finishedLines: WidgetRow[] = [];
|
|
548
|
+
const finishedLines: { record: AgentRecord; lines: string[] }[] = [];
|
|
664
549
|
for (const a of finished) {
|
|
665
550
|
const marker = a.id === selectedId ? theme.fg("accent", "●") : theme.fg("dim", "○");
|
|
666
551
|
finishedLines.push({
|
|
@@ -669,32 +554,23 @@ export class AgentWidget {
|
|
|
669
554
|
});
|
|
670
555
|
}
|
|
671
556
|
|
|
672
|
-
const runningLines:
|
|
557
|
+
const runningLines: { record: AgentRecord; lines: string[] }[] = []; // each entry is [header, activity]
|
|
673
558
|
for (const a of running) {
|
|
559
|
+
const name = getDisplayName(a.type);
|
|
674
560
|
const modeLabel = getPromptModeLabel(a.type);
|
|
675
561
|
const modeTag = modeLabel ? ` ${theme.fg("dim", `(${modeLabel})`)}` : "";
|
|
676
562
|
const elapsed = formatMs(Date.now() - a.startedAt);
|
|
677
563
|
|
|
678
564
|
const bg = this.agentActivity.get(a.id);
|
|
679
565
|
const toolUses = bg?.toolUses ?? a.toolUses;
|
|
680
|
-
|
|
681
|
-
// survives the agent finishing and includes nested-child usage.
|
|
682
|
-
const tokens = getLifetimeTotal(a.lifetimeUsage);
|
|
566
|
+
const tokens = getLifetimeTotal(bg?.lifetimeUsage);
|
|
683
567
|
const contextPercent = getSessionContextPercent(bg?.session);
|
|
684
568
|
const tokenText = tokens > 0 ? formatSessionTokens(tokens, contextPercent, theme, a.compactionCount) : "";
|
|
685
|
-
const costText = this.showCost() ? formatCost(getLifetimeCost(a.lifetimeUsage)) : "";
|
|
686
569
|
|
|
687
570
|
const parts: string[] = [];
|
|
688
|
-
if (this.showModel()) {
|
|
689
|
-
const { modelName, tags } = buildInvocationTags(a.invocation);
|
|
690
|
-
if (modelName) parts.push(modelName);
|
|
691
|
-
const thinkingTag = tags.find(tag => tag.startsWith("thinking: "));
|
|
692
|
-
if (thinkingTag) parts.push(thinkingTag);
|
|
693
|
-
}
|
|
694
571
|
if (bg) parts.push(formatTurns(bg.turnCount, bg.maxTurns));
|
|
695
572
|
if (toolUses > 0) parts.push(`${toolUses} tool use${toolUses === 1 ? "" : "s"}`);
|
|
696
573
|
if (tokenText) parts.push(tokenText);
|
|
697
|
-
if (costText) parts.push(costText);
|
|
698
574
|
parts.push(elapsed);
|
|
699
575
|
const statsText = parts.join(" · ");
|
|
700
576
|
|
|
@@ -704,13 +580,13 @@ export class AgentWidget {
|
|
|
704
580
|
runningLines.push({
|
|
705
581
|
record: a,
|
|
706
582
|
lines: [
|
|
707
|
-
truncate(theme.fg("dim", "├─") + ` ${marker} ${theme.fg("accent", frame)} ${
|
|
708
|
-
truncate(theme.fg("dim", "│ ") + ` ${theme.fg("dim", `⎿ ${
|
|
583
|
+
truncate(theme.fg("dim", "├─") + ` ${marker} ${theme.fg("accent", frame)} ${theme.bold(name)}${modeTag} ${theme.fg("muted", truncateLine(a.description))} ${theme.fg("dim", "·")} ${fgPreservingNestedStyles(theme, "dim", statsText)}`),
|
|
584
|
+
truncate(theme.fg("dim", "│ ") + ` ${theme.fg("dim", `⎿ ${activity}`)}`),
|
|
709
585
|
],
|
|
710
586
|
});
|
|
711
587
|
}
|
|
712
588
|
|
|
713
|
-
const queuedLines:
|
|
589
|
+
const queuedLines: { record: AgentRecord; lines: string[] }[] = queued.map(a => {
|
|
714
590
|
const marker = a.id === selectedId ? theme.fg("accent", "●") : theme.fg("dim", "○");
|
|
715
591
|
return {
|
|
716
592
|
record: a,
|
|
@@ -718,25 +594,10 @@ export class AgentWidget {
|
|
|
718
594
|
};
|
|
719
595
|
});
|
|
720
596
|
|
|
721
|
-
// Keep the idle widget compact: a queue is one logical status line until
|
|
722
|
-
// navigation starts. While navigating, expand it back to the same per-agent
|
|
723
|
-
// roster used by key handling so every selected queued record can render.
|
|
724
|
-
const queuedSummary: WidgetRow[] = queued.length > 0 && !this.navigationActive
|
|
725
|
-
? [{
|
|
726
|
-
record: queued[0],
|
|
727
|
-
recordCount: queued.length,
|
|
728
|
-
lines: [truncate(theme.fg("dim", "├─") + ` ${theme.fg("dim", "○")} ${theme.fg("muted", "◦")} ${theme.fg("dim", `${queued.length} queued`)}`)],
|
|
729
|
-
}]
|
|
730
|
-
: [];
|
|
731
|
-
|
|
732
597
|
// Assemble with a responsive cap (heading + overflow indicator = 2
|
|
733
598
|
// reserved lines when content exceeds the available body budget).
|
|
734
599
|
const maxBody = maxLines - 1; // heading takes 1 line
|
|
735
|
-
const rows
|
|
736
|
-
...runningLines,
|
|
737
|
-
...(this.navigationActive ? queuedLines : queuedSummary),
|
|
738
|
-
...finishedLines,
|
|
739
|
-
];
|
|
600
|
+
const rows = [...runningLines, ...queuedLines, ...finishedLines];
|
|
740
601
|
const totalBody = rows.reduce((total, row) => total + row.lines.length, 0);
|
|
741
602
|
|
|
742
603
|
const heading = "Agents ↑↓ select · enter view · esc back";
|
|
@@ -753,79 +614,11 @@ export class AgentWidget {
|
|
|
753
614
|
if (rows.length > 0) {
|
|
754
615
|
const lastRow = rows[rows.length - 1];
|
|
755
616
|
const lastStart = lines.length - lastRow.lines.length;
|
|
756
|
-
// Keep the selected row's bullet adjacent to the normal branch marker.
|
|
757
|
-
// This makes the active row unambiguous even when it is the last row.
|
|
758
|
-
const lastRowSelected = this.navigationActive && lastRow.record.id === selectedId;
|
|
759
|
-
if (!lastRowSelected) lines[lastStart] = lines[lastStart].replace("├─", "└─");
|
|
760
|
-
if (lastRow.lines.length === 2) {
|
|
761
|
-
lines[lastStart + 1] = lines[lastStart + 1].replace("│ ", " ");
|
|
762
|
-
}
|
|
763
|
-
}
|
|
764
|
-
} else if (!this.navigationActive && queuedSummary.length > 0) {
|
|
765
|
-
// In the idle view the queue is deliberately one summary row. Preserve
|
|
766
|
-
// that row before spending space on terminal history, and account for
|
|
767
|
-
// the real number of queued records if the summary itself cannot fit.
|
|
768
|
-
// Navigation expands the queue into per-record rows and uses the full
|
|
769
|
-
// contiguous viewport below.
|
|
770
|
-
const visibleRows: WidgetRow[] = [];
|
|
771
|
-
const hiddenRows: WidgetRow[] = [];
|
|
772
|
-
let bodyBudget = Math.max(0, maxBody - 1); // reserve the footer
|
|
773
|
-
let runningBudget = Math.max(0, bodyBudget - 1); // reserve queued summary
|
|
774
|
-
|
|
775
|
-
for (const row of runningLines) {
|
|
776
|
-
const height = row.lines.length;
|
|
777
|
-
if (height <= runningBudget) {
|
|
778
|
-
visibleRows.push(row);
|
|
779
|
-
runningBudget -= height;
|
|
780
|
-
bodyBudget -= height;
|
|
781
|
-
} else {
|
|
782
|
-
hiddenRows.push(row);
|
|
783
|
-
}
|
|
784
|
-
}
|
|
785
|
-
|
|
786
|
-
const summaryRow = queuedSummary[0];
|
|
787
|
-
if (bodyBudget > 0) {
|
|
788
|
-
visibleRows.push(summaryRow);
|
|
789
|
-
bodyBudget -= summaryRow.lines.length;
|
|
790
|
-
} else {
|
|
791
|
-
hiddenRows.push(summaryRow);
|
|
792
|
-
}
|
|
793
|
-
|
|
794
|
-
for (const row of finishedLines) {
|
|
795
|
-
const height = row.lines.length;
|
|
796
|
-
if (height <= bodyBudget) {
|
|
797
|
-
visibleRows.push(row);
|
|
798
|
-
bodyBudget -= height;
|
|
799
|
-
} else {
|
|
800
|
-
hiddenRows.push(row);
|
|
801
|
-
}
|
|
802
|
-
}
|
|
803
|
-
|
|
804
|
-
for (const row of visibleRows) lines.push(...row.lines);
|
|
805
|
-
if (visibleRows.length > 0) {
|
|
806
|
-
const lastRow = visibleRows[visibleRows.length - 1];
|
|
807
|
-
const lastStart = lines.length - lastRow.lines.length;
|
|
808
617
|
lines[lastStart] = lines[lastStart].replace("├─", "└─");
|
|
809
618
|
if (lastRow.lines.length === 2) {
|
|
810
619
|
lines[lastStart + 1] = lines[lastStart + 1].replace("│ ", " ");
|
|
811
620
|
}
|
|
812
621
|
}
|
|
813
|
-
|
|
814
|
-
const hiddenCount = (rows: readonly WidgetRow[]): number =>
|
|
815
|
-
rows.reduce((total, row) => total + (row.recordCount ?? 1), 0);
|
|
816
|
-
const categoryCount = (status: string): number => hiddenRows
|
|
817
|
-
.filter(row => row.record.status === status)
|
|
818
|
-
.reduce((total, row) => total + (row.recordCount ?? 1), 0);
|
|
819
|
-
const finishedCount = hiddenRows
|
|
820
|
-
.filter(row => row.record.status !== "running" && row.record.status !== "queued")
|
|
821
|
-
.reduce((total, row) => total + (row.recordCount ?? 1), 0);
|
|
822
|
-
const categoryCounts: string[] = [
|
|
823
|
-
["running", categoryCount("running")] as [string, number],
|
|
824
|
-
["queued", categoryCount("queued")] as [string, number],
|
|
825
|
-
["finished", finishedCount] as [string, number],
|
|
826
|
-
].filter(([, count]) => count > 0).map(([label, count]) => `${count} ${label}`);
|
|
827
|
-
const hidden = hiddenCount(hiddenRows);
|
|
828
|
-
lines.push(truncate(theme.fg("dim", "└─") + ` ${theme.fg("dim", `+${hidden} more (${categoryCounts.join(", ")})`)}`));
|
|
829
622
|
} else {
|
|
830
623
|
// Reserve one line for a directional overflow summary. The viewport is
|
|
831
624
|
// a contiguous slice in roster order, so the same slice is navigable and
|
|
@@ -877,21 +670,14 @@ export class AgentWidget {
|
|
|
877
670
|
end = start + 1;
|
|
878
671
|
}
|
|
879
672
|
|
|
880
|
-
const
|
|
881
|
-
|
|
882
|
-
const hiddenBefore = countRows(rows.slice(0, start));
|
|
883
|
-
const hiddenAfter = countRows(rows.slice(end));
|
|
673
|
+
const hiddenBefore = start;
|
|
674
|
+
const hiddenAfter = Math.max(0, rows.length - end);
|
|
884
675
|
const hidden = hiddenBefore + hiddenAfter;
|
|
885
676
|
const hiddenRows = rows.filter((_row, index) => index < start || index >= end);
|
|
886
|
-
const categoryCount = (status: string): number => hiddenRows
|
|
887
|
-
.filter(row => row.record.status === status)
|
|
888
|
-
.reduce((total, row) => total + (row.recordCount ?? 1), 0);
|
|
889
677
|
const categoryCounts: string[] = [
|
|
890
|
-
["running",
|
|
891
|
-
["queued",
|
|
892
|
-
["finished", hiddenRows
|
|
893
|
-
.filter(row => row.record.status !== "running" && row.record.status !== "queued")
|
|
894
|
-
.reduce((total, row) => total + (row.recordCount ?? 1), 0)] as [string, number],
|
|
678
|
+
["running", hiddenRows.filter(row => row.record.status === "running").length] as [string, number],
|
|
679
|
+
["queued", hiddenRows.filter(row => row.record.status === "queued").length] as [string, number],
|
|
680
|
+
["finished", hiddenRows.filter(row => row.record.status !== "running" && row.record.status !== "queued").length] as [string, number],
|
|
895
681
|
].filter(([, count]) => count > 0).map(([label, count]) => `${count} ${label}`);
|
|
896
682
|
const direction = [
|
|
897
683
|
...(hiddenBefore > 0 ? [`↑ ${hiddenBefore} more`] : []),
|
|
@@ -972,7 +758,7 @@ export class AgentWidget {
|
|
|
972
758
|
isBackground: a.isBackground,
|
|
973
759
|
hasSession: a.session !== undefined,
|
|
974
760
|
transcriptPath: a.transcriptPath,
|
|
975
|
-
openableHistory: this.
|
|
761
|
+
openableHistory: this.options.canOpenHistory(a),
|
|
976
762
|
})),
|
|
977
763
|
activities,
|
|
978
764
|
navigationActive: this.navigationActive,
|
|
@@ -984,10 +770,8 @@ export class AgentWidget {
|
|
|
984
770
|
/** Force an immediate widget update. `advanceSpinner` is reserved for the timer. */
|
|
985
771
|
update(advanceSpinner = false) {
|
|
986
772
|
if (!this.uiCtx) return;
|
|
987
|
-
// Refresh the manager snapshot once per lifecycle update. Arrow handling
|
|
988
|
-
// only changes selection and requests a render; it never scans the manager.
|
|
989
|
-
const roster = this.refreshRoster();
|
|
990
773
|
const allAgents = this.widgetAgents();
|
|
774
|
+
const roster = this.roster();
|
|
991
775
|
|
|
992
776
|
// Lightweight existence checks — full categorization happens in renderWidget()
|
|
993
777
|
let runningCount = 0;
|
|
@@ -1101,11 +885,6 @@ export class AgentWidget {
|
|
|
1101
885
|
this.selectedAgentId = undefined;
|
|
1102
886
|
this.selectedRosterIndex = 0;
|
|
1103
887
|
this.viewportStart = 0;
|
|
1104
|
-
this.cachedAgents = [];
|
|
1105
|
-
this.cachedRoster = [];
|
|
1106
|
-
this.rosterCacheKey = undefined;
|
|
1107
|
-
this.rosterCacheInitialized = false;
|
|
1108
|
-
this.historyOpenabilityCache.clear();
|
|
1109
888
|
this.uiCtx = undefined;
|
|
1110
889
|
}
|
|
1111
890
|
}
|
|
@@ -251,10 +251,6 @@ function bashBlock(
|
|
|
251
251
|
const body = command ? `$ ${command}${output ? `\n${output}` : ""}` : output;
|
|
252
252
|
const toolLine = `· bash → ${command || "(no command)"}`;
|
|
253
253
|
const fullText = bound(body, maxChars);
|
|
254
|
-
const result: ConversationToolResultSnapshot = {
|
|
255
|
-
toolName: "bash",
|
|
256
|
-
content: [{ type: "text", text: body }],
|
|
257
|
-
};
|
|
258
254
|
return {
|
|
259
255
|
id,
|
|
260
256
|
kind: "tool",
|
|
@@ -265,8 +261,6 @@ function bashBlock(
|
|
|
265
261
|
fullText,
|
|
266
262
|
toolLine,
|
|
267
263
|
toolName: "bash",
|
|
268
|
-
toolResult: result,
|
|
269
|
-
result,
|
|
270
264
|
};
|
|
271
265
|
}
|
|
272
266
|
|