@llblab/pi-actors 0.26.1 → 0.26.3
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 +10 -0
- package/dist/lib/async-runs.js +22 -0
- package/dist/lib/tools.js +24 -1
- package/dist/skills/actors/SKILL.md +1 -1
- package/dist/skills/swarm/SKILL.md +1 -1
- package/lib/async-runs.ts +24 -0
- package/lib/tools.ts +22 -1
- package/package.json +1 -1
- package/skills/actors/SKILL.md +1 -1
- package/skills/swarm/SKILL.md +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.26.3: Branch Message Delivery UX Hotfix
|
|
6
|
+
|
|
7
|
+
- `[Actor Messages]` Treat branch mailbox persistence as a successful queued outcome even when the parent run control endpoint is unavailable, returning compact `queued=true` delivery diagnostics instead of throwing an unframed tool error.
|
|
8
|
+
- `[UX]` Preserve the compact action-output contract for branch `message` calls by keeping delivery diagnostics in the normal result formatter, including the leading blank-line separation used by other tool actions.
|
|
9
|
+
|
|
10
|
+
## 0.26.2: Terminal Progress Hotfix
|
|
11
|
+
|
|
12
|
+
- `[Async Runs]` Keep `progress.json` aligned with terminal kill/cancel handling by writing `phase=killed` or `phase=cancelled` and clearing active subagent counts when a run is stopped by runtime control.
|
|
13
|
+
- `[Inspector]` Prevent killed runs from showing contradictory `status=killed` with `progress.phase=running`, preserving compact operator status trust after `control.kill`.
|
|
14
|
+
|
|
5
15
|
## 0.26.1: Coordinator Empty-Synthesis Hotfix
|
|
6
16
|
|
|
7
17
|
- `[Coordinator]` Treat all-failed participant rounds and empty synthesis as a failed coordinator run instead of a clean success, preserving operator trust when swarm branches all exit non-zero.
|
package/dist/lib/async-runs.js
CHANGED
|
@@ -565,6 +565,10 @@ export function readRunStateIndex(stateRoot = DEFAULT_STATE_ROOT) {
|
|
|
565
565
|
});
|
|
566
566
|
if (valid.some((entry) => !existsSync(join(entry.state_dir, "run.json"))))
|
|
567
567
|
return undefined;
|
|
568
|
+
const indexedDirs = new Set(valid.map((entry) => resolve(entry.state_dir)));
|
|
569
|
+
const stateDirs = listRunStateDirs(stateRoot).map((stateDir) => resolve(stateDir));
|
|
570
|
+
if (stateDirs.some((stateDir) => !indexedDirs.has(stateDir)))
|
|
571
|
+
return undefined;
|
|
568
572
|
return valid;
|
|
569
573
|
}
|
|
570
574
|
export function listRuns(stateRoot = DEFAULT_STATE_ROOT, statusFilter) {
|
|
@@ -1010,6 +1014,20 @@ function markTerminalHandled(stateDir, details) {
|
|
|
1010
1014
|
ts: new Date().toISOString(),
|
|
1011
1015
|
});
|
|
1012
1016
|
}
|
|
1017
|
+
function markTerminalProgress(stateDir, phase) {
|
|
1018
|
+
const existing = readJson(join(stateDir, "progress.json"));
|
|
1019
|
+
const progress = existing && typeof existing === "object" && !Array.isArray(existing)
|
|
1020
|
+
? existing
|
|
1021
|
+
: {};
|
|
1022
|
+
const { activeSubagents: _activeSubagents, ...rest } = progress;
|
|
1023
|
+
writeJsonAtomic(join(stateDir, "progress.json"), {
|
|
1024
|
+
...rest,
|
|
1025
|
+
completed: typeof progress.completed === "number" ? progress.completed : 0,
|
|
1026
|
+
failures: Array.isArray(progress.failures) ? progress.failures : [],
|
|
1027
|
+
phase,
|
|
1028
|
+
updatedAt: new Date().toISOString(),
|
|
1029
|
+
});
|
|
1030
|
+
}
|
|
1013
1031
|
function stopRun(runOrDir, signal, event) {
|
|
1014
1032
|
const status = getRunStatus(runOrDir);
|
|
1015
1033
|
const pid = Number(status.pid || 0);
|
|
@@ -1024,6 +1042,10 @@ function stopRun(runOrDir, signal, event) {
|
|
|
1024
1042
|
const signalResult = signalOwnedRunProcess(pid, signal);
|
|
1025
1043
|
writeFileSync(join(stateDir, "events.jsonl"), `${JSON.stringify({ event, pid, signal, ...signalResult, ts: new Date().toISOString() })}\n`, { flag: "a" });
|
|
1026
1044
|
markTerminalHandled(stateDir, { event, signal });
|
|
1045
|
+
if (event === "run.kill")
|
|
1046
|
+
markTerminalProgress(stateDir, "killed");
|
|
1047
|
+
if (event === "run.cancel")
|
|
1048
|
+
markTerminalProgress(stateDir, "cancelled");
|
|
1027
1049
|
return { stopped: true, pid, signal, ...signalResult, state_dir: stateDir };
|
|
1028
1050
|
}
|
|
1029
1051
|
export function cancelRun(runOrDir) {
|
package/dist/lib/tools.js
CHANGED
|
@@ -441,6 +441,8 @@ function compactActorMessageResult(message, result) {
|
|
|
441
441
|
];
|
|
442
442
|
if (result.bytes !== undefined)
|
|
443
443
|
tokens.push(`bytes=${String(result.bytes)}`);
|
|
444
|
+
if (result.queued === true)
|
|
445
|
+
tokens.push("queued=true");
|
|
444
446
|
if (result.control)
|
|
445
447
|
tokens.push(`control=${String(result.control)}`);
|
|
446
448
|
if (result.outbox)
|
|
@@ -459,6 +461,9 @@ function compactActorMessageResult(message, result) {
|
|
|
459
461
|
tokens.push(`signal=${String(result.signal)}`);
|
|
460
462
|
if (result.invoked === true)
|
|
461
463
|
tokens.push("invoked=true");
|
|
464
|
+
if (result.delivery_error) {
|
|
465
|
+
tokens.push(`delivery_error=${compactPreview(result.delivery_error, 96)}`);
|
|
466
|
+
}
|
|
462
467
|
return `\n${tokens.join(" ")}`;
|
|
463
468
|
}
|
|
464
469
|
function maybeJsonText(value, verbose, compact) {
|
|
@@ -543,7 +548,25 @@ function assertMessageSenderBelongsToRun(message, run, routeLabel) {
|
|
|
543
548
|
async function routeBranchEnvelope(stateDir, runId, recipient, message, _options) {
|
|
544
549
|
const branchMessage = { ...message, to: recipient };
|
|
545
550
|
ActorRooms.appendBranchInboxMessage(stateDir, runId, recipient, branchMessage);
|
|
546
|
-
|
|
551
|
+
try {
|
|
552
|
+
return await AsyncRuns.sendRunMessage(runId, JSON.stringify(branchMessage));
|
|
553
|
+
}
|
|
554
|
+
catch (error) {
|
|
555
|
+
const record = error && typeof error === "object" ? error : {};
|
|
556
|
+
if (record.queued === true) {
|
|
557
|
+
return {
|
|
558
|
+
control_path: record.control_path,
|
|
559
|
+
control_type: record.control_type,
|
|
560
|
+
delivery_error: record.delivery_error ?? (error instanceof Error ? error.message : String(error)),
|
|
561
|
+
inbox_id: record.inbox_id,
|
|
562
|
+
queued: true,
|
|
563
|
+
run: runId,
|
|
564
|
+
sent: false,
|
|
565
|
+
state_dir: stateDir,
|
|
566
|
+
};
|
|
567
|
+
}
|
|
568
|
+
throw error;
|
|
569
|
+
}
|
|
547
570
|
}
|
|
548
571
|
function getRoomMulticastRecipients(message, run) {
|
|
549
572
|
const raw = message.metadata?.recipients;
|
|
@@ -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.26.
|
|
5
|
+
version: 0.26.3
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Actors (pi-actors)
|
package/lib/async-runs.ts
CHANGED
|
@@ -805,6 +805,9 @@ export function readRunStateIndex(
|
|
|
805
805
|
});
|
|
806
806
|
if (valid.some((entry) => !existsSync(join(entry.state_dir, "run.json"))))
|
|
807
807
|
return undefined;
|
|
808
|
+
const indexedDirs = new Set(valid.map((entry) => resolve(entry.state_dir)));
|
|
809
|
+
const stateDirs = listRunStateDirs(stateRoot).map((stateDir) => resolve(stateDir));
|
|
810
|
+
if (stateDirs.some((stateDir) => !indexedDirs.has(stateDir))) return undefined;
|
|
808
811
|
return valid;
|
|
809
812
|
}
|
|
810
813
|
|
|
@@ -1403,6 +1406,25 @@ function markTerminalHandled(
|
|
|
1403
1406
|
});
|
|
1404
1407
|
}
|
|
1405
1408
|
|
|
1409
|
+
function markTerminalProgress(
|
|
1410
|
+
stateDir: string,
|
|
1411
|
+
phase: "cancelled" | "killed",
|
|
1412
|
+
): void {
|
|
1413
|
+
const existing = readJson(join(stateDir, "progress.json"));
|
|
1414
|
+
const progress =
|
|
1415
|
+
existing && typeof existing === "object" && !Array.isArray(existing)
|
|
1416
|
+
? (existing as Record<string, unknown>)
|
|
1417
|
+
: {};
|
|
1418
|
+
const { activeSubagents: _activeSubagents, ...rest } = progress;
|
|
1419
|
+
writeJsonAtomic(join(stateDir, "progress.json"), {
|
|
1420
|
+
...rest,
|
|
1421
|
+
completed: typeof progress.completed === "number" ? progress.completed : 0,
|
|
1422
|
+
failures: Array.isArray(progress.failures) ? progress.failures : [],
|
|
1423
|
+
phase,
|
|
1424
|
+
updatedAt: new Date().toISOString(),
|
|
1425
|
+
});
|
|
1426
|
+
}
|
|
1427
|
+
|
|
1406
1428
|
function stopRun(
|
|
1407
1429
|
runOrDir: string,
|
|
1408
1430
|
signal: NodeJS.Signals,
|
|
@@ -1425,6 +1447,8 @@ function stopRun(
|
|
|
1425
1447
|
{ flag: "a" },
|
|
1426
1448
|
);
|
|
1427
1449
|
markTerminalHandled(stateDir, { event, signal });
|
|
1450
|
+
if (event === "run.kill") markTerminalProgress(stateDir, "killed");
|
|
1451
|
+
if (event === "run.cancel") markTerminalProgress(stateDir, "cancelled");
|
|
1428
1452
|
return { stopped: true, pid, signal, ...signalResult, state_dir: stateDir };
|
|
1429
1453
|
}
|
|
1430
1454
|
|
package/lib/tools.ts
CHANGED
|
@@ -535,6 +535,7 @@ function compactActorMessageResult(
|
|
|
535
535
|
`message=${result.sent === true || result.stopped === true ? "sent" : "not_sent"}`,
|
|
536
536
|
];
|
|
537
537
|
if (result.bytes !== undefined) tokens.push(`bytes=${String(result.bytes)}`);
|
|
538
|
+
if (result.queued === true) tokens.push("queued=true");
|
|
538
539
|
if (result.control) tokens.push(`control=${String(result.control)}`);
|
|
539
540
|
if (result.outbox) tokens.push(`messages=${String(result.outbox)}`);
|
|
540
541
|
if (result.message_count !== undefined)
|
|
@@ -546,6 +547,9 @@ function compactActorMessageResult(
|
|
|
546
547
|
if (result.stopped === true) tokens.push("stopped=true");
|
|
547
548
|
if (result.signal) tokens.push(`signal=${String(result.signal)}`);
|
|
548
549
|
if (result.invoked === true) tokens.push("invoked=true");
|
|
550
|
+
if (result.delivery_error) {
|
|
551
|
+
tokens.push(`delivery_error=${compactPreview(result.delivery_error, 96)}`);
|
|
552
|
+
}
|
|
549
553
|
return `\n${tokens.join(" ")}`;
|
|
550
554
|
}
|
|
551
555
|
|
|
@@ -695,7 +699,24 @@ async function routeBranchEnvelope(
|
|
|
695
699
|
recipient,
|
|
696
700
|
branchMessage,
|
|
697
701
|
);
|
|
698
|
-
|
|
702
|
+
try {
|
|
703
|
+
return await AsyncRuns.sendRunMessage(runId, JSON.stringify(branchMessage));
|
|
704
|
+
} catch (error) {
|
|
705
|
+
const record = error && typeof error === "object" ? (error as Record<string, unknown>) : {};
|
|
706
|
+
if (record.queued === true) {
|
|
707
|
+
return {
|
|
708
|
+
control_path: record.control_path,
|
|
709
|
+
control_type: record.control_type,
|
|
710
|
+
delivery_error: record.delivery_error ?? (error instanceof Error ? error.message : String(error)),
|
|
711
|
+
inbox_id: record.inbox_id,
|
|
712
|
+
queued: true,
|
|
713
|
+
run: runId,
|
|
714
|
+
sent: false,
|
|
715
|
+
state_dir: stateDir,
|
|
716
|
+
};
|
|
717
|
+
}
|
|
718
|
+
throw error;
|
|
719
|
+
}
|
|
699
720
|
}
|
|
700
721
|
|
|
701
722
|
function getRoomMulticastRecipients(
|
package/package.json
CHANGED
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.26.
|
|
5
|
+
version: 0.26.3
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Actors (pi-actors)
|
package/skills/swarm/SKILL.md
CHANGED