@llblab/pi-actors 0.24.0 → 0.24.2
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 +1 -1
- package/BACKLOG.md +72 -1
- package/CHANGELOG.md +10 -1
- package/dist/index.js +1 -1
- package/dist/lib/actor-inspector-tui.js +18 -9
- package/dist/lib/coordinator.js +17 -0
- package/dist/scripts/build-dist.mjs +22 -14
- package/dist/skills/actors/SKILL.md +4 -2
- package/dist/skills/swarm/SKILL.md +1 -1
- package/index.ts +11 -10
- package/lib/actor-inspector-tui.ts +18 -9
- package/lib/coordinator.ts +15 -0
- package/package.json +1 -1
- package/scripts/build-dist.mjs +22 -14
- package/skills/actors/SKILL.md +4 -2
- package/skills/swarm/SKILL.md +1 -1
- package/dist/lib/build-dist.d.ts +0 -5
- package/dist/lib/build-dist.js +0 -24
- package/lib/build-dist.ts +0 -30
package/AGENTS.md
CHANGED
|
@@ -33,7 +33,7 @@ Treat this extension as an experimental self-evolution membrane for the agent ha
|
|
|
33
33
|
## Repo Surfaces
|
|
34
34
|
|
|
35
35
|
- `/scripts/*.mjs`: Stable executable shims for detached/helper processes.
|
|
36
|
-
- `/lib/*.ts`: Compiled domain and script-entrypoint logic. Keep `scripts/*.mjs` lightweight and move substantive behavior into named domain modules so `dist/lib` is the JS-only runtime surface. This intentionally grows a standard library: script-born behavior should gain a clear domain name when reuse is plausible. Exception: self-contained application scripts with no expected second consumer, such as `music-player.mjs`, may remain standalone `.mjs` files.
|
|
36
|
+
- `/lib/*.ts`: Compiled domain and script-entrypoint logic. Keep `scripts/*.mjs` lightweight and move substantive behavior into named domain modules so `dist/lib` is the JS-only runtime surface. This intentionally grows a standard library: script-born behavior should gain a clear domain name when reuse is plausible. Exception: self-contained application/build scripts with no expected second consumer, such as `music-player.mjs` or `build-dist.mjs`, may remain standalone `.mjs` files.
|
|
37
37
|
- `/recipes/*.json`: Packaged standard recipe library. Keep recipes optional, composable, policy-light, and caller-configurable.
|
|
38
38
|
- `/skills/actors/SKILL.md`: Dense practical reference for operating pi-actors itself.
|
|
39
39
|
- `/skills/swarm/SKILL.md`: Bundled methodology skill for multi-agent standards, strategies, and portable examples.
|
package/BACKLOG.md
CHANGED
|
@@ -162,6 +162,71 @@ The backlog is intentionally pruned to the 20% of work most likely to deliver 80
|
|
|
162
162
|
- `npm run pack:dry` includes expected compiled/script files.
|
|
163
163
|
- Recipe paths remain stable or migrations are explicitly documented.
|
|
164
164
|
|
|
165
|
+
### M-08 Recipe Doctor Remediation UX
|
|
166
|
+
|
|
167
|
+
- Priority: High.
|
|
168
|
+
- Status: Open.
|
|
169
|
+
- Goal: Turn recipe doctor output into an operator action surface, not just a diagnostic listing.
|
|
170
|
+
- Why now: Recipe registry warnings are intentionally actionable; the next value is helping operators decide whether to fix, disable, delete, or inspect a recipe without hiding the warning.
|
|
171
|
+
- Direction:
|
|
172
|
+
- Summarize invalid, blocking, shadowed, disabled, and risky shell-boundary entries with compact recommended actions.
|
|
173
|
+
- Keep remediation advisory by default; no automatic mutation of user recipes.
|
|
174
|
+
- Preserve detailed diagnostics through verbose inspection.
|
|
175
|
+
- Acceptance:
|
|
176
|
+
- `inspect target=recipes view=doctor` identifies the highest-priority actionable maintenance item.
|
|
177
|
+
- Blocking invalid recipes include the blocked lower-priority candidate when available.
|
|
178
|
+
- Tests cover at least invalid/blocking, disabled, shadowed, and risky shell diagnostics.
|
|
179
|
+
|
|
180
|
+
### M-09 Actor Worker v2
|
|
181
|
+
|
|
182
|
+
- Priority: High.
|
|
183
|
+
- Status: Open.
|
|
184
|
+
- Goal: Promote `actor-worker` from a minimal demo into the canonical standard-worker reference pattern.
|
|
185
|
+
- Why now: Mailbox-loop semantics are now stable enough to show artifact production, compact status, and stale-claim recovery without adding a scheduler or broker.
|
|
186
|
+
- Direction:
|
|
187
|
+
- Add optional task result artifact writing.
|
|
188
|
+
- Expose compact worker status for `inspect` and room events.
|
|
189
|
+
- Add stale-claim recovery or timeout semantics where they fit the mailbox-loop helper.
|
|
190
|
+
- Preserve policy-light behavior: no model choice, prompt design, or project task selection.
|
|
191
|
+
- Acceptance:
|
|
192
|
+
- Worker can produce a durable artifact path for handled work.
|
|
193
|
+
- Stale claimed work can be surfaced or recovered deterministically.
|
|
194
|
+
- The actors skill documents the v2 worker pattern.
|
|
195
|
+
|
|
196
|
+
### M-10 Dist Package Contract Hardening
|
|
197
|
+
|
|
198
|
+
- Priority: Medium.
|
|
199
|
+
- Status: Open.
|
|
200
|
+
- Goal: Make the dist-first package contract difficult to regress after the 0.24 packaging shift.
|
|
201
|
+
- Why now: `dist/` is now the default JS-only runtime surface and carries mirrored scripts, recipes, fixtures, and skills.
|
|
202
|
+
- Direction:
|
|
203
|
+
- Add package-layout checks for default metadata, source metadata, mirrored assets, and compiled script-domain modules.
|
|
204
|
+
- Add negative checks for stale renamed dist files and source-only runtime imports from installed packages.
|
|
205
|
+
- Keep source files packaged for TypeScript-native runtimes unless a future package-size decision changes that explicitly.
|
|
206
|
+
- Acceptance:
|
|
207
|
+
- `npm run validate` fails if default Pi metadata points outside `dist` unexpectedly.
|
|
208
|
+
- Installed-package tests cover every script shim that imports compiled domain logic.
|
|
209
|
+
- Pack dry assertions cover `dist/scripts`, `dist/recipes`, `dist/fixtures`, and `dist/skills`.
|
|
210
|
+
|
|
211
|
+
### M-11 Actor Termination Semantics
|
|
212
|
+
|
|
213
|
+
- Priority: Medium.
|
|
214
|
+
- Status: Open.
|
|
215
|
+
- Goal: Make `control.kill` the canonical parent-to-actor termination action while keeping `control.stop` and `control.cancel` as actor-domain messages whose meaning depends on the actor protocol.
|
|
216
|
+
- Why now: Mailbox workers need a clearer lifecycle boundary before v2 patterns harden. Treating `stop`, `cancel`, and `kill` as equivalent stop messages blurs actor termination with domain-specific task or playback control.
|
|
217
|
+
- Direction:
|
|
218
|
+
- Document `control.kill` as the universal lifecycle action for a parent/supervisor terminating an actor or run.
|
|
219
|
+
- Reframe `control.stop` as actor-defined domain control, such as stopping music playback or ending an actor-specific loop when that actor declares it.
|
|
220
|
+
- Reframe `control.cancel` as actor-defined domain control, such as cancelling the current subagent task while keeping the worker actor alive for later assignments.
|
|
221
|
+
- Split mailbox-loop helper semantics so lifecycle termination detection is distinct from general control-message detection.
|
|
222
|
+
- While the package is pre-1.0, allow a small intentional minor-version contract break: remove legacy treatment that aliases `control.stop` or `control.cancel` to actor termination.
|
|
223
|
+
- Do not preserve compatibility shims for the old stop/cancel-as-termination behavior before the first 1.0 major release unless a concrete safety issue appears during implementation.
|
|
224
|
+
- Acceptance:
|
|
225
|
+
- Docs and actors skill advertise `control.kill` as canonical parent-to-actor termination.
|
|
226
|
+
- Mailbox-loop helpers/tests distinguish actor termination from actor-domain `stop`/`cancel` handling.
|
|
227
|
+
- Packaged recipes declare `stop`/`cancel` only when the actor-specific behavior is meaningful.
|
|
228
|
+
- Tests assert that generic mailbox-loop termination is not triggered by `control.stop` or `control.cancel`.
|
|
229
|
+
|
|
165
230
|
## Explicitly Deferred
|
|
166
231
|
|
|
167
232
|
These are valid ideas but not current focus. Reintroduce only with concrete evidence from real actor workflows.
|
|
@@ -176,4 +241,10 @@ These are valid ideas but not current focus. Reintroduce only with concrete evid
|
|
|
176
241
|
|
|
177
242
|
## Suggested Milestone Order
|
|
178
243
|
|
|
179
|
-
|
|
244
|
+
```text
|
|
245
|
+
0.25 — Operator remediation and worker maturity:
|
|
246
|
+
M-08, M-09
|
|
247
|
+
|
|
248
|
+
0.26 — Package contract hardening and lifecycle semantics:
|
|
249
|
+
M-10, M-11
|
|
250
|
+
```
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.24.2: Actor Inspector Item View Hotfix
|
|
6
|
+
|
|
7
|
+
- `[Inspector]` Removed the roster panel from selected actor-message item inspection, kept the compact route header styling, added the same left/right gutter used by inspector rows, and renamed the selected-item `body_preview` label to `body`.
|
|
8
|
+
|
|
9
|
+
## 0.24.1: Build Script Package Hygiene Hotfix
|
|
10
|
+
|
|
11
|
+
- `[Backlog]` Added the next focused backlog set for recipe doctor remediation UX, actor worker v2, and dist package contract hardening.
|
|
12
|
+
- `[Skills]` Clarified agent-governed promotion of successful transient actor patterns into durable local tools via `register_tool`, without UI buttons or automatic registration.
|
|
13
|
+
|
|
5
14
|
## 0.24.0: Reliability, Mailbox Workers, and Dist-First Packaging
|
|
6
15
|
|
|
7
16
|
- `[Prompts]` Clarified that recipe registry warnings are actionable maintenance: invalid or blocking recipes should be fixed, removed, or disabled rather than ignored.
|
|
@@ -18,7 +27,7 @@
|
|
|
18
27
|
- `[Packaging]` Build output now mirrors packaged `skills/` into `dist/` alongside scripts, recipes, and fixtures so the JS-only distributive tree carries the project skills; package skill metadata now points at `dist/skills` with `pi.sourceSkills` preserving root TypeScript/source-tree paths, and README now documents the dist-first/source-optional package shape. The dist build pipeline now lives in `scripts/build-dist.mjs` instead of an inline package script, completing the compiled script entrypoint backlog slice.
|
|
19
28
|
- `[Docs]` Added a platform support matrix for mailbox-only, FIFO, named-pipe, and process-control behavior across Linux/macOS/WSL and native Windows, with regressions proving native Windows FIFO limits remain visible and the canonical worker recipe stays mailbox-only.
|
|
20
29
|
- `[Backlog]` Marked the reliability, mailbox loop, protocol fixture, portability, and compiled-entrypoint milestone set complete; future backlog additions should come from concrete actor workflow evidence.
|
|
21
|
-
- `[Scripts]` Migrated `recipe-utils`, `
|
|
30
|
+
- `[Scripts]` Migrated `recipe-utils`, `locker`, `coordinator`, and `validate-recipe` command logic behind compiled TypeScript domain modules while preserving the stable `scripts/*.mjs` shim paths; project guidance frames this as deliberate standard-library growth with clear domain boundaries while keeping self-contained application/build scripts such as `music-player.mjs` and `build-dist.mjs` standalone.
|
|
22
31
|
- `[Protocol]` Added compact protocol fixtures for actor messages, mailbox contracts, run inbox/outbox records, room messages/rosters, run state, recipe summaries, and artifact manifests with regression coverage.
|
|
23
32
|
- `[Skills]` Documented the passive-active skill evolution discipline: `actors` tracks extension mechanics while `swarm` tracks orchestration standards and lessons.
|
|
24
33
|
|
package/dist/index.js
CHANGED
|
@@ -94,7 +94,7 @@ export default function toolRegistryExtension(pi) {
|
|
|
94
94
|
? ActorInspectorTui.renderInspectorItemView(previews, width, style, { sequence: selectedInspectorSequence })
|
|
95
95
|
: ActorInspectorTui.renderInspectorWidget(previews, width, style)) ?? [];
|
|
96
96
|
const run = previews[0]?.run;
|
|
97
|
-
const roster = run
|
|
97
|
+
const roster = selectedInspectorSequence === undefined && run
|
|
98
98
|
? ActorInspectorTui.renderInspectorRosterPanel(ActorInspectorTui.readActorInspectorRoster(RUN_STATE_ROOT, run), width, style)
|
|
99
99
|
: undefined;
|
|
100
100
|
return roster ? [...roster, ...rows] : rows;
|
|
@@ -318,6 +318,11 @@ function propertyValue(value) {
|
|
|
318
318
|
return String(value);
|
|
319
319
|
return JSON.stringify(value);
|
|
320
320
|
}
|
|
321
|
+
function itemViewKeyLabel(key) {
|
|
322
|
+
if (key === "body_preview")
|
|
323
|
+
return "body";
|
|
324
|
+
return key;
|
|
325
|
+
}
|
|
321
326
|
function displayWidth(value) {
|
|
322
327
|
return visibleWidth(value);
|
|
323
328
|
}
|
|
@@ -499,6 +504,9 @@ export function renderInspectorItemView(previews, width = 80, styles = {}, optio
|
|
|
499
504
|
if (!preview)
|
|
500
505
|
return undefined;
|
|
501
506
|
const safeWidth = Math.max(1, width);
|
|
507
|
+
const prefix = " ";
|
|
508
|
+
const suffix = " ";
|
|
509
|
+
const contentWidth = Math.max(8, safeWidth - prefix.length - suffix.length);
|
|
502
510
|
const orderedKeys = [
|
|
503
511
|
"channel",
|
|
504
512
|
"run",
|
|
@@ -522,20 +530,21 @@ export function renderInspectorItemView(previews, width = 80, styles = {}, optio
|
|
|
522
530
|
const sequencePadding = " ".repeat(Math.max(0, keyWidth - displayWidth(sequenceText)));
|
|
523
531
|
const headerSeparator = " ";
|
|
524
532
|
const route = routeText(preview);
|
|
525
|
-
const visibleRoute = boundedLine(route, Math.max(0,
|
|
533
|
+
const visibleRoute = boundedLine(route, Math.max(0, contentWidth - keyWidth - headerSeparator.length));
|
|
526
534
|
const headerPlain = `${sequenceText}${sequencePadding}${headerSeparator}${visibleRoute}`;
|
|
527
535
|
const header = `${style(styles.muted, sequenceText)}${sequencePadding}${headerSeparator}${style(styles.target, visibleRoute)}`;
|
|
528
|
-
const headerPadding = Math.max(0,
|
|
529
|
-
const lines = [`${header}${" ".repeat(headerPadding)}`, ""];
|
|
536
|
+
const headerPadding = Math.max(0, contentWidth - visibleWidth(headerPlain));
|
|
537
|
+
const lines = [`${prefix}${header}${" ".repeat(headerPadding)}${suffix}`, ""];
|
|
530
538
|
for (const [key, value] of entries) {
|
|
531
|
-
const
|
|
539
|
+
const label = itemViewKeyLabel(key);
|
|
540
|
+
const keyPadding = " ".repeat(Math.max(0, keyWidth - displayWidth(label)));
|
|
532
541
|
const separator = " ";
|
|
533
|
-
const valueWidth = Math.max(0,
|
|
542
|
+
const valueWidth = Math.max(0, contentWidth - keyWidth - separator.length);
|
|
534
543
|
const visibleValue = boundedLine(value, valueWidth);
|
|
535
|
-
const plain = `${
|
|
536
|
-
const rendered = `${style(styles.muted,
|
|
537
|
-
const padding = Math.max(0,
|
|
538
|
-
lines.push(`${rendered}${" ".repeat(padding)}`);
|
|
544
|
+
const plain = `${label}${keyPadding}${separator}${visibleValue}`;
|
|
545
|
+
const rendered = `${style(styles.muted, label)}${keyPadding}${separator}${style(styles.preview, visibleValue)}`;
|
|
546
|
+
const padding = Math.max(0, contentWidth - visibleWidth(plain));
|
|
547
|
+
lines.push(`${prefix}${rendered}${" ".repeat(padding)}${suffix}`);
|
|
539
548
|
}
|
|
540
549
|
return lines;
|
|
541
550
|
}
|
package/dist/lib/coordinator.js
CHANGED
|
@@ -136,6 +136,22 @@ async function writeLockerMessage(locker, message) {
|
|
|
136
136
|
await writeFile(control.path, line, { flag: "a" });
|
|
137
137
|
await sleep(50);
|
|
138
138
|
}
|
|
139
|
+
async function waitForLockerJournal(locker, pattern, timeoutMs = 2000) {
|
|
140
|
+
if (!locker)
|
|
141
|
+
return;
|
|
142
|
+
const journalPath = `${locker.stateDir}/journal.jsonl`;
|
|
143
|
+
const deadline = Date.now() + timeoutMs;
|
|
144
|
+
while (Date.now() < deadline) {
|
|
145
|
+
try {
|
|
146
|
+
if (pattern.test(await readFile(journalPath, "utf8")))
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
// Journal may not exist yet.
|
|
151
|
+
}
|
|
152
|
+
await sleep(25);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
139
155
|
function scriptPath(name) {
|
|
140
156
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
141
157
|
const root = here.endsWith(`${join("dist", "lib")}`)
|
|
@@ -261,6 +277,7 @@ async function synthesize(config, locker) {
|
|
|
261
277
|
type: "lock.complete",
|
|
262
278
|
body: { id: "coordinator-artifact", artifact: config.artifactPath },
|
|
263
279
|
});
|
|
280
|
+
await waitForLockerJournal(locker, /lock\.complete/);
|
|
264
281
|
await writeLockerMessage(locker, {
|
|
265
282
|
type: "lock.release",
|
|
266
283
|
body: { resource: config.artifactPath },
|
|
@@ -1,25 +1,33 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Build the JavaScript-only distributive tree
|
|
4
|
+
* Build the JavaScript-only distributive tree.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* This is intentionally standalone: it is package/build glue, not reusable
|
|
7
|
+
* actor-domain behavior. It cleans dist, compiles TypeScript, mirrors runtime
|
|
8
|
+
* assets, and syntax-checks packaged script shims.
|
|
8
9
|
*/
|
|
9
10
|
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
11
|
+
import { spawnSync } from "node:child_process";
|
|
12
|
+
import { cpSync, mkdirSync, readdirSync, rmSync } from "node:fs";
|
|
13
|
+
import { join } from "node:path";
|
|
13
14
|
|
|
14
|
-
function
|
|
15
|
-
|
|
15
|
+
function run(command, args) {
|
|
16
|
+
const result = spawnSync(command, args, { stdio: "inherit" });
|
|
17
|
+
if (result.status !== 0) process.exit(result.status ?? 1);
|
|
16
18
|
}
|
|
17
19
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
rmSync("dist", { recursive: true, force: true });
|
|
21
|
+
mkdirSync("dist", { recursive: true });
|
|
22
|
+
|
|
23
|
+
run("tsc", ["-p", "tsconfig.build.json"]);
|
|
24
|
+
|
|
25
|
+
for (const dir of ["scripts", "recipes", "fixtures", "skills"]) {
|
|
26
|
+
cpSync(dir, join("dist", dir), { recursive: true });
|
|
22
27
|
}
|
|
23
28
|
|
|
24
|
-
const
|
|
25
|
-
|
|
29
|
+
const builtScripts = readdirSync(join("dist", "scripts"))
|
|
30
|
+
.filter((name) => name.endsWith(".mjs"))
|
|
31
|
+
.map((name) => join("dist", "scripts", name));
|
|
32
|
+
|
|
33
|
+
run(process.execPath, ["--check", ...builtScripts]);
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: actors
|
|
3
3
|
description: Highest-density practical guide for pi-actors. Read this skill whenever prompt and tools are not enough for spawn, message, inspect, actor runs, tools, recipes, command templates, async lifecycle, mailboxes, artifacts, and local orchestration mechanics.
|
|
4
4
|
metadata:
|
|
5
|
-
version: 0.24.
|
|
5
|
+
version: 0.24.2
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Actors (pi-actors)
|
|
@@ -65,7 +65,7 @@ Rules:
|
|
|
65
65
|
|
|
66
66
|
- Use `file`/`recipe` for saved recipes; bare names resolve under `~/.pi/agent/recipes`.
|
|
67
67
|
- Use inline `template` for one-off experiments; promote useful repeats to recipes.
|
|
68
|
-
- When a successful actor follow-up suggests persistence,
|
|
68
|
+
- When a successful actor follow-up suggests persistence, decide whether the pattern deserves durable tool memory; call `register_tool` yourself only when the evidence is strong, and ask before writing the user recipe root.
|
|
69
69
|
- Use stable `as` names when you will inspect or message the actor later.
|
|
70
70
|
- `async: true` on the recipe is the detached run switch.
|
|
71
71
|
|
|
@@ -238,6 +238,8 @@ Muscle-memory lens: `~/.pi/agent/recipes/*.json` and `*.md` are the agent's capa
|
|
|
238
238
|
|
|
239
239
|
Usage lens: user recipes may carry extension-maintained launch metadata such as `usage.calls` and `usage.last_called`. The extension increments the counter when it starts that concrete recipe; agents should not hand-edit counters as part of normal recipe maintenance. Treat usage as evidence for usefulness analysis: heavily used recipes are good candidates for promotion, documentation, or stronger tests; unused recipes are cleanup candidates. Do not use failure counts as a primary usefulness signal because failures may reflect bad caller judgment rather than bad recipes. Do not delete or demote solely from counters without operator approval.
|
|
240
240
|
|
|
241
|
+
Promotion lens: successful transient/ad hoc actor runs are evidence, not commands. If the run was repeatable, parameterized, safe enough, and likely useful later, the agent may promote it by calling `register_tool` with a concise name, typed args/defaults, and a reviewed template or recipe path. Do not wait for UI buttons; do not auto-register every success; do not persist temp paths, secrets, one-off prompts, or project-private assumptions without normalization and approval.
|
|
242
|
+
|
|
241
243
|
Cleanup rule: periodically inspect `~/.pi/agent/recipes` as the live muscle-memory set. For each stale, duplicate, too-specific, or low-value recipe, choose one explicit action: keep as a tool, move it out of the agent recipe root to retain recipe-only memory, merge into a better recipe, or delete/archive the file. Prefer moving over deletion when the recipe may still be useful as a component. Never silently remove tools during unrelated work.
|
|
242
244
|
|
|
243
245
|
## Registered Tools
|
package/index.ts
CHANGED
|
@@ -129,16 +129,17 @@ export default function toolRegistryExtension(pi: ExtensionAPI) {
|
|
|
129
129
|
style,
|
|
130
130
|
)) ?? [];
|
|
131
131
|
const run = previews[0]?.run;
|
|
132
|
-
const roster =
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
132
|
+
const roster =
|
|
133
|
+
selectedInspectorSequence === undefined && run
|
|
134
|
+
? ActorInspectorTui.renderInspectorRosterPanel(
|
|
135
|
+
ActorInspectorTui.readActorInspectorRoster(
|
|
136
|
+
RUN_STATE_ROOT,
|
|
137
|
+
run,
|
|
138
|
+
),
|
|
139
|
+
width,
|
|
140
|
+
style,
|
|
141
|
+
)
|
|
142
|
+
: undefined;
|
|
142
143
|
return roster ? [...roster, ...rows] : rows;
|
|
143
144
|
},
|
|
144
145
|
};
|
|
@@ -478,6 +478,11 @@ function propertyValue(value: unknown): string {
|
|
|
478
478
|
return JSON.stringify(value);
|
|
479
479
|
}
|
|
480
480
|
|
|
481
|
+
function itemViewKeyLabel(key: string): string {
|
|
482
|
+
if (key === "body_preview") return "body";
|
|
483
|
+
return key;
|
|
484
|
+
}
|
|
485
|
+
|
|
481
486
|
function displayWidth(value: string): number {
|
|
482
487
|
return visibleWidth(value);
|
|
483
488
|
}
|
|
@@ -727,6 +732,9 @@ export function renderInspectorItemView(
|
|
|
727
732
|
const preview = previews.find((item) => item.sequence === options.sequence);
|
|
728
733
|
if (!preview) return undefined;
|
|
729
734
|
const safeWidth = Math.max(1, width);
|
|
735
|
+
const prefix = " ";
|
|
736
|
+
const suffix = " ";
|
|
737
|
+
const contentWidth = Math.max(8, safeWidth - prefix.length - suffix.length);
|
|
730
738
|
const orderedKeys = [
|
|
731
739
|
"channel",
|
|
732
740
|
"run",
|
|
@@ -754,21 +762,22 @@ export function renderInspectorItemView(
|
|
|
754
762
|
const route = routeText(preview);
|
|
755
763
|
const visibleRoute = boundedLine(
|
|
756
764
|
route,
|
|
757
|
-
Math.max(0,
|
|
765
|
+
Math.max(0, contentWidth - keyWidth - headerSeparator.length),
|
|
758
766
|
);
|
|
759
767
|
const headerPlain = `${sequenceText}${sequencePadding}${headerSeparator}${visibleRoute}`;
|
|
760
768
|
const header = `${style(styles.muted, sequenceText)}${sequencePadding}${headerSeparator}${style(styles.target, visibleRoute)}`;
|
|
761
|
-
const headerPadding = Math.max(0,
|
|
762
|
-
const lines = [`${header}${" ".repeat(headerPadding)}`, ""];
|
|
769
|
+
const headerPadding = Math.max(0, contentWidth - visibleWidth(headerPlain));
|
|
770
|
+
const lines = [`${prefix}${header}${" ".repeat(headerPadding)}${suffix}`, ""];
|
|
763
771
|
for (const [key, value] of entries) {
|
|
764
|
-
const
|
|
772
|
+
const label = itemViewKeyLabel(key);
|
|
773
|
+
const keyPadding = " ".repeat(Math.max(0, keyWidth - displayWidth(label)));
|
|
765
774
|
const separator = " ";
|
|
766
|
-
const valueWidth = Math.max(0,
|
|
775
|
+
const valueWidth = Math.max(0, contentWidth - keyWidth - separator.length);
|
|
767
776
|
const visibleValue = boundedLine(value, valueWidth);
|
|
768
|
-
const plain = `${
|
|
769
|
-
const rendered = `${style(styles.muted,
|
|
770
|
-
const padding = Math.max(0,
|
|
771
|
-
lines.push(`${rendered}${" ".repeat(padding)}`);
|
|
777
|
+
const plain = `${label}${keyPadding}${separator}${visibleValue}`;
|
|
778
|
+
const rendered = `${style(styles.muted, label)}${keyPadding}${separator}${style(styles.preview, visibleValue)}`;
|
|
779
|
+
const padding = Math.max(0, contentWidth - visibleWidth(plain));
|
|
780
|
+
lines.push(`${prefix}${rendered}${" ".repeat(padding)}${suffix}`);
|
|
772
781
|
}
|
|
773
782
|
return lines;
|
|
774
783
|
}
|
package/lib/coordinator.ts
CHANGED
|
@@ -155,6 +155,20 @@ async function writeLockerMessage(locker, message) {
|
|
|
155
155
|
await sleep(50);
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
+
async function waitForLockerJournal(locker, pattern, timeoutMs = 2000) {
|
|
159
|
+
if (!locker) return;
|
|
160
|
+
const journalPath = `${locker.stateDir}/journal.jsonl`;
|
|
161
|
+
const deadline = Date.now() + timeoutMs;
|
|
162
|
+
while (Date.now() < deadline) {
|
|
163
|
+
try {
|
|
164
|
+
if (pattern.test(await readFile(journalPath, "utf8"))) return;
|
|
165
|
+
} catch {
|
|
166
|
+
// Journal may not exist yet.
|
|
167
|
+
}
|
|
168
|
+
await sleep(25);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
158
172
|
function scriptPath(name) {
|
|
159
173
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
160
174
|
const root = here.endsWith(`${join("dist", "lib")}`)
|
|
@@ -293,6 +307,7 @@ async function synthesize(config, locker) {
|
|
|
293
307
|
type: "lock.complete",
|
|
294
308
|
body: { id: "coordinator-artifact", artifact: config.artifactPath },
|
|
295
309
|
});
|
|
310
|
+
await waitForLockerJournal(locker, /lock\.complete/);
|
|
296
311
|
await writeLockerMessage(locker, {
|
|
297
312
|
type: "lock.release",
|
|
298
313
|
body: { resource: config.artifactPath },
|
package/package.json
CHANGED
package/scripts/build-dist.mjs
CHANGED
|
@@ -1,25 +1,33 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Build the JavaScript-only distributive tree
|
|
4
|
+
* Build the JavaScript-only distributive tree.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* This is intentionally standalone: it is package/build glue, not reusable
|
|
7
|
+
* actor-domain behavior. It cleans dist, compiles TypeScript, mirrors runtime
|
|
8
|
+
* assets, and syntax-checks packaged script shims.
|
|
8
9
|
*/
|
|
9
10
|
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
11
|
+
import { spawnSync } from "node:child_process";
|
|
12
|
+
import { cpSync, mkdirSync, readdirSync, rmSync } from "node:fs";
|
|
13
|
+
import { join } from "node:path";
|
|
13
14
|
|
|
14
|
-
function
|
|
15
|
-
|
|
15
|
+
function run(command, args) {
|
|
16
|
+
const result = spawnSync(command, args, { stdio: "inherit" });
|
|
17
|
+
if (result.status !== 0) process.exit(result.status ?? 1);
|
|
16
18
|
}
|
|
17
19
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
rmSync("dist", { recursive: true, force: true });
|
|
21
|
+
mkdirSync("dist", { recursive: true });
|
|
22
|
+
|
|
23
|
+
run("tsc", ["-p", "tsconfig.build.json"]);
|
|
24
|
+
|
|
25
|
+
for (const dir of ["scripts", "recipes", "fixtures", "skills"]) {
|
|
26
|
+
cpSync(dir, join("dist", dir), { recursive: true });
|
|
22
27
|
}
|
|
23
28
|
|
|
24
|
-
const
|
|
25
|
-
|
|
29
|
+
const builtScripts = readdirSync(join("dist", "scripts"))
|
|
30
|
+
.filter((name) => name.endsWith(".mjs"))
|
|
31
|
+
.map((name) => join("dist", "scripts", name));
|
|
32
|
+
|
|
33
|
+
run(process.execPath, ["--check", ...builtScripts]);
|
package/skills/actors/SKILL.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: actors
|
|
3
3
|
description: Highest-density practical guide for pi-actors. Read this skill whenever prompt and tools are not enough for spawn, message, inspect, actor runs, tools, recipes, command templates, async lifecycle, mailboxes, artifacts, and local orchestration mechanics.
|
|
4
4
|
metadata:
|
|
5
|
-
version: 0.24.
|
|
5
|
+
version: 0.24.2
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Actors (pi-actors)
|
|
@@ -65,7 +65,7 @@ Rules:
|
|
|
65
65
|
|
|
66
66
|
- Use `file`/`recipe` for saved recipes; bare names resolve under `~/.pi/agent/recipes`.
|
|
67
67
|
- Use inline `template` for one-off experiments; promote useful repeats to recipes.
|
|
68
|
-
- When a successful actor follow-up suggests persistence,
|
|
68
|
+
- When a successful actor follow-up suggests persistence, decide whether the pattern deserves durable tool memory; call `register_tool` yourself only when the evidence is strong, and ask before writing the user recipe root.
|
|
69
69
|
- Use stable `as` names when you will inspect or message the actor later.
|
|
70
70
|
- `async: true` on the recipe is the detached run switch.
|
|
71
71
|
|
|
@@ -238,6 +238,8 @@ Muscle-memory lens: `~/.pi/agent/recipes/*.json` and `*.md` are the agent's capa
|
|
|
238
238
|
|
|
239
239
|
Usage lens: user recipes may carry extension-maintained launch metadata such as `usage.calls` and `usage.last_called`. The extension increments the counter when it starts that concrete recipe; agents should not hand-edit counters as part of normal recipe maintenance. Treat usage as evidence for usefulness analysis: heavily used recipes are good candidates for promotion, documentation, or stronger tests; unused recipes are cleanup candidates. Do not use failure counts as a primary usefulness signal because failures may reflect bad caller judgment rather than bad recipes. Do not delete or demote solely from counters without operator approval.
|
|
240
240
|
|
|
241
|
+
Promotion lens: successful transient/ad hoc actor runs are evidence, not commands. If the run was repeatable, parameterized, safe enough, and likely useful later, the agent may promote it by calling `register_tool` with a concise name, typed args/defaults, and a reviewed template or recipe path. Do not wait for UI buttons; do not auto-register every success; do not persist temp paths, secrets, one-off prompts, or project-private assumptions without normalization and approval.
|
|
242
|
+
|
|
241
243
|
Cleanup rule: periodically inspect `~/.pi/agent/recipes` as the live muscle-memory set. For each stale, duplicate, too-specific, or low-value recipe, choose one explicit action: keep as a tool, move it out of the agent recipe root to retain recipe-only memory, merge into a better recipe, or delete/archive the file. Prefer moving over deletion when the recipe may still be useful as a component. Never silently remove tools during unrelated work.
|
|
242
244
|
|
|
243
245
|
## Registered Tools
|
package/skills/swarm/SKILL.md
CHANGED
package/dist/lib/build-dist.d.ts
DELETED
package/dist/lib/build-dist.js
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Dist build pipeline entrypoint logic.
|
|
3
|
-
* Zones: packaging, JavaScript-only distributive tree
|
|
4
|
-
*/
|
|
5
|
-
import { spawnSync } from "node:child_process";
|
|
6
|
-
import { cpSync, mkdirSync, readdirSync, rmSync } from "node:fs";
|
|
7
|
-
import { join } from "node:path";
|
|
8
|
-
function run(command, args) {
|
|
9
|
-
const result = spawnSync(command, args, { stdio: "inherit" });
|
|
10
|
-
if (result.status !== 0)
|
|
11
|
-
process.exit(result.status ?? 1);
|
|
12
|
-
}
|
|
13
|
-
export function buildDist() {
|
|
14
|
-
rmSync("dist", { recursive: true, force: true });
|
|
15
|
-
mkdirSync("dist", { recursive: true });
|
|
16
|
-
run("tsc", ["-p", "tsconfig.build.json"]);
|
|
17
|
-
for (const dir of ["scripts", "recipes", "fixtures", "skills"]) {
|
|
18
|
-
cpSync(dir, join("dist", dir), { recursive: true });
|
|
19
|
-
}
|
|
20
|
-
const builtScripts = readdirSync(join("dist", "scripts"))
|
|
21
|
-
.filter((name) => name.endsWith(".mjs"))
|
|
22
|
-
.map((name) => join("dist", "scripts", name));
|
|
23
|
-
run(process.execPath, ["--check", ...builtScripts]);
|
|
24
|
-
}
|
package/lib/build-dist.ts
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Dist build pipeline entrypoint logic.
|
|
3
|
-
* Zones: packaging, JavaScript-only distributive tree
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { spawnSync } from "node:child_process";
|
|
7
|
-
import { cpSync, mkdirSync, readdirSync, rmSync } from "node:fs";
|
|
8
|
-
import { join } from "node:path";
|
|
9
|
-
|
|
10
|
-
function run(command: string, args: string[]): void {
|
|
11
|
-
const result = spawnSync(command, args, { stdio: "inherit" });
|
|
12
|
-
if (result.status !== 0) process.exit(result.status ?? 1);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export function buildDist(): void {
|
|
16
|
-
rmSync("dist", { recursive: true, force: true });
|
|
17
|
-
mkdirSync("dist", { recursive: true });
|
|
18
|
-
|
|
19
|
-
run("tsc", ["-p", "tsconfig.build.json"]);
|
|
20
|
-
|
|
21
|
-
for (const dir of ["scripts", "recipes", "fixtures", "skills"]) {
|
|
22
|
-
cpSync(dir, join("dist", dir), { recursive: true });
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const builtScripts = readdirSync(join("dist", "scripts"))
|
|
26
|
-
.filter((name) => name.endsWith(".mjs"))
|
|
27
|
-
.map((name) => join("dist", "scripts", name));
|
|
28
|
-
|
|
29
|
-
run(process.execPath, ["--check", ...builtScripts]);
|
|
30
|
-
}
|