@llblab/pi-actors 0.41.1 → 0.42.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +11 -6
- package/BACKLOG.md +2 -62
- package/CHANGELOG.md +15 -0
- package/README.md +22 -4
- package/dist/index.js +25 -121
- package/dist/lib/async-runs.d.ts +25 -5
- package/dist/lib/async-runs.js +136 -47
- package/dist/lib/automatic-review-runtime.d.ts +18 -0
- package/dist/lib/automatic-review-runtime.js +96 -0
- 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 +20 -3
- package/dist/lib/inspector-overlay.js +275 -74
- package/dist/lib/inspector.d.ts +9 -0
- package/dist/lib/inspector.js +48 -0
- package/dist/lib/observability.d.ts +1 -0
- package/dist/lib/observability.js +11 -1
- 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 +8 -2
- package/dist/lib/runs-control.js +23 -6
- 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 +1 -1
- 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 +1 -1
- 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 +6 -48
- package/dist/lib/runs-status.d.ts +5 -3
- package/dist/lib/runs-status.js +4 -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 +26 -3
- package/dist/lib/tools-local.js +4 -2
- package/dist/lib/tools-message.d.ts +1 -0
- package/dist/lib/tools-message.js +29 -17
- package/dist/lib/tools-spawn.js +4 -1
- 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/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 +9 -1
- package/docs/recipe-library.md +7 -3
- package/docs/template-recipes.md +4 -11
- package/docs/tool-registry.md +11 -4
- package/index.ts +27 -142
- package/lib/async-runs.ts +218 -62
- package/lib/automatic-review-runtime.ts +135 -0
- 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 +325 -91
- package/lib/inspector.ts +72 -0
- package/lib/observability.ts +14 -1
- 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 +49 -5
- 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 +1 -1
- package/lib/runs-parent-teardown.ts +257 -0
- package/lib/runs-process.ts +1 -1
- package/lib/runs-retention.ts +1 -1
- package/lib/runs-start.ts +12 -68
- package/lib/runs-status.ts +12 -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 +33 -3
- package/lib/tools-local.ts +8 -2
- package/lib/tools-message.ts +45 -30
- package/lib/tools-spawn.ts +8 -1
- package/lib/tools.ts +5 -0
- package/package.json +3 -2
- package/recipes/draft-review.json +24 -0
- package/recipes/tool-review.json +24 -0
- package/scripts/release-gates.mjs +165 -0
- package/skills/actors/SKILL.md +9 -8
- package/skills/swarm/SKILL.md +1 -1
|
@@ -5,12 +5,19 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import * as path from "node:path";
|
|
7
7
|
import { stripVTControlCharacters } from "node:util";
|
|
8
|
-
import { matchesKey, truncateToWidth, visibleWidth,
|
|
8
|
+
import { matchesKey, truncateToWidth, visibleWidth, } from "@earendil-works/pi-tui";
|
|
9
9
|
import * as Inspector from "./inspector.js";
|
|
10
|
+
const INSPECTOR_TABS = [
|
|
11
|
+
"recipe",
|
|
12
|
+
"communications",
|
|
13
|
+
"turns",
|
|
14
|
+
];
|
|
10
15
|
const DETAIL_SECTION_BREAK = { sectionBreak: true };
|
|
11
16
|
export class ActorInspectorOverlay {
|
|
12
17
|
done;
|
|
18
|
+
killRun;
|
|
13
19
|
ownerId;
|
|
20
|
+
readTurns;
|
|
14
21
|
stateRoot;
|
|
15
22
|
theme;
|
|
16
23
|
tui;
|
|
@@ -24,23 +31,32 @@ export class ActorInspectorOverlay {
|
|
|
24
31
|
detailOpen = false;
|
|
25
32
|
detailScroll = 0;
|
|
26
33
|
detailTurn;
|
|
27
|
-
|
|
34
|
+
feedback;
|
|
35
|
+
killConfirmation;
|
|
28
36
|
readKeys = new Set();
|
|
29
37
|
focus = "tabs";
|
|
30
38
|
filterControlIndex = 0;
|
|
31
39
|
menuLevel = "filter";
|
|
32
40
|
selectorIndex = 0;
|
|
41
|
+
recipeScroll = 0;
|
|
33
42
|
rowIndex = 0;
|
|
34
43
|
selectedRun;
|
|
35
44
|
subagentIndex = 0;
|
|
36
|
-
tab = "
|
|
45
|
+
tab = "recipe";
|
|
46
|
+
turnCache;
|
|
37
47
|
constructor(options) {
|
|
38
48
|
this.done = options.done;
|
|
49
|
+
this.killRun = options.killRun;
|
|
39
50
|
this.ownerId = options.ownerId;
|
|
51
|
+
this.readTurns = options.readTurns ?? Inspector.readActorInspectorTurns;
|
|
40
52
|
this.stateRoot = options.stateRoot;
|
|
41
53
|
this.theme = options.theme;
|
|
42
54
|
this.tui = options.tui;
|
|
43
|
-
this.refreshTimer = setInterval(() =>
|
|
55
|
+
this.refreshTimer = setInterval(() => {
|
|
56
|
+
if (this.focus !== "list" && this.focus !== "detail")
|
|
57
|
+
this.turnCache = undefined;
|
|
58
|
+
this.tui.requestRender();
|
|
59
|
+
}, 1000);
|
|
44
60
|
this.refreshTimer.unref?.();
|
|
45
61
|
}
|
|
46
62
|
handleInput(data) {
|
|
@@ -50,6 +66,22 @@ export class ActorInspectorOverlay {
|
|
|
50
66
|
this.done();
|
|
51
67
|
return;
|
|
52
68
|
}
|
|
69
|
+
if (this.killConfirmation) {
|
|
70
|
+
if (matchesKey(data, "escape") || data.toLowerCase() === "n") {
|
|
71
|
+
this.feedback = { ok: false, message: "Kill cancelled." };
|
|
72
|
+
this.killConfirmation = undefined;
|
|
73
|
+
}
|
|
74
|
+
else if (matchesKey(data, "return") || data.toLowerCase() === "y") {
|
|
75
|
+
this.confirmKill();
|
|
76
|
+
}
|
|
77
|
+
this.tui.requestRender();
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
if (data.toLowerCase() === "k") {
|
|
81
|
+
this.requestKill(runs);
|
|
82
|
+
this.tui.requestRender();
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
53
85
|
if (matchesKey(data, "escape")) {
|
|
54
86
|
if (this.focus === "select") {
|
|
55
87
|
if (this.menuLevel === "value") {
|
|
@@ -61,6 +93,8 @@ export class ActorInspectorOverlay {
|
|
|
61
93
|
}
|
|
62
94
|
else if (this.focus === "detail")
|
|
63
95
|
this.backDetail();
|
|
96
|
+
else if (this.focus === "recipe")
|
|
97
|
+
this.focus = "tabs";
|
|
64
98
|
else
|
|
65
99
|
this.done();
|
|
66
100
|
this.tui.requestRender();
|
|
@@ -80,19 +114,35 @@ export class ActorInspectorOverlay {
|
|
|
80
114
|
this.tui.requestRender();
|
|
81
115
|
return;
|
|
82
116
|
}
|
|
117
|
+
if (this.focus === "recipe") {
|
|
118
|
+
if (matchesKey(data, "up")) {
|
|
119
|
+
if (this.recipeScroll === 0)
|
|
120
|
+
this.focus = "tabs";
|
|
121
|
+
else
|
|
122
|
+
this.recipeScroll -= 1;
|
|
123
|
+
}
|
|
124
|
+
else if (matchesKey(data, "down"))
|
|
125
|
+
this.recipeScroll += 1;
|
|
126
|
+
else if (matchesKey(data, "pageUp"))
|
|
127
|
+
this.recipeScroll = Math.max(0, this.recipeScroll - this.contentViewportRows());
|
|
128
|
+
else if (matchesKey(data, "pageDown"))
|
|
129
|
+
this.recipeScroll += this.contentViewportRows();
|
|
130
|
+
else if (matchesKey(data, "left"))
|
|
131
|
+
this.focus = "tabs";
|
|
132
|
+
this.tui.requestRender();
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
83
135
|
if (this.focus === "detail") {
|
|
84
136
|
if (matchesKey(data, "up"))
|
|
85
137
|
this.detailScroll = Math.max(0, this.detailScroll - 1);
|
|
86
138
|
else if (matchesKey(data, "down"))
|
|
87
139
|
this.detailScroll += 1;
|
|
140
|
+
else if (matchesKey(data, "pageUp"))
|
|
141
|
+
this.detailScroll = Math.max(0, this.detailScroll - this.contentViewportRows());
|
|
142
|
+
else if (matchesKey(data, "pageDown"))
|
|
143
|
+
this.detailScroll += this.contentViewportRows();
|
|
88
144
|
else if (matchesKey(data, "left"))
|
|
89
145
|
this.backDetail();
|
|
90
|
-
else if ((matchesKey(data, "right") || matchesKey(data, "return")) &&
|
|
91
|
-
this.detailView === "evidence" &&
|
|
92
|
-
this.tab === "turns") {
|
|
93
|
-
this.detailView = "transcript";
|
|
94
|
-
this.detailScroll = 0;
|
|
95
|
-
}
|
|
96
146
|
this.tui.requestRender();
|
|
97
147
|
return;
|
|
98
148
|
}
|
|
@@ -108,12 +158,18 @@ export class ActorInspectorOverlay {
|
|
|
108
158
|
}
|
|
109
159
|
else if (this.focus === "tabs") {
|
|
110
160
|
if (matchesKey(data, "left") || matchesKey(data, "right")) {
|
|
111
|
-
|
|
161
|
+
const current = INSPECTOR_TABS.indexOf(this.tab);
|
|
162
|
+
const direction = matchesKey(data, "right") ? 1 : -1;
|
|
163
|
+
this.tab = INSPECTOR_TABS[(current + direction + INSPECTOR_TABS.length) % INSPECTOR_TABS.length];
|
|
112
164
|
this.filterControlIndex = 0;
|
|
165
|
+
this.recipeScroll = 0;
|
|
113
166
|
this.rowIndex = 0;
|
|
114
167
|
}
|
|
115
168
|
else if (matchesKey(data, "up"))
|
|
116
169
|
this.focus = "runs";
|
|
170
|
+
else if ((matchesKey(data, "down") || matchesKey(data, "return")) &&
|
|
171
|
+
this.tab === "recipe")
|
|
172
|
+
this.focus = "recipe";
|
|
117
173
|
else if (matchesKey(data, "down") && this.listItemCount() > 0)
|
|
118
174
|
this.focus = "list";
|
|
119
175
|
else if (matchesKey(data, "return"))
|
|
@@ -121,7 +177,13 @@ export class ActorInspectorOverlay {
|
|
|
121
177
|
}
|
|
122
178
|
else if (this.focus === "list") {
|
|
123
179
|
const count = this.tab === "communications" ? this.communicationPreviews().length : this.turnItems().length;
|
|
124
|
-
if (matchesKey(data, "
|
|
180
|
+
if (matchesKey(data, "left"))
|
|
181
|
+
this.focus = "tabs";
|
|
182
|
+
else if (matchesKey(data, "pageUp"))
|
|
183
|
+
this.rowIndex = Math.max(0, this.rowIndex - this.contentViewportRows());
|
|
184
|
+
else if (matchesKey(data, "pageDown"))
|
|
185
|
+
this.rowIndex = Math.min(Math.max(0, count - 1), this.rowIndex + this.contentViewportRows());
|
|
186
|
+
else if (matchesKey(data, "up")) {
|
|
125
187
|
if (this.rowIndex === 0)
|
|
126
188
|
this.focus = "tabs";
|
|
127
189
|
else
|
|
@@ -167,11 +229,23 @@ export class ActorInspectorOverlay {
|
|
|
167
229
|
else
|
|
168
230
|
lines.push(this.border("├", "", "┤", innerWidth));
|
|
169
231
|
this.contentStripeIndices = [];
|
|
170
|
-
const
|
|
171
|
-
? this.
|
|
172
|
-
: this.
|
|
173
|
-
? this.
|
|
174
|
-
: this.
|
|
232
|
+
const baseContent = this.killConfirmation
|
|
233
|
+
? this.renderKillConfirmation()
|
|
234
|
+
: this.detailOpen
|
|
235
|
+
? this.renderDetail(innerWidth)
|
|
236
|
+
: this.selectedRun
|
|
237
|
+
? this.renderTimeline(innerWidth)
|
|
238
|
+
: this.renderRunSelector(innerWidth);
|
|
239
|
+
const content = this.feedback && !this.killConfirmation
|
|
240
|
+
? [
|
|
241
|
+
this.theme.fg(this.feedback.ok
|
|
242
|
+
? "success"
|
|
243
|
+
: /^Kill (failed|rejected):/.test(this.feedback.message)
|
|
244
|
+
? "error"
|
|
245
|
+
: "warning", ` ${this.feedback.message}`),
|
|
246
|
+
...baseContent.slice(0, Math.max(0, this.contentViewportRows() - 1)),
|
|
247
|
+
]
|
|
248
|
+
: baseContent;
|
|
175
249
|
const contentViewportRows = this.contentViewportRows();
|
|
176
250
|
const selectorRows = selector.slice(1);
|
|
177
251
|
for (let index = 0; index < Math.max(content.length, contentViewportRows); index += 1) {
|
|
@@ -215,7 +289,9 @@ export class ActorInspectorOverlay {
|
|
|
215
289
|
}
|
|
216
290
|
selectRun(run, index) {
|
|
217
291
|
this.selectedRun = run;
|
|
292
|
+
this.turnCache = undefined;
|
|
218
293
|
this.runIndex = index;
|
|
294
|
+
this.recipeScroll = 0;
|
|
219
295
|
this.rowIndex = 0;
|
|
220
296
|
this.subagentIndex = 0;
|
|
221
297
|
this.communicationFrom = "all";
|
|
@@ -229,7 +305,67 @@ export class ActorInspectorOverlay {
|
|
|
229
305
|
if (next)
|
|
230
306
|
this.selectRun(next.run, nextIndex);
|
|
231
307
|
}
|
|
308
|
+
requestKill(runs) {
|
|
309
|
+
this.feedback = undefined;
|
|
310
|
+
if (this.focus !== "runs") {
|
|
311
|
+
this.feedback = { ok: false, message: "Focus Run before using Kill." };
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
const run = runs.find((item) => item.run === this.selectedRun);
|
|
315
|
+
if (!run) {
|
|
316
|
+
this.feedback = { ok: false, message: "Kill unavailable: no owned run." };
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
if (!this.killRun) {
|
|
320
|
+
this.feedback = { ok: false, message: "Kill unavailable in this Inspector." };
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
if (run.status !== "running") {
|
|
324
|
+
this.feedback = {
|
|
325
|
+
ok: false,
|
|
326
|
+
message: `Kill unavailable: run is ${run.status}.`,
|
|
327
|
+
};
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
if (!run.runInstanceId) {
|
|
331
|
+
this.feedback = {
|
|
332
|
+
ok: false,
|
|
333
|
+
message: "Kill unavailable: run generation unavailable.",
|
|
334
|
+
};
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
this.killConfirmation = {
|
|
338
|
+
run: run.run,
|
|
339
|
+
runInstanceId: run.runInstanceId,
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
confirmKill() {
|
|
343
|
+
const confirmation = this.killConfirmation;
|
|
344
|
+
this.killConfirmation = undefined;
|
|
345
|
+
if (!confirmation || !this.killRun) {
|
|
346
|
+
this.feedback = { ok: false, message: "Kill unavailable in this Inspector." };
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
try {
|
|
350
|
+
this.feedback = this.killRun(confirmation.run, confirmation.runInstanceId);
|
|
351
|
+
}
|
|
352
|
+
catch (error) {
|
|
353
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
354
|
+
this.feedback = {
|
|
355
|
+
ok: false,
|
|
356
|
+
message: `Kill failed: ${message.replaceAll(/\s+/g, " ").slice(0, 160)}.`,
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
renderKillConfirmation() {
|
|
361
|
+
return [
|
|
362
|
+
this.theme.fg("error", ` Kill running actor run:${this.killConfirmation?.run}?`),
|
|
363
|
+
this.theme.fg("muted", " This sends canonical control.kill and cannot be undone."),
|
|
364
|
+
];
|
|
365
|
+
}
|
|
232
366
|
listItemCount() {
|
|
367
|
+
if (this.tab === "recipe")
|
|
368
|
+
return 0;
|
|
233
369
|
return this.tab === "communications"
|
|
234
370
|
? this.communicationPreviews().length
|
|
235
371
|
: this.turnItems().length;
|
|
@@ -239,24 +375,35 @@ export class ActorInspectorOverlay {
|
|
|
239
375
|
const runControl = stripVTControlCharacters(this.renderRunControl());
|
|
240
376
|
return Math.max(0, runControl.indexOf("Run:") - 2);
|
|
241
377
|
}
|
|
242
|
-
const label = this.tab === "communications"
|
|
378
|
+
const label = this.tab === "communications"
|
|
379
|
+
? "Messages"
|
|
380
|
+
: this.tab === "turns"
|
|
381
|
+
? "Turns"
|
|
382
|
+
: "Recipe";
|
|
243
383
|
const tabs = stripVTControlCharacters(this.renderTabs());
|
|
244
384
|
return Math.max(0, tabs.indexOf(label) - 2);
|
|
245
385
|
}
|
|
246
386
|
renderKeyHints() {
|
|
247
387
|
const hint = (keys, description) => `${this.theme.fg("accent", keys)}${this.theme.fg("dim", ` ${description}`)}`;
|
|
388
|
+
if (this.killConfirmation)
|
|
389
|
+
return ` ${hint("y/enter", "confirm kill")} ${hint("n/esc", "cancel")}`;
|
|
248
390
|
if (this.focus === "select")
|
|
249
391
|
return this.menuLevel === "value"
|
|
250
392
|
? ` ${hint("↑↓", "option")} ${hint("enter", "apply")} ${hint("←/esc", "back")}`
|
|
251
393
|
: ` ${hint("↑↓", "option")} ${hint("→/enter", "open")} ${hint("←/esc", "back")}`;
|
|
394
|
+
if (this.focus === "recipe")
|
|
395
|
+
return ` ${hint("↑↓/pgup/pgdn", "scroll")} ${hint("↑ at top", "tabs")} ${hint("←/esc", "tabs")}`;
|
|
252
396
|
if (this.focus === "detail")
|
|
253
|
-
return
|
|
254
|
-
? ` ${hint("↑↓", "scroll")} ${hint("→/enter", "readable")} ${hint("←/esc", "back")}`
|
|
255
|
-
: ` ${hint("↑↓", "scroll")} ${hint("←/esc", this.detailView === "transcript" ? "evidence" : "back")}`;
|
|
397
|
+
return ` ${hint("↑↓/pgup/pgdn", "scroll")} ${hint("←/esc", "back")}`;
|
|
256
398
|
if (this.focus === "list")
|
|
257
|
-
return ` ${hint("
|
|
258
|
-
if (this.focus === "runs")
|
|
259
|
-
|
|
399
|
+
return ` ${hint("↑↓/pgup/pgdn", "row")} ${hint("→/enter", "open")} ${hint("←", "tabs")} ${hint("esc", "close")}`;
|
|
400
|
+
if (this.focus === "runs") {
|
|
401
|
+
const run = this.runs().find((item) => item.run === this.selectedRun);
|
|
402
|
+
const killHint = this.killRun && run?.status === "running" && run.runInstanceId
|
|
403
|
+
? ` ${hint("k", "kill")}`
|
|
404
|
+
: "";
|
|
405
|
+
return ` ${hint("←→", "run")} ${hint("↓", "tabs")} ${hint("enter", "list")}${killHint} ${hint("esc", "close")}`;
|
|
406
|
+
}
|
|
260
407
|
return ` ${hint("←→", "navigate")} ${hint("↑↓", "change row")} ${hint("enter", "select")} ${hint("esc", "close")}`;
|
|
261
408
|
}
|
|
262
409
|
renderRunControl() {
|
|
@@ -306,7 +453,7 @@ export class ActorInspectorOverlay {
|
|
|
306
453
|
const subagent = this.subagents()[this.subagentIndex] ?? "all";
|
|
307
454
|
const messagesLabel = `Messages${messageFilters.length > 0 ? ` (${messageFilters.join(", ")})` : ""}`;
|
|
308
455
|
const turnsLabel = `Turns${subagent === "all" ? "" : ` (${subagent})`}`;
|
|
309
|
-
return `${tab("communications", messagesLabel)} ${tab("turns", turnsLabel)}`;
|
|
456
|
+
return `${tab("recipe", "Recipe")} ${tab("communications", messagesLabel)} ${tab("turns", turnsLabel)}`;
|
|
310
457
|
}
|
|
311
458
|
renderRunSelector(width) {
|
|
312
459
|
const runs = this.runs();
|
|
@@ -331,6 +478,8 @@ export class ActorInspectorOverlay {
|
|
|
331
478
|
this.selectedRun = undefined;
|
|
332
479
|
return [this.theme.fg("warning", " Selected run is no longer owned by this session")];
|
|
333
480
|
}
|
|
481
|
+
if (this.tab === "recipe")
|
|
482
|
+
return this.renderRecipe(width);
|
|
334
483
|
const rows = this.tab === "communications"
|
|
335
484
|
? this.communicationRows()
|
|
336
485
|
: this.turnRows();
|
|
@@ -363,6 +512,28 @@ export class ActorInspectorOverlay {
|
|
|
363
512
|
: ` ${row}`;
|
|
364
513
|
});
|
|
365
514
|
}
|
|
515
|
+
renderRecipe(width) {
|
|
516
|
+
if (!this.selectedRun)
|
|
517
|
+
return [];
|
|
518
|
+
const recipe = Inspector.readActorInspectorRecipe(path.join(this.stateRoot, this.selectedRun));
|
|
519
|
+
const logicalLines = [
|
|
520
|
+
...this.detailSection("Identity", this.readableValueLines(recipe.identity, " ")),
|
|
521
|
+
...(recipe.definition
|
|
522
|
+
? this.detailSection("Recipe", this.readableValueLines(recipe.definition, " "))
|
|
523
|
+
: []),
|
|
524
|
+
...this.detailSection("Launch", this.readableValueLines(recipe.launch, " ")),
|
|
525
|
+
...(recipe.composition.length > 0
|
|
526
|
+
? this.detailSection("Composition", this.readableValueLines(recipe.composition, " "))
|
|
527
|
+
: []),
|
|
528
|
+
...this.detailSection("Diagnostics", recipe.diagnostics),
|
|
529
|
+
];
|
|
530
|
+
const wrapped = this.wrapDetailLines(logicalLines.length > 0 ? logicalLines : [" No persisted recipe evidence"], width);
|
|
531
|
+
const viewportRows = this.contentViewportRows();
|
|
532
|
+
const maxStart = Math.max(0, wrapped.lines.length - viewportRows);
|
|
533
|
+
this.recipeScroll = Math.min(this.recipeScroll, maxStart);
|
|
534
|
+
this.contentStripeIndices = wrapped.stripeIndices.slice(this.recipeScroll, this.recipeScroll + viewportRows);
|
|
535
|
+
return wrapped.lines.slice(this.recipeScroll, this.recipeScroll + viewportRows);
|
|
536
|
+
}
|
|
366
537
|
communicationPreviews() {
|
|
367
538
|
if (!this.selectedRun)
|
|
368
539
|
return [];
|
|
@@ -419,16 +590,18 @@ export class ActorInspectorOverlay {
|
|
|
419
590
|
["inbox", preview.inbox_status],
|
|
420
591
|
["timestamp", preview.timestamp],
|
|
421
592
|
];
|
|
422
|
-
|
|
593
|
+
const lines = fields
|
|
423
594
|
.filter(([, value]) => value !== undefined)
|
|
424
595
|
.map(([label, value]) => truncateToWidth(` ${label.padEnd(12, " ")} ${typeof value === "string" ? value : JSON.stringify(value)}`, width, "…"));
|
|
596
|
+
const viewportRows = this.contentViewportRows();
|
|
597
|
+
const maxStart = Math.max(0, lines.length - viewportRows);
|
|
598
|
+
this.detailScroll = Math.min(this.detailScroll, maxStart);
|
|
599
|
+
return lines.slice(this.detailScroll, this.detailScroll + viewportRows);
|
|
425
600
|
}
|
|
426
601
|
const turn = this.detailTurn;
|
|
427
602
|
if (!turn)
|
|
428
603
|
return [this.theme.fg("warning", " Turn row is no longer available")];
|
|
429
|
-
const logicalLines = this.
|
|
430
|
-
? this.turnTranscriptLines(turn)
|
|
431
|
-
: this.turnEvidenceLines(turn);
|
|
604
|
+
const logicalLines = this.turnEvidenceLines(turn);
|
|
432
605
|
const wrapped = this.wrapDetailLines(logicalLines, width);
|
|
433
606
|
const viewportRows = this.contentViewportRows();
|
|
434
607
|
const maxStart = Math.max(0, wrapped.lines.length - viewportRows);
|
|
@@ -452,9 +625,31 @@ export class ActorInspectorOverlay {
|
|
|
452
625
|
const leading = physicalLine.match(/^ */)?.[0] ?? "";
|
|
453
626
|
const content = physicalLine.slice(leading.length);
|
|
454
627
|
const contentWidth = Math.max(1, width - visibleWidth(leading));
|
|
455
|
-
const
|
|
456
|
-
|
|
457
|
-
|
|
628
|
+
const labeled = /^(\s*(?:-\s+)?[^:]+:\s+)(\S.*)$/u.exec(physicalLine);
|
|
629
|
+
let wrapped;
|
|
630
|
+
if (visibleWidth(physicalLine) > width &&
|
|
631
|
+
labeled &&
|
|
632
|
+
visibleWidth(labeled[1]) < width) {
|
|
633
|
+
const prefix = labeled[1];
|
|
634
|
+
const value = labeled[2];
|
|
635
|
+
const firstWidth = Math.max(1, width - visibleWidth(prefix));
|
|
636
|
+
const first = this.wrapPreferred(value, firstWidth)[0] ??
|
|
637
|
+
this.takeVisiblePrefix(value, firstWidth);
|
|
638
|
+
const remainder = value.slice(first.length).trimStart();
|
|
639
|
+
const continuation = `${leading} `;
|
|
640
|
+
const continuationWidth = Math.max(1, width - visibleWidth(continuation));
|
|
641
|
+
wrapped = [
|
|
642
|
+
`${prefix}${first}`,
|
|
643
|
+
...(remainder
|
|
644
|
+
? this.wrapPreferred(remainder, continuationWidth).map((visualLine) => `${continuation}${visualLine}`)
|
|
645
|
+
: []),
|
|
646
|
+
];
|
|
647
|
+
}
|
|
648
|
+
else {
|
|
649
|
+
wrapped = visibleWidth(physicalLine) <= width
|
|
650
|
+
? [physicalLine]
|
|
651
|
+
: this.wrapPreferred(content, contentWidth).map((visualLine) => `${leading}${visualLine}`);
|
|
652
|
+
}
|
|
458
653
|
for (const visualLine of wrapped) {
|
|
459
654
|
if (!visualLine.trim())
|
|
460
655
|
continue;
|
|
@@ -465,6 +660,40 @@ export class ActorInspectorOverlay {
|
|
|
465
660
|
}
|
|
466
661
|
return { lines: wrappedLines, stripeIndices };
|
|
467
662
|
}
|
|
663
|
+
wrapPreferred(content, width) {
|
|
664
|
+
const lines = [];
|
|
665
|
+
let remaining = stripVTControlCharacters(content).trimEnd();
|
|
666
|
+
while (remaining && visibleWidth(remaining) > width) {
|
|
667
|
+
const candidate = this.takeVisiblePrefix(remaining, width);
|
|
668
|
+
let splitAt = -1;
|
|
669
|
+
let trimBoundary = false;
|
|
670
|
+
for (let index = 1; index < candidate.length; index += 1) {
|
|
671
|
+
if (/\s/u.test(candidate[index])) {
|
|
672
|
+
splitAt = index + 1;
|
|
673
|
+
trimBoundary = true;
|
|
674
|
+
}
|
|
675
|
+
else if (/[-+=/_,.:]/u.test(candidate[index])) {
|
|
676
|
+
splitAt = index;
|
|
677
|
+
trimBoundary = false;
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
if (splitAt <= 0)
|
|
681
|
+
splitAt = candidate.length;
|
|
682
|
+
const head = remaining.slice(0, splitAt).trimEnd();
|
|
683
|
+
if (!head) {
|
|
684
|
+
lines.push(candidate);
|
|
685
|
+
remaining = remaining.slice(candidate.length);
|
|
686
|
+
continue;
|
|
687
|
+
}
|
|
688
|
+
lines.push(head);
|
|
689
|
+
remaining = remaining.slice(splitAt);
|
|
690
|
+
if (trimBoundary)
|
|
691
|
+
remaining = remaining.trimStart();
|
|
692
|
+
}
|
|
693
|
+
if (remaining)
|
|
694
|
+
lines.push(remaining);
|
|
695
|
+
return lines;
|
|
696
|
+
}
|
|
468
697
|
detailSection(title, content) {
|
|
469
698
|
if (content.length === 0)
|
|
470
699
|
return [];
|
|
@@ -508,9 +737,10 @@ export class ActorInspectorOverlay {
|
|
|
508
737
|
}
|
|
509
738
|
if (!item || typeof item !== "object")
|
|
510
739
|
return [`${indent}- ${String(item)}`];
|
|
740
|
+
const [first = "", ...rest] = this.readableValueLines(item, `${indent} `, depth + 1);
|
|
511
741
|
return [
|
|
512
|
-
`${indent}
|
|
513
|
-
...
|
|
742
|
+
`${indent}- ${first.trimStart()}`,
|
|
743
|
+
...rest,
|
|
514
744
|
];
|
|
515
745
|
});
|
|
516
746
|
}
|
|
@@ -543,7 +773,7 @@ export class ActorInspectorOverlay {
|
|
|
543
773
|
: "";
|
|
544
774
|
lines.push(` Subagent ${subagent}${stage}`, DETAIL_SECTION_BREAK);
|
|
545
775
|
if (turn.userText)
|
|
546
|
-
lines.push(...this.detailSection("User", [turn.userText]));
|
|
776
|
+
lines.push(...this.detailSection("User", [this.readablePromptText(turn.userText)]));
|
|
547
777
|
if (turn.thinking)
|
|
548
778
|
lines.push(...this.detailSection("Thinking", [turn.thinking]));
|
|
549
779
|
if (turn.assistantText)
|
|
@@ -586,12 +816,6 @@ export class ActorInspectorOverlay {
|
|
|
586
816
|
` ${turn.promptFile}${turn.promptBytes ? ` (${turn.promptBytes} bytes)` : ""}`,
|
|
587
817
|
]
|
|
588
818
|
: []),
|
|
589
|
-
...(turn.recipeContext === undefined
|
|
590
|
-
? []
|
|
591
|
-
: [
|
|
592
|
-
"Recipe Context",
|
|
593
|
-
...this.readableValueLines(turn.recipeContext, " "),
|
|
594
|
-
]),
|
|
595
819
|
];
|
|
596
820
|
lines.push(...this.detailSection("Provenance", provenance));
|
|
597
821
|
return lines.length > 0 ? lines : [" No persisted turn evidence"];
|
|
@@ -600,34 +824,18 @@ export class ActorInspectorOverlay {
|
|
|
600
824
|
const wrapped = /^\s*<file\b[^>]*>\s*([\s\S]*?)\s*<\/file>\s*$/i.exec(text);
|
|
601
825
|
return wrapped?.[1]?.trim() || text;
|
|
602
826
|
}
|
|
603
|
-
|
|
604
|
-
const lines = [];
|
|
605
|
-
if (turn.userText)
|
|
606
|
-
lines.push(...this.detailSection("User", [this.readablePromptText(turn.userText)]));
|
|
607
|
-
if (turn.thinking)
|
|
608
|
-
lines.push(...this.detailSection("Thinking", [turn.thinking]));
|
|
609
|
-
if (turn.assistantText)
|
|
610
|
-
lines.push(...this.detailSection("Assistant", [turn.assistantText]));
|
|
611
|
-
for (const tool of turn.toolCalls) {
|
|
612
|
-
const content = [
|
|
613
|
-
...(tool.arguments === undefined
|
|
614
|
-
? []
|
|
615
|
-
: ["Input", ...this.readableValueLines(tool.arguments, " ")]),
|
|
616
|
-
...(tool.result === undefined
|
|
617
|
-
? []
|
|
618
|
-
: ["Result", ...this.readableValueLines(tool.result, " ")]),
|
|
619
|
-
];
|
|
620
|
-
lines.push(...this.detailSection(`Tool (${tool.name}${tool.resultError ? ", Error" : ""})`, content));
|
|
621
|
-
}
|
|
622
|
-
if (turn.error)
|
|
623
|
-
lines.push(...this.detailSection("Error", [turn.error]));
|
|
624
|
-
return lines.length > 0 ? lines : [" No readable turn content"];
|
|
625
|
-
}
|
|
626
|
-
turnItems() {
|
|
827
|
+
allTurnItems() {
|
|
627
828
|
if (!this.selectedRun)
|
|
628
829
|
return [];
|
|
830
|
+
if (this.turnCache?.run === this.selectedRun)
|
|
831
|
+
return this.turnCache.turns;
|
|
832
|
+
const turns = this.readTurns(path.join(this.stateRoot, this.selectedRun));
|
|
833
|
+
this.turnCache = { run: this.selectedRun, turns };
|
|
834
|
+
return turns;
|
|
835
|
+
}
|
|
836
|
+
turnItems() {
|
|
629
837
|
const selectedSubagent = this.subagents()[this.subagentIndex];
|
|
630
|
-
return
|
|
838
|
+
return this.allTurnItems()
|
|
631
839
|
.filter((turn) => !selectedSubagent ||
|
|
632
840
|
selectedSubagent === "all" ||
|
|
633
841
|
turn.commandId === selectedSubagent)
|
|
@@ -662,7 +870,7 @@ export class ActorInspectorOverlay {
|
|
|
662
870
|
subagents() {
|
|
663
871
|
if (!this.selectedRun || this.tab !== "turns")
|
|
664
872
|
return ["all"];
|
|
665
|
-
const ids =
|
|
873
|
+
const ids = this.allTurnItems().map((turn) => turn.commandId);
|
|
666
874
|
return ["all", ...new Set(ids)];
|
|
667
875
|
}
|
|
668
876
|
valueOptions() {
|
|
@@ -795,17 +1003,11 @@ export class ActorInspectorOverlay {
|
|
|
795
1003
|
else
|
|
796
1004
|
this.detailTurn = this.turnItems()[this.rowIndex];
|
|
797
1005
|
this.detailScroll = 0;
|
|
798
|
-
this.detailView = "evidence";
|
|
799
1006
|
this.detailOpen = Boolean(this.detailCommunication || this.detailTurn);
|
|
800
1007
|
if (this.detailOpen)
|
|
801
1008
|
this.focus = "detail";
|
|
802
1009
|
}
|
|
803
1010
|
backDetail() {
|
|
804
|
-
if (this.detailView === "transcript") {
|
|
805
|
-
this.detailView = "evidence";
|
|
806
|
-
this.detailScroll = 0;
|
|
807
|
-
return;
|
|
808
|
-
}
|
|
809
1011
|
this.closeDetail();
|
|
810
1012
|
}
|
|
811
1013
|
closeDetail() {
|
|
@@ -813,7 +1015,6 @@ export class ActorInspectorOverlay {
|
|
|
813
1015
|
this.detailCommunication = undefined;
|
|
814
1016
|
this.detailTurn = undefined;
|
|
815
1017
|
this.detailScroll = 0;
|
|
816
|
-
this.detailView = "evidence";
|
|
817
1018
|
this.focus = "list";
|
|
818
1019
|
}
|
|
819
1020
|
border(left, title, right, width) {
|
package/dist/lib/inspector.d.ts
CHANGED
|
@@ -55,10 +55,19 @@ export interface ActorInspectorPreviewReadOptions {
|
|
|
55
55
|
}
|
|
56
56
|
export interface ActorInspectorRunItem {
|
|
57
57
|
run: string;
|
|
58
|
+
runInstanceId?: string;
|
|
58
59
|
status: string;
|
|
59
60
|
updatedAt?: string;
|
|
60
61
|
}
|
|
61
62
|
export declare function readActorInspectorRuns(stateRoot: string, ownerId: string): ActorInspectorRunItem[];
|
|
63
|
+
export interface ActorInspectorRecipeView {
|
|
64
|
+
composition: Array<Record<string, unknown>>;
|
|
65
|
+
definition?: Record<string, unknown>;
|
|
66
|
+
diagnostics: string[];
|
|
67
|
+
identity: Record<string, unknown>;
|
|
68
|
+
launch: Record<string, unknown>;
|
|
69
|
+
}
|
|
70
|
+
export declare function readActorInspectorRecipe(stateDir: string): ActorInspectorRecipeView;
|
|
62
71
|
export interface ActorInspectorTurnItem extends SessionEvidence.SessionEvidenceTurn {
|
|
63
72
|
commandId: string;
|
|
64
73
|
diagnostics: string[];
|
package/dist/lib/inspector.js
CHANGED
|
@@ -19,10 +19,14 @@ export function readActorInspectorRuns(stateRoot, ownerId) {
|
|
|
19
19
|
const stateDir = path.join(stateRoot, entry.name);
|
|
20
20
|
if (!matchesOwner(stateDir, ownerId))
|
|
21
21
|
return [];
|
|
22
|
+
const runMeta = readJsonFileResilient(path.join(stateDir, "run.json"), {}).value;
|
|
22
23
|
const progress = readJsonFileResilient(path.join(stateDir, "progress.json"), {}).value;
|
|
23
24
|
const result = readJsonFileResilient(path.join(stateDir, "result.json"), {}).value;
|
|
24
25
|
return [{
|
|
25
26
|
run: entry.name,
|
|
27
|
+
...(typeof runMeta.run_instance_id === "string"
|
|
28
|
+
? { runInstanceId: runMeta.run_instance_id }
|
|
29
|
+
: {}),
|
|
26
30
|
status: String(progress.phase ?? (Object.keys(result).length ? "terminal" : "unknown")),
|
|
27
31
|
...(typeof progress.updatedAt === "string"
|
|
28
32
|
? { updatedAt: progress.updatedAt }
|
|
@@ -42,6 +46,50 @@ export function readActorInspectorRuns(stateRoot, ownerId) {
|
|
|
42
46
|
return [];
|
|
43
47
|
}
|
|
44
48
|
}
|
|
49
|
+
export function readActorInspectorRecipe(stateDir) {
|
|
50
|
+
const result = readJsonFileResilient(path.join(stateDir, "run.json"), {});
|
|
51
|
+
const meta = result.value;
|
|
52
|
+
const contexts = Array.isArray(meta.recipe_context_records)
|
|
53
|
+
? meta.recipe_context_records
|
|
54
|
+
.filter((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry)))
|
|
55
|
+
: [];
|
|
56
|
+
const primary = contexts.find((entry) => entry.depth === 0) ?? contexts[0];
|
|
57
|
+
const definition = primary?.recipe && typeof primary.recipe === "object" &&
|
|
58
|
+
!Array.isArray(primary.recipe)
|
|
59
|
+
? primary.recipe
|
|
60
|
+
: undefined;
|
|
61
|
+
const redacted = (value) => SessionEvidence.redactSessionEvidenceValue(value);
|
|
62
|
+
const redactedRecord = (value) => redacted(Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== undefined)));
|
|
63
|
+
return {
|
|
64
|
+
composition: contexts.map((entry) => redactedRecord({
|
|
65
|
+
...(typeof entry.alias === "string" ? { alias: entry.alias } : {}),
|
|
66
|
+
depth: entry.depth,
|
|
67
|
+
file: entry.file,
|
|
68
|
+
import_path: entry.import_path,
|
|
69
|
+
name: entry.name,
|
|
70
|
+
})),
|
|
71
|
+
...(definition
|
|
72
|
+
? { definition: redactedRecord(definition) }
|
|
73
|
+
: {}),
|
|
74
|
+
diagnostics: result.diagnostics.map((diagnostic) => diagnostic.message),
|
|
75
|
+
identity: redactedRecord({
|
|
76
|
+
run: meta.run,
|
|
77
|
+
recipe: meta.recipe,
|
|
78
|
+
recipe_file: meta.recipe_file,
|
|
79
|
+
launch_source: meta.launch_source,
|
|
80
|
+
}),
|
|
81
|
+
launch: redactedRecord({
|
|
82
|
+
cwd: meta.cwd,
|
|
83
|
+
template: meta.template,
|
|
84
|
+
values: meta.values,
|
|
85
|
+
model_policy: meta.model_policy,
|
|
86
|
+
mailbox: meta.mailbox,
|
|
87
|
+
artifacts: meta.artifacts,
|
|
88
|
+
notification_policy: meta.notification_policy,
|
|
89
|
+
retire_when: meta.retire_when,
|
|
90
|
+
}),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
45
93
|
function ownedSessionPath(stateDir, sessionFile) {
|
|
46
94
|
if (path.isAbsolute(sessionFile))
|
|
47
95
|
return undefined;
|
|
@@ -17,6 +17,7 @@ export interface RunObservation {
|
|
|
17
17
|
artifacts?: Record<string, string>;
|
|
18
18
|
launchSource?: AsyncRuns.AsyncRunLaunchSource;
|
|
19
19
|
modelPolicy?: Record<string, unknown>;
|
|
20
|
+
notificationPolicy?: "normal" | "silent";
|
|
20
21
|
recipeFile?: string;
|
|
21
22
|
terminalHandled?: boolean;
|
|
22
23
|
retireWhen?: string;
|