@llblab/pi-actors 0.24.2 → 0.24.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/dist/lib/actor-inspector-tui.js +20 -26
- package/dist/lib/tools.js +2 -0
- package/dist/recipes/music-player.json +2 -1
- package/dist/scripts/music-player.mjs +37 -18
- package/dist/skills/actors/SKILL.md +1 -1
- package/dist/skills/swarm/SKILL.md +1 -1
- package/lib/actor-inspector-tui.ts +21 -27
- package/lib/tools.ts +1 -0
- package/package.json +1 -1
- package/recipes/music-player.json +2 -1
- package/scripts/music-player.mjs +37 -18
- package/skills/actors/SKILL.md +1 -1
- package/skills/swarm/SKILL.md +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.24.4: Music Player Message Control Hotfix
|
|
6
|
+
|
|
7
|
+
- `[Music Player]` Preserve typed actor-message envelopes for run-mailbox delivery so `player.<action>` messages dispatch by message type instead of relying on body payload text.
|
|
8
|
+
- `[Music Player]` Emit `player.stopped` when playback stops so actor inspector and run messages show an explicit domain stop event before terminal completion.
|
|
9
|
+
|
|
10
|
+
## 0.24.3: Actor Inspector Body Field Hotfix
|
|
11
|
+
|
|
12
|
+
- `[Inspector]` Removed the selected-item key-label shim by projecting actor-message detail rows with a real `body` field instead of rendering `body_preview` under an alias.
|
|
13
|
+
|
|
5
14
|
## 0.24.2: Actor Inspector Item View Hotfix
|
|
6
15
|
|
|
7
16
|
- `[Inspector]` Removed the roster panel from selected actor-message item inspection, kept the compact route header styling, added the same left/right gutter used by inspector rows, and renamed the selected-item `body_preview` label to `body`.
|
|
@@ -318,11 +318,6 @@ function propertyValue(value) {
|
|
|
318
318
|
return String(value);
|
|
319
319
|
return JSON.stringify(value);
|
|
320
320
|
}
|
|
321
|
-
function itemViewKeyLabel(key) {
|
|
322
|
-
if (key === "body_preview")
|
|
323
|
-
return "body";
|
|
324
|
-
return key;
|
|
325
|
-
}
|
|
326
321
|
function displayWidth(value) {
|
|
327
322
|
return visibleWidth(value);
|
|
328
323
|
}
|
|
@@ -507,24 +502,24 @@ export function renderInspectorItemView(previews, width = 80, styles = {}, optio
|
|
|
507
502
|
const prefix = " ";
|
|
508
503
|
const suffix = " ";
|
|
509
504
|
const contentWidth = Math.max(8, safeWidth - prefix.length - suffix.length);
|
|
510
|
-
const
|
|
511
|
-
"channel",
|
|
512
|
-
"run",
|
|
513
|
-
"event_id",
|
|
514
|
-
"from",
|
|
515
|
-
"from_display",
|
|
516
|
-
"to",
|
|
517
|
-
"type",
|
|
518
|
-
"summary",
|
|
519
|
-
"
|
|
520
|
-
"message_id",
|
|
521
|
-
"needs_response",
|
|
522
|
-
"timestamp",
|
|
523
|
-
"stripe",
|
|
505
|
+
const rawEntries = [
|
|
506
|
+
["channel", preview.channel],
|
|
507
|
+
["run", preview.run],
|
|
508
|
+
["event_id", preview.event_id],
|
|
509
|
+
["from", preview.from],
|
|
510
|
+
["from_display", preview.from_display],
|
|
511
|
+
["to", preview.to],
|
|
512
|
+
["type", preview.type],
|
|
513
|
+
["summary", preview.summary],
|
|
514
|
+
["body", preview.body_preview],
|
|
515
|
+
["message_id", preview.message_id],
|
|
516
|
+
["needs_response", preview.needs_response],
|
|
517
|
+
["timestamp", preview.timestamp],
|
|
518
|
+
["stripe", preview.stripe],
|
|
524
519
|
];
|
|
525
|
-
const entries =
|
|
526
|
-
.filter((
|
|
527
|
-
.map((key) => [key, propertyValue(
|
|
520
|
+
const entries = rawEntries
|
|
521
|
+
.filter((entry) => entry[1] !== undefined)
|
|
522
|
+
.map(([key, value]) => [key, propertyValue(value)]);
|
|
528
523
|
const keyWidth = Math.max(1, ...entries.map(([key]) => displayWidth(key)));
|
|
529
524
|
const sequenceText = String(preview.sequence ?? options.sequence);
|
|
530
525
|
const sequencePadding = " ".repeat(Math.max(0, keyWidth - displayWidth(sequenceText)));
|
|
@@ -536,13 +531,12 @@ export function renderInspectorItemView(previews, width = 80, styles = {}, optio
|
|
|
536
531
|
const headerPadding = Math.max(0, contentWidth - visibleWidth(headerPlain));
|
|
537
532
|
const lines = [`${prefix}${header}${" ".repeat(headerPadding)}${suffix}`, ""];
|
|
538
533
|
for (const [key, value] of entries) {
|
|
539
|
-
const
|
|
540
|
-
const keyPadding = " ".repeat(Math.max(0, keyWidth - displayWidth(label)));
|
|
534
|
+
const keyPadding = " ".repeat(Math.max(0, keyWidth - displayWidth(key)));
|
|
541
535
|
const separator = " ";
|
|
542
536
|
const valueWidth = Math.max(0, contentWidth - keyWidth - separator.length);
|
|
543
537
|
const visibleValue = boundedLine(value, valueWidth);
|
|
544
|
-
const plain = `${
|
|
545
|
-
const rendered = `${style(styles.muted,
|
|
538
|
+
const plain = `${key}${keyPadding}${separator}${visibleValue}`;
|
|
539
|
+
const rendered = `${style(styles.muted, key)}${keyPadding}${separator}${style(styles.preview, visibleValue)}`;
|
|
546
540
|
const padding = Math.max(0, contentWidth - visibleWidth(plain));
|
|
547
541
|
lines.push(`${prefix}${rendered}${" ".repeat(padding)}${suffix}`);
|
|
548
542
|
}
|
package/dist/lib/tools.js
CHANGED
|
@@ -481,6 +481,8 @@ function getRunOwnerId(ctx) {
|
|
|
481
481
|
return ctx.sessionManager?.getSessionId?.();
|
|
482
482
|
}
|
|
483
483
|
function messageBodyToRunLine(message) {
|
|
484
|
+
if (message.type !== "run.message")
|
|
485
|
+
return JSON.stringify(message);
|
|
484
486
|
if (typeof message.body === "string")
|
|
485
487
|
return message.body;
|
|
486
488
|
if (message.body === undefined)
|
|
@@ -404,26 +404,39 @@ function readText(path) {
|
|
|
404
404
|
}
|
|
405
405
|
}
|
|
406
406
|
|
|
407
|
-
function
|
|
408
|
-
const title = track.split(/[\\/]/).filter(Boolean).pop() || track;
|
|
407
|
+
function emitPlayerEvent(ctx, type, summary, body = {}) {
|
|
409
408
|
writeText(
|
|
410
409
|
ctx.eventFile,
|
|
411
410
|
`${JSON.stringify({
|
|
412
|
-
body
|
|
413
|
-
data:
|
|
411
|
+
body,
|
|
412
|
+
data: body,
|
|
414
413
|
delivery: "log",
|
|
415
|
-
event:
|
|
414
|
+
event: type,
|
|
416
415
|
from: `run:${basename(ctx.stateDir)}`,
|
|
417
416
|
level: "info",
|
|
418
|
-
summary
|
|
417
|
+
summary,
|
|
419
418
|
to: "coordinator",
|
|
420
419
|
ts: new Date().toISOString(),
|
|
421
|
-
type
|
|
420
|
+
type,
|
|
422
421
|
})}\n`,
|
|
423
422
|
"a",
|
|
424
423
|
);
|
|
425
424
|
}
|
|
426
425
|
|
|
426
|
+
function emitTrackEvent(ctx, index, count, track, player) {
|
|
427
|
+
const title = track.split(/[\\/]/).filter(Boolean).pop() || track;
|
|
428
|
+
emitPlayerEvent(ctx, "player.track", `Now playing: ${title}`, {
|
|
429
|
+
count,
|
|
430
|
+
index,
|
|
431
|
+
player,
|
|
432
|
+
track,
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
function emitStoppedEvent(ctx, reason = "stop") {
|
|
437
|
+
emitPlayerEvent(ctx, "player.stopped", "Music player stopped", { reason });
|
|
438
|
+
}
|
|
439
|
+
|
|
427
440
|
function writeStatus(ctx, state, index, count, track, player, pid = "") {
|
|
428
441
|
const updatedAt = new Date().toISOString();
|
|
429
442
|
writeText(
|
|
@@ -611,23 +624,27 @@ function writeInboxMessages(ctx, messages) {
|
|
|
611
624
|
);
|
|
612
625
|
}
|
|
613
626
|
|
|
627
|
+
function commandFromControlToken(token) {
|
|
628
|
+
if (CONTROL_COMMANDS.has(token)) return token;
|
|
629
|
+
if (token === "control.stop" || token === "control.cancel") return "stop";
|
|
630
|
+
const match = /^player\.(.+)$/.exec(token);
|
|
631
|
+
if (match && CONTROL_COMMANDS.has(match[1])) return match[1];
|
|
632
|
+
return undefined;
|
|
633
|
+
}
|
|
634
|
+
|
|
614
635
|
function commandFromInboxMessage(message) {
|
|
615
|
-
if (
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
636
|
+
if (typeof message.type === "string") {
|
|
637
|
+
const command = commandFromControlToken(message.type.trim());
|
|
638
|
+
if (command) return command;
|
|
639
|
+
}
|
|
640
|
+
if (typeof message.body === "string") {
|
|
641
|
+
const command = message.body.trim();
|
|
642
|
+
if (CONTROL_COMMANDS.has(command)) return command;
|
|
620
643
|
}
|
|
621
644
|
if (message.body && typeof message.body === "object") {
|
|
622
645
|
const command = String(message.body.command ?? "").trim();
|
|
623
646
|
if (CONTROL_COMMANDS.has(command)) return command;
|
|
624
647
|
}
|
|
625
|
-
if (typeof message.type === "string") {
|
|
626
|
-
if (message.type === "control.stop" || message.type === "control.cancel")
|
|
627
|
-
return "stop";
|
|
628
|
-
const match = /^player\.(.+)$/.exec(message.type);
|
|
629
|
-
if (match && CONTROL_COMMANDS.has(match[1])) return match[1];
|
|
630
|
-
}
|
|
631
648
|
return undefined;
|
|
632
649
|
}
|
|
633
650
|
|
|
@@ -895,6 +912,7 @@ async function playMain(args) {
|
|
|
895
912
|
const command = readAndClearCommand(ctx);
|
|
896
913
|
if (command === "stop") {
|
|
897
914
|
writeStatus(ctx, "stopped", index, count, track, player, "");
|
|
915
|
+
emitStoppedEvent(ctx, "message");
|
|
898
916
|
return;
|
|
899
917
|
}
|
|
900
918
|
if (command === "previous" || command === "prev") {
|
|
@@ -913,6 +931,7 @@ async function playMain(args) {
|
|
|
913
931
|
}
|
|
914
932
|
}
|
|
915
933
|
writeStatus(ctx, "stopped", index, tracks.length, "", player, "");
|
|
934
|
+
emitStoppedEvent(ctx, "complete");
|
|
916
935
|
} finally {
|
|
917
936
|
cleanup();
|
|
918
937
|
await Promise.race([controlLoop?.promise ?? Promise.resolve(), sleep(100)]);
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: actors
|
|
3
3
|
description: Highest-density practical guide for pi-actors. Read this skill whenever prompt and tools are not enough for spawn, message, inspect, actor runs, tools, recipes, command templates, async lifecycle, mailboxes, artifacts, and local orchestration mechanics.
|
|
4
4
|
metadata:
|
|
5
|
-
version: 0.24.
|
|
5
|
+
version: 0.24.4
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Actors (pi-actors)
|
|
@@ -478,11 +478,6 @@ function propertyValue(value: unknown): string {
|
|
|
478
478
|
return JSON.stringify(value);
|
|
479
479
|
}
|
|
480
480
|
|
|
481
|
-
function itemViewKeyLabel(key: string): string {
|
|
482
|
-
if (key === "body_preview") return "body";
|
|
483
|
-
return key;
|
|
484
|
-
}
|
|
485
|
-
|
|
486
481
|
function displayWidth(value: string): number {
|
|
487
482
|
return visibleWidth(value);
|
|
488
483
|
}
|
|
@@ -735,24 +730,24 @@ export function renderInspectorItemView(
|
|
|
735
730
|
const prefix = " ";
|
|
736
731
|
const suffix = " ";
|
|
737
732
|
const contentWidth = Math.max(8, safeWidth - prefix.length - suffix.length);
|
|
738
|
-
const
|
|
739
|
-
"channel",
|
|
740
|
-
"run",
|
|
741
|
-
"event_id",
|
|
742
|
-
"from",
|
|
743
|
-
"from_display",
|
|
744
|
-
"to",
|
|
745
|
-
"type",
|
|
746
|
-
"summary",
|
|
747
|
-
"
|
|
748
|
-
"message_id",
|
|
749
|
-
"needs_response",
|
|
750
|
-
"timestamp",
|
|
751
|
-
"stripe",
|
|
752
|
-
]
|
|
753
|
-
const entries =
|
|
754
|
-
.filter((
|
|
755
|
-
.map((key) => [key, propertyValue(
|
|
733
|
+
const rawEntries: Array<readonly [string, unknown]> = [
|
|
734
|
+
["channel", preview.channel],
|
|
735
|
+
["run", preview.run],
|
|
736
|
+
["event_id", preview.event_id],
|
|
737
|
+
["from", preview.from],
|
|
738
|
+
["from_display", preview.from_display],
|
|
739
|
+
["to", preview.to],
|
|
740
|
+
["type", preview.type],
|
|
741
|
+
["summary", preview.summary],
|
|
742
|
+
["body", preview.body_preview],
|
|
743
|
+
["message_id", preview.message_id],
|
|
744
|
+
["needs_response", preview.needs_response],
|
|
745
|
+
["timestamp", preview.timestamp],
|
|
746
|
+
["stripe", preview.stripe],
|
|
747
|
+
];
|
|
748
|
+
const entries = rawEntries
|
|
749
|
+
.filter((entry) => entry[1] !== undefined)
|
|
750
|
+
.map(([key, value]) => [key, propertyValue(value)] as const);
|
|
756
751
|
const keyWidth = Math.max(1, ...entries.map(([key]) => displayWidth(key)));
|
|
757
752
|
const sequenceText = String(preview.sequence ?? options.sequence);
|
|
758
753
|
const sequencePadding = " ".repeat(
|
|
@@ -769,13 +764,12 @@ export function renderInspectorItemView(
|
|
|
769
764
|
const headerPadding = Math.max(0, contentWidth - visibleWidth(headerPlain));
|
|
770
765
|
const lines = [`${prefix}${header}${" ".repeat(headerPadding)}${suffix}`, ""];
|
|
771
766
|
for (const [key, value] of entries) {
|
|
772
|
-
const
|
|
773
|
-
const keyPadding = " ".repeat(Math.max(0, keyWidth - displayWidth(label)));
|
|
767
|
+
const keyPadding = " ".repeat(Math.max(0, keyWidth - displayWidth(key)));
|
|
774
768
|
const separator = " ";
|
|
775
769
|
const valueWidth = Math.max(0, contentWidth - keyWidth - separator.length);
|
|
776
770
|
const visibleValue = boundedLine(value, valueWidth);
|
|
777
|
-
const plain = `${
|
|
778
|
-
const rendered = `${style(styles.muted,
|
|
771
|
+
const plain = `${key}${keyPadding}${separator}${visibleValue}`;
|
|
772
|
+
const rendered = `${style(styles.muted, key)}${keyPadding}${separator}${style(styles.preview, visibleValue)}`;
|
|
779
773
|
const padding = Math.max(0, contentWidth - visibleWidth(plain));
|
|
780
774
|
lines.push(`${prefix}${rendered}${" ".repeat(padding)}${suffix}`);
|
|
781
775
|
}
|
package/lib/tools.ts
CHANGED
|
@@ -593,6 +593,7 @@ function getRunOwnerId(ctx: AsyncRunToolContext): string | undefined {
|
|
|
593
593
|
}
|
|
594
594
|
|
|
595
595
|
function messageBodyToRunLine(message: ActorMessages.ActorMessage): string {
|
|
596
|
+
if (message.type !== "run.message") return JSON.stringify(message);
|
|
596
597
|
if (typeof message.body === "string") return message.body;
|
|
597
598
|
if (message.body === undefined) return message.type;
|
|
598
599
|
return JSON.stringify(message.body);
|
package/package.json
CHANGED
package/scripts/music-player.mjs
CHANGED
|
@@ -404,26 +404,39 @@ function readText(path) {
|
|
|
404
404
|
}
|
|
405
405
|
}
|
|
406
406
|
|
|
407
|
-
function
|
|
408
|
-
const title = track.split(/[\\/]/).filter(Boolean).pop() || track;
|
|
407
|
+
function emitPlayerEvent(ctx, type, summary, body = {}) {
|
|
409
408
|
writeText(
|
|
410
409
|
ctx.eventFile,
|
|
411
410
|
`${JSON.stringify({
|
|
412
|
-
body
|
|
413
|
-
data:
|
|
411
|
+
body,
|
|
412
|
+
data: body,
|
|
414
413
|
delivery: "log",
|
|
415
|
-
event:
|
|
414
|
+
event: type,
|
|
416
415
|
from: `run:${basename(ctx.stateDir)}`,
|
|
417
416
|
level: "info",
|
|
418
|
-
summary
|
|
417
|
+
summary,
|
|
419
418
|
to: "coordinator",
|
|
420
419
|
ts: new Date().toISOString(),
|
|
421
|
-
type
|
|
420
|
+
type,
|
|
422
421
|
})}\n`,
|
|
423
422
|
"a",
|
|
424
423
|
);
|
|
425
424
|
}
|
|
426
425
|
|
|
426
|
+
function emitTrackEvent(ctx, index, count, track, player) {
|
|
427
|
+
const title = track.split(/[\\/]/).filter(Boolean).pop() || track;
|
|
428
|
+
emitPlayerEvent(ctx, "player.track", `Now playing: ${title}`, {
|
|
429
|
+
count,
|
|
430
|
+
index,
|
|
431
|
+
player,
|
|
432
|
+
track,
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
function emitStoppedEvent(ctx, reason = "stop") {
|
|
437
|
+
emitPlayerEvent(ctx, "player.stopped", "Music player stopped", { reason });
|
|
438
|
+
}
|
|
439
|
+
|
|
427
440
|
function writeStatus(ctx, state, index, count, track, player, pid = "") {
|
|
428
441
|
const updatedAt = new Date().toISOString();
|
|
429
442
|
writeText(
|
|
@@ -611,23 +624,27 @@ function writeInboxMessages(ctx, messages) {
|
|
|
611
624
|
);
|
|
612
625
|
}
|
|
613
626
|
|
|
627
|
+
function commandFromControlToken(token) {
|
|
628
|
+
if (CONTROL_COMMANDS.has(token)) return token;
|
|
629
|
+
if (token === "control.stop" || token === "control.cancel") return "stop";
|
|
630
|
+
const match = /^player\.(.+)$/.exec(token);
|
|
631
|
+
if (match && CONTROL_COMMANDS.has(match[1])) return match[1];
|
|
632
|
+
return undefined;
|
|
633
|
+
}
|
|
634
|
+
|
|
614
635
|
function commandFromInboxMessage(message) {
|
|
615
|
-
if (
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
636
|
+
if (typeof message.type === "string") {
|
|
637
|
+
const command = commandFromControlToken(message.type.trim());
|
|
638
|
+
if (command) return command;
|
|
639
|
+
}
|
|
640
|
+
if (typeof message.body === "string") {
|
|
641
|
+
const command = message.body.trim();
|
|
642
|
+
if (CONTROL_COMMANDS.has(command)) return command;
|
|
620
643
|
}
|
|
621
644
|
if (message.body && typeof message.body === "object") {
|
|
622
645
|
const command = String(message.body.command ?? "").trim();
|
|
623
646
|
if (CONTROL_COMMANDS.has(command)) return command;
|
|
624
647
|
}
|
|
625
|
-
if (typeof message.type === "string") {
|
|
626
|
-
if (message.type === "control.stop" || message.type === "control.cancel")
|
|
627
|
-
return "stop";
|
|
628
|
-
const match = /^player\.(.+)$/.exec(message.type);
|
|
629
|
-
if (match && CONTROL_COMMANDS.has(match[1])) return match[1];
|
|
630
|
-
}
|
|
631
648
|
return undefined;
|
|
632
649
|
}
|
|
633
650
|
|
|
@@ -895,6 +912,7 @@ async function playMain(args) {
|
|
|
895
912
|
const command = readAndClearCommand(ctx);
|
|
896
913
|
if (command === "stop") {
|
|
897
914
|
writeStatus(ctx, "stopped", index, count, track, player, "");
|
|
915
|
+
emitStoppedEvent(ctx, "message");
|
|
898
916
|
return;
|
|
899
917
|
}
|
|
900
918
|
if (command === "previous" || command === "prev") {
|
|
@@ -913,6 +931,7 @@ async function playMain(args) {
|
|
|
913
931
|
}
|
|
914
932
|
}
|
|
915
933
|
writeStatus(ctx, "stopped", index, tracks.length, "", player, "");
|
|
934
|
+
emitStoppedEvent(ctx, "complete");
|
|
916
935
|
} finally {
|
|
917
936
|
cleanup();
|
|
918
937
|
await Promise.race([controlLoop?.promise ?? Promise.resolve(), sleep(100)]);
|
package/skills/actors/SKILL.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: actors
|
|
3
3
|
description: Highest-density practical guide for pi-actors. Read this skill whenever prompt and tools are not enough for spawn, message, inspect, actor runs, tools, recipes, command templates, async lifecycle, mailboxes, artifacts, and local orchestration mechanics.
|
|
4
4
|
metadata:
|
|
5
|
-
version: 0.24.
|
|
5
|
+
version: 0.24.4
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Actors (pi-actors)
|
package/skills/swarm/SKILL.md
CHANGED