@llblab/pi-actors 0.32.0 → 0.33.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 +4 -3
- package/BACKLOG.md +1 -29
- package/CHANGELOG.md +8 -0
- package/dist/lib/paths.d.ts +1 -1
- package/dist/lib/paths.js +3 -3
- package/dist/lib/recipe-discovery.d.ts +1 -1
- package/dist/lib/recipe-discovery.js +11 -11
- package/dist/lib/tools.d.ts +6 -1
- package/dist/lib/tools.js +15 -22
- package/dist/scripts/conformance.mjs +34 -18
- package/dist/skills/actors/SKILL.md +2 -2
- package/dist/skills/swarm/SKILL.md +2 -2
- package/docs/tool-registry.md +3 -3
- package/index.ts +1 -1
- package/lib/paths.ts +3 -3
- package/lib/recipe-discovery.ts +11 -11
- package/lib/tools.ts +22 -27
- package/package.json +1 -1
- package/scripts/conformance.mjs +34 -18
- package/skills/actors/SKILL.md +2 -2
- package/skills/swarm/SKILL.md +2 -2
- package/dist/lib/conformance.d.ts +0 -12
- package/dist/lib/conformance.js +0 -28
- package/lib/conformance.ts +0 -46
package/AGENTS.md
CHANGED
|
@@ -20,7 +20,7 @@ Pi host
|
|
|
20
20
|
-> index.ts composition root
|
|
21
21
|
-> lib/tools.ts / prompts.ts public tool + injected prompt surface
|
|
22
22
|
-> lib/runtime.ts / registry.ts active user recipe tools
|
|
23
|
-
-> lib/recipe-*.ts packaged/user/
|
|
23
|
+
-> lib/recipe-*.ts packaged/user/draft recipe discovery
|
|
24
24
|
-> lib/async-runs.ts spawn lifecycle and run state
|
|
25
25
|
-> lib/actor-rooms.ts room, roster, mailbox, communication log
|
|
26
26
|
-> scripts/*.mjs thin process entrypoints
|
|
@@ -45,8 +45,8 @@ Pi host
|
|
|
45
45
|
|
|
46
46
|
## Repo Surfaces
|
|
47
47
|
|
|
48
|
-
- `/scripts/*.mjs`: Stable
|
|
49
|
-
- `/lib/*.ts`: Compiled
|
|
48
|
+
- `/scripts/*.mjs`: Stable executables for detached/helper processes. Keep script-only release/build/glue behavior standalone when it has no plausible second consumer.
|
|
49
|
+
- `/lib/*.ts`: Compiled domains and shared script-entrypoint logic. Move substantive script behavior into named domain modules only when reuse, tests, packaged JS-only execution, or clear ownership justifies it. Do not create a lib domain solely to keep a tiny script shim thin.
|
|
50
50
|
- `/recipes/*.json`: Packaged standard recipe library. Keep recipes optional, composable, policy-light, and caller-configurable.
|
|
51
51
|
- `/skills/actors/SKILL.md`: Dense practical reference for operating pi-actors itself.
|
|
52
52
|
- `/skills/swarm/SKILL.md`: Bundled methodology skill for multi-agent standards, strategies, and portable examples.
|
|
@@ -62,6 +62,7 @@ Pi host
|
|
|
62
62
|
- Keep published documentation portable: use `~`, `<repo>`, or relative paths instead of machine-local absolute paths.
|
|
63
63
|
- Preserve runtime output discipline because tool output flows directly into agent context.
|
|
64
64
|
- Optimize every actor-facing surface for signal over volume: prefer compact state-backed hints and fewer concepts over broad explanatory prose or speculative guidance.
|
|
65
|
+
- Until a stable release greater than `1.x.x`, favor context compression over compatibility shims: do not preserve legacy actor-facing names, aliases, fields, env vars, paths, or docs solely for backward compatibility when a clearer current term exists. Remove compatibility layers in the same slice that renames a concept, and record the break in `CHANGELOG.md`.
|
|
65
66
|
- Keep the project lens local-first and cybernetic: agents wrap durable local capabilities as actors, then use semantic tools and messages instead of repeatedly reconstructing shell commands.
|
|
66
67
|
- Design recipes as agent-callable tools: make prompts, scopes, paths, models, and policy knobs public args/defaults when the caller should decide them at invocation time.
|
|
67
68
|
- Decompose oversized bullets into sublists or hierarchy; long flat list items are a context-smell.
|
package/BACKLOG.md
CHANGED
|
@@ -67,20 +67,6 @@ The backlog is intentionally pruned to the 20% of work most likely to deliver 80
|
|
|
67
67
|
- Stale claims are reproducible and visible in worker status.
|
|
68
68
|
- Tests cover stale-claim counting without adding scheduler/broker policy.
|
|
69
69
|
|
|
70
|
-
### M-23 Tool Boundary Type Tightening
|
|
71
|
-
|
|
72
|
-
- Priority: Low.
|
|
73
|
-
- Status: Planned.
|
|
74
|
-
- Goal: Remove avoidable `any` at the Pi/tool boundary where a narrow local type can express the real contract without broad rewiring.
|
|
75
|
-
- Why now: `index.ts` still keeps runtime tool definitions in a `Map<string, any>`; this is small but visible in the composition root.
|
|
76
|
-
- Direction:
|
|
77
|
-
- Add or reuse a narrow exported tool-definition type from the Pi adapter or tools domain.
|
|
78
|
-
- Keep SDK details behind `lib/pi.ts`.
|
|
79
|
-
- Do not introduce a broad type-modeling pass across every schema helper.
|
|
80
|
-
- Acceptance:
|
|
81
|
-
- `index.ts` no longer uses `Map<string, any>` for actor tool definitions.
|
|
82
|
-
- TypeScript validation still passes without weakening public tool schemas.
|
|
83
|
-
|
|
84
70
|
### M-17 Message Delivery Outcome Contract
|
|
85
71
|
|
|
86
72
|
- Priority: High.
|
|
@@ -103,7 +89,7 @@ The backlog is intentionally pruned to the 20% of work most likely to deliver 80
|
|
|
103
89
|
- Priority: High.
|
|
104
90
|
- Status: Planned.
|
|
105
91
|
- Goal: Make successful ad hoc actor patterns easy to promote manually from draft memory into active user recipe memory.
|
|
106
|
-
- Why now: Draft recipes under `~/.pi/agent/recipes/
|
|
92
|
+
- Why now: Draft recipes under `~/.pi/agent/recipes/drafts` are replayable but intentionally not active tools, and the two-stage memory model needs an explicit operator-gated promotion path.
|
|
107
93
|
- Direction:
|
|
108
94
|
- List draft recipes with source run, timestamp, fingerprint, description/template preview, and validation status.
|
|
109
95
|
- Promote a selected draft to `~/.pi/agent/recipes/<name>.json` only through an explicit action or explicit tool argument.
|
|
@@ -116,20 +102,6 @@ The backlog is intentionally pruned to the 20% of work most likely to deliver 80
|
|
|
116
102
|
- Tests cover valid promotion, invalid draft, name collision, and packaged-recipe shadowing.
|
|
117
103
|
- Docs explain draft memory vs active tool memory in one compact section.
|
|
118
104
|
|
|
119
|
-
### M-24 Registry Path Naming Cleanup
|
|
120
|
-
|
|
121
|
-
- Priority: Low.
|
|
122
|
-
- Status: Planned.
|
|
123
|
-
- Goal: Reduce legacy-storage naming noise without changing the persistent file path.
|
|
124
|
-
- Why now: `legacy-tool-registry.json` is still a compatibility storage path, but helper names and tests should make clear that the stable path is retained intentionally.
|
|
125
|
-
- Direction:
|
|
126
|
-
- Prefer neutral helper/test wording such as registry path or retained registry storage path.
|
|
127
|
-
- Keep the on-disk filename unchanged unless a separate migration is justified.
|
|
128
|
-
- Do not reintroduce legacy migration code.
|
|
129
|
-
- Acceptance:
|
|
130
|
-
- Path helpers and tests no longer imply an unfinished migration.
|
|
131
|
-
- Existing registry storage compatibility remains unchanged.
|
|
132
|
-
|
|
133
105
|
### M-19 Recipe Doctor Risk Labels v2
|
|
134
106
|
|
|
135
107
|
- Priority: Medium.
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.33.0: Signal-First Compatibility Pruning
|
|
6
|
+
|
|
7
|
+
- `[Breaking]` Removed pre-1.x compatibility shims for draft recipe terminology: draft captures now live under `~/.pi/agent/recipes/drafts`, recipe inspection no longer emits `candidates`, spawn details no longer emit `candidate_recipe`, and shadow diagnostics use `blocked_fallback` instead of `blocked_candidate`.
|
|
8
|
+
- `[Context]` Documented the pre-stable policy that context compression wins over compatibility aliases until after a stable release greater than `1.x.x`; rename slices should remove legacy actor-facing names, fields, env vars, paths, and docs instead of carrying shims.
|
|
9
|
+
- `[Breaking]` Renamed retained registry storage from `legacy-tool-registry.json` to `tool-registry.json`, removing the stale compatibility noun from runtime path helpers and tests.
|
|
10
|
+
- `[Types]` Added a narrow actor tool-definition type and removed the remaining `Map<string, any>` from the composition root without expanding SDK type exposure.
|
|
11
|
+
- `[Scripts]` Collapsed the conformance runner back into a standalone script and removed its one-off lib domain, documenting that script-only glue should stay self-contained unless reuse or packaged runtime constraints justify a domain.
|
|
12
|
+
|
|
5
13
|
## 0.32.0: Actor Surface Minimization
|
|
6
14
|
|
|
7
15
|
- `[Context]` Added durable signal/noise guidance for actor-facing surfaces: keep the model-facing concept ladder minimal, make feedback hints state-backed and action-shaped, and avoid speculative advisory prose.
|
package/dist/lib/paths.d.ts
CHANGED
|
@@ -17,5 +17,5 @@ export declare const EXTENSION_RUNTIME_PATHS: ExtensionRuntimePaths;
|
|
|
17
17
|
export declare function getExtensionSkillsDir(extensionUrl: string): string;
|
|
18
18
|
export declare function getExistingExtensionSkillPaths(extensionUrl: string): string[];
|
|
19
19
|
export declare function getRecipeRoot(agentDir?: string): string;
|
|
20
|
-
export declare function
|
|
20
|
+
export declare function getRecipeDraftRoot(agentDir?: string): string;
|
|
21
21
|
export declare function getPackagedRecipeRoot(): string;
|
package/dist/lib/paths.js
CHANGED
|
@@ -13,7 +13,7 @@ export function getAgentDir(env = process.env) {
|
|
|
13
13
|
: join(homedir(), ".pi", "agent");
|
|
14
14
|
}
|
|
15
15
|
export function getConfigPath(agentDir = getAgentDir()) {
|
|
16
|
-
return join(agentDir, "
|
|
16
|
+
return join(agentDir, "tool-registry.json");
|
|
17
17
|
}
|
|
18
18
|
export function getExtensionTmpDir(agentDir = getAgentDir(), extensionName = "pi-actors") {
|
|
19
19
|
return join(agentDir, "tmp", extensionName);
|
|
@@ -39,8 +39,8 @@ export function getExistingExtensionSkillPaths(extensionUrl) {
|
|
|
39
39
|
export function getRecipeRoot(agentDir = getAgentDir()) {
|
|
40
40
|
return join(agentDir, "recipes");
|
|
41
41
|
}
|
|
42
|
-
export function
|
|
43
|
-
return join(getRecipeRoot(agentDir), "
|
|
42
|
+
export function getRecipeDraftRoot(agentDir = getAgentDir()) {
|
|
43
|
+
return join(getRecipeRoot(agentDir), "drafts");
|
|
44
44
|
}
|
|
45
45
|
export function getPackagedRecipeRoot() {
|
|
46
46
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
@@ -47,6 +47,6 @@ export declare function discoverRecipeSources(sources: RecipeDiscoverySource[]):
|
|
|
47
47
|
export declare function discoverRecipes(roots: string[]): RecipeDiscoveryResult;
|
|
48
48
|
export declare function createRecipeIntegrityManifest(result: RecipeDiscoveryResult): RecipeIntegrityManifestEntry[];
|
|
49
49
|
export declare function getShadowedLaunchDiagnostic(result: RecipeDiscoveryResult, id: string): Record<string, unknown> | undefined;
|
|
50
|
-
export declare function
|
|
50
|
+
export declare function listDraftRecipes(root: string): Array<Record<string, unknown>>;
|
|
51
51
|
export declare function summarizeDiscovery(result: RecipeDiscoveryResult): Record<string, unknown>;
|
|
52
52
|
export declare function toRegisteredTool(entry: DiscoveredRecipe): RegisteredTool | undefined;
|
|
@@ -324,16 +324,16 @@ function diagnosticDetails(result) {
|
|
|
324
324
|
}
|
|
325
325
|
function remediationForEntry(entry, activePath) {
|
|
326
326
|
const riskyDiagnostics = entry.diagnostics.filter((message) => diagnosticSeverity(message) === "warning");
|
|
327
|
-
const
|
|
327
|
+
const blockedFallback = entry.shadows[0];
|
|
328
328
|
if (entry.invalid) {
|
|
329
329
|
return {
|
|
330
330
|
id: entry.id,
|
|
331
|
-
kind:
|
|
331
|
+
kind: blockedFallback ? "blocking_invalid" : "invalid",
|
|
332
332
|
severity: "error",
|
|
333
333
|
path: entry.path,
|
|
334
|
-
...(
|
|
335
|
-
reason:
|
|
336
|
-
? "invalid higher-priority recipe blocks a lower-priority
|
|
334
|
+
...(blockedFallback ? { blocked_fallback: blockedFallback } : {}),
|
|
335
|
+
reason: blockedFallback
|
|
336
|
+
? "invalid higher-priority recipe blocks a lower-priority fallback"
|
|
337
337
|
: "recipe is invalid and cannot be exposed as a tool",
|
|
338
338
|
action: "fix recipe syntax/config, or disable/delete/archive it to restore fallback",
|
|
339
339
|
};
|
|
@@ -341,12 +341,12 @@ function remediationForEntry(entry, activePath) {
|
|
|
341
341
|
if (entry.disabled && entry.active) {
|
|
342
342
|
return {
|
|
343
343
|
id: entry.id,
|
|
344
|
-
kind:
|
|
344
|
+
kind: blockedFallback ? "blocking_disabled" : "disabled",
|
|
345
345
|
severity: "warning",
|
|
346
346
|
path: entry.path,
|
|
347
|
-
...(
|
|
348
|
-
reason:
|
|
349
|
-
? "disabled higher-priority recipe intentionally blocks a lower-priority
|
|
347
|
+
...(blockedFallback ? { blocked_fallback: blockedFallback } : {}),
|
|
348
|
+
reason: blockedFallback
|
|
349
|
+
? "disabled higher-priority recipe intentionally blocks a lower-priority fallback"
|
|
350
350
|
: "recipe is disabled and not exposed as a tool",
|
|
351
351
|
action: "keep disabled intentionally, re-enable, or delete/archive the file",
|
|
352
352
|
};
|
|
@@ -420,12 +420,12 @@ export function getShadowedLaunchDiagnostic(result, id) {
|
|
|
420
420
|
return undefined;
|
|
421
421
|
return {
|
|
422
422
|
active_path: active.path,
|
|
423
|
-
|
|
423
|
+
blocked_fallback: active.shadows[0],
|
|
424
424
|
hint: "inspect_recipes_doctor",
|
|
425
425
|
reason: active.invalid ? "shadowed_invalid" : "shadowed_disabled",
|
|
426
426
|
};
|
|
427
427
|
}
|
|
428
|
-
export function
|
|
428
|
+
export function listDraftRecipes(root) {
|
|
429
429
|
return listRecipeFiles(root).map((path) => {
|
|
430
430
|
const id = RecipeReferences.getRecipeIdFromPath(path);
|
|
431
431
|
const config = RecipeReferences.readRawRecipeConfig(path);
|
package/dist/lib/tools.d.ts
CHANGED
|
@@ -8,6 +8,11 @@ import * as Execution from "./execution.ts";
|
|
|
8
8
|
import * as Registry from "./registry.ts";
|
|
9
9
|
export type RegisterToolInput = Registry.RegisterToolInput;
|
|
10
10
|
export type RegisterToolRuntimeDeps<TContext> = Registry.RegisterToolRuntimeDeps<TContext>;
|
|
11
|
+
export interface ActorToolDefinition {
|
|
12
|
+
name: string;
|
|
13
|
+
execute?: (...args: never[]) => unknown;
|
|
14
|
+
[key: string]: unknown;
|
|
15
|
+
}
|
|
11
16
|
export interface CoreActorToolDefinitionDeps<TContext extends AsyncRunToolContext> {
|
|
12
17
|
configPath: string;
|
|
13
18
|
getActiveTools: () => string[];
|
|
@@ -43,6 +48,6 @@ export interface ActorMessageToolDeps<TContext = unknown> {
|
|
|
43
48
|
getTool?: (name: string) => any | undefined;
|
|
44
49
|
}
|
|
45
50
|
export declare function createActorMessageToolDefinition<TContext = unknown>(deps?: ActorMessageToolDeps<TContext>): any;
|
|
46
|
-
export declare function createCoreActorToolDefinitions<TContext extends AsyncRunToolContext>(deps: CoreActorToolDefinitionDeps<TContext>):
|
|
51
|
+
export declare function createCoreActorToolDefinitions<TContext extends AsyncRunToolContext>(deps: CoreActorToolDefinitionDeps<TContext>): ActorToolDefinition[];
|
|
47
52
|
export declare function createRuntimeToolDefinition(cfg: RegisteredTool, exec: Execution.RegisteredToolExec): any;
|
|
48
53
|
export {};
|
package/dist/lib/tools.js
CHANGED
|
@@ -168,7 +168,7 @@ function compactAsyncRunStatus(value) {
|
|
|
168
168
|
tokens.push(`code=${String(result.code)}`);
|
|
169
169
|
if (result.killed === true)
|
|
170
170
|
tokens.push("killed=true");
|
|
171
|
-
const draftRecipe = status.draft_recipe
|
|
171
|
+
const draftRecipe = status.draft_recipe;
|
|
172
172
|
if (draftRecipe)
|
|
173
173
|
tokens.push(`draft_recipe=${String(draftRecipe)}`);
|
|
174
174
|
const nextActions = actorRunNextActions(run);
|
|
@@ -504,8 +504,8 @@ function compactRecipeDoctor(summary) {
|
|
|
504
504
|
}
|
|
505
505
|
for (const item of remediations.slice(0, 8)) {
|
|
506
506
|
const action = compactPreview(item.action, Limits.DOCTOR_ACTION_PREVIEW_CHARS);
|
|
507
|
-
const blocked = item.
|
|
508
|
-
? ` blocked=${compactPreview(item.
|
|
507
|
+
const blocked = item.blocked_fallback
|
|
508
|
+
? ` blocked=${compactPreview(item.blocked_fallback, Limits.DOCTOR_ACTION_PREVIEW_CHARS)}`
|
|
509
509
|
: "";
|
|
510
510
|
lines.push(`${String(item.severity ?? "info")} kind=${String(item.kind ?? "inspect")} id=${String(item.id ?? "root")}${blocked} action=${action ?? "inspect"}`);
|
|
511
511
|
}
|
|
@@ -557,11 +557,7 @@ function compactRecipeRegistry(summary) {
|
|
|
557
557
|
const diagnostics = Array.isArray(summary.diagnostics)
|
|
558
558
|
? summary.diagnostics.length
|
|
559
559
|
: 0;
|
|
560
|
-
const drafts = Array.isArray(summary.drafts)
|
|
561
|
-
? summary.drafts.length
|
|
562
|
-
: Array.isArray(summary.candidates)
|
|
563
|
-
? summary.candidates.length
|
|
564
|
-
: 0;
|
|
560
|
+
const drafts = Array.isArray(summary.drafts) ? summary.drafts.length : 0;
|
|
565
561
|
const recommendations = Array.isArray(summary.recommendations)
|
|
566
562
|
? summary.recommendations.length
|
|
567
563
|
: 0;
|
|
@@ -702,10 +698,10 @@ function shadowedRecipeLaunchDiagnostic(recipe) {
|
|
|
702
698
|
]);
|
|
703
699
|
return RecipeDiscovery.getShadowedLaunchDiagnostic(discovery, recipe);
|
|
704
700
|
}
|
|
705
|
-
function
|
|
701
|
+
function draftRecipeName(run) {
|
|
706
702
|
return `${run.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "spawn"}.json`;
|
|
707
703
|
}
|
|
708
|
-
function
|
|
704
|
+
function draftRecipeDefaults(values) {
|
|
709
705
|
const ignored = new Set([
|
|
710
706
|
"actor_address",
|
|
711
707
|
"communication_file",
|
|
@@ -716,18 +712,18 @@ function candidateRecipeDefaults(values) {
|
|
|
716
712
|
const defaults = Object.fromEntries(Object.entries(values).filter(([key]) => !ignored.has(key)));
|
|
717
713
|
return Object.keys(defaults).length > 0 ? defaults : undefined;
|
|
718
714
|
}
|
|
719
|
-
function
|
|
715
|
+
function writeSpawnDraftRecipe(input, meta) {
|
|
720
716
|
if (process.env.NODE_TEST_CONTEXT &&
|
|
721
|
-
process.env.
|
|
717
|
+
process.env.PI_ACTORS_ENABLE_SPAWN_DRAFTS_IN_TEST !== "1")
|
|
722
718
|
return undefined;
|
|
723
719
|
if (input.template === undefined ||
|
|
724
720
|
input.file !== undefined ||
|
|
725
721
|
input.recipe !== undefined)
|
|
726
722
|
return undefined;
|
|
727
|
-
const root = Paths.
|
|
723
|
+
const root = Paths.getRecipeDraftRoot();
|
|
728
724
|
mkdirSync(root, { recursive: true });
|
|
729
|
-
const path = join(root,
|
|
730
|
-
const defaults =
|
|
725
|
+
const path = join(root, draftRecipeName(String(meta.run)));
|
|
726
|
+
const defaults = draftRecipeDefaults(meta.values);
|
|
731
727
|
const recipe = {
|
|
732
728
|
async: true,
|
|
733
729
|
description: `Draft recipe captured from spawn run ${String(meta.run)}`,
|
|
@@ -743,7 +739,7 @@ function enhanceSpawnRecipeError(error, recipe) {
|
|
|
743
739
|
if (!diagnostic)
|
|
744
740
|
return error instanceof Error ? error : new Error(String(error));
|
|
745
741
|
const original = error instanceof Error ? error.message : String(error);
|
|
746
|
-
return Object.assign(new Error(`${original} reason=${diagnostic.reason} active_path=${diagnostic.active_path}
|
|
742
|
+
return Object.assign(new Error(`${original} reason=${diagnostic.reason} active_path=${diagnostic.active_path} blocked_fallback=${diagnostic.blocked_fallback} hint=${diagnostic.hint}`), {
|
|
747
743
|
...diagnostic,
|
|
748
744
|
original_error: original,
|
|
749
745
|
});
|
|
@@ -864,13 +860,11 @@ export function createSpawnToolDefinition() {
|
|
|
864
860
|
catch (error) {
|
|
865
861
|
throw enhanceSpawnRecipeError(error, recipe);
|
|
866
862
|
}
|
|
867
|
-
const
|
|
863
|
+
const draftRecipe = writeSpawnDraftRecipe(input, meta);
|
|
868
864
|
const nextActions = actorRunNextActions(meta.run);
|
|
869
865
|
const details = {
|
|
870
866
|
...meta,
|
|
871
|
-
...(
|
|
872
|
-
? { candidate_recipe: candidateRecipe, draft_recipe: candidateRecipe }
|
|
873
|
-
: {}),
|
|
867
|
+
...(draftRecipe ? { draft_recipe: draftRecipe } : {}),
|
|
874
868
|
next_actions: nextActions,
|
|
875
869
|
};
|
|
876
870
|
ActorRooms.ensureDefaultRoom(meta.state_dir, String(meta.run));
|
|
@@ -962,8 +956,7 @@ export function createInspectToolDefinition(deps = {}) {
|
|
|
962
956
|
const recipeRoot = deps.recipeRoot ?? Paths.getRecipeRoot();
|
|
963
957
|
const summaryBase = {
|
|
964
958
|
...RecipeDiscovery.summarizeDiscovery(discovered),
|
|
965
|
-
drafts: RecipeDiscovery.
|
|
966
|
-
candidates: RecipeDiscovery.listCandidateRecipes(join(recipeRoot, "candidates")),
|
|
959
|
+
drafts: RecipeDiscovery.listDraftRecipes(join(recipeRoot, "drafts")),
|
|
967
960
|
};
|
|
968
961
|
const summary = {
|
|
969
962
|
...summaryBase,
|
|
@@ -1,33 +1,49 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Internal conformance runner
|
|
4
|
+
* Internal conformance runner.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* This script is intentionally standalone package/release glue rather than a
|
|
7
|
+
* lib domain: it only selects regression suites and formats their summary.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import {
|
|
11
|
-
import { dirname
|
|
12
|
-
import { fileURLToPath
|
|
10
|
+
import { spawnSync } from "node:child_process";
|
|
11
|
+
import { dirname } from "node:path";
|
|
12
|
+
import { fileURLToPath } from "node:url";
|
|
13
|
+
|
|
14
|
+
const conformanceSuites = [
|
|
15
|
+
"tests/protocol-examples.test.ts",
|
|
16
|
+
"tests/recipe-discovery.test.ts",
|
|
17
|
+
"tests/registry.test.ts",
|
|
18
|
+
"tests/runtime-registry.test.ts",
|
|
19
|
+
"tests/async-runs.test.ts",
|
|
20
|
+
"tests/actor-rooms.test.ts",
|
|
21
|
+
"tests/tools.test.ts",
|
|
22
|
+
];
|
|
13
23
|
|
|
14
24
|
function packageRoot() {
|
|
15
25
|
return dirname(dirname(fileURLToPath(import.meta.url)));
|
|
16
26
|
}
|
|
17
27
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
28
|
+
const result = spawnSync(
|
|
29
|
+
process.execPath,
|
|
30
|
+
["--experimental-strip-types", "--test", ...conformanceSuites],
|
|
31
|
+
{ cwd: packageRoot(), encoding: "utf8", stdio: "pipe" },
|
|
32
|
+
);
|
|
23
33
|
|
|
24
|
-
const {
|
|
25
|
-
const
|
|
34
|
+
const output = `${result.stdout ?? ""}${result.stderr ?? ""}`;
|
|
35
|
+
const summary = output
|
|
36
|
+
.split("\n")
|
|
37
|
+
.filter((line) =>
|
|
38
|
+
/^ℹ (tests|pass|fail|cancelled|skipped|todo|duration_ms) /.test(line),
|
|
39
|
+
)
|
|
40
|
+
.join("\n");
|
|
26
41
|
|
|
27
42
|
console.log("pi-actors conformance");
|
|
28
|
-
console.log(`suites ${
|
|
29
|
-
|
|
30
|
-
if (
|
|
31
|
-
|
|
32
|
-
|
|
43
|
+
console.log(`suites ${conformanceSuites.length}`);
|
|
44
|
+
|
|
45
|
+
if (summary) console.log(summary);
|
|
46
|
+
if ((result.status ?? 1) !== 0) {
|
|
47
|
+
console.error(output.trimEnd());
|
|
48
|
+
process.exit(result.status ?? 1);
|
|
33
49
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: actors
|
|
3
3
|
description: Required practical guide for non-trivial pi-actors use. Read before using or changing 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.33.0
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Actors (pi-actors)
|
|
@@ -213,7 +213,7 @@ Only matching filename ids compete. Higher priority shadows lower priority; with
|
|
|
213
213
|
Muscle-memory lens: pi-actors has two durable executable-memory layers.
|
|
214
214
|
|
|
215
215
|
1. `~/.pi/agent/recipes/*.json` and `*.md` are the agent's active capability memory. Every recipe in that directory becomes an easy-to-call tool automatically and survives into later sessions. Descriptions matter here because they become the tool's operator-facing title/context.
|
|
216
|
-
2. `~/.pi/agent/recipes/
|
|
216
|
+
2. `~/.pi/agent/recipes/drafts/*.json` is draft memory captured from successful inline `spawn template=...` runs. Drafts do not enter the injected tool surface. They remain reusable by explicit path, e.g. `spawn file="~/.pi/agent/recipes/drafts/<name>.json"`, and can be promoted by moving or copying one level up into `~/.pi/agent/recipes`.
|
|
217
217
|
|
|
218
218
|
Agents grow active memory by calling `register_tool` or by deliberate recipe-file edits. They grow draft memory by trying ad hoc actors successfully. Treat both as executable habits: drafts are the workbench/proving ground; root recipes are promoted muscle memory.
|
|
219
219
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: swarm
|
|
3
3
|
description: Subagent orchestration with scoped locks and quorum consensus. Use for multi-model review, parallel scoped work, delegated audit, and coordinated subagent execution.
|
|
4
4
|
metadata:
|
|
5
|
-
version: 0.
|
|
5
|
+
version: 0.33.0
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Swarm
|
|
@@ -31,7 +31,7 @@ Maintain this skill as a living orchestration standard. When real swarm work exp
|
|
|
31
31
|
- `Lens`: A deliberately narrow cognitive role assigned to one subagent, such as security, tests, architecture, economics, or operator UX.
|
|
32
32
|
- `Task Card`: A bounded implementation assignment with goal, allowed files, avoided files, expected output, and validation gates.
|
|
33
33
|
- `Component Capability`: An abstract adapter operation such as launcher, reviewer, verifier, merger, quorum, checkpoint, follow-up, judge, or normalizer. Swarm may target these capabilities, but local adapters bind them to concrete tools, recipes, draft recipes, command templates, async runs, or services.
|
|
34
|
-
- `Draft Recipe`: A reusable but non-registered recipe captured from a successful inline actor spawn
|
|
34
|
+
- `Draft Recipe`: A reusable but non-registered recipe captured from a successful inline actor spawn under `~/.pi/agent/recipes/drafts`. It can be replayed by explicit file path and later promoted into the active tool recipe root after enough dogfood.
|
|
35
35
|
- `Coordinator Checkpoint`: A deliberate subagent pause where the subagent preserves its working context, sends a bounded question or status to the orchestrator, receives a coordinator reply, and continues in the same subagent context.
|
|
36
36
|
- `Evidence Checkpoint`: A deliberate stop where a subagent records sources, assumptions, confidence, contradictions, or blocking evidence gaps before synthesis.
|
|
37
37
|
- `Integrator`: The human or agent that merges isolated branches/worktrees into the shared target and owns conflict resolution.
|
package/docs/tool-registry.md
CHANGED
|
@@ -10,7 +10,7 @@ The registry source is location-discovered recipes, not a live tool-only JSON fi
|
|
|
10
10
|
|
|
11
11
|
- `~/.pi/agent/recipes/*.json` and `*.md` are the highest-priority user recipe root and the operator-managed tool set.
|
|
12
12
|
- Recipes in that root are tools by location.
|
|
13
|
-
- `~/.pi/agent/recipes/
|
|
13
|
+
- `~/.pi/agent/recipes/drafts/*.json` stores captured inline-spawn draft recipes, not registered tools. Promote one by moving or copying it up one level into `~/.pi/agent/recipes`. `inspect target=recipes view=summary` reports their count, and verbose output lists their paths/descriptions for explicit replay by file path.
|
|
14
14
|
- Packaged pi-actors recipes are the lower-priority standard library of declarative actor config components, not automatically registered tools.
|
|
15
15
|
- Ad hoc recipe files outside the user recipe root are components unless explicitly registered/copied into `~/.pi/agent/recipes`.
|
|
16
16
|
- Recipe identity is the filename basename; `~/.pi/agent/recipes/docs_review.json` and `docs_review.md` both have id/tool name `docs_review`.
|
|
@@ -31,9 +31,9 @@ inspect target=recipes view=summary verbose=true
|
|
|
31
31
|
|
|
32
32
|
`tool:pi-actors` is a reserved runtime-status actor: it reports the loaded package version, package root, source/dist mode, entrypoint path, recipe roots, and git commit when available. Use it after reloads to confirm which extension code is actually live.
|
|
33
33
|
|
|
34
|
-
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
|
|
34
|
+
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 fallback paths when a broken or disabled higher-priority recipe masks a fallback.
|
|
35
35
|
|
|
36
|
-
Routine shadowing is quiet. If a bare `spawn` recipe launch already fails because an invalid or `disabled: true` user recipe blocks a lower-priority
|
|
36
|
+
Routine shadowing is quiet. If a bare `spawn` recipe launch already fails because an invalid or `disabled: true` user recipe blocks a lower-priority fallback, the launch error adds compact tokens such as `reason=shadowed_invalid` or `reason=shadowed_disabled`, `active_path`, `blocked_fallback`, and `hint=inspect_recipes_doctor`.
|
|
37
37
|
|
|
38
38
|
## Registering Tools
|
|
39
39
|
|
package/index.ts
CHANGED
|
@@ -102,7 +102,7 @@ export default function toolRegistryExtension(pi: Pi.ExtensionAPI) {
|
|
|
102
102
|
onChange: () =>
|
|
103
103
|
activeRunContext && scheduleRunEventUpdate(activeRunContext),
|
|
104
104
|
});
|
|
105
|
-
const actorToolDefinitions = new Map<string,
|
|
105
|
+
const actorToolDefinitions = new Map<string, Tools.ActorToolDefinition>();
|
|
106
106
|
const runtime = Runtime.createAutoToolsRuntime({
|
|
107
107
|
configPath: Paths.EXTENSION_RUNTIME_PATHS.configPath,
|
|
108
108
|
exec: CommandTemplates.execCommandTemplate,
|
package/lib/paths.ts
CHANGED
|
@@ -24,7 +24,7 @@ export interface ExtensionRuntimePaths {
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
export function getConfigPath(agentDir = getAgentDir()): string {
|
|
27
|
-
return join(agentDir, "
|
|
27
|
+
return join(agentDir, "tool-registry.json");
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
export function getExtensionTmpDir(
|
|
@@ -63,8 +63,8 @@ export function getRecipeRoot(agentDir = getAgentDir()): string {
|
|
|
63
63
|
return join(agentDir, "recipes");
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
export function
|
|
67
|
-
return join(getRecipeRoot(agentDir), "
|
|
66
|
+
export function getRecipeDraftRoot(agentDir = getAgentDir()): string {
|
|
67
|
+
return join(getRecipeRoot(agentDir), "drafts");
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
export function getPackagedRecipeRoot(): string {
|
package/lib/recipe-discovery.ts
CHANGED
|
@@ -461,16 +461,16 @@ function remediationForEntry(
|
|
|
461
461
|
const riskyDiagnostics = entry.diagnostics.filter(
|
|
462
462
|
(message) => diagnosticSeverity(message) === "warning",
|
|
463
463
|
);
|
|
464
|
-
const
|
|
464
|
+
const blockedFallback = entry.shadows[0];
|
|
465
465
|
if (entry.invalid) {
|
|
466
466
|
return {
|
|
467
467
|
id: entry.id,
|
|
468
|
-
kind:
|
|
468
|
+
kind: blockedFallback ? "blocking_invalid" : "invalid",
|
|
469
469
|
severity: "error",
|
|
470
470
|
path: entry.path,
|
|
471
|
-
...(
|
|
472
|
-
reason:
|
|
473
|
-
? "invalid higher-priority recipe blocks a lower-priority
|
|
471
|
+
...(blockedFallback ? { blocked_fallback: blockedFallback } : {}),
|
|
472
|
+
reason: blockedFallback
|
|
473
|
+
? "invalid higher-priority recipe blocks a lower-priority fallback"
|
|
474
474
|
: "recipe is invalid and cannot be exposed as a tool",
|
|
475
475
|
action:
|
|
476
476
|
"fix recipe syntax/config, or disable/delete/archive it to restore fallback",
|
|
@@ -479,12 +479,12 @@ function remediationForEntry(
|
|
|
479
479
|
if (entry.disabled && entry.active) {
|
|
480
480
|
return {
|
|
481
481
|
id: entry.id,
|
|
482
|
-
kind:
|
|
482
|
+
kind: blockedFallback ? "blocking_disabled" : "disabled",
|
|
483
483
|
severity: "warning",
|
|
484
484
|
path: entry.path,
|
|
485
|
-
...(
|
|
486
|
-
reason:
|
|
487
|
-
? "disabled higher-priority recipe intentionally blocks a lower-priority
|
|
485
|
+
...(blockedFallback ? { blocked_fallback: blockedFallback } : {}),
|
|
486
|
+
reason: blockedFallback
|
|
487
|
+
? "disabled higher-priority recipe intentionally blocks a lower-priority fallback"
|
|
488
488
|
: "recipe is disabled and not exposed as a tool",
|
|
489
489
|
action:
|
|
490
490
|
"keep disabled intentionally, re-enable, or delete/archive the file",
|
|
@@ -568,13 +568,13 @@ export function getShadowedLaunchDiagnostic(
|
|
|
568
568
|
if (!active.invalid && !active.disabled) return undefined;
|
|
569
569
|
return {
|
|
570
570
|
active_path: active.path,
|
|
571
|
-
|
|
571
|
+
blocked_fallback: active.shadows[0],
|
|
572
572
|
hint: "inspect_recipes_doctor",
|
|
573
573
|
reason: active.invalid ? "shadowed_invalid" : "shadowed_disabled",
|
|
574
574
|
};
|
|
575
575
|
}
|
|
576
576
|
|
|
577
|
-
export function
|
|
577
|
+
export function listDraftRecipes(root: string): Array<Record<string, unknown>> {
|
|
578
578
|
return listRecipeFiles(root).map((path) => {
|
|
579
579
|
const id = RecipeReferences.getRecipeIdFromPath(path);
|
|
580
580
|
const config = RecipeReferences.readRawRecipeConfig(path);
|
package/lib/tools.ts
CHANGED
|
@@ -27,6 +27,12 @@ export type RegisterToolInput = Registry.RegisterToolInput;
|
|
|
27
27
|
export type RegisterToolRuntimeDeps<TContext> =
|
|
28
28
|
Registry.RegisterToolRuntimeDeps<TContext>;
|
|
29
29
|
|
|
30
|
+
export interface ActorToolDefinition {
|
|
31
|
+
name: string;
|
|
32
|
+
execute?: (...args: never[]) => unknown;
|
|
33
|
+
[key: string]: unknown;
|
|
34
|
+
}
|
|
35
|
+
|
|
30
36
|
export interface CoreActorToolDefinitionDeps<TContext extends AsyncRunToolContext> {
|
|
31
37
|
configPath: string;
|
|
32
38
|
getActiveTools: () => string[];
|
|
@@ -222,7 +228,7 @@ function compactAsyncRunStatus(value: unknown): string {
|
|
|
222
228
|
tokens.push(`failures=${failures}`);
|
|
223
229
|
if (result.code !== undefined) tokens.push(`code=${String(result.code)}`);
|
|
224
230
|
if (result.killed === true) tokens.push("killed=true");
|
|
225
|
-
const draftRecipe = status.draft_recipe
|
|
231
|
+
const draftRecipe = status.draft_recipe;
|
|
226
232
|
if (draftRecipe) tokens.push(`draft_recipe=${String(draftRecipe)}`);
|
|
227
233
|
const nextActions = actorRunNextActions(run);
|
|
228
234
|
if (nextActions.length > 0)
|
|
@@ -627,8 +633,8 @@ function compactRecipeDoctor(summary: Record<string, unknown>): string {
|
|
|
627
633
|
item.action,
|
|
628
634
|
Limits.DOCTOR_ACTION_PREVIEW_CHARS,
|
|
629
635
|
);
|
|
630
|
-
const blocked = item.
|
|
631
|
-
? ` blocked=${compactPreview(item.
|
|
636
|
+
const blocked = item.blocked_fallback
|
|
637
|
+
? ` blocked=${compactPreview(item.blocked_fallback, Limits.DOCTOR_ACTION_PREVIEW_CHARS)}`
|
|
632
638
|
: "";
|
|
633
639
|
lines.push(
|
|
634
640
|
`${String(item.severity ?? "info")} kind=${String(item.kind ?? "inspect")} id=${String(item.id ?? "root")}${blocked} action=${action ?? "inspect"}`,
|
|
@@ -683,11 +689,7 @@ function compactRecipeRegistry(summary: Record<string, unknown>): string {
|
|
|
683
689
|
const diagnostics = Array.isArray(summary.diagnostics)
|
|
684
690
|
? summary.diagnostics.length
|
|
685
691
|
: 0;
|
|
686
|
-
const drafts = Array.isArray(summary.drafts)
|
|
687
|
-
? summary.drafts.length
|
|
688
|
-
: Array.isArray(summary.candidates)
|
|
689
|
-
? summary.candidates.length
|
|
690
|
-
: 0;
|
|
692
|
+
const drafts = Array.isArray(summary.drafts) ? summary.drafts.length : 0;
|
|
691
693
|
const recommendations = Array.isArray(summary.recommendations)
|
|
692
694
|
? summary.recommendations.length
|
|
693
695
|
: 0;
|
|
@@ -873,11 +875,11 @@ function shadowedRecipeLaunchDiagnostic(
|
|
|
873
875
|
return RecipeDiscovery.getShadowedLaunchDiagnostic(discovery, recipe);
|
|
874
876
|
}
|
|
875
877
|
|
|
876
|
-
function
|
|
878
|
+
function draftRecipeName(run: string): string {
|
|
877
879
|
return `${run.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "spawn"}.json`;
|
|
878
880
|
}
|
|
879
881
|
|
|
880
|
-
function
|
|
882
|
+
function draftRecipeDefaults(
|
|
881
883
|
values: Record<string, unknown>,
|
|
882
884
|
): Record<string, unknown> | undefined {
|
|
883
885
|
const ignored = new Set([
|
|
@@ -893,13 +895,13 @@ function candidateRecipeDefaults(
|
|
|
893
895
|
return Object.keys(defaults).length > 0 ? defaults : undefined;
|
|
894
896
|
}
|
|
895
897
|
|
|
896
|
-
function
|
|
898
|
+
function writeSpawnDraftRecipe(
|
|
897
899
|
input: Record<string, unknown>,
|
|
898
900
|
meta: AsyncRuns.AsyncRunMeta,
|
|
899
901
|
): string | undefined {
|
|
900
902
|
if (
|
|
901
903
|
process.env.NODE_TEST_CONTEXT &&
|
|
902
|
-
process.env.
|
|
904
|
+
process.env.PI_ACTORS_ENABLE_SPAWN_DRAFTS_IN_TEST !== "1"
|
|
903
905
|
)
|
|
904
906
|
return undefined;
|
|
905
907
|
if (
|
|
@@ -908,10 +910,10 @@ function writeSpawnCandidateRecipe(
|
|
|
908
910
|
input.recipe !== undefined
|
|
909
911
|
)
|
|
910
912
|
return undefined;
|
|
911
|
-
const root = Paths.
|
|
913
|
+
const root = Paths.getRecipeDraftRoot();
|
|
912
914
|
mkdirSync(root, { recursive: true });
|
|
913
|
-
const path = join(root,
|
|
914
|
-
const defaults =
|
|
915
|
+
const path = join(root, draftRecipeName(String(meta.run)));
|
|
916
|
+
const defaults = draftRecipeDefaults(meta.values);
|
|
915
917
|
const recipe = {
|
|
916
918
|
async: true,
|
|
917
919
|
description: `Draft recipe captured from spawn run ${String(meta.run)}`,
|
|
@@ -930,7 +932,7 @@ function enhanceSpawnRecipeError(error: unknown, recipe: unknown): Error {
|
|
|
930
932
|
const original = error instanceof Error ? error.message : String(error);
|
|
931
933
|
return Object.assign(
|
|
932
934
|
new Error(
|
|
933
|
-
`${original} reason=${diagnostic.reason} active_path=${diagnostic.active_path}
|
|
935
|
+
`${original} reason=${diagnostic.reason} active_path=${diagnostic.active_path} blocked_fallback=${diagnostic.blocked_fallback} hint=${diagnostic.hint}`,
|
|
934
936
|
),
|
|
935
937
|
{
|
|
936
938
|
...diagnostic,
|
|
@@ -1118,13 +1120,11 @@ export function createSpawnToolDefinition<
|
|
|
1118
1120
|
} catch (error) {
|
|
1119
1121
|
throw enhanceSpawnRecipeError(error, recipe);
|
|
1120
1122
|
}
|
|
1121
|
-
const
|
|
1123
|
+
const draftRecipe = writeSpawnDraftRecipe(input, meta);
|
|
1122
1124
|
const nextActions = actorRunNextActions(meta.run);
|
|
1123
1125
|
const details = {
|
|
1124
1126
|
...meta,
|
|
1125
|
-
...(
|
|
1126
|
-
? { candidate_recipe: candidateRecipe, draft_recipe: candidateRecipe }
|
|
1127
|
-
: {}),
|
|
1127
|
+
...(draftRecipe ? { draft_recipe: draftRecipe } : {}),
|
|
1128
1128
|
next_actions: nextActions,
|
|
1129
1129
|
};
|
|
1130
1130
|
ActorRooms.ensureDefaultRoom(meta.state_dir, String(meta.run));
|
|
@@ -1275,12 +1275,7 @@ export function createInspectToolDefinition<TContext = unknown>(
|
|
|
1275
1275
|
const recipeRoot = deps.recipeRoot ?? Paths.getRecipeRoot();
|
|
1276
1276
|
const summaryBase = {
|
|
1277
1277
|
...RecipeDiscovery.summarizeDiscovery(discovered),
|
|
1278
|
-
drafts: RecipeDiscovery.
|
|
1279
|
-
join(recipeRoot, "candidates"),
|
|
1280
|
-
),
|
|
1281
|
-
candidates: RecipeDiscovery.listCandidateRecipes(
|
|
1282
|
-
join(recipeRoot, "candidates"),
|
|
1283
|
-
),
|
|
1278
|
+
drafts: RecipeDiscovery.listDraftRecipes(join(recipeRoot, "drafts")),
|
|
1284
1279
|
};
|
|
1285
1280
|
const summary = {
|
|
1286
1281
|
...summaryBase,
|
|
@@ -1943,7 +1938,7 @@ export function createActorMessageToolDefinition<TContext = unknown>(
|
|
|
1943
1938
|
|
|
1944
1939
|
export function createCoreActorToolDefinitions<TContext extends AsyncRunToolContext>(
|
|
1945
1940
|
deps: CoreActorToolDefinitionDeps<TContext>,
|
|
1946
|
-
):
|
|
1941
|
+
): ActorToolDefinition[] {
|
|
1947
1942
|
return [
|
|
1948
1943
|
createRegisterToolDefinition<TContext>({
|
|
1949
1944
|
configPath: deps.configPath,
|
package/package.json
CHANGED
package/scripts/conformance.mjs
CHANGED
|
@@ -1,33 +1,49 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Internal conformance runner
|
|
4
|
+
* Internal conformance runner.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* This script is intentionally standalone package/release glue rather than a
|
|
7
|
+
* lib domain: it only selects regression suites and formats their summary.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import {
|
|
11
|
-
import { dirname
|
|
12
|
-
import { fileURLToPath
|
|
10
|
+
import { spawnSync } from "node:child_process";
|
|
11
|
+
import { dirname } from "node:path";
|
|
12
|
+
import { fileURLToPath } from "node:url";
|
|
13
|
+
|
|
14
|
+
const conformanceSuites = [
|
|
15
|
+
"tests/protocol-examples.test.ts",
|
|
16
|
+
"tests/recipe-discovery.test.ts",
|
|
17
|
+
"tests/registry.test.ts",
|
|
18
|
+
"tests/runtime-registry.test.ts",
|
|
19
|
+
"tests/async-runs.test.ts",
|
|
20
|
+
"tests/actor-rooms.test.ts",
|
|
21
|
+
"tests/tools.test.ts",
|
|
22
|
+
];
|
|
13
23
|
|
|
14
24
|
function packageRoot() {
|
|
15
25
|
return dirname(dirname(fileURLToPath(import.meta.url)));
|
|
16
26
|
}
|
|
17
27
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
28
|
+
const result = spawnSync(
|
|
29
|
+
process.execPath,
|
|
30
|
+
["--experimental-strip-types", "--test", ...conformanceSuites],
|
|
31
|
+
{ cwd: packageRoot(), encoding: "utf8", stdio: "pipe" },
|
|
32
|
+
);
|
|
23
33
|
|
|
24
|
-
const {
|
|
25
|
-
const
|
|
34
|
+
const output = `${result.stdout ?? ""}${result.stderr ?? ""}`;
|
|
35
|
+
const summary = output
|
|
36
|
+
.split("\n")
|
|
37
|
+
.filter((line) =>
|
|
38
|
+
/^ℹ (tests|pass|fail|cancelled|skipped|todo|duration_ms) /.test(line),
|
|
39
|
+
)
|
|
40
|
+
.join("\n");
|
|
26
41
|
|
|
27
42
|
console.log("pi-actors conformance");
|
|
28
|
-
console.log(`suites ${
|
|
29
|
-
|
|
30
|
-
if (
|
|
31
|
-
|
|
32
|
-
|
|
43
|
+
console.log(`suites ${conformanceSuites.length}`);
|
|
44
|
+
|
|
45
|
+
if (summary) console.log(summary);
|
|
46
|
+
if ((result.status ?? 1) !== 0) {
|
|
47
|
+
console.error(output.trimEnd());
|
|
48
|
+
process.exit(result.status ?? 1);
|
|
33
49
|
}
|
package/skills/actors/SKILL.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: actors
|
|
3
3
|
description: Required practical guide for non-trivial pi-actors use. Read before using or changing 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.33.0
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Actors (pi-actors)
|
|
@@ -213,7 +213,7 @@ Only matching filename ids compete. Higher priority shadows lower priority; with
|
|
|
213
213
|
Muscle-memory lens: pi-actors has two durable executable-memory layers.
|
|
214
214
|
|
|
215
215
|
1. `~/.pi/agent/recipes/*.json` and `*.md` are the agent's active capability memory. Every recipe in that directory becomes an easy-to-call tool automatically and survives into later sessions. Descriptions matter here because they become the tool's operator-facing title/context.
|
|
216
|
-
2. `~/.pi/agent/recipes/
|
|
216
|
+
2. `~/.pi/agent/recipes/drafts/*.json` is draft memory captured from successful inline `spawn template=...` runs. Drafts do not enter the injected tool surface. They remain reusable by explicit path, e.g. `spawn file="~/.pi/agent/recipes/drafts/<name>.json"`, and can be promoted by moving or copying one level up into `~/.pi/agent/recipes`.
|
|
217
217
|
|
|
218
218
|
Agents grow active memory by calling `register_tool` or by deliberate recipe-file edits. They grow draft memory by trying ad hoc actors successfully. Treat both as executable habits: drafts are the workbench/proving ground; root recipes are promoted muscle memory.
|
|
219
219
|
|
package/skills/swarm/SKILL.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: swarm
|
|
3
3
|
description: Subagent orchestration with scoped locks and quorum consensus. Use for multi-model review, parallel scoped work, delegated audit, and coordinated subagent execution.
|
|
4
4
|
metadata:
|
|
5
|
-
version: 0.
|
|
5
|
+
version: 0.33.0
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Swarm
|
|
@@ -31,7 +31,7 @@ Maintain this skill as a living orchestration standard. When real swarm work exp
|
|
|
31
31
|
- `Lens`: A deliberately narrow cognitive role assigned to one subagent, such as security, tests, architecture, economics, or operator UX.
|
|
32
32
|
- `Task Card`: A bounded implementation assignment with goal, allowed files, avoided files, expected output, and validation gates.
|
|
33
33
|
- `Component Capability`: An abstract adapter operation such as launcher, reviewer, verifier, merger, quorum, checkpoint, follow-up, judge, or normalizer. Swarm may target these capabilities, but local adapters bind them to concrete tools, recipes, draft recipes, command templates, async runs, or services.
|
|
34
|
-
- `Draft Recipe`: A reusable but non-registered recipe captured from a successful inline actor spawn
|
|
34
|
+
- `Draft Recipe`: A reusable but non-registered recipe captured from a successful inline actor spawn under `~/.pi/agent/recipes/drafts`. It can be replayed by explicit file path and later promoted into the active tool recipe root after enough dogfood.
|
|
35
35
|
- `Coordinator Checkpoint`: A deliberate subagent pause where the subagent preserves its working context, sends a bounded question or status to the orchestrator, receives a coordinator reply, and continues in the same subagent context.
|
|
36
36
|
- `Evidence Checkpoint`: A deliberate stop where a subagent records sources, assumptions, confidence, contradictions, or blocking evidence gaps before synthesis.
|
|
37
37
|
- `Integrator`: The human or agent that merges isolated branches/worktrees into the shared target and owns conflict resolution.
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Internal conformance runner logic.
|
|
3
|
-
* Zones: CI/release validation, protocol regression suite selection
|
|
4
|
-
*/
|
|
5
|
-
export declare const conformanceSuites: string[];
|
|
6
|
-
export interface ConformanceReport {
|
|
7
|
-
code: number;
|
|
8
|
-
output: string;
|
|
9
|
-
summary: string;
|
|
10
|
-
suites: number;
|
|
11
|
-
}
|
|
12
|
-
export declare function runConformance(cwd?: URL): ConformanceReport;
|
package/dist/lib/conformance.js
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Internal conformance runner logic.
|
|
3
|
-
* Zones: CI/release validation, protocol regression suite selection
|
|
4
|
-
*/
|
|
5
|
-
import { spawnSync } from "node:child_process";
|
|
6
|
-
export const conformanceSuites = [
|
|
7
|
-
"tests/protocol-examples.test.ts",
|
|
8
|
-
"tests/recipe-discovery.test.ts",
|
|
9
|
-
"tests/registry.test.ts",
|
|
10
|
-
"tests/runtime-registry.test.ts",
|
|
11
|
-
"tests/async-runs.test.ts",
|
|
12
|
-
"tests/actor-rooms.test.ts",
|
|
13
|
-
"tests/tools.test.ts",
|
|
14
|
-
];
|
|
15
|
-
export function runConformance(cwd = new URL("..", import.meta.url)) {
|
|
16
|
-
const result = spawnSync(process.execPath, ["--experimental-strip-types", "--test", ...conformanceSuites], { cwd, encoding: "utf8", stdio: "pipe" });
|
|
17
|
-
const output = `${result.stdout ?? ""}${result.stderr ?? ""}`;
|
|
18
|
-
const summary = output
|
|
19
|
-
.split("\n")
|
|
20
|
-
.filter((line) => /^ℹ (tests|pass|fail|cancelled|skipped|todo|duration_ms) /.test(line))
|
|
21
|
-
.join("\n");
|
|
22
|
-
return {
|
|
23
|
-
code: result.status ?? 1,
|
|
24
|
-
output,
|
|
25
|
-
summary,
|
|
26
|
-
suites: conformanceSuites.length,
|
|
27
|
-
};
|
|
28
|
-
}
|
package/lib/conformance.ts
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Internal conformance runner logic.
|
|
3
|
-
* Zones: CI/release validation, protocol regression suite selection
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { spawnSync } from "node:child_process";
|
|
7
|
-
|
|
8
|
-
export const conformanceSuites = [
|
|
9
|
-
"tests/protocol-examples.test.ts",
|
|
10
|
-
"tests/recipe-discovery.test.ts",
|
|
11
|
-
"tests/registry.test.ts",
|
|
12
|
-
"tests/runtime-registry.test.ts",
|
|
13
|
-
"tests/async-runs.test.ts",
|
|
14
|
-
"tests/actor-rooms.test.ts",
|
|
15
|
-
"tests/tools.test.ts",
|
|
16
|
-
];
|
|
17
|
-
|
|
18
|
-
export interface ConformanceReport {
|
|
19
|
-
code: number;
|
|
20
|
-
output: string;
|
|
21
|
-
summary: string;
|
|
22
|
-
suites: number;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export function runConformance(cwd = new URL("..", import.meta.url)): ConformanceReport {
|
|
26
|
-
const result = spawnSync(
|
|
27
|
-
process.execPath,
|
|
28
|
-
["--experimental-strip-types", "--test", ...conformanceSuites],
|
|
29
|
-
{ cwd, encoding: "utf8", stdio: "pipe" },
|
|
30
|
-
);
|
|
31
|
-
|
|
32
|
-
const output = `${result.stdout ?? ""}${result.stderr ?? ""}`;
|
|
33
|
-
const summary = output
|
|
34
|
-
.split("\n")
|
|
35
|
-
.filter((line) =>
|
|
36
|
-
/^ℹ (tests|pass|fail|cancelled|skipped|todo|duration_ms) /.test(line),
|
|
37
|
-
)
|
|
38
|
-
.join("\n");
|
|
39
|
-
|
|
40
|
-
return {
|
|
41
|
-
code: result.status ?? 1,
|
|
42
|
-
output,
|
|
43
|
-
summary,
|
|
44
|
-
suites: conformanceSuites.length,
|
|
45
|
-
};
|
|
46
|
-
}
|