@llblab/pi-actors 0.22.5 → 0.24.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 +125 -59
- package/BACKLOG.md +88 -403
- package/CHANGELOG.md +39 -0
- package/README.md +14 -1
- package/dist/fixtures/protocol/actor-message-branch.json +13 -0
- package/dist/fixtures/protocol/artifact-manifest.json +9 -0
- package/dist/fixtures/protocol/mailbox-contract.json +15 -0
- package/dist/fixtures/protocol/recipe-summary.json +16 -0
- package/dist/fixtures/protocol/room-message.json +11 -0
- package/dist/fixtures/protocol/room-roster.json +11 -0
- package/dist/fixtures/protocol/run-inbox-message.json +9 -0
- package/dist/fixtures/protocol/run-outbox-event.json +9 -0
- package/dist/fixtures/protocol/run-state.json +10 -0
- package/dist/index.js +18 -2
- package/dist/lib/actor-inspector-tui.d.ts +4 -0
- package/dist/lib/actor-inspector-tui.js +58 -44
- package/dist/lib/actor-rooms.d.ts +16 -0
- package/dist/lib/actor-rooms.js +123 -28
- package/dist/lib/actor-worker.d.ts +13 -0
- package/dist/lib/actor-worker.js +86 -0
- package/dist/lib/async-runner.d.ts +5 -0
- package/dist/lib/async-runner.js +134 -0
- package/dist/lib/async-runs.d.ts +34 -2
- package/dist/lib/async-runs.js +224 -50
- package/dist/lib/build-dist.d.ts +5 -0
- package/dist/lib/build-dist.js +24 -0
- package/dist/lib/command-templates.js +5 -5
- package/dist/lib/conformance.d.ts +12 -0
- package/dist/lib/conformance.js +28 -0
- package/dist/lib/coordinator.d.ts +5 -0
- package/dist/lib/coordinator.js +557 -0
- package/dist/lib/limits.d.ts +11 -0
- package/dist/lib/limits.js +11 -0
- package/dist/lib/locker.d.ts +5 -0
- package/dist/lib/locker.js +310 -0
- package/dist/lib/mailbox-loop.d.ts +41 -0
- package/dist/lib/mailbox-loop.js +62 -0
- package/dist/lib/observability.d.ts +2 -2
- package/dist/lib/observability.js +57 -57
- package/dist/lib/output.js +4 -5
- package/dist/lib/prompts.d.ts +1 -1
- package/dist/lib/prompts.js +1 -1
- package/dist/lib/recipe-discovery.js +1 -0
- package/dist/lib/recipe-references.d.ts +11 -2
- package/dist/lib/recipe-references.js +29 -4
- package/dist/lib/recipe-usage.d.ts +2 -1
- package/dist/lib/recipe-usage.js +15 -3
- package/dist/lib/recipe-utils.d.ts +5 -0
- package/dist/lib/recipe-utils.js +385 -0
- package/dist/lib/runtime-notifier.js +3 -7
- package/dist/lib/runtime.js +12 -2
- package/dist/lib/state-readers.d.ts +21 -0
- package/dist/lib/state-readers.js +74 -0
- package/dist/lib/tools.js +198 -39
- package/dist/lib/validate-recipe.d.ts +6 -0
- package/dist/lib/validate-recipe.js +104 -0
- package/dist/recipes/actor-worker.json +35 -0
- package/dist/recipes/coordinator-locker.json +45 -0
- package/dist/recipes/lens-swarm.json +66 -0
- package/dist/recipes/locker.json +45 -0
- package/dist/recipes/music-player.json +38 -0
- package/dist/recipes/pipeline-architect-coordinator.json +95 -0
- package/dist/recipes/pipeline-artifact-bundle.json +100 -0
- package/dist/recipes/pipeline-artifact-report.json +58 -0
- package/dist/recipes/pipeline-artifact-write.json +72 -0
- package/dist/recipes/pipeline-async-run-ops.json +70 -0
- package/dist/recipes/pipeline-checkpoint-continuation.json +67 -0
- package/dist/recipes/pipeline-development-tasking.json +81 -0
- package/dist/recipes/pipeline-docs-maintenance.json +80 -0
- package/dist/recipes/pipeline-media-library.json +59 -0
- package/dist/recipes/pipeline-quorum-review.json +79 -0
- package/dist/recipes/pipeline-release-readiness.json +110 -0
- package/dist/recipes/pipeline-release-summary.json +88 -0
- package/dist/recipes/pipeline-repo-health.json +89 -0
- package/dist/recipes/pipeline-research-synthesis.json +94 -0
- package/dist/recipes/pipeline-review-readiness.json +54 -0
- package/dist/recipes/pipeline-room-swarm.json +50 -0
- package/dist/recipes/subagent-artifact.json +32 -0
- package/dist/recipes/subagent-checkpoint.json +33 -0
- package/dist/recipes/subagent-conflict-report.json +32 -0
- package/dist/recipes/subagent-contradiction-map.json +33 -0
- package/dist/recipes/subagent-critic.json +35 -0
- package/dist/recipes/subagent-evidence-map.json +33 -0
- package/dist/recipes/subagent-followup.json +33 -0
- package/dist/recipes/subagent-judge.json +33 -0
- package/dist/recipes/subagent-merge.json +33 -0
- package/dist/recipes/subagent-message.json +34 -0
- package/dist/recipes/subagent-normalize.json +31 -0
- package/dist/recipes/subagent-plan.json +33 -0
- package/dist/recipes/subagent-prompt.json +28 -0
- package/dist/recipes/subagent-quorum.json +43 -0
- package/dist/recipes/subagent-review-coordinator.json +114 -0
- package/dist/recipes/subagent-review.json +37 -0
- package/dist/recipes/subagent-task-card.json +35 -0
- package/dist/recipes/subagent-tools.json +27 -0
- package/dist/recipes/subagent-verify.json +34 -0
- package/dist/recipes/subagents-prompts.json +51 -0
- package/dist/recipes/utility-actor-message.json +23 -0
- package/dist/recipes/utility-artifact-manifest.json +16 -0
- package/dist/recipes/utility-artifact-write.json +16 -0
- package/dist/recipes/utility-changelog-head.json +11 -0
- package/dist/recipes/utility-changelog-section.json +13 -0
- package/dist/recipes/utility-coordinator-lock-snapshot.json +13 -0
- package/dist/recipes/utility-git-log.json +11 -0
- package/dist/recipes/utility-git-status.json +9 -0
- package/dist/recipes/utility-jsonl-tail.json +10 -0
- package/dist/recipes/utility-markdown-index.json +14 -0
- package/dist/recipes/utility-package-summary.json +11 -0
- package/dist/recipes/utility-playlist-build.json +17 -0
- package/dist/recipes/utility-playlist-scan.json +11 -0
- package/dist/recipes/utility-run-ops-snapshot.json +17 -0
- package/dist/recipes/utility-run-state-files.json +13 -0
- package/dist/recipes/utility-run-summary.json +11 -0
- package/dist/recipes/utility-skill-summary.json +13 -0
- package/dist/recipes/utility-validate-recipe.json +13 -0
- package/dist/recipes/utility-validation-wrapper.json +13 -0
- package/dist/scripts/actor-worker.mjs +31 -0
- package/dist/scripts/async-runner.mjs +31 -0
- package/dist/scripts/build-dist.mjs +25 -0
- package/dist/scripts/conformance.mjs +33 -0
- package/dist/scripts/coordinator.mjs +31 -0
- package/dist/scripts/locker.mjs +33 -0
- package/dist/scripts/music-player.mjs +964 -0
- package/dist/scripts/recipe-utils.mjs +31 -0
- package/dist/scripts/validate-recipe.mjs +34 -0
- package/dist/skills/actors/SKILL.md +375 -0
- package/dist/skills/swarm/SKILL.md +467 -0
- package/dist/skills/swarm/references/development-swarm.md +596 -0
- package/docs/actor-messages.md +2 -2
- package/docs/async-runs.md +13 -1
- package/docs/template-recipes.md +2 -2
- package/docs/tool-registry.md +0 -1
- package/fixtures/protocol/actor-message-branch.json +13 -0
- package/fixtures/protocol/artifact-manifest.json +9 -0
- package/fixtures/protocol/mailbox-contract.json +15 -0
- package/fixtures/protocol/recipe-summary.json +16 -0
- package/fixtures/protocol/room-message.json +11 -0
- package/fixtures/protocol/room-roster.json +11 -0
- package/fixtures/protocol/run-inbox-message.json +9 -0
- package/fixtures/protocol/run-outbox-event.json +9 -0
- package/fixtures/protocol/run-state.json +10 -0
- package/index.ts +21 -0
- package/lib/actor-inspector-tui.ts +138 -59
- package/lib/actor-rooms.ts +241 -60
- package/lib/actor-worker.ts +118 -0
- package/lib/async-runner.ts +173 -0
- package/lib/async-runs.ts +302 -53
- package/lib/build-dist.ts +30 -0
- package/lib/command-templates.ts +5 -5
- package/lib/conformance.ts +46 -0
- package/lib/coordinator.ts +649 -0
- package/lib/limits.ts +12 -0
- package/lib/locker.ts +340 -0
- package/lib/mailbox-loop.ts +148 -0
- package/lib/observability.ts +34 -23
- package/lib/output.ts +4 -6
- package/lib/prompts.ts +1 -1
- package/lib/recipe-discovery.ts +1 -0
- package/lib/recipe-references.ts +57 -6
- package/lib/recipe-usage.ts +31 -4
- package/lib/recipe-utils.ts +486 -0
- package/lib/runtime-notifier.ts +4 -6
- package/lib/runtime.ts +31 -7
- package/lib/state-readers.ts +93 -0
- package/lib/tools.ts +297 -58
- package/lib/validate-recipe.ts +110 -0
- package/package.json +11 -2
- package/recipes/actor-worker.json +35 -0
- package/recipes/pipeline-quorum-review.json +12 -7
- package/scripts/actor-worker.mjs +31 -0
- package/scripts/async-runner.mjs +11 -195
- package/scripts/build-dist.mjs +25 -0
- package/scripts/conformance.mjs +33 -0
- package/scripts/coordinator.mjs +19 -616
- package/scripts/locker.mjs +23 -322
- package/scripts/music-player.mjs +21 -2
- package/scripts/recipe-utils.mjs +20 -467
- package/scripts/validate-recipe.mjs +23 -113
- package/skills/actors/SKILL.md +7 -4
- package/skills/swarm/SKILL.md +3 -1
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "actor-worker",
|
|
3
|
+
"path": "recipes/actor-worker.json",
|
|
4
|
+
"location": "packaged",
|
|
5
|
+
"active": true,
|
|
6
|
+
"args": [
|
|
7
|
+
"run",
|
|
8
|
+
"branch",
|
|
9
|
+
"poll_ms",
|
|
10
|
+
"state_dir"
|
|
11
|
+
],
|
|
12
|
+
"mailbox": {
|
|
13
|
+
"kind": "branch",
|
|
14
|
+
"accepts": ["task.assign", "control.stop"]
|
|
15
|
+
}
|
|
16
|
+
}
|
package/index.ts
CHANGED
|
@@ -48,6 +48,7 @@ export default function toolRegistryExtension(pi: ExtensionAPI) {
|
|
|
48
48
|
const runDirWatchers = new Map<string, FSWatcher>();
|
|
49
49
|
const observedRuns = new Map<string, Observability.RunObservedStatus>();
|
|
50
50
|
const observedRunEventLines = new Map<string, number>();
|
|
51
|
+
const observedRunOutboxEventIds = new Map<string, Set<string>>();
|
|
51
52
|
const retirementAttempts = new Set<string>();
|
|
52
53
|
let runStatusFrame = 0;
|
|
53
54
|
let communicationWidgetVisible = false;
|
|
@@ -60,6 +61,7 @@ export default function toolRegistryExtension(pi: ExtensionAPI) {
|
|
|
60
61
|
let actorInspectorUnreadOnly = false;
|
|
61
62
|
let actorInspectorRoomLimitPerRun = 12;
|
|
62
63
|
let selectedInspectorSequence: number | undefined;
|
|
64
|
+
const actorInspectorReadKeys = new Set<string>();
|
|
63
65
|
let recipeWatcherFailureNotified = false;
|
|
64
66
|
const getRunOwnerId = (ctx: ExtensionContext): string =>
|
|
65
67
|
ctx.sessionManager.getSessionId();
|
|
@@ -108,6 +110,7 @@ export default function toolRegistryExtension(pi: ExtensionAPI) {
|
|
|
108
110
|
branch: actorInspectorBranch,
|
|
109
111
|
mention: actorInspectorMention,
|
|
110
112
|
ownerId,
|
|
113
|
+
readKeys: actorInspectorReadKeys,
|
|
111
114
|
roomLimitPerRun: actorInspectorRoomLimitPerRun,
|
|
112
115
|
unreadOnly: actorInspectorUnreadOnly,
|
|
113
116
|
},
|
|
@@ -150,6 +153,7 @@ export default function toolRegistryExtension(pi: ExtensionAPI) {
|
|
|
150
153
|
const outboxEvents = Observability.detectRunOutboxEvents(
|
|
151
154
|
observedRunEventLines,
|
|
152
155
|
summary,
|
|
156
|
+
observedRunOutboxEventIds,
|
|
153
157
|
);
|
|
154
158
|
if (!notify) return;
|
|
155
159
|
retireCandidateRuns(ctx, summary);
|
|
@@ -175,6 +179,7 @@ export default function toolRegistryExtension(pi: ExtensionAPI) {
|
|
|
175
179
|
observedRunEventLines,
|
|
176
180
|
summary,
|
|
177
181
|
transitions.map((transition) => transition.stateDir ?? transition.run),
|
|
182
|
+
observedRunOutboxEventIds,
|
|
178
183
|
);
|
|
179
184
|
for (const event of outboxEvents) {
|
|
180
185
|
if (!Observability.shouldNotifyRunOutboxEvent(event)) continue;
|
|
@@ -414,6 +419,22 @@ export default function toolRegistryExtension(pi: ExtensionAPI) {
|
|
|
414
419
|
ctx.ui.notify("Usage: /actors-inspect <number>", "warning");
|
|
415
420
|
return;
|
|
416
421
|
}
|
|
422
|
+
const previews = ActorInspectorTui.readActorInspectorPreviews(
|
|
423
|
+
RUN_STATE_ROOT,
|
|
424
|
+
actorInspectorRows,
|
|
425
|
+
{
|
|
426
|
+
channels: actorInspectorChannels,
|
|
427
|
+
currentRunOnly: true,
|
|
428
|
+
branch: actorInspectorBranch,
|
|
429
|
+
mention: actorInspectorMention,
|
|
430
|
+
ownerId: getRunOwnerId(ctx),
|
|
431
|
+
readKeys: actorInspectorReadKeys,
|
|
432
|
+
roomLimitPerRun: actorInspectorRoomLimitPerRun,
|
|
433
|
+
unreadOnly: actorInspectorUnreadOnly,
|
|
434
|
+
},
|
|
435
|
+
);
|
|
436
|
+
const preview = previews.find((item) => item.sequence === sequence);
|
|
437
|
+
if (preview) actorInspectorReadKeys.add(ActorInspectorTui.inspectorPreviewReadKey(preview));
|
|
417
438
|
selectedInspectorSequence = sequence;
|
|
418
439
|
communicationWidgetVisible = true;
|
|
419
440
|
updateRunUi(ctx);
|
|
@@ -9,16 +9,20 @@ import * as path from "node:path";
|
|
|
9
9
|
import { visibleWidth } from "@earendil-works/pi-tui";
|
|
10
10
|
|
|
11
11
|
import type { ActorMessage } from "./actor-messages.ts";
|
|
12
|
+
import * as Limits from "./limits.ts";
|
|
12
13
|
import * as Paths from "./paths.ts";
|
|
14
|
+
import { readJsonFileResilient, readJsonlFileResilient } from "./state-readers.ts";
|
|
13
15
|
|
|
14
16
|
export interface ActorInspectorPreview {
|
|
15
17
|
body_preview?: string;
|
|
16
18
|
branch?: string;
|
|
17
19
|
channel: "broadcast" | "direct" | "room";
|
|
20
|
+
event_id?: string;
|
|
18
21
|
from?: string;
|
|
19
22
|
from_display?: string;
|
|
20
23
|
inbox_status?: string;
|
|
21
24
|
message_id?: string;
|
|
25
|
+
needs_response?: boolean;
|
|
22
26
|
run: string;
|
|
23
27
|
sequence?: number;
|
|
24
28
|
summary?: string;
|
|
@@ -57,6 +61,7 @@ export interface ActorInspectorPreviewReadOptions {
|
|
|
57
61
|
currentRunOnly?: boolean;
|
|
58
62
|
channels?: ActorInspectorPreview["channel"][];
|
|
59
63
|
mention?: string;
|
|
64
|
+
readKeys?: Iterable<string>;
|
|
60
65
|
roomLimitPerRun?: number;
|
|
61
66
|
unreadOnly?: boolean;
|
|
62
67
|
}
|
|
@@ -68,25 +73,13 @@ function asRecord(value: unknown): Record<string, unknown> {
|
|
|
68
73
|
}
|
|
69
74
|
|
|
70
75
|
function readJsonLines(file: string): Record<string, unknown>[] {
|
|
71
|
-
|
|
72
|
-
return fs
|
|
73
|
-
.readFileSync(file, "utf8")
|
|
74
|
-
.split("\n")
|
|
75
|
-
.filter(Boolean)
|
|
76
|
-
.flatMap((line) => {
|
|
77
|
-
try {
|
|
78
|
-
return [JSON.parse(line) as Record<string, unknown>];
|
|
79
|
-
} catch {
|
|
80
|
-
return [];
|
|
81
|
-
}
|
|
82
|
-
});
|
|
83
|
-
} catch (error) {
|
|
84
|
-
if ((error as NodeJS.ErrnoException).code === "ENOENT") return [];
|
|
85
|
-
return [];
|
|
86
|
-
}
|
|
76
|
+
return readJsonlFileResilient<Record<string, unknown>>(file).records;
|
|
87
77
|
}
|
|
88
78
|
|
|
89
|
-
function previewValue(
|
|
79
|
+
function previewValue(
|
|
80
|
+
value: unknown,
|
|
81
|
+
maxLength = Limits.INSPECTOR_BODY_PREVIEW_CHARS,
|
|
82
|
+
): string | undefined {
|
|
90
83
|
if (value === undefined) return undefined;
|
|
91
84
|
const text = typeof value === "string" ? value : JSON.stringify(value);
|
|
92
85
|
const compact = text.replaceAll(/\s+/g, " ").trim();
|
|
@@ -118,6 +111,11 @@ function previewFromMessage(
|
|
|
118
111
|
const summary =
|
|
119
112
|
typeof message.summary === "string" ? message.summary : undefined;
|
|
120
113
|
const body = asRecord(message.body);
|
|
114
|
+
const metadata = asRecord(message.metadata);
|
|
115
|
+
const eventId =
|
|
116
|
+
typeof message.id === "string" && message.id.trim()
|
|
117
|
+
? message.id.trim()
|
|
118
|
+
: undefined;
|
|
121
119
|
const display = from
|
|
122
120
|
? typeof body.display === "string" && body.display.trim()
|
|
123
121
|
? body.display.trim()
|
|
@@ -128,8 +126,10 @@ function previewFromMessage(
|
|
|
128
126
|
? { body_preview: previewValue(message.body) }
|
|
129
127
|
: {}),
|
|
130
128
|
channel: channelFor({ to }),
|
|
129
|
+
...(eventId ? { event_id: eventId } : {}),
|
|
131
130
|
...(from ? { from } : {}),
|
|
132
131
|
...(display ? { from_display: display } : {}),
|
|
132
|
+
...(metadata.requires_response === true ? { needs_response: true } : {}),
|
|
133
133
|
run,
|
|
134
134
|
...(summary ? { summary } : {}),
|
|
135
135
|
timestamp,
|
|
@@ -142,21 +142,25 @@ function readRoomRosterRecords(
|
|
|
142
142
|
stateDir: string,
|
|
143
143
|
room: string,
|
|
144
144
|
): Record<string, Record<string, unknown>> {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
} catch {
|
|
150
|
-
return {};
|
|
151
|
-
}
|
|
145
|
+
return readJsonFileResilient<Record<string, Record<string, unknown>>>(
|
|
146
|
+
path.join(stateDir, "rooms", room, "roster.json"),
|
|
147
|
+
{},
|
|
148
|
+
).value;
|
|
152
149
|
}
|
|
153
150
|
|
|
154
|
-
function memberDisplay(
|
|
155
|
-
|
|
151
|
+
function memberDisplay(
|
|
152
|
+
_address: string,
|
|
153
|
+
member: Record<string, unknown>,
|
|
154
|
+
): string | undefined {
|
|
155
|
+
const display =
|
|
156
|
+
typeof member.display === "string" ? member.display.trim() : "";
|
|
156
157
|
return display || undefined;
|
|
157
158
|
}
|
|
158
159
|
|
|
159
|
-
function readRoomDisplayNames(
|
|
160
|
+
function readRoomDisplayNames(
|
|
161
|
+
stateDir: string,
|
|
162
|
+
room: string,
|
|
163
|
+
): Record<string, string> {
|
|
160
164
|
const roster = readRoomRosterRecords(stateDir, room);
|
|
161
165
|
return Object.fromEntries(
|
|
162
166
|
Object.entries(roster).flatMap(([address, member]) => {
|
|
@@ -227,17 +231,28 @@ function readBranchInboxPreviews(
|
|
|
227
231
|
const preview = previewFromMessage(
|
|
228
232
|
run,
|
|
229
233
|
message,
|
|
230
|
-
String(
|
|
234
|
+
String(
|
|
235
|
+
message.queued_at ??
|
|
236
|
+
message.received_at ??
|
|
237
|
+
message.timestamp ??
|
|
238
|
+
"",
|
|
239
|
+
),
|
|
231
240
|
);
|
|
232
241
|
if (!preview) return undefined;
|
|
233
242
|
return {
|
|
234
243
|
...preview,
|
|
235
244
|
branch: entry.name,
|
|
236
|
-
...(typeof message.id === "string"
|
|
237
|
-
|
|
245
|
+
...(typeof message.id === "string"
|
|
246
|
+
? { message_id: message.id }
|
|
247
|
+
: {}),
|
|
248
|
+
...(typeof message.status === "string"
|
|
249
|
+
? { inbox_status: message.status }
|
|
250
|
+
: {}),
|
|
238
251
|
};
|
|
239
252
|
})
|
|
240
|
-
.filter((preview): preview is ActorInspectorPreview =>
|
|
253
|
+
.filter((preview): preview is ActorInspectorPreview =>
|
|
254
|
+
Boolean(preview),
|
|
255
|
+
),
|
|
241
256
|
);
|
|
242
257
|
} catch (error) {
|
|
243
258
|
if ((error as NodeJS.ErrnoException).code === "ENOENT") return [];
|
|
@@ -262,22 +277,40 @@ function readOutboxPreviews(
|
|
|
262
277
|
}
|
|
263
278
|
|
|
264
279
|
function getRunOwnerId(stateDir: string): string | undefined {
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
} catch {
|
|
271
|
-
return undefined;
|
|
272
|
-
}
|
|
280
|
+
const meta = readJsonFileResilient<Record<string, unknown> | undefined>(
|
|
281
|
+
path.join(stateDir, "run.json"),
|
|
282
|
+
undefined,
|
|
283
|
+
).value;
|
|
284
|
+
return typeof meta?.ownerId === "string" ? meta.ownerId : undefined;
|
|
273
285
|
}
|
|
274
286
|
|
|
275
287
|
function matchesOwner(stateDir: string, ownerId: string | undefined): boolean {
|
|
276
288
|
return ownerId === undefined || getRunOwnerId(stateDir) === ownerId;
|
|
277
289
|
}
|
|
278
290
|
|
|
279
|
-
function
|
|
280
|
-
|
|
291
|
+
export function inspectorPreviewReadKey(
|
|
292
|
+
preview: ActorInspectorPreview,
|
|
293
|
+
): string {
|
|
294
|
+
return [
|
|
295
|
+
preview.channel,
|
|
296
|
+
preview.run,
|
|
297
|
+
preview.branch ?? "",
|
|
298
|
+
preview.event_id ?? preview.message_id ?? preview.timestamp,
|
|
299
|
+
preview.from ?? "",
|
|
300
|
+
preview.to,
|
|
301
|
+
preview.type,
|
|
302
|
+
].join("|");
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function isUnreadPreview(
|
|
306
|
+
preview: ActorInspectorPreview,
|
|
307
|
+
readKeys?: Set<string>,
|
|
308
|
+
): boolean {
|
|
309
|
+
if (readKeys?.has(inspectorPreviewReadKey(preview))) return false;
|
|
310
|
+
return (
|
|
311
|
+
preview.inbox_status === "queued" ||
|
|
312
|
+
(preview.inbox_status === undefined && preview.branch !== undefined)
|
|
313
|
+
);
|
|
281
314
|
}
|
|
282
315
|
|
|
283
316
|
function matchesBranchFilter(
|
|
@@ -298,7 +331,8 @@ function matchesPreviewFilter(
|
|
|
298
331
|
if (options.channels?.length && !options.channels.includes(preview.channel)) {
|
|
299
332
|
return false;
|
|
300
333
|
}
|
|
301
|
-
|
|
334
|
+
const readKeys = options.readKeys ? new Set(options.readKeys) : undefined;
|
|
335
|
+
if (options.unreadOnly && !isUnreadPreview(preview, readKeys)) return false;
|
|
302
336
|
if (!matchesBranchFilter(preview, options.branch)) return false;
|
|
303
337
|
const mention = options.mention?.trim().toLowerCase();
|
|
304
338
|
if (!mention) return true;
|
|
@@ -421,6 +455,10 @@ function routeText(preview: ActorInspectorPreview): string {
|
|
|
421
455
|
return `${actor} → ${actorName(preview.to)}`;
|
|
422
456
|
}
|
|
423
457
|
|
|
458
|
+
function attentionMarker(preview: ActorInspectorPreview): string {
|
|
459
|
+
return preview.needs_response ? "! " : "";
|
|
460
|
+
}
|
|
461
|
+
|
|
424
462
|
function style(
|
|
425
463
|
styleFn: ((text: string) => string) | undefined,
|
|
426
464
|
text: string,
|
|
@@ -435,7 +473,8 @@ function previewText(preview: ActorInspectorPreview): string {
|
|
|
435
473
|
function propertyValue(value: unknown): string {
|
|
436
474
|
if (value === undefined) return "";
|
|
437
475
|
if (typeof value === "string") return value;
|
|
438
|
-
if (typeof value === "number" || typeof value === "boolean")
|
|
476
|
+
if (typeof value === "number" || typeof value === "boolean")
|
|
477
|
+
return String(value);
|
|
439
478
|
return JSON.stringify(value);
|
|
440
479
|
}
|
|
441
480
|
|
|
@@ -491,7 +530,7 @@ function renderCompactInspectorEntry(
|
|
|
491
530
|
const contentWidth = Math.max(8, width - prefix.length - suffix.length);
|
|
492
531
|
const sequence = String(preview.sequence ?? 0).padStart(sequenceWidth, " ");
|
|
493
532
|
const sequencePrefix = `${sequence}${separator}`;
|
|
494
|
-
const route = routeText(preview)
|
|
533
|
+
const route = `${attentionMarker(preview)}${routeText(preview)}`;
|
|
495
534
|
const routePadding = " ".repeat(
|
|
496
535
|
Math.max(0, routeWidth - displayWidth(route)),
|
|
497
536
|
);
|
|
@@ -537,14 +576,19 @@ function renderInspectorEntry(
|
|
|
537
576
|
const contentWidth = Math.max(8, width - prefix.length - suffix.length);
|
|
538
577
|
const sequence = String(preview.sequence ?? 0).padStart(sequenceWidth, " ");
|
|
539
578
|
const sequencePrefix = `${sequence}${separator}`;
|
|
540
|
-
const route = routeText(preview)
|
|
579
|
+
const route = `${attentionMarker(preview)}${routeText(preview)}`;
|
|
541
580
|
const type = preview.type;
|
|
542
581
|
const summary = preview.summary?.trim() ?? "";
|
|
543
|
-
const body =
|
|
582
|
+
const body =
|
|
583
|
+
preview.body_preview?.trim() || (!summary ? previewText(preview) : "-");
|
|
544
584
|
const visibleRoute = boundedLine(route, routeWidth);
|
|
545
585
|
const visibleType = boundedLine(type, typeWidth);
|
|
546
|
-
const boundedRoutePadding = " ".repeat(
|
|
547
|
-
|
|
586
|
+
const boundedRoutePadding = " ".repeat(
|
|
587
|
+
Math.max(0, routeWidth - displayWidth(visibleRoute)),
|
|
588
|
+
);
|
|
589
|
+
const boundedTypePadding = " ".repeat(
|
|
590
|
+
Math.max(0, typeWidth - displayWidth(visibleType)),
|
|
591
|
+
);
|
|
548
592
|
const leadParts = [
|
|
549
593
|
style(styles.muted, sequencePrefix),
|
|
550
594
|
style(styles.target, visibleRoute),
|
|
@@ -557,11 +601,20 @@ function renderInspectorEntry(
|
|
|
557
601
|
let lead = `${sequencePrefix}${visibleRoute}${boundedRoutePadding}${separator}${visibleType}${boundedTypePadding}${separator}`;
|
|
558
602
|
if (summary) {
|
|
559
603
|
const visibleSummary = boundedLine(summary, summaryWidth);
|
|
560
|
-
const summaryPadding = " ".repeat(
|
|
604
|
+
const summaryPadding = " ".repeat(
|
|
605
|
+
Math.max(0, summaryWidth - displayWidth(visibleSummary)),
|
|
606
|
+
);
|
|
561
607
|
lead += `${visibleSummary}${summaryPadding}${separator}`;
|
|
562
|
-
leadParts.push(
|
|
608
|
+
leadParts.push(
|
|
609
|
+
style(styles.preview, visibleSummary),
|
|
610
|
+
summaryPadding,
|
|
611
|
+
separator,
|
|
612
|
+
);
|
|
563
613
|
}
|
|
564
|
-
const visibleBody = boundedLine(
|
|
614
|
+
const visibleBody = boundedLine(
|
|
615
|
+
body,
|
|
616
|
+
Math.max(0, contentWidth - displayWidth(lead)),
|
|
617
|
+
);
|
|
565
618
|
const plain = `${lead}${visibleBody}`;
|
|
566
619
|
const rendered = `${leadParts.join("")}${style(styles.preview, visibleBody)}`;
|
|
567
620
|
const line = `${prefix}${padLine(plain, rendered, contentWidth, styles)}${suffix}`;
|
|
@@ -589,7 +642,10 @@ export function readActorInspectorRoster(
|
|
|
589
642
|
|
|
590
643
|
function rosterRoleText(role: string | undefined): string | undefined {
|
|
591
644
|
const roleLabel = role?.split(";")[0] ?? "";
|
|
592
|
-
const cleaned = roleLabel
|
|
645
|
+
const cleaned = roleLabel
|
|
646
|
+
.replaceAll(/\s*\([^)]*\)\s*$/g, "")
|
|
647
|
+
.trim()
|
|
648
|
+
.toLowerCase();
|
|
593
649
|
if (!cleaned || cleaned === "actor") return undefined;
|
|
594
650
|
return cleaned.replaceAll(/\s+/g, "-");
|
|
595
651
|
}
|
|
@@ -603,7 +659,12 @@ function rosterMemberText(member: ActorInspectorRosterMember): string {
|
|
|
603
659
|
|
|
604
660
|
function isRosterMemberActive(member: ActorInspectorRosterMember): boolean {
|
|
605
661
|
const status = member.status?.trim().toLowerCase();
|
|
606
|
-
return
|
|
662
|
+
return (
|
|
663
|
+
!status ||
|
|
664
|
+
status === "present" ||
|
|
665
|
+
status === "active" ||
|
|
666
|
+
status === "running"
|
|
667
|
+
);
|
|
607
668
|
}
|
|
608
669
|
|
|
609
670
|
export function renderInspectorRosterLine(
|
|
@@ -633,12 +694,17 @@ export function renderInspectorRosterPanel(
|
|
|
633
694
|
const flush = () => {
|
|
634
695
|
const visible = boundedLine(plain, innerWidth);
|
|
635
696
|
const line = visible === plain ? rendered : style(styles.muted, visible);
|
|
636
|
-
lines.push(
|
|
697
|
+
lines.push(
|
|
698
|
+
` ${line}${" ".repeat(Math.max(0, innerWidth - displayWidth(visible)))} `,
|
|
699
|
+
);
|
|
637
700
|
};
|
|
638
701
|
for (const token of tokens) {
|
|
639
702
|
const separator = plain === prefix ? "" : ", ";
|
|
640
703
|
const nextPlain = `${plain}${separator}${token.text}`;
|
|
641
|
-
const renderedToken = style(
|
|
704
|
+
const renderedToken = style(
|
|
705
|
+
token.active ? styles.target : styles.muted,
|
|
706
|
+
token.text,
|
|
707
|
+
);
|
|
642
708
|
if (plain !== prefix && displayWidth(nextPlain) > innerWidth) {
|
|
643
709
|
flush();
|
|
644
710
|
plain = ` ${token.text}`;
|
|
@@ -664,12 +730,15 @@ export function renderInspectorItemView(
|
|
|
664
730
|
const orderedKeys = [
|
|
665
731
|
"channel",
|
|
666
732
|
"run",
|
|
733
|
+
"event_id",
|
|
667
734
|
"from",
|
|
668
735
|
"from_display",
|
|
669
736
|
"to",
|
|
670
737
|
"type",
|
|
671
738
|
"summary",
|
|
672
739
|
"body_preview",
|
|
740
|
+
"message_id",
|
|
741
|
+
"needs_response",
|
|
673
742
|
"timestamp",
|
|
674
743
|
"stripe",
|
|
675
744
|
] as const;
|
|
@@ -678,7 +747,9 @@ export function renderInspectorItemView(
|
|
|
678
747
|
.map((key) => [key, propertyValue(preview[key])] as const);
|
|
679
748
|
const keyWidth = Math.max(1, ...entries.map(([key]) => displayWidth(key)));
|
|
680
749
|
const sequenceText = String(preview.sequence ?? options.sequence);
|
|
681
|
-
const sequencePadding = " ".repeat(
|
|
750
|
+
const sequencePadding = " ".repeat(
|
|
751
|
+
Math.max(0, keyWidth - displayWidth(sequenceText)),
|
|
752
|
+
);
|
|
682
753
|
const headerSeparator = " ";
|
|
683
754
|
const route = routeText(preview);
|
|
684
755
|
const visibleRoute = boundedLine(
|
|
@@ -728,7 +799,9 @@ export function renderInspectorWidget(
|
|
|
728
799
|
safeWidth - 1 - sequencePrefixWidth - fixedSeparatorsWidth,
|
|
729
800
|
);
|
|
730
801
|
const naturalRouteWidth = Math.max(
|
|
731
|
-
...visible.map(({ preview }) =>
|
|
802
|
+
...visible.map(({ preview }) =>
|
|
803
|
+
displayWidth(`${attentionMarker(preview)}${routeText(preview)}`),
|
|
804
|
+
),
|
|
732
805
|
);
|
|
733
806
|
const naturalTypeWidth = Math.max(
|
|
734
807
|
...visible.map(({ preview }) => displayWidth(preview.type)),
|
|
@@ -741,13 +814,19 @@ export function renderInspectorWidget(
|
|
|
741
814
|
naturalTypeWidth,
|
|
742
815
|
Math.max(4, Math.floor(availableForColumns * 0.25)),
|
|
743
816
|
);
|
|
744
|
-
const messageWidth = Math.max(
|
|
817
|
+
const messageWidth = Math.max(
|
|
818
|
+
0,
|
|
819
|
+
availableForColumns - routeWidth - typeWidth,
|
|
820
|
+
);
|
|
745
821
|
const summaryWidths = visible
|
|
746
822
|
.map(({ preview }) => preview.summary?.trim())
|
|
747
823
|
.filter((summary): summary is string => Boolean(summary))
|
|
748
824
|
.map((summary) => displayWidth(summary));
|
|
749
825
|
const summaryWidth = summaryWidths.length
|
|
750
|
-
? Math.min(
|
|
826
|
+
? Math.min(
|
|
827
|
+
Math.max(...summaryWidths),
|
|
828
|
+
Math.max(1, Math.floor(messageWidth * 0.5)),
|
|
829
|
+
)
|
|
751
830
|
: 0;
|
|
752
831
|
for (const { preview, stripe } of visible) {
|
|
753
832
|
lines.push(
|