@llblab/pi-actors 0.26.0 → 0.26.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +5 -0
- package/dist/lib/coordinator.js +31 -4
- package/dist/skills/actors/SKILL.md +1 -1
- package/dist/skills/swarm/SKILL.md +1 -1
- package/lib/coordinator.ts +33 -4
- 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,11 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.26.1: Coordinator Empty-Synthesis Hotfix
|
|
6
|
+
|
|
7
|
+
- `[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.
|
|
8
|
+
- `[Artifacts]` Write compact diagnostics into empty synthesis artifacts, including participant attempt/success/failure counts and transcript size, so failed swarms leave actionable evidence instead of only `No synthesis output`.
|
|
9
|
+
|
|
5
10
|
## 0.26.0: Actor Worker v2 and Package Contract Hardening
|
|
6
11
|
|
|
7
12
|
- `[Actor Worker]` Promoted the packaged `actor-worker` into a v2 reference pattern with compact `worker-status.json`, optional per-task result artifacts under `worker-artifacts/`, stale-claim surfacing through status, and artifact metadata on `task.result` room messages.
|
package/dist/lib/coordinator.js
CHANGED
|
@@ -259,7 +259,7 @@ async function readRoomTranscript(config) {
|
|
|
259
259
|
}
|
|
260
260
|
async function synthesize(config, locker) {
|
|
261
261
|
if (!config.artifactPath)
|
|
262
|
-
return;
|
|
262
|
+
return { code: 0, empty: false };
|
|
263
263
|
if (locker) {
|
|
264
264
|
await writeLockerMessage(locker, {
|
|
265
265
|
type: "lock.claim",
|
|
@@ -269,9 +269,13 @@ async function synthesize(config, locker) {
|
|
|
269
269
|
const transcript = await readRoomTranscript(config);
|
|
270
270
|
const prompt = `Synthesize this transcript into a concise Markdown artifact. Mission: ${config.mission}. Include: Title, Consensus, Roles, Protocol, Final Artifact Shape, Next Actions, Open Questions. Use only the transcript evidence below.\n\nTRANSCRIPT:\n${transcript.slice(-24000)}`;
|
|
271
271
|
const result = await runPi(prompt, config.model, config.thinking);
|
|
272
|
-
|
|
272
|
+
const output = result.stdout.trim();
|
|
273
|
+
const diagnostics = config.stats
|
|
274
|
+
? `\n\n## Diagnostics\n\nparticipant_attempts=${config.stats.participantAttempts}\nparticipant_success=${config.stats.participantSuccess}\nparticipant_failures=${config.stats.participantFailures}\nsynthesis_code=${result.code}\ntranscript_messages=${transcript.trim() ? transcript.split("\n").length : 0}\n`
|
|
275
|
+
: "";
|
|
276
|
+
await writeFile(config.artifactPath, output
|
|
273
277
|
? result.stdout
|
|
274
|
-
:
|
|
278
|
+
: `# Synthesis Artifact\n\nNo synthesis output.${diagnostics}\n`, "utf8");
|
|
275
279
|
if (locker) {
|
|
276
280
|
await writeLockerMessage(locker, {
|
|
277
281
|
type: "lock.complete",
|
|
@@ -284,6 +288,7 @@ async function synthesize(config, locker) {
|
|
|
284
288
|
});
|
|
285
289
|
}
|
|
286
290
|
process.stdout.write(`artifact=${config.artifactPath}\n`);
|
|
291
|
+
return { code: result.code, empty: !output };
|
|
287
292
|
}
|
|
288
293
|
async function readInboxLines(inboxPath) {
|
|
289
294
|
if (!existsSync(inboxPath))
|
|
@@ -378,9 +383,17 @@ async function participantRound(role, round, config) {
|
|
|
378
383
|
const address = `branch:${config.runId}/${role.name}`;
|
|
379
384
|
const prompt = `You are ${displayName} (${address}), ${role.persona}. Mission: ${config.mission}. Round ${round}/${config.rounds}. First call inspect target=${config.room} view=previews lines=30 and inspect target=${config.room} view=contacts. Then call message once to ${config.room} from ${address} type=chat.message. Body: 2-4 sentences that react to a named participant, propose the next coordination step, and refine the shared artifact. Use contacts for peer names and addresses. End stdout with summary <=160 chars.`;
|
|
380
385
|
const result = await executeParticipantPrompt(role, prompt, config);
|
|
386
|
+
if (config.stats) {
|
|
387
|
+
config.stats.participantAttempts += 1;
|
|
388
|
+
if (result.code === 0)
|
|
389
|
+
config.stats.participantSuccess += 1;
|
|
390
|
+
else
|
|
391
|
+
config.stats.participantFailures += 1;
|
|
392
|
+
}
|
|
381
393
|
process.stdout.write(`[${role.name} round ${round}] code=${result.code}\n${result.stdout}\n`);
|
|
382
394
|
if (result.stderr.trim())
|
|
383
395
|
process.stderr.write(`[${role.name} round ${round}] ${result.stderr}\n`);
|
|
396
|
+
return result;
|
|
384
397
|
}
|
|
385
398
|
async function participantJoin(role, config) {
|
|
386
399
|
const displayName = role.name;
|
|
@@ -546,6 +559,11 @@ export async function runCoordinator(argv = process.argv.slice(2)) {
|
|
|
546
559
|
artifactPath: arg("artifact-path", ""),
|
|
547
560
|
locker: boolArg("locker", false),
|
|
548
561
|
lockerLeaseMs: numberArg("locker-lease-ms", 600000),
|
|
562
|
+
stats: {
|
|
563
|
+
participantAttempts: 0,
|
|
564
|
+
participantSuccess: 0,
|
|
565
|
+
participantFailures: 0,
|
|
566
|
+
},
|
|
549
567
|
};
|
|
550
568
|
if (!config.model) {
|
|
551
569
|
console.error("--model is required");
|
|
@@ -559,7 +577,16 @@ export async function runCoordinator(argv = process.argv.slice(2)) {
|
|
|
559
577
|
if (!runMode)
|
|
560
578
|
throw new Error(`Unknown coordinator mode: ${config.mode}`);
|
|
561
579
|
await runMode(config, locker);
|
|
562
|
-
await synthesize(config, locker);
|
|
580
|
+
const synthesis = await synthesize(config, locker);
|
|
581
|
+
if (config.stats.participantAttempts > 0 && config.stats.participantSuccess === 0) {
|
|
582
|
+
failures.push(`All participant rounds failed: attempts=${config.stats.participantAttempts}`);
|
|
583
|
+
}
|
|
584
|
+
if (synthesis?.empty && config.stats.participantFailures > 0) {
|
|
585
|
+
failures.push(`Synthesis output was empty after participant failures: failures=${config.stats.participantFailures}`);
|
|
586
|
+
}
|
|
587
|
+
if (synthesis && synthesis.code !== 0) {
|
|
588
|
+
failures.push(`Synthesis command failed: code=${synthesis.code}`);
|
|
589
|
+
}
|
|
563
590
|
}
|
|
564
591
|
catch (globalError) {
|
|
565
592
|
failures.push(`Global coordinator error: ${globalError.message}`);
|
|
@@ -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.1
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Actors (pi-actors)
|
package/lib/coordinator.ts
CHANGED
|
@@ -285,7 +285,7 @@ async function readRoomTranscript(config) {
|
|
|
285
285
|
}
|
|
286
286
|
|
|
287
287
|
async function synthesize(config, locker) {
|
|
288
|
-
if (!config.artifactPath) return;
|
|
288
|
+
if (!config.artifactPath) return { code: 0, empty: false };
|
|
289
289
|
if (locker) {
|
|
290
290
|
await writeLockerMessage(locker, {
|
|
291
291
|
type: "lock.claim",
|
|
@@ -295,11 +295,15 @@ async function synthesize(config, locker) {
|
|
|
295
295
|
const transcript = await readRoomTranscript(config);
|
|
296
296
|
const prompt = `Synthesize this transcript into a concise Markdown artifact. Mission: ${config.mission}. Include: Title, Consensus, Roles, Protocol, Final Artifact Shape, Next Actions, Open Questions. Use only the transcript evidence below.\n\nTRANSCRIPT:\n${transcript.slice(-24000)}`;
|
|
297
297
|
const result = await runPi(prompt, config.model, config.thinking);
|
|
298
|
+
const output = result.stdout.trim();
|
|
299
|
+
const diagnostics = config.stats
|
|
300
|
+
? `\n\n## Diagnostics\n\nparticipant_attempts=${config.stats.participantAttempts}\nparticipant_success=${config.stats.participantSuccess}\nparticipant_failures=${config.stats.participantFailures}\nsynthesis_code=${result.code}\ntranscript_messages=${transcript.trim() ? transcript.split("\n").length : 0}\n`
|
|
301
|
+
: "";
|
|
298
302
|
await writeFile(
|
|
299
303
|
config.artifactPath,
|
|
300
|
-
|
|
304
|
+
output
|
|
301
305
|
? result.stdout
|
|
302
|
-
:
|
|
306
|
+
: `# Synthesis Artifact\n\nNo synthesis output.${diagnostics}\n`,
|
|
303
307
|
"utf8",
|
|
304
308
|
);
|
|
305
309
|
if (locker) {
|
|
@@ -314,6 +318,7 @@ async function synthesize(config, locker) {
|
|
|
314
318
|
});
|
|
315
319
|
}
|
|
316
320
|
process.stdout.write(`artifact=${config.artifactPath}\n`);
|
|
321
|
+
return { code: result.code, empty: !output };
|
|
317
322
|
}
|
|
318
323
|
|
|
319
324
|
async function readInboxLines(inboxPath) {
|
|
@@ -432,11 +437,17 @@ async function participantRound(role, round, config) {
|
|
|
432
437
|
const address = `branch:${config.runId}/${role.name}`;
|
|
433
438
|
const prompt = `You are ${displayName} (${address}), ${role.persona}. Mission: ${config.mission}. Round ${round}/${config.rounds}. First call inspect target=${config.room} view=previews lines=30 and inspect target=${config.room} view=contacts. Then call message once to ${config.room} from ${address} type=chat.message. Body: 2-4 sentences that react to a named participant, propose the next coordination step, and refine the shared artifact. Use contacts for peer names and addresses. End stdout with summary <=160 chars.`;
|
|
434
439
|
const result = await executeParticipantPrompt(role, prompt, config);
|
|
440
|
+
if (config.stats) {
|
|
441
|
+
config.stats.participantAttempts += 1;
|
|
442
|
+
if (result.code === 0) config.stats.participantSuccess += 1;
|
|
443
|
+
else config.stats.participantFailures += 1;
|
|
444
|
+
}
|
|
435
445
|
process.stdout.write(
|
|
436
446
|
`[${role.name} round ${round}] code=${result.code}\n${result.stdout}\n`,
|
|
437
447
|
);
|
|
438
448
|
if (result.stderr.trim())
|
|
439
449
|
process.stderr.write(`[${role.name} round ${round}] ${result.stderr}\n`);
|
|
450
|
+
return result;
|
|
440
451
|
}
|
|
441
452
|
|
|
442
453
|
async function participantJoin(role, config) {
|
|
@@ -635,6 +646,11 @@ const config = {
|
|
|
635
646
|
artifactPath: arg("artifact-path", ""),
|
|
636
647
|
locker: boolArg("locker", false),
|
|
637
648
|
lockerLeaseMs: numberArg("locker-lease-ms", 600000),
|
|
649
|
+
stats: {
|
|
650
|
+
participantAttempts: 0,
|
|
651
|
+
participantSuccess: 0,
|
|
652
|
+
participantFailures: 0,
|
|
653
|
+
},
|
|
638
654
|
};
|
|
639
655
|
|
|
640
656
|
if (!config.model) {
|
|
@@ -650,7 +666,20 @@ try {
|
|
|
650
666
|
const runMode = coordinatorModes[config.mode];
|
|
651
667
|
if (!runMode) throw new Error(`Unknown coordinator mode: ${config.mode}`);
|
|
652
668
|
await runMode(config, locker);
|
|
653
|
-
await synthesize(config, locker);
|
|
669
|
+
const synthesis = await synthesize(config, locker);
|
|
670
|
+
if (config.stats.participantAttempts > 0 && config.stats.participantSuccess === 0) {
|
|
671
|
+
failures.push(
|
|
672
|
+
`All participant rounds failed: attempts=${config.stats.participantAttempts}`,
|
|
673
|
+
);
|
|
674
|
+
}
|
|
675
|
+
if (synthesis?.empty && config.stats.participantFailures > 0) {
|
|
676
|
+
failures.push(
|
|
677
|
+
`Synthesis output was empty after participant failures: failures=${config.stats.participantFailures}`,
|
|
678
|
+
);
|
|
679
|
+
}
|
|
680
|
+
if (synthesis && synthesis.code !== 0) {
|
|
681
|
+
failures.push(`Synthesis command failed: code=${synthesis.code}`);
|
|
682
|
+
}
|
|
654
683
|
} catch (globalError) {
|
|
655
684
|
failures.push(`Global coordinator error: ${globalError.message}`);
|
|
656
685
|
} finally {
|
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.1
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Actors (pi-actors)
|
package/skills/swarm/SKILL.md
CHANGED