@llblab/pi-actors 0.41.1 → 0.42.1
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/AGENTS.md +11 -6
- package/BACKLOG.md +1 -65
- package/CHANGELOG.md +25 -0
- package/README.md +24 -4
- package/dist/index.js +25 -121
- package/dist/lib/async-runs.d.ts +37 -5
- package/dist/lib/async-runs.js +181 -51
- package/dist/lib/automatic-review-runtime.d.ts +18 -0
- package/dist/lib/automatic-review-runtime.js +96 -0
- package/dist/lib/command-templates.js +1 -1
- package/dist/lib/draft-consolidation-transaction.d.ts +65 -0
- package/dist/lib/draft-consolidation-transaction.js +610 -0
- package/dist/lib/draft-consolidation.d.ts +35 -0
- package/dist/lib/draft-consolidation.js +126 -0
- package/dist/lib/draft-review.d.ts +56 -0
- package/dist/lib/draft-review.js +254 -0
- package/dist/lib/draft-sleep.d.ts +65 -0
- package/dist/lib/draft-sleep.js +468 -0
- package/dist/lib/file-state.d.ts +8 -1
- package/dist/lib/file-state.js +115 -18
- package/dist/lib/inspector-actions.d.ts +16 -0
- package/dist/lib/inspector-actions.js +57 -0
- package/dist/lib/inspector-command.d.ts +7 -0
- package/dist/lib/inspector-command.js +37 -0
- package/dist/lib/inspector-overlay.d.ts +23 -3
- package/dist/lib/inspector-overlay.js +327 -70
- package/dist/lib/inspector.d.ts +9 -0
- package/dist/lib/inspector.js +49 -1
- package/dist/lib/observability.d.ts +13 -0
- package/dist/lib/observability.js +160 -6
- package/dist/lib/paths.d.ts +7 -0
- package/dist/lib/paths.js +28 -0
- package/dist/lib/recipes-discovery.js +32 -24
- package/dist/lib/recipes-usage.d.ts +18 -6
- package/dist/lib/recipes-usage.js +445 -34
- package/dist/lib/review-control.d.ts +14 -0
- package/dist/lib/review-control.js +111 -0
- package/dist/lib/review-diagnostics.d.ts +11 -0
- package/dist/lib/review-diagnostics.js +148 -0
- package/dist/lib/review-projection.d.ts +14 -0
- package/dist/lib/review-projection.js +170 -0
- package/dist/lib/run-ui-runtime.d.ts +18 -0
- package/dist/lib/run-ui-runtime.js +123 -0
- package/dist/lib/runs-artifacts.d.ts +1 -1
- package/dist/lib/runs-artifacts.js +1 -1
- package/dist/lib/runs-control.d.ts +10 -2
- package/dist/lib/runs-control.js +37 -7
- package/dist/lib/runs-identity.d.ts +1 -1
- package/dist/lib/runs-identity.js +1 -1
- package/dist/lib/runs-index.d.ts +11 -2
- package/dist/lib/runs-index.js +46 -23
- package/dist/lib/runs-mailbox.d.ts +1 -1
- package/dist/lib/runs-mailbox.js +1 -1
- package/dist/lib/runs-messages.d.ts +1 -1
- package/dist/lib/runs-messages.js +1 -1
- package/dist/lib/runs-outbox.d.ts +1 -1
- package/dist/lib/runs-outbox.js +1 -1
- package/dist/lib/runs-ownership.d.ts +1 -1
- package/dist/lib/runs-ownership.js +18 -4
- package/dist/lib/runs-parent-teardown.d.ts +51 -0
- package/dist/lib/runs-parent-teardown.js +172 -0
- package/dist/lib/runs-process.d.ts +1 -1
- package/dist/lib/runs-process.js +5 -4
- package/dist/lib/runs-retention.d.ts +1 -1
- package/dist/lib/runs-retention.js +1 -1
- package/dist/lib/runs-start.d.ts +5 -3
- package/dist/lib/runs-start.js +7 -48
- package/dist/lib/runs-status.d.ts +5 -3
- package/dist/lib/runs-status.js +7 -6
- package/dist/lib/runtime.d.ts +7 -1
- package/dist/lib/runtime.js +32 -18
- package/dist/lib/tool-review-lineage-transaction.d.ts +27 -0
- package/dist/lib/tool-review-lineage-transaction.js +597 -0
- package/dist/lib/tool-review-lineage.d.ts +24 -0
- package/dist/lib/tool-review-lineage.js +98 -0
- package/dist/lib/tool-review-scheduler.d.ts +80 -0
- package/dist/lib/tool-review-scheduler.js +494 -0
- package/dist/lib/tool-review-transaction.d.ts +50 -0
- package/dist/lib/tool-review-transaction.js +362 -0
- package/dist/lib/tool-review.d.ts +56 -0
- package/dist/lib/tool-review.js +197 -0
- package/dist/lib/tools-inspect.js +28 -4
- package/dist/lib/tools-local.js +21 -4
- package/dist/lib/tools-message.d.ts +1 -0
- package/dist/lib/tools-message.js +29 -17
- package/dist/lib/tools-spawn.js +14 -2
- package/dist/lib/tools.d.ts +1 -0
- package/dist/lib/tools.js +1 -0
- package/dist/recipes/draft-review.json +24 -0
- package/dist/recipes/tool-review.json +24 -0
- package/dist/scripts/async-runner.mjs +9 -9
- package/dist/scripts/build-dist.mjs +6 -1
- package/dist/scripts/conformance.mjs +6 -1
- package/dist/scripts/recipe-utils.mjs +3 -3
- package/dist/scripts/release-gates.mjs +165 -0
- package/dist/skills/actors/SKILL.md +9 -8
- package/dist/skills/swarm/SKILL.md +1 -1
- package/docs/actor-inspector.md +20 -11
- package/docs/async-runs.md +16 -2
- package/docs/recipe-library.md +7 -3
- package/docs/template-recipes.md +4 -11
- package/docs/tool-registry.md +13 -4
- package/index.ts +27 -142
- package/lib/async-runs.ts +283 -67
- package/lib/automatic-review-runtime.ts +135 -0
- package/lib/command-templates.ts +1 -1
- package/lib/draft-consolidation-transaction.ts +821 -0
- package/lib/draft-consolidation.ts +181 -0
- package/lib/draft-review.ts +325 -0
- package/lib/draft-sleep.ts +576 -0
- package/lib/file-state.ts +143 -19
- package/lib/inspector-actions.ts +79 -0
- package/lib/inspector-command.ts +54 -0
- package/lib/inspector-overlay.ts +377 -91
- package/lib/inspector.ts +73 -1
- package/lib/observability.ts +194 -5
- package/lib/paths.ts +43 -0
- package/lib/recipes-discovery.ts +34 -26
- package/lib/recipes-usage.ts +569 -40
- package/lib/review-control.ts +137 -0
- package/lib/review-diagnostics.ts +164 -0
- package/lib/review-projection.ts +200 -0
- package/lib/run-ui-runtime.ts +153 -0
- package/lib/runs-artifacts.ts +1 -1
- package/lib/runs-control.ts +69 -6
- package/lib/runs-identity.ts +1 -1
- package/lib/runs-index.ts +57 -21
- package/lib/runs-mailbox.ts +1 -1
- package/lib/runs-messages.ts +1 -1
- package/lib/runs-outbox.ts +1 -1
- package/lib/runs-ownership.ts +23 -4
- package/lib/runs-parent-teardown.ts +257 -0
- package/lib/runs-process.ts +5 -4
- package/lib/runs-retention.ts +1 -1
- package/lib/runs-start.ts +13 -68
- package/lib/runs-status.ts +17 -8
- package/lib/runtime.ts +34 -17
- package/lib/tool-review-lineage-transaction.ts +881 -0
- package/lib/tool-review-lineage.ts +145 -0
- package/lib/tool-review-scheduler.ts +635 -0
- package/lib/tool-review-transaction.ts +563 -0
- package/lib/tool-review.ts +270 -0
- package/lib/tools-inspect.ts +35 -4
- package/lib/tools-local.ts +29 -4
- package/lib/tools-message.ts +45 -30
- package/lib/tools-spawn.ts +22 -2
- package/lib/tools.ts +5 -0
- package/package.json +5 -3
- package/recipes/draft-review.json +24 -0
- package/recipes/tool-review.json +24 -0
- package/scripts/async-runner.mjs +9 -9
- package/scripts/build-dist.mjs +6 -1
- package/scripts/conformance.mjs +6 -1
- package/scripts/recipe-utils.mjs +3 -3
- package/scripts/release-gates.mjs +165 -0
- package/skills/actors/SKILL.md +9 -8
- package/skills/swarm/SKILL.md +1 -1
package/lib/inspector-overlay.ts
CHANGED
|
@@ -12,21 +12,36 @@ import {
|
|
|
12
12
|
matchesKey,
|
|
13
13
|
truncateToWidth,
|
|
14
14
|
visibleWidth,
|
|
15
|
-
wrapTextWithAnsi,
|
|
16
15
|
type TUI,
|
|
17
16
|
} from "@earendil-works/pi-tui";
|
|
18
17
|
|
|
19
18
|
import * as Inspector from "./inspector.ts";
|
|
20
19
|
|
|
21
|
-
export type ActorInspectorOverlayTab = "communications" | "turns";
|
|
20
|
+
export type ActorInspectorOverlayTab = "recipe" | "communications" | "turns";
|
|
21
|
+
|
|
22
|
+
const INSPECTOR_TABS: ActorInspectorOverlayTab[] = [
|
|
23
|
+
"recipe",
|
|
24
|
+
"communications",
|
|
25
|
+
"turns",
|
|
26
|
+
];
|
|
22
27
|
|
|
23
28
|
type DetailLogicalLine = string | { sectionBreak: true };
|
|
24
29
|
|
|
25
30
|
const DETAIL_SECTION_BREAK: DetailLogicalLine = { sectionBreak: true };
|
|
26
31
|
|
|
32
|
+
export interface ActorInspectorOverlayActionResult {
|
|
33
|
+
ok: boolean;
|
|
34
|
+
message: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
27
37
|
export interface ActorInspectorOverlayOptions {
|
|
28
38
|
done: () => void;
|
|
39
|
+
killRun?: (
|
|
40
|
+
run: string,
|
|
41
|
+
runInstanceId: string,
|
|
42
|
+
) => ActorInspectorOverlayActionResult;
|
|
29
43
|
ownerId: string;
|
|
44
|
+
readTurns?: (stateDir: string) => Inspector.ActorInspectorTurnItem[];
|
|
30
45
|
stateRoot: string;
|
|
31
46
|
theme: Theme;
|
|
32
47
|
tui: TUI;
|
|
@@ -34,7 +49,12 @@ export interface ActorInspectorOverlayOptions {
|
|
|
34
49
|
|
|
35
50
|
export class ActorInspectorOverlay {
|
|
36
51
|
private readonly done: () => void;
|
|
52
|
+
private readonly killRun?: (
|
|
53
|
+
run: string,
|
|
54
|
+
runInstanceId: string,
|
|
55
|
+
) => ActorInspectorOverlayActionResult;
|
|
37
56
|
private readonly ownerId: string;
|
|
57
|
+
private readonly readTurns: (stateDir: string) => Inspector.ActorInspectorTurnItem[];
|
|
38
58
|
private readonly stateRoot: string;
|
|
39
59
|
private readonly theme: Theme;
|
|
40
60
|
private readonly tui: TUI;
|
|
@@ -48,24 +68,33 @@ export class ActorInspectorOverlay {
|
|
|
48
68
|
private detailOpen = false;
|
|
49
69
|
private detailScroll = 0;
|
|
50
70
|
private detailTurn?: Inspector.ActorInspectorTurnItem;
|
|
51
|
-
private
|
|
71
|
+
private feedback?: ActorInspectorOverlayActionResult;
|
|
72
|
+
private killConfirmation?: { run: string; runInstanceId: string; status: string };
|
|
73
|
+
private killDialogChoice: "cancel" | "kill" = "cancel";
|
|
52
74
|
private readonly readKeys = new Set<string>();
|
|
53
|
-
private focus: "runs" | "tabs" | "list" | "detail" | "select" = "tabs";
|
|
75
|
+
private focus: "runs" | "tabs" | "recipe" | "list" | "detail" | "select" = "tabs";
|
|
54
76
|
private filterControlIndex = 0;
|
|
55
77
|
private menuLevel: "run" | "filter" | "value" = "filter";
|
|
56
78
|
private selectorIndex = 0;
|
|
79
|
+
private recipeScroll = 0;
|
|
57
80
|
private rowIndex = 0;
|
|
58
81
|
private selectedRun?: string;
|
|
59
82
|
private subagentIndex = 0;
|
|
60
|
-
private tab: ActorInspectorOverlayTab = "
|
|
83
|
+
private tab: ActorInspectorOverlayTab = "recipe";
|
|
84
|
+
private turnCache?: { run: string; turns: Inspector.ActorInspectorTurnItem[] };
|
|
61
85
|
|
|
62
86
|
constructor(options: ActorInspectorOverlayOptions) {
|
|
63
87
|
this.done = options.done;
|
|
88
|
+
this.killRun = options.killRun;
|
|
64
89
|
this.ownerId = options.ownerId;
|
|
90
|
+
this.readTurns = options.readTurns ?? Inspector.readActorInspectorTurns;
|
|
65
91
|
this.stateRoot = options.stateRoot;
|
|
66
92
|
this.theme = options.theme;
|
|
67
93
|
this.tui = options.tui;
|
|
68
|
-
this.refreshTimer = setInterval(() =>
|
|
94
|
+
this.refreshTimer = setInterval(() => {
|
|
95
|
+
if (this.focus !== "list" && this.focus !== "detail") this.turnCache = undefined;
|
|
96
|
+
this.tui.requestRender();
|
|
97
|
+
}, 1000);
|
|
69
98
|
this.refreshTimer.unref?.();
|
|
70
99
|
}
|
|
71
100
|
|
|
@@ -76,6 +105,31 @@ export class ActorInspectorOverlay {
|
|
|
76
105
|
this.done();
|
|
77
106
|
return;
|
|
78
107
|
}
|
|
108
|
+
if (this.killConfirmation) {
|
|
109
|
+
const key = data.toLowerCase();
|
|
110
|
+
if (matchesKey(data, "escape") || key === "n") {
|
|
111
|
+
this.cancelKill();
|
|
112
|
+
} else if (
|
|
113
|
+
matchesKey(data, "left") ||
|
|
114
|
+
matchesKey(data, "right") ||
|
|
115
|
+
matchesKey(data, "tab")
|
|
116
|
+
) {
|
|
117
|
+
this.killDialogChoice =
|
|
118
|
+
this.killDialogChoice === "cancel" ? "kill" : "cancel";
|
|
119
|
+
} else if (key === "y") {
|
|
120
|
+
this.confirmKill();
|
|
121
|
+
} else if (matchesKey(data, "return")) {
|
|
122
|
+
if (this.killDialogChoice === "kill") this.confirmKill();
|
|
123
|
+
else this.cancelKill();
|
|
124
|
+
}
|
|
125
|
+
this.tui.requestRender();
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
if (data.toLowerCase() === "k") {
|
|
129
|
+
this.requestKill(runs);
|
|
130
|
+
this.tui.requestRender();
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
79
133
|
if (matchesKey(data, "escape")) {
|
|
80
134
|
if (this.focus === "select") {
|
|
81
135
|
if (this.menuLevel === "value") {
|
|
@@ -83,6 +137,7 @@ export class ActorInspectorOverlay {
|
|
|
83
137
|
this.selectorIndex = this.filterControlIndex;
|
|
84
138
|
} else this.focus = this.menuLevel === "run" ? "runs" : "tabs";
|
|
85
139
|
} else if (this.focus === "detail") this.backDetail();
|
|
140
|
+
else if (this.focus === "recipe") this.focus = "tabs";
|
|
86
141
|
else this.done();
|
|
87
142
|
this.tui.requestRender();
|
|
88
143
|
return;
|
|
@@ -102,18 +157,27 @@ export class ActorInspectorOverlay {
|
|
|
102
157
|
this.tui.requestRender();
|
|
103
158
|
return;
|
|
104
159
|
}
|
|
160
|
+
if (this.focus === "recipe") {
|
|
161
|
+
if (matchesKey(data, "up")) {
|
|
162
|
+
if (this.recipeScroll === 0) this.focus = "tabs";
|
|
163
|
+
else this.recipeScroll -= 1;
|
|
164
|
+
} else if (matchesKey(data, "down")) this.recipeScroll += 1;
|
|
165
|
+
else if (matchesKey(data, "pageUp"))
|
|
166
|
+
this.recipeScroll = Math.max(0, this.recipeScroll - this.contentViewportRows());
|
|
167
|
+
else if (matchesKey(data, "pageDown"))
|
|
168
|
+
this.recipeScroll += this.contentViewportRows();
|
|
169
|
+
else if (matchesKey(data, "left")) this.focus = "tabs";
|
|
170
|
+
this.tui.requestRender();
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
105
173
|
if (this.focus === "detail") {
|
|
106
174
|
if (matchesKey(data, "up")) this.detailScroll = Math.max(0, this.detailScroll - 1);
|
|
107
175
|
else if (matchesKey(data, "down")) this.detailScroll += 1;
|
|
176
|
+
else if (matchesKey(data, "pageUp"))
|
|
177
|
+
this.detailScroll = Math.max(0, this.detailScroll - this.contentViewportRows());
|
|
178
|
+
else if (matchesKey(data, "pageDown"))
|
|
179
|
+
this.detailScroll += this.contentViewportRows();
|
|
108
180
|
else if (matchesKey(data, "left")) this.backDetail();
|
|
109
|
-
else if (
|
|
110
|
-
(matchesKey(data, "right") || matchesKey(data, "return")) &&
|
|
111
|
-
this.detailView === "evidence" &&
|
|
112
|
-
this.tab === "turns"
|
|
113
|
-
) {
|
|
114
|
-
this.detailView = "transcript";
|
|
115
|
-
this.detailScroll = 0;
|
|
116
|
-
}
|
|
117
181
|
this.tui.requestRender();
|
|
118
182
|
return;
|
|
119
183
|
}
|
|
@@ -124,16 +188,30 @@ export class ActorInspectorOverlay {
|
|
|
124
188
|
else if (matchesKey(data, "return") && runs.length > 0) this.openRunMenu();
|
|
125
189
|
} else if (this.focus === "tabs") {
|
|
126
190
|
if (matchesKey(data, "left") || matchesKey(data, "right")) {
|
|
127
|
-
|
|
191
|
+
const current = INSPECTOR_TABS.indexOf(this.tab);
|
|
192
|
+
const direction = matchesKey(data, "right") ? 1 : -1;
|
|
193
|
+
this.tab = INSPECTOR_TABS[
|
|
194
|
+
(current + direction + INSPECTOR_TABS.length) % INSPECTOR_TABS.length
|
|
195
|
+
]!;
|
|
128
196
|
this.filterControlIndex = 0;
|
|
197
|
+
this.recipeScroll = 0;
|
|
129
198
|
this.rowIndex = 0;
|
|
130
199
|
} else if (matchesKey(data, "up")) this.focus = "runs";
|
|
200
|
+
else if (
|
|
201
|
+
(matchesKey(data, "down") || matchesKey(data, "return")) &&
|
|
202
|
+
this.tab === "recipe"
|
|
203
|
+
) this.focus = "recipe";
|
|
131
204
|
else if (matchesKey(data, "down") && this.listItemCount() > 0)
|
|
132
205
|
this.focus = "list";
|
|
133
206
|
else if (matchesKey(data, "return")) this.openFilterMenu();
|
|
134
207
|
} else if (this.focus === "list") {
|
|
135
208
|
const count = this.tab === "communications" ? this.communicationPreviews().length : this.turnItems().length;
|
|
136
|
-
if (matchesKey(data, "
|
|
209
|
+
if (matchesKey(data, "left")) this.focus = "tabs";
|
|
210
|
+
else if (matchesKey(data, "pageUp"))
|
|
211
|
+
this.rowIndex = Math.max(0, this.rowIndex - this.contentViewportRows());
|
|
212
|
+
else if (matchesKey(data, "pageDown"))
|
|
213
|
+
this.rowIndex = Math.min(Math.max(0, count - 1), this.rowIndex + this.contentViewportRows());
|
|
214
|
+
else if (matchesKey(data, "up")) {
|
|
137
215
|
if (this.rowIndex === 0) this.focus = "tabs";
|
|
138
216
|
else this.rowIndex -= 1;
|
|
139
217
|
} else if (matchesKey(data, "down"))
|
|
@@ -148,6 +226,8 @@ export class ActorInspectorOverlay {
|
|
|
148
226
|
this.ensureSelectedRun(this.runs());
|
|
149
227
|
const safeWidth = Math.max(24, width);
|
|
150
228
|
const innerWidth = Math.max(1, safeWidth - 2);
|
|
229
|
+
if (this.killConfirmation)
|
|
230
|
+
return this.renderKillDialog(safeWidth, innerWidth);
|
|
151
231
|
const lines: string[] = [];
|
|
152
232
|
lines.push(this.border("╭", " Actor Inspector ", "╮", innerWidth));
|
|
153
233
|
lines.push(this.row(this.renderRunControl(), innerWidth));
|
|
@@ -194,11 +274,24 @@ export class ActorInspectorOverlay {
|
|
|
194
274
|
);
|
|
195
275
|
} else lines.push(this.border("├", "", "┤", innerWidth));
|
|
196
276
|
this.contentStripeIndices = [];
|
|
197
|
-
const
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
277
|
+
const baseContent = this.detailOpen
|
|
278
|
+
? this.renderDetail(innerWidth)
|
|
279
|
+
: this.selectedRun
|
|
280
|
+
? this.renderTimeline(innerWidth)
|
|
281
|
+
: this.renderRunSelector(innerWidth);
|
|
282
|
+
const content = this.feedback && !this.killConfirmation
|
|
283
|
+
? [
|
|
284
|
+
this.theme.fg(
|
|
285
|
+
this.feedback.ok
|
|
286
|
+
? "success"
|
|
287
|
+
: /^Kill (failed|rejected):/.test(this.feedback.message)
|
|
288
|
+
? "error"
|
|
289
|
+
: "warning",
|
|
290
|
+
` ${this.feedback.message}`,
|
|
291
|
+
),
|
|
292
|
+
...baseContent.slice(0, Math.max(0, this.contentViewportRows() - 1)),
|
|
293
|
+
]
|
|
294
|
+
: baseContent;
|
|
202
295
|
const contentViewportRows = this.contentViewportRows();
|
|
203
296
|
const selectorRows = selector.slice(1);
|
|
204
297
|
for (let index = 0; index < Math.max(content.length, contentViewportRows); index += 1) {
|
|
@@ -256,7 +349,9 @@ export class ActorInspectorOverlay {
|
|
|
256
349
|
|
|
257
350
|
private selectRun(run: string, index: number): void {
|
|
258
351
|
this.selectedRun = run;
|
|
352
|
+
this.turnCache = undefined;
|
|
259
353
|
this.runIndex = index;
|
|
354
|
+
this.recipeScroll = 0;
|
|
260
355
|
this.rowIndex = 0;
|
|
261
356
|
this.subagentIndex = 0;
|
|
262
357
|
this.communicationFrom = "all";
|
|
@@ -276,7 +371,109 @@ export class ActorInspectorOverlay {
|
|
|
276
371
|
if (next) this.selectRun(next.run, nextIndex);
|
|
277
372
|
}
|
|
278
373
|
|
|
374
|
+
private requestKill(runs: Inspector.ActorInspectorRunItem[]): void {
|
|
375
|
+
this.feedback = undefined;
|
|
376
|
+
if (this.focus !== "runs") {
|
|
377
|
+
this.feedback = { ok: false, message: "Focus Run before using Kill." };
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
const run = runs.find((item) => item.run === this.selectedRun);
|
|
381
|
+
if (!run) {
|
|
382
|
+
this.feedback = { ok: false, message: "Kill unavailable: no owned run." };
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
if (!this.killRun) {
|
|
386
|
+
this.feedback = { ok: false, message: "Kill unavailable in this Inspector." };
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
if (run.status !== "running") {
|
|
390
|
+
this.feedback = {
|
|
391
|
+
ok: false,
|
|
392
|
+
message: `Kill unavailable: run is ${run.status}.`,
|
|
393
|
+
};
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
if (!run.runInstanceId) {
|
|
397
|
+
this.feedback = {
|
|
398
|
+
ok: false,
|
|
399
|
+
message: "Kill unavailable: run generation unavailable.",
|
|
400
|
+
};
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
this.killConfirmation = {
|
|
404
|
+
run: run.run,
|
|
405
|
+
runInstanceId: run.runInstanceId,
|
|
406
|
+
status: run.status,
|
|
407
|
+
};
|
|
408
|
+
this.killDialogChoice = "cancel";
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
private cancelKill(): void {
|
|
412
|
+
this.feedback = { ok: false, message: "Kill cancelled." };
|
|
413
|
+
this.killConfirmation = undefined;
|
|
414
|
+
this.killDialogChoice = "cancel";
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
private confirmKill(): void {
|
|
418
|
+
const confirmation = this.killConfirmation;
|
|
419
|
+
this.killConfirmation = undefined;
|
|
420
|
+
this.killDialogChoice = "cancel";
|
|
421
|
+
if (!confirmation || !this.killRun) {
|
|
422
|
+
this.feedback = { ok: false, message: "Kill unavailable in this Inspector." };
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
try {
|
|
426
|
+
this.feedback = this.killRun(
|
|
427
|
+
confirmation.run,
|
|
428
|
+
confirmation.runInstanceId,
|
|
429
|
+
);
|
|
430
|
+
} catch (error) {
|
|
431
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
432
|
+
this.feedback = {
|
|
433
|
+
ok: false,
|
|
434
|
+
message: `Kill failed: ${message.replaceAll(/\s+/g, " ").slice(0, 160)}.`,
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
private renderKillDialog(_width: number, innerWidth: number): string[] {
|
|
440
|
+
const confirmation = this.killConfirmation!;
|
|
441
|
+
const totalRows = this.contentViewportRows() + 7;
|
|
442
|
+
const cancel = this.killDialogChoice === "cancel"
|
|
443
|
+
? this.theme.bg("selectedBg", this.theme.fg("accent", " Cancel "))
|
|
444
|
+
: this.theme.fg("muted", " Cancel ");
|
|
445
|
+
const kill = this.killDialogChoice === "kill"
|
|
446
|
+
? this.theme.bg("selectedBg", this.theme.fg("error", " Kill actor "))
|
|
447
|
+
: this.theme.fg("error", " Kill actor ");
|
|
448
|
+
const body = [
|
|
449
|
+
"",
|
|
450
|
+
this.theme.fg("error", "Kill this running actor?"),
|
|
451
|
+
"",
|
|
452
|
+
`${this.theme.fg("muted", "Run:")} ${this.theme.fg("accent", `run:${confirmation.run}`)}`,
|
|
453
|
+
`${this.theme.fg("muted", "Current status:")} ${this.theme.fg("warning", confirmation.status)}`,
|
|
454
|
+
"",
|
|
455
|
+
this.theme.fg("text", "This sends canonical control.kill."),
|
|
456
|
+
this.theme.fg("error", "The action is destructive and cannot be undone."),
|
|
457
|
+
"",
|
|
458
|
+
`${cancel} ${kill}`,
|
|
459
|
+
"",
|
|
460
|
+
];
|
|
461
|
+
const lines = [this.border("╭", " Confirm Actor Kill ", "╮", innerWidth)];
|
|
462
|
+
const availableRows = Math.max(1, totalRows - 2);
|
|
463
|
+
const topPadding = Math.max(0, Math.floor((availableRows - body.length) / 2));
|
|
464
|
+
const dialogRows = [
|
|
465
|
+
...Array.from({ length: topPadding }, () => ""),
|
|
466
|
+
...body,
|
|
467
|
+
].slice(0, availableRows);
|
|
468
|
+
while (dialogRows.length < availableRows) dialogRows.push("");
|
|
469
|
+
for (const line of dialogRows)
|
|
470
|
+
lines.push(this.row(this.center(line, innerWidth), innerWidth));
|
|
471
|
+
lines.push(this.border("╰", " ←→/tab choose · enter activate · esc cancel ", "╯", innerWidth));
|
|
472
|
+
return lines;
|
|
473
|
+
}
|
|
474
|
+
|
|
279
475
|
private listItemCount(): number {
|
|
476
|
+
if (this.tab === "recipe") return 0;
|
|
280
477
|
return this.tab === "communications"
|
|
281
478
|
? this.communicationPreviews().length
|
|
282
479
|
: this.turnItems().length;
|
|
@@ -287,7 +484,11 @@ export class ActorInspectorOverlay {
|
|
|
287
484
|
const runControl = stripVTControlCharacters(this.renderRunControl());
|
|
288
485
|
return Math.max(0, runControl.indexOf("Run:") - 2);
|
|
289
486
|
}
|
|
290
|
-
const label = this.tab === "communications"
|
|
487
|
+
const label = this.tab === "communications"
|
|
488
|
+
? "Messages"
|
|
489
|
+
: this.tab === "turns"
|
|
490
|
+
? "Turns"
|
|
491
|
+
: "Recipe";
|
|
291
492
|
const tabs = stripVTControlCharacters(this.renderTabs());
|
|
292
493
|
return Math.max(0, tabs.indexOf(label) - 2);
|
|
293
494
|
}
|
|
@@ -295,18 +496,25 @@ export class ActorInspectorOverlay {
|
|
|
295
496
|
private renderKeyHints(): string {
|
|
296
497
|
const hint = (keys: string, description: string) =>
|
|
297
498
|
`${this.theme.fg("accent", keys)}${this.theme.fg("dim", ` ${description}`)}`;
|
|
499
|
+
if (this.killConfirmation)
|
|
500
|
+
return ` ${hint("y/enter", "confirm kill")} ${hint("n/esc", "cancel")}`;
|
|
298
501
|
if (this.focus === "select")
|
|
299
502
|
return this.menuLevel === "value"
|
|
300
503
|
? ` ${hint("↑↓", "option")} ${hint("enter", "apply")} ${hint("←/esc", "back")}`
|
|
301
504
|
: ` ${hint("↑↓", "option")} ${hint("→/enter", "open")} ${hint("←/esc", "back")}`;
|
|
505
|
+
if (this.focus === "recipe")
|
|
506
|
+
return ` ${hint("↑↓/pgup/pgdn", "scroll")} ${hint("↑ at top", "tabs")} ${hint("←/esc", "tabs")}`;
|
|
302
507
|
if (this.focus === "detail")
|
|
303
|
-
return
|
|
304
|
-
? ` ${hint("↑↓", "scroll")} ${hint("→/enter", "readable")} ${hint("←/esc", "back")}`
|
|
305
|
-
: ` ${hint("↑↓", "scroll")} ${hint("←/esc", this.detailView === "transcript" ? "evidence" : "back")}`;
|
|
508
|
+
return ` ${hint("↑↓/pgup/pgdn", "scroll")} ${hint("←/esc", "back")}`;
|
|
306
509
|
if (this.focus === "list")
|
|
307
|
-
return ` ${hint("
|
|
308
|
-
if (this.focus === "runs")
|
|
309
|
-
|
|
510
|
+
return ` ${hint("↑↓/pgup/pgdn", "row")} ${hint("→/enter", "open")} ${hint("←", "tabs")} ${hint("esc", "close")}`;
|
|
511
|
+
if (this.focus === "runs") {
|
|
512
|
+
const run = this.runs().find((item) => item.run === this.selectedRun);
|
|
513
|
+
const killHint = this.killRun && run?.status === "running" && run.runInstanceId
|
|
514
|
+
? ` ${hint("k", "kill")}`
|
|
515
|
+
: "";
|
|
516
|
+
return ` ${hint("←→", "run")} ${hint("↓", "tabs")} ${hint("enter", "list")}${killHint} ${hint("esc", "close")}`;
|
|
517
|
+
}
|
|
310
518
|
return ` ${hint("←→", "navigate")} ${hint("↑↓", "change row")} ${hint("enter", "select")} ${hint("esc", "close")}`;
|
|
311
519
|
}
|
|
312
520
|
|
|
@@ -359,7 +567,7 @@ export class ActorInspectorOverlay {
|
|
|
359
567
|
const subagent = this.subagents()[this.subagentIndex] ?? "all";
|
|
360
568
|
const messagesLabel = `Messages${messageFilters.length > 0 ? ` (${messageFilters.join(", ")})` : ""}`;
|
|
361
569
|
const turnsLabel = `Turns${subagent === "all" ? "" : ` (${subagent})`}`;
|
|
362
|
-
return `${tab("communications", messagesLabel)} ${tab("turns", turnsLabel)}`;
|
|
570
|
+
return `${tab("recipe", "Recipe")} ${tab("communications", messagesLabel)} ${tab("turns", turnsLabel)}`;
|
|
363
571
|
}
|
|
364
572
|
|
|
365
573
|
private renderRunSelector(width: number): string[] {
|
|
@@ -384,6 +592,7 @@ export class ActorInspectorOverlay {
|
|
|
384
592
|
this.selectedRun = undefined;
|
|
385
593
|
return [this.theme.fg("warning", " Selected run is no longer owned by this session")];
|
|
386
594
|
}
|
|
595
|
+
if (this.tab === "recipe") return this.renderRecipe(width);
|
|
387
596
|
const rows =
|
|
388
597
|
this.tab === "communications"
|
|
389
598
|
? this.communicationRows()
|
|
@@ -431,6 +640,51 @@ export class ActorInspectorOverlay {
|
|
|
431
640
|
});
|
|
432
641
|
}
|
|
433
642
|
|
|
643
|
+
private renderRecipe(width: number): string[] {
|
|
644
|
+
if (!this.selectedRun) return [];
|
|
645
|
+
const recipe = Inspector.readActorInspectorRecipe(
|
|
646
|
+
path.join(this.stateRoot, this.selectedRun),
|
|
647
|
+
);
|
|
648
|
+
const logicalLines: DetailLogicalLine[] = [
|
|
649
|
+
...this.detailSection(
|
|
650
|
+
"Identity",
|
|
651
|
+
this.readableValueLines(recipe.identity, " "),
|
|
652
|
+
),
|
|
653
|
+
...(recipe.definition
|
|
654
|
+
? this.detailSection(
|
|
655
|
+
"Recipe",
|
|
656
|
+
this.readableValueLines(recipe.definition, " "),
|
|
657
|
+
)
|
|
658
|
+
: []),
|
|
659
|
+
...this.detailSection(
|
|
660
|
+
"Launch",
|
|
661
|
+
this.readableValueLines(recipe.launch, " "),
|
|
662
|
+
),
|
|
663
|
+
...(recipe.composition.length > 0
|
|
664
|
+
? this.detailSection(
|
|
665
|
+
"Composition",
|
|
666
|
+
this.readableValueLines(recipe.composition, " "),
|
|
667
|
+
)
|
|
668
|
+
: []),
|
|
669
|
+
...this.detailSection("Diagnostics", recipe.diagnostics),
|
|
670
|
+
];
|
|
671
|
+
const wrapped = this.wrapDetailLines(
|
|
672
|
+
logicalLines.length > 0 ? logicalLines : [" No persisted recipe evidence"],
|
|
673
|
+
width,
|
|
674
|
+
);
|
|
675
|
+
const viewportRows = this.contentViewportRows();
|
|
676
|
+
const maxStart = Math.max(0, wrapped.lines.length - viewportRows);
|
|
677
|
+
this.recipeScroll = Math.min(this.recipeScroll, maxStart);
|
|
678
|
+
this.contentStripeIndices = wrapped.stripeIndices.slice(
|
|
679
|
+
this.recipeScroll,
|
|
680
|
+
this.recipeScroll + viewportRows,
|
|
681
|
+
);
|
|
682
|
+
return wrapped.lines.slice(
|
|
683
|
+
this.recipeScroll,
|
|
684
|
+
this.recipeScroll + viewportRows,
|
|
685
|
+
);
|
|
686
|
+
}
|
|
687
|
+
|
|
434
688
|
private communicationPreviews(): Inspector.ActorInspectorPreview[] {
|
|
435
689
|
if (!this.selectedRun) return [];
|
|
436
690
|
const channels =
|
|
@@ -496,7 +750,7 @@ export class ActorInspectorOverlay {
|
|
|
496
750
|
["inbox", preview.inbox_status],
|
|
497
751
|
["timestamp", preview.timestamp],
|
|
498
752
|
];
|
|
499
|
-
|
|
753
|
+
const lines = fields
|
|
500
754
|
.filter(([, value]) => value !== undefined)
|
|
501
755
|
.map(([label, value]) =>
|
|
502
756
|
truncateToWidth(
|
|
@@ -505,12 +759,14 @@ export class ActorInspectorOverlay {
|
|
|
505
759
|
"…",
|
|
506
760
|
),
|
|
507
761
|
);
|
|
762
|
+
const viewportRows = this.contentViewportRows();
|
|
763
|
+
const maxStart = Math.max(0, lines.length - viewportRows);
|
|
764
|
+
this.detailScroll = Math.min(this.detailScroll, maxStart);
|
|
765
|
+
return lines.slice(this.detailScroll, this.detailScroll + viewportRows);
|
|
508
766
|
}
|
|
509
767
|
const turn = this.detailTurn;
|
|
510
768
|
if (!turn) return [this.theme.fg("warning", " Turn row is no longer available")];
|
|
511
|
-
const logicalLines = this.
|
|
512
|
-
? this.turnTranscriptLines(turn)
|
|
513
|
-
: this.turnEvidenceLines(turn);
|
|
769
|
+
const logicalLines = this.turnEvidenceLines(turn);
|
|
514
770
|
const wrapped = this.wrapDetailLines(logicalLines, width);
|
|
515
771
|
const viewportRows = this.contentViewportRows();
|
|
516
772
|
const maxStart = Math.max(0, wrapped.lines.length - viewportRows);
|
|
@@ -543,11 +799,39 @@ export class ActorInspectorOverlay {
|
|
|
543
799
|
const leading = physicalLine.match(/^ */)?.[0] ?? "";
|
|
544
800
|
const content = physicalLine.slice(leading.length);
|
|
545
801
|
const contentWidth = Math.max(1, width - visibleWidth(leading));
|
|
546
|
-
const
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
802
|
+
const labeled = /^(\s*(?:-\s+)?[^:]+:\s+)(\S.*)$/u.exec(physicalLine);
|
|
803
|
+
let wrapped: string[];
|
|
804
|
+
if (
|
|
805
|
+
visibleWidth(physicalLine) > width &&
|
|
806
|
+
labeled &&
|
|
807
|
+
visibleWidth(labeled[1]) < width
|
|
808
|
+
) {
|
|
809
|
+
const prefix = labeled[1];
|
|
810
|
+
const value = labeled[2];
|
|
811
|
+
const firstWidth = Math.max(1, width - visibleWidth(prefix));
|
|
812
|
+
const first = this.wrapPreferred(value, firstWidth)[0] ??
|
|
813
|
+
this.takeVisiblePrefix(value, firstWidth);
|
|
814
|
+
const remainder = value.slice(first.length).trimStart();
|
|
815
|
+
const continuation = `${leading} `;
|
|
816
|
+
const continuationWidth = Math.max(
|
|
817
|
+
1,
|
|
818
|
+
width - visibleWidth(continuation),
|
|
819
|
+
);
|
|
820
|
+
wrapped = [
|
|
821
|
+
`${prefix}${first}`,
|
|
822
|
+
...(remainder
|
|
823
|
+
? this.wrapPreferred(remainder, continuationWidth).map(
|
|
824
|
+
(visualLine) => `${continuation}${visualLine}`,
|
|
825
|
+
)
|
|
826
|
+
: []),
|
|
827
|
+
];
|
|
828
|
+
} else {
|
|
829
|
+
wrapped = visibleWidth(physicalLine) <= width
|
|
830
|
+
? [physicalLine]
|
|
831
|
+
: this.wrapPreferred(content, contentWidth).map(
|
|
832
|
+
(visualLine) => `${leading}${visualLine}`,
|
|
833
|
+
);
|
|
834
|
+
}
|
|
551
835
|
for (const visualLine of wrapped) {
|
|
552
836
|
if (!visualLine.trim()) continue;
|
|
553
837
|
wrappedLines.push(visualLine);
|
|
@@ -558,6 +842,37 @@ export class ActorInspectorOverlay {
|
|
|
558
842
|
return { lines: wrappedLines, stripeIndices };
|
|
559
843
|
}
|
|
560
844
|
|
|
845
|
+
private wrapPreferred(content: string, width: number): string[] {
|
|
846
|
+
const lines: string[] = [];
|
|
847
|
+
let remaining = stripVTControlCharacters(content).trimEnd();
|
|
848
|
+
while (remaining && visibleWidth(remaining) > width) {
|
|
849
|
+
const candidate = this.takeVisiblePrefix(remaining, width);
|
|
850
|
+
let splitAt = -1;
|
|
851
|
+
let trimBoundary = false;
|
|
852
|
+
for (let index = 1; index < candidate.length; index += 1) {
|
|
853
|
+
if (/\s/u.test(candidate[index]!)) {
|
|
854
|
+
splitAt = index + 1;
|
|
855
|
+
trimBoundary = true;
|
|
856
|
+
} else if (/[-+=/_,.:]/u.test(candidate[index]!)) {
|
|
857
|
+
splitAt = index;
|
|
858
|
+
trimBoundary = false;
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
if (splitAt <= 0) splitAt = candidate.length;
|
|
862
|
+
const head = remaining.slice(0, splitAt).trimEnd();
|
|
863
|
+
if (!head) {
|
|
864
|
+
lines.push(candidate);
|
|
865
|
+
remaining = remaining.slice(candidate.length);
|
|
866
|
+
continue;
|
|
867
|
+
}
|
|
868
|
+
lines.push(head);
|
|
869
|
+
remaining = remaining.slice(splitAt);
|
|
870
|
+
if (trimBoundary) remaining = remaining.trimStart();
|
|
871
|
+
}
|
|
872
|
+
if (remaining) lines.push(remaining);
|
|
873
|
+
return lines;
|
|
874
|
+
}
|
|
875
|
+
|
|
561
876
|
private detailSection(
|
|
562
877
|
title: string,
|
|
563
878
|
content: string[],
|
|
@@ -612,9 +927,14 @@ export class ActorInspectorOverlay {
|
|
|
612
927
|
}
|
|
613
928
|
if (!item || typeof item !== "object")
|
|
614
929
|
return [`${indent}- ${String(item)}`];
|
|
930
|
+
const [first = "", ...rest] = this.readableValueLines(
|
|
931
|
+
item,
|
|
932
|
+
`${indent} `,
|
|
933
|
+
depth + 1,
|
|
934
|
+
);
|
|
615
935
|
return [
|
|
616
|
-
`${indent}
|
|
617
|
-
...
|
|
936
|
+
`${indent}- ${first.trimStart()}`,
|
|
937
|
+
...rest,
|
|
618
938
|
];
|
|
619
939
|
});
|
|
620
940
|
}
|
|
@@ -650,7 +970,9 @@ export class ActorInspectorOverlay {
|
|
|
650
970
|
: "";
|
|
651
971
|
lines.push(` Subagent ${subagent}${stage}`, DETAIL_SECTION_BREAK);
|
|
652
972
|
if (turn.userText)
|
|
653
|
-
lines.push(
|
|
973
|
+
lines.push(
|
|
974
|
+
...this.detailSection("User", [this.readablePromptText(turn.userText)]),
|
|
975
|
+
);
|
|
654
976
|
if (turn.thinking)
|
|
655
977
|
lines.push(...this.detailSection("Thinking", [turn.thinking]));
|
|
656
978
|
if (turn.assistantText)
|
|
@@ -698,12 +1020,6 @@ export class ActorInspectorOverlay {
|
|
|
698
1020
|
` ${turn.promptFile}${turn.promptBytes ? ` (${turn.promptBytes} bytes)` : ""}`,
|
|
699
1021
|
]
|
|
700
1022
|
: []),
|
|
701
|
-
...(turn.recipeContext === undefined
|
|
702
|
-
? []
|
|
703
|
-
: [
|
|
704
|
-
"Recipe Context",
|
|
705
|
-
...this.readableValueLines(turn.recipeContext, " "),
|
|
706
|
-
]),
|
|
707
1023
|
];
|
|
708
1024
|
lines.push(...this.detailSection("Provenance", provenance));
|
|
709
1025
|
return lines.length > 0 ? lines : [" No persisted turn evidence"];
|
|
@@ -714,44 +1030,17 @@ export class ActorInspectorOverlay {
|
|
|
714
1030
|
return wrapped?.[1]?.trim() || text;
|
|
715
1031
|
}
|
|
716
1032
|
|
|
717
|
-
private
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
const
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
...this.detailSection("User", [this.readablePromptText(turn.userText)]),
|
|
724
|
-
);
|
|
725
|
-
if (turn.thinking)
|
|
726
|
-
lines.push(...this.detailSection("Thinking", [turn.thinking]));
|
|
727
|
-
if (turn.assistantText)
|
|
728
|
-
lines.push(...this.detailSection("Assistant", [turn.assistantText]));
|
|
729
|
-
for (const tool of turn.toolCalls) {
|
|
730
|
-
const content = [
|
|
731
|
-
...(tool.arguments === undefined
|
|
732
|
-
? []
|
|
733
|
-
: ["Input", ...this.readableValueLines(tool.arguments, " ")]),
|
|
734
|
-
...(tool.result === undefined
|
|
735
|
-
? []
|
|
736
|
-
: ["Result", ...this.readableValueLines(tool.result, " ")]),
|
|
737
|
-
];
|
|
738
|
-
lines.push(
|
|
739
|
-
...this.detailSection(
|
|
740
|
-
`Tool (${tool.name}${tool.resultError ? ", Error" : ""})`,
|
|
741
|
-
content,
|
|
742
|
-
),
|
|
743
|
-
);
|
|
744
|
-
}
|
|
745
|
-
if (turn.error) lines.push(...this.detailSection("Error", [turn.error]));
|
|
746
|
-
return lines.length > 0 ? lines : [" No readable turn content"];
|
|
1033
|
+
private allTurnItems(): Inspector.ActorInspectorTurnItem[] {
|
|
1034
|
+
if (!this.selectedRun) return [];
|
|
1035
|
+
if (this.turnCache?.run === this.selectedRun) return this.turnCache.turns;
|
|
1036
|
+
const turns = this.readTurns(path.join(this.stateRoot, this.selectedRun));
|
|
1037
|
+
this.turnCache = { run: this.selectedRun, turns };
|
|
1038
|
+
return turns;
|
|
747
1039
|
}
|
|
748
1040
|
|
|
749
1041
|
private turnItems(): Inspector.ActorInspectorTurnItem[] {
|
|
750
|
-
if (!this.selectedRun) return [];
|
|
751
1042
|
const selectedSubagent = this.subagents()[this.subagentIndex];
|
|
752
|
-
return
|
|
753
|
-
path.join(this.stateRoot, this.selectedRun),
|
|
754
|
-
)
|
|
1043
|
+
return this.allTurnItems()
|
|
755
1044
|
.filter(
|
|
756
1045
|
(turn) =>
|
|
757
1046
|
!selectedSubagent ||
|
|
@@ -793,9 +1082,7 @@ export class ActorInspectorOverlay {
|
|
|
793
1082
|
|
|
794
1083
|
private subagents(): string[] {
|
|
795
1084
|
if (!this.selectedRun || this.tab !== "turns") return ["all"];
|
|
796
|
-
const ids =
|
|
797
|
-
path.join(this.stateRoot, this.selectedRun),
|
|
798
|
-
).map((turn) => turn.commandId);
|
|
1085
|
+
const ids = this.allTurnItems().map((turn) => turn.commandId);
|
|
799
1086
|
return ["all", ...new Set(ids)];
|
|
800
1087
|
}
|
|
801
1088
|
|
|
@@ -982,17 +1269,11 @@ export class ActorInspectorOverlay {
|
|
|
982
1269
|
if (preview) this.readKeys.add(Inspector.inspectorPreviewReadKey(preview));
|
|
983
1270
|
} else this.detailTurn = this.turnItems()[this.rowIndex];
|
|
984
1271
|
this.detailScroll = 0;
|
|
985
|
-
this.detailView = "evidence";
|
|
986
1272
|
this.detailOpen = Boolean(this.detailCommunication || this.detailTurn);
|
|
987
1273
|
if (this.detailOpen) this.focus = "detail";
|
|
988
1274
|
}
|
|
989
1275
|
|
|
990
1276
|
private backDetail(): void {
|
|
991
|
-
if (this.detailView === "transcript") {
|
|
992
|
-
this.detailView = "evidence";
|
|
993
|
-
this.detailScroll = 0;
|
|
994
|
-
return;
|
|
995
|
-
}
|
|
996
1277
|
this.closeDetail();
|
|
997
1278
|
}
|
|
998
1279
|
|
|
@@ -1001,7 +1282,6 @@ export class ActorInspectorOverlay {
|
|
|
1001
1282
|
this.detailCommunication = undefined;
|
|
1002
1283
|
this.detailTurn = undefined;
|
|
1003
1284
|
this.detailScroll = 0;
|
|
1004
|
-
this.detailView = "evidence";
|
|
1005
1285
|
this.focus = "list";
|
|
1006
1286
|
}
|
|
1007
1287
|
|
|
@@ -1059,6 +1339,12 @@ export class ActorInspectorOverlay {
|
|
|
1059
1339
|
return plain.slice(index);
|
|
1060
1340
|
}
|
|
1061
1341
|
|
|
1342
|
+
private center(content: string, width: number): string {
|
|
1343
|
+
const contentWidth = visibleWidth(content);
|
|
1344
|
+
const leading = Math.max(0, Math.floor((width - contentWidth) / 2));
|
|
1345
|
+
return `${" ".repeat(leading)}${content}`;
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1062
1348
|
private fit(content: string, width: number): string {
|
|
1063
1349
|
// A Component render entry must remain exactly one terminal row. Persisted
|
|
1064
1350
|
// prompts, messages, and tool results may contain line breaks or tabs;
|