@llblab/pi-actors 0.27.1 → 0.28.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/BACKLOG.md +10 -7
- package/CHANGELOG.md +8 -1
- package/dist/lib/async-runs.js +3 -0
- package/dist/lib/recipe-discovery.d.ts +1 -0
- package/dist/lib/recipe-discovery.js +13 -0
- package/dist/lib/tools.js +50 -24
- package/dist/skills/actors/SKILL.md +1 -1
- package/dist/skills/swarm/SKILL.md +1 -1
- package/docs/template-recipes.md +1 -1
- package/docs/tool-registry.md +2 -0
- package/lib/async-runs.ts +3 -0
- package/lib/recipe-discovery.ts +21 -3
- package/lib/tools.ts +66 -33
- package/package.json +1 -1
- package/skills/actors/SKILL.md +1 -1
- package/skills/swarm/SKILL.md +1 -1
package/BACKLOG.md
CHANGED
|
@@ -247,18 +247,21 @@ The backlog is intentionally pruned to the 20% of work most likely to deliver 80
|
|
|
247
247
|
- Coordinator/session status exposes other-session counts without leaking unrelated run details by default.
|
|
248
248
|
- Tests cover version inspection, session mismatch shape, and other-session count summaries.
|
|
249
249
|
|
|
250
|
-
### M-13 Shadowed Recipe Launch
|
|
250
|
+
### M-13 Shadowed Recipe Launch Diagnostics
|
|
251
251
|
|
|
252
252
|
- Priority: High.
|
|
253
|
-
- Status:
|
|
254
|
-
- Goal: Make broken user recipes that shadow packaged/ad hoc candidates obvious at launch
|
|
253
|
+
- Status: Done.
|
|
254
|
+
- Goal: Make broken user recipes that shadow packaged/ad hoc candidates obvious only at the moment a launch already fails.
|
|
255
255
|
- Why now: 0.26 dogfood found a broken `~/.pi/agent/recipes/actor-worker.json` shadowing the packaged `actor-worker`; Recipe Doctor exposed the evidence, but the launch path did not provide a direct hint.
|
|
256
256
|
- Direction:
|
|
257
|
+
- Treat shadowing as a normal, intentional override mechanism; do not warn on healthy shadowing.
|
|
257
258
|
- When recipe resolution or launch fails because the active user recipe is invalid/disabled and a lower-priority candidate exists, surface `reason=shadowed_invalid` or `reason=shadowed_disabled` where practical.
|
|
258
|
-
-
|
|
259
|
-
-
|
|
259
|
+
- Treat disabled template recipes as non-launchable so disabled shadowing fails consistently instead of silently executing.
|
|
260
|
+
- Include minimal compact tokens: active path, blocked candidate path, and `hint=inspect_recipes_doctor`.
|
|
261
|
+
- Keep remediation advisory only; do not auto-disable, delete, rewrite, or nag about user recipes.
|
|
260
262
|
- Acceptance:
|
|
261
|
-
-
|
|
263
|
+
- Healthy user overrides remain silent.
|
|
264
|
+
- Launch failures caused by invalid/disabled shadowing include a compact actionable hint.
|
|
262
265
|
- Verbose details expose the active broken recipe and blocked fallback candidate.
|
|
263
266
|
- Tests cover invalid and disabled user recipes shadowing a packaged candidate.
|
|
264
267
|
|
|
@@ -306,5 +309,5 @@ These are valid ideas but not current focus. Reintroduce only with concrete evid
|
|
|
306
309
|
## Suggested Milestone Order
|
|
307
310
|
|
|
308
311
|
```text
|
|
309
|
-
Next milestone: M-
|
|
312
|
+
Next milestone: M-14 Session Mismatch Follow-through.
|
|
310
313
|
```
|
package/CHANGELOG.md
CHANGED
|
@@ -2,10 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.28.0: Shadowed Recipe Launch Diagnostics
|
|
6
|
+
|
|
7
|
+
- `[Spawn]` Added minimal shadowed-recipe diagnostics when a bare recipe launch already fails because an invalid or disabled higher-priority recipe blocks a lower-priority fallback; healthy recipe overrides remain silent.
|
|
8
|
+
- `[Async Runs]` Treat disabled template recipes as non-launchable so disabled shadowing fails consistently instead of silently executing.
|
|
9
|
+
- `[Docs]` Clarified the quiet shadowing contract, disabled recipe launch behavior, and recipe-doctor hint path.
|
|
10
|
+
- `[Backlog]` Reframed shadowed recipe launch diagnostics as diagnostic-on-failure only, preserving shadowing as an intentional user override mechanism without startup warnings or automatic remediation.
|
|
11
|
+
|
|
5
12
|
## 0.27.1: Changelog and Backlog Hotfix
|
|
6
13
|
|
|
7
14
|
- `[Changelog]` Moved the 0.27.0 runtime/session observability notes out of `Unreleased` into a proper release section so published package history matches the npm/tag release.
|
|
8
|
-
- `[Backlog]` Marked
|
|
15
|
+
- `[Backlog]` Marked runtime/session observability complete and added the next evidence-backed candidates for shadowed recipe launch UX, session mismatch follow-through, and worker stale-claim dogfood.
|
|
9
16
|
|
|
10
17
|
## 0.27.0: Runtime and Session Observability UX
|
|
11
18
|
|
package/dist/lib/async-runs.js
CHANGED
|
@@ -164,6 +164,9 @@ function readRecipeFile(file) {
|
|
|
164
164
|
if (!config) {
|
|
165
165
|
throw new Error(`Template recipe must define template: ${path}`);
|
|
166
166
|
}
|
|
167
|
+
if (config.disabled === true) {
|
|
168
|
+
throw new Error(`Template recipe is disabled: ${path}`);
|
|
169
|
+
}
|
|
167
170
|
return {
|
|
168
171
|
...config,
|
|
169
172
|
file: path,
|
|
@@ -46,5 +46,6 @@ export interface RecipeDiscoverySource {
|
|
|
46
46
|
export declare function discoverRecipeSources(sources: RecipeDiscoverySource[]): RecipeDiscoveryResult;
|
|
47
47
|
export declare function discoverRecipes(roots: string[]): RecipeDiscoveryResult;
|
|
48
48
|
export declare function createRecipeIntegrityManifest(result: RecipeDiscoveryResult): RecipeIntegrityManifestEntry[];
|
|
49
|
+
export declare function getShadowedLaunchDiagnostic(result: RecipeDiscoveryResult, id: string): Record<string, unknown> | undefined;
|
|
49
50
|
export declare function summarizeDiscovery(result: RecipeDiscoveryResult): Record<string, unknown>;
|
|
50
51
|
export declare function toRegisteredTool(entry: DiscoveredRecipe): RegisteredTool | undefined;
|
|
@@ -413,6 +413,19 @@ function recommendationForEntry(entry, activePath) {
|
|
|
413
413
|
}
|
|
414
414
|
return recommendation;
|
|
415
415
|
}
|
|
416
|
+
export function getShadowedLaunchDiagnostic(result, id) {
|
|
417
|
+
const active = result.active.get(id.trim());
|
|
418
|
+
if (!active || active.shadows.length === 0)
|
|
419
|
+
return undefined;
|
|
420
|
+
if (!active.invalid && !active.disabled)
|
|
421
|
+
return undefined;
|
|
422
|
+
return {
|
|
423
|
+
active_path: active.path,
|
|
424
|
+
blocked_candidate: active.shadows[0],
|
|
425
|
+
hint: "inspect_recipes_doctor",
|
|
426
|
+
reason: active.invalid ? "shadowed_invalid" : "shadowed_disabled",
|
|
427
|
+
};
|
|
428
|
+
}
|
|
416
429
|
export function summarizeDiscovery(result) {
|
|
417
430
|
const recommendations = result.entries
|
|
418
431
|
.map((entry) => recommendationForEntry(entry, result.active.get(entry.id)?.path))
|
package/dist/lib/tools.js
CHANGED
|
@@ -572,6 +572,25 @@ function formatToolActorFailure(tool, message, params, error) {
|
|
|
572
572
|
tool,
|
|
573
573
|
});
|
|
574
574
|
}
|
|
575
|
+
function shadowedRecipeLaunchDiagnostic(recipe) {
|
|
576
|
+
if (typeof recipe !== "string" || recipe.includes("/") || recipe.includes("~"))
|
|
577
|
+
return undefined;
|
|
578
|
+
const discovery = RecipeDiscovery.discoverRecipeSources([
|
|
579
|
+
{ root: Paths.getRecipeRoot(), defaultTool: true, mutableUsage: true },
|
|
580
|
+
{ root: Paths.getPackagedRecipeRoot(), defaultTool: false },
|
|
581
|
+
]);
|
|
582
|
+
return RecipeDiscovery.getShadowedLaunchDiagnostic(discovery, recipe);
|
|
583
|
+
}
|
|
584
|
+
function enhanceSpawnRecipeError(error, recipe) {
|
|
585
|
+
const diagnostic = shadowedRecipeLaunchDiagnostic(recipe);
|
|
586
|
+
if (!diagnostic)
|
|
587
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
588
|
+
const original = error instanceof Error ? error.message : String(error);
|
|
589
|
+
return Object.assign(new Error(`${original} reason=${diagnostic.reason} active_path=${diagnostic.active_path} blocked_candidate=${diagnostic.blocked_candidate} hint=${diagnostic.hint}`), {
|
|
590
|
+
...diagnostic,
|
|
591
|
+
original_error: original,
|
|
592
|
+
});
|
|
593
|
+
}
|
|
575
594
|
function runIdFromActorAddress(address) {
|
|
576
595
|
if (!address)
|
|
577
596
|
return undefined;
|
|
@@ -654,30 +673,37 @@ export function createSpawnToolDefinition() {
|
|
|
654
673
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
655
674
|
const input = asRecord(params);
|
|
656
675
|
const runId = runIdFromActorAddress(typeof input.as === "string" ? input.as : undefined);
|
|
657
|
-
const
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
676
|
+
const recipe = typeof input.file === "string"
|
|
677
|
+
? input.file
|
|
678
|
+
: typeof input.recipe === "string"
|
|
679
|
+
? input.recipe
|
|
680
|
+
: undefined;
|
|
681
|
+
let meta;
|
|
682
|
+
try {
|
|
683
|
+
meta = AsyncRuns.startRun({
|
|
684
|
+
file: recipe,
|
|
685
|
+
launch_source: "spawn",
|
|
686
|
+
ownerId: getRunOwnerId(ctx),
|
|
687
|
+
run_id: runId,
|
|
688
|
+
state_dir: typeof input.state_dir === "string" ? input.state_dir : undefined,
|
|
689
|
+
...(input.template !== undefined
|
|
690
|
+
? {
|
|
691
|
+
template: input.template,
|
|
692
|
+
}
|
|
693
|
+
: {}),
|
|
694
|
+
values: asRecord(input.values),
|
|
695
|
+
...(input.artifacts &&
|
|
696
|
+
typeof input.artifacts === "object" &&
|
|
697
|
+
!Array.isArray(input.artifacts)
|
|
698
|
+
? {
|
|
699
|
+
artifacts: input.artifacts,
|
|
700
|
+
}
|
|
701
|
+
: {}),
|
|
702
|
+
}, ctx.cwd);
|
|
703
|
+
}
|
|
704
|
+
catch (error) {
|
|
705
|
+
throw enhanceSpawnRecipeError(error, recipe);
|
|
706
|
+
}
|
|
681
707
|
ActorRooms.ensureDefaultRoom(meta.state_dir, String(meta.run));
|
|
682
708
|
ActorRooms.writeCommunicationSnapshot(meta.state_dir, String(meta.run));
|
|
683
709
|
return {
|
|
@@ -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.
|
|
5
|
+
version: 0.28.0
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Actors (pi-actors)
|
package/docs/template-recipes.md
CHANGED
|
@@ -105,7 +105,7 @@ Recipe priority only matters when two discovered recipes have the same filename
|
|
|
105
105
|
|
|
106
106
|
The high-priority user recipe directory is also the default tool set: recipes placed there are agent tools by location. This preserves the old advantage of a tool-only registry because listing `~/.pi/agent/recipes` shows the operator-managed tool surface. Packaged and ad hoc recipes are recipe components by default; they become tools only when copied or registered into the agent recipe root.
|
|
107
107
|
|
|
108
|
-
Higher-priority files shadow lower-priority files with the same basename. Within one priority layer, same-id JSON shadows Markdown because JSON is the canonical precise format. A highest-priority invalid recipe is still visible and blocks fallback so operators do not accidentally run packaged behavior when a user override is broken. A highest-priority recipe with `disabled: true` also blocks fallback and intentionally disables that id.
|
|
108
|
+
Higher-priority files shadow lower-priority files with the same basename. Within one priority layer, same-id JSON shadows Markdown because JSON is the canonical precise format. A highest-priority invalid recipe is still visible and blocks fallback so operators do not accidentally run packaged behavior when a user override is broken. A highest-priority recipe with `disabled: true` also blocks fallback, is not launchable, and intentionally disables that id. Healthy overrides are silent; failed bare-name launches caused by invalid or disabled shadowing include compact `reason=shadowed_invalid` or `reason=shadowed_disabled` diagnostics with the active path, blocked candidate, and recipe-doctor hint.
|
|
109
109
|
|
|
110
110
|
## Usage Metadata
|
|
111
111
|
|
package/docs/tool-registry.md
CHANGED
|
@@ -32,6 +32,8 @@ inspect target=recipes view=summary verbose=true
|
|
|
32
32
|
|
|
33
33
|
The recipe summary reports active, shadowed, invalid, disabled, and diagnostic entries so operators can answer why a tool is present, hidden, broken, or disabled. The doctor view keeps the same registry evidence but promotes an advisory action surface: compact output includes the highest-priority `top` remediation plus ordered actions for invalid/blocking, disabled, risky shell-boundary, and shadowed recipes. Verbose inspection keeps the structured `remediations`, `top_action`, diagnostic details, and blocked lower-priority candidate paths when a broken or disabled higher-priority recipe masks a fallback.
|
|
34
34
|
|
|
35
|
+
Routine shadowing is quiet. If a bare `spawn` recipe launch already fails because an invalid or `disabled: true` user recipe blocks a lower-priority candidate, the launch error adds compact tokens such as `reason=shadowed_invalid` or `reason=shadowed_disabled`, `active_path`, `blocked_candidate`, and `hint=inspect_recipes_doctor`.
|
|
36
|
+
|
|
35
37
|
## Registering Tools
|
|
36
38
|
|
|
37
39
|
`register_tool` is the interactive API for listing, creating, updating, or deleting persistent tools. Call it without arguments to list registered tools.
|
package/lib/async-runs.ts
CHANGED
|
@@ -318,6 +318,9 @@ function readRecipeFile(file: string): AsyncRunStartParams {
|
|
|
318
318
|
if (!config) {
|
|
319
319
|
throw new Error(`Template recipe must define template: ${path}`);
|
|
320
320
|
}
|
|
321
|
+
if (config.disabled === true) {
|
|
322
|
+
throw new Error(`Template recipe is disabled: ${path}`);
|
|
323
|
+
}
|
|
321
324
|
return {
|
|
322
325
|
...(config as AsyncRunStartParams),
|
|
323
326
|
file: path,
|
package/lib/recipe-discovery.ts
CHANGED
|
@@ -473,7 +473,8 @@ function remediationForEntry(
|
|
|
473
473
|
reason: blockedCandidate
|
|
474
474
|
? "invalid higher-priority recipe blocks a lower-priority candidate"
|
|
475
475
|
: "recipe is invalid and cannot be exposed as a tool",
|
|
476
|
-
action:
|
|
476
|
+
action:
|
|
477
|
+
"fix recipe syntax/config, or disable/delete/archive it to restore fallback",
|
|
477
478
|
};
|
|
478
479
|
}
|
|
479
480
|
if (entry.disabled && entry.active) {
|
|
@@ -486,7 +487,8 @@ function remediationForEntry(
|
|
|
486
487
|
reason: blockedCandidate
|
|
487
488
|
? "disabled higher-priority recipe intentionally blocks a lower-priority candidate"
|
|
488
489
|
: "recipe is disabled and not exposed as a tool",
|
|
489
|
-
action:
|
|
490
|
+
action:
|
|
491
|
+
"keep disabled intentionally, re-enable, or delete/archive the file",
|
|
490
492
|
};
|
|
491
493
|
}
|
|
492
494
|
if (riskyDiagnostics.length > 0) {
|
|
@@ -496,7 +498,8 @@ function remediationForEntry(
|
|
|
496
498
|
severity: "warning",
|
|
497
499
|
path: entry.path,
|
|
498
500
|
reason: riskyDiagnostics[0],
|
|
499
|
-
action:
|
|
501
|
+
action:
|
|
502
|
+
"audit trusted command boundary; keep only if the recipe is local and intentional",
|
|
500
503
|
};
|
|
501
504
|
}
|
|
502
505
|
if (entry.shadowed) {
|
|
@@ -557,6 +560,21 @@ function recommendationForEntry(
|
|
|
557
560
|
return recommendation;
|
|
558
561
|
}
|
|
559
562
|
|
|
563
|
+
export function getShadowedLaunchDiagnostic(
|
|
564
|
+
result: RecipeDiscoveryResult,
|
|
565
|
+
id: string,
|
|
566
|
+
): Record<string, unknown> | undefined {
|
|
567
|
+
const active = result.active.get(id.trim());
|
|
568
|
+
if (!active || active.shadows.length === 0) return undefined;
|
|
569
|
+
if (!active.invalid && !active.disabled) return undefined;
|
|
570
|
+
return {
|
|
571
|
+
active_path: active.path,
|
|
572
|
+
blocked_candidate: active.shadows[0],
|
|
573
|
+
hint: "inspect_recipes_doctor",
|
|
574
|
+
reason: active.invalid ? "shadowed_invalid" : "shadowed_disabled",
|
|
575
|
+
};
|
|
576
|
+
}
|
|
577
|
+
|
|
560
578
|
export function summarizeDiscovery(
|
|
561
579
|
result: RecipeDiscoveryResult,
|
|
562
580
|
): Record<string, unknown> {
|
package/lib/tools.ts
CHANGED
|
@@ -710,6 +710,33 @@ function formatToolActorFailure(
|
|
|
710
710
|
);
|
|
711
711
|
}
|
|
712
712
|
|
|
713
|
+
function shadowedRecipeLaunchDiagnostic(
|
|
714
|
+
recipe: unknown,
|
|
715
|
+
): Record<string, unknown> | undefined {
|
|
716
|
+
if (typeof recipe !== "string" || recipe.includes("/") || recipe.includes("~"))
|
|
717
|
+
return undefined;
|
|
718
|
+
const discovery = RecipeDiscovery.discoverRecipeSources([
|
|
719
|
+
{ root: Paths.getRecipeRoot(), defaultTool: true, mutableUsage: true },
|
|
720
|
+
{ root: Paths.getPackagedRecipeRoot(), defaultTool: false },
|
|
721
|
+
]);
|
|
722
|
+
return RecipeDiscovery.getShadowedLaunchDiagnostic(discovery, recipe);
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
function enhanceSpawnRecipeError(error: unknown, recipe: unknown): Error {
|
|
726
|
+
const diagnostic = shadowedRecipeLaunchDiagnostic(recipe);
|
|
727
|
+
if (!diagnostic) return error instanceof Error ? error : new Error(String(error));
|
|
728
|
+
const original = error instanceof Error ? error.message : String(error);
|
|
729
|
+
return Object.assign(
|
|
730
|
+
new Error(
|
|
731
|
+
`${original} reason=${diagnostic.reason} active_path=${diagnostic.active_path} blocked_candidate=${diagnostic.blocked_candidate} hint=${diagnostic.hint}`,
|
|
732
|
+
),
|
|
733
|
+
{
|
|
734
|
+
...diagnostic,
|
|
735
|
+
original_error: original,
|
|
736
|
+
},
|
|
737
|
+
);
|
|
738
|
+
}
|
|
739
|
+
|
|
713
740
|
function runIdFromActorAddress(
|
|
714
741
|
address: string | undefined,
|
|
715
742
|
): string | undefined {
|
|
@@ -845,39 +872,45 @@ export function createSpawnToolDefinition<
|
|
|
845
872
|
const runId = runIdFromActorAddress(
|
|
846
873
|
typeof input.as === "string" ? input.as : undefined,
|
|
847
874
|
);
|
|
848
|
-
const
|
|
849
|
-
|
|
850
|
-
file
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
875
|
+
const recipe =
|
|
876
|
+
typeof input.file === "string"
|
|
877
|
+
? input.file
|
|
878
|
+
: typeof input.recipe === "string"
|
|
879
|
+
? input.recipe
|
|
880
|
+
: undefined;
|
|
881
|
+
let meta: AsyncRuns.AsyncRunMeta;
|
|
882
|
+
try {
|
|
883
|
+
meta = AsyncRuns.startRun(
|
|
884
|
+
{
|
|
885
|
+
file: recipe,
|
|
886
|
+
launch_source: "spawn",
|
|
887
|
+
ownerId: getRunOwnerId(ctx),
|
|
888
|
+
run_id: runId,
|
|
889
|
+
state_dir:
|
|
890
|
+
typeof input.state_dir === "string" ? input.state_dir : undefined,
|
|
891
|
+
...(input.template !== undefined
|
|
892
|
+
? {
|
|
893
|
+
template:
|
|
894
|
+
input.template as AsyncRuns.AsyncRunStartParams["template"],
|
|
895
|
+
}
|
|
896
|
+
: {}),
|
|
897
|
+
values: asRecord(input.values),
|
|
898
|
+
...(input.artifacts &&
|
|
899
|
+
typeof input.artifacts === "object" &&
|
|
900
|
+
!Array.isArray(input.artifacts)
|
|
901
|
+
? {
|
|
902
|
+
artifacts: input.artifacts as Record<
|
|
903
|
+
string,
|
|
904
|
+
AsyncRuns.RunArtifactDeclaration
|
|
905
|
+
>,
|
|
906
|
+
}
|
|
907
|
+
: {}),
|
|
908
|
+
},
|
|
909
|
+
ctx.cwd,
|
|
910
|
+
);
|
|
911
|
+
} catch (error) {
|
|
912
|
+
throw enhanceSpawnRecipeError(error, recipe);
|
|
913
|
+
}
|
|
881
914
|
ActorRooms.ensureDefaultRoom(meta.state_dir, String(meta.run));
|
|
882
915
|
ActorRooms.writeCommunicationSnapshot(meta.state_dir, String(meta.run));
|
|
883
916
|
return {
|
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.
|
|
5
|
+
version: 0.28.0
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Actors (pi-actors)
|
package/skills/swarm/SKILL.md
CHANGED