@llblab/pi-actors 0.26.3 → 0.27.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 +24 -1
- package/CHANGELOG.md +4 -0
- package/dist/lib/tools.js +83 -14
- package/dist/skills/actors/SKILL.md +3 -2
- package/dist/skills/swarm/SKILL.md +1 -1
- package/docs/tool-registry.md +5 -2
- package/lib/tools.ts +105 -20
- package/package.json +1 -1
- package/skills/actors/SKILL.md +3 -2
- package/skills/swarm/SKILL.md +1 -1
package/BACKLOG.md
CHANGED
|
@@ -225,12 +225,35 @@ The backlog is intentionally pruned to the 20% of work most likely to deliver 80
|
|
|
225
225
|
- Packaged recipes declare `stop`/`cancel` only when the actor-specific behavior is meaningful.
|
|
226
226
|
- Tests assert that generic mailbox-loop termination is not triggered by `control.stop` or `control.cancel`.
|
|
227
227
|
|
|
228
|
+
### M-12 Runtime and Session Observability UX
|
|
229
|
+
|
|
230
|
+
- Priority: High.
|
|
231
|
+
- Status: Planned.
|
|
232
|
+
- Goal: Make reload/session/runtime mismatches visible without changing ownership or lifecycle policy.
|
|
233
|
+
- Why now: 0.26 dogfood showed that actors, mailbox workers, and hotfixes work after a full Pi restart, but ordinary reloads can leave operators unsure which extension code is live. Session ownership mismatches also surface as terse strings instead of structured diagnostics or navigation hints.
|
|
234
|
+
- Direction:
|
|
235
|
+
- Add an intentional runtime/version inspection surface that reports loaded package version, entrypoint path, source/dist mode, package root, recipe roots, and git commit when available.
|
|
236
|
+
- Make session ownership denials structured in tool details with compact `reason=session_mismatch owner_session=... current_session=...` text and hints to inspect the owning session.
|
|
237
|
+
- Make coordinator/session status show when other sessions or other-session runs exist so `runs=0` is not misleading after reload or resume.
|
|
238
|
+
- Document reload vs full restart verification in the actors/swarm guidance.
|
|
239
|
+
- Opportunistically improve invalid shadowing recipe launch hints only if it stays diagnostic-only.
|
|
240
|
+
- Non-goals:
|
|
241
|
+
- No cross-session force kill.
|
|
242
|
+
- No session attach/adopt/reparent policy.
|
|
243
|
+
- No relaxation of current ownership gates.
|
|
244
|
+
- Acceptance:
|
|
245
|
+
- Operators can verify the loaded pi-actors version/path from an inspect/tool surface after reload.
|
|
246
|
+
- Session mismatch responses carry structured details and a compact human hint.
|
|
247
|
+
- Coordinator/session status exposes other-session counts without leaking unrelated run details by default.
|
|
248
|
+
- Tests cover version inspection, session mismatch shape, and other-session count summaries.
|
|
249
|
+
|
|
228
250
|
## Explicitly Deferred
|
|
229
251
|
|
|
230
252
|
These are valid ideas but not current focus. Reintroduce only with concrete evidence from real actor workflows.
|
|
231
253
|
|
|
232
254
|
- Spawn preflight mode: useful later, but lower value than resilient inspect and mailbox-loop consolidation.
|
|
233
255
|
- Run restart/reattach policy: risky for isolation; defer until corruption recovery and protocol fixtures are stronger.
|
|
256
|
+
- Cross-session force kill or attach/adopt/reparent: useful later, but ownership policy should not change until observability makes current boundaries clear.
|
|
234
257
|
- Actor address helper CLI: keep diagnostics improving opportunistically inside existing parser/tests.
|
|
235
258
|
- Documentation refactor: defer until the canonical mailbox loop and worker recipe exist; avoid rewriting docs twice.
|
|
236
259
|
- Host-level tool unregistration: blocked on host API support.
|
|
@@ -240,5 +263,5 @@ These are valid ideas but not current focus. Reintroduce only with concrete evid
|
|
|
240
263
|
## Suggested Milestone Order
|
|
241
264
|
|
|
242
265
|
```text
|
|
243
|
-
Next milestone:
|
|
266
|
+
Next milestone: M-12 Runtime and Session Observability UX.
|
|
244
267
|
```
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
- `[Inspect]` Added `inspect target=tool:pi-actors view=status` as a runtime verification surface with loaded version, package root, source/dist mode, entrypoint path, recipe roots, and git commit when available.
|
|
6
|
+
- `[Sessions]` Started structured session mismatch diagnostics with `reason=session_mismatch`, owner/current session fields, and compact inspect-session hints while preserving current ownership gates.
|
|
7
|
+
- `[Sessions]` Added other-session run counts to coordinator/session status summaries so reload/resume states do not misleadingly report only `runs=0` without nearby context.
|
|
8
|
+
|
|
5
9
|
## 0.26.3: Branch Message Delivery UX Hotfix
|
|
6
10
|
|
|
7
11
|
- `[Actor Messages]` Treat branch mailbox persistence as a successful queued outcome even when the parent run control endpoint is unavailable, returning compact `queued=true` delivery diagnostics instead of throwing an unframed tool error.
|
package/dist/lib/tools.js
CHANGED
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
* Zones: pi tools, registry tools, async run launchers
|
|
4
4
|
* Owns generated runtime tool schemas and the register_tool management tool schema
|
|
5
5
|
*/
|
|
6
|
+
import { execFileSync } from "node:child_process";
|
|
7
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
8
|
+
import { dirname, join } from "node:path";
|
|
6
9
|
import * as ActorMessages from "./actor-messages.js";
|
|
7
10
|
import * as ActorRooms from "./actor-rooms.js";
|
|
8
11
|
import * as AsyncRuns from "./async-runs.js";
|
|
@@ -345,10 +348,21 @@ function compactActorFiles(status) {
|
|
|
345
348
|
: "";
|
|
346
349
|
return `\nrun=${run}${artifactText}${files.length ? ` files=${files.join(",")}` : ""}`;
|
|
347
350
|
}
|
|
348
|
-
function
|
|
351
|
+
function summarizeOtherSessions(currentSession, allRuns) {
|
|
352
|
+
const otherRuns = allRuns.filter((run) => run.ownerId && run.ownerId !== currentSession);
|
|
353
|
+
return {
|
|
354
|
+
other_runs: otherRuns.length,
|
|
355
|
+
other_sessions: new Set(otherRuns.map((run) => run.ownerId)).size,
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
function compactSessionRuns(session, runs, summary = {}) {
|
|
359
|
+
const suffix = [
|
|
360
|
+
summary.other_sessions !== undefined ? `other_sessions=${String(summary.other_sessions)}` : "",
|
|
361
|
+
summary.other_runs !== undefined ? `other_runs=${String(summary.other_runs)}` : "",
|
|
362
|
+
].filter(Boolean).join(" ");
|
|
349
363
|
if (runs.length === 0)
|
|
350
|
-
return `\nsession=${session} runs=0`;
|
|
351
|
-
return `\nsession=${session} runs=${runs.length}\n${runs
|
|
364
|
+
return `\nsession=${session} runs=0${suffix ? ` ${suffix}` : ""}`;
|
|
365
|
+
return `\nsession=${session} runs=${runs.length}${suffix ? ` ${suffix}` : ""}\n${runs
|
|
352
366
|
.map((run) => {
|
|
353
367
|
const tokens = [
|
|
354
368
|
`run=${String(run.run ?? "")}`,
|
|
@@ -362,6 +376,38 @@ function compactSessionRuns(session, runs) {
|
|
|
362
376
|
})
|
|
363
377
|
.join("\n")}`;
|
|
364
378
|
}
|
|
379
|
+
function getPiActorsRuntimeStatus() {
|
|
380
|
+
const packagedRecipeRoot = Paths.getPackagedRecipeRoot();
|
|
381
|
+
const packageRoot = dirname(packagedRecipeRoot);
|
|
382
|
+
const packageJsonPath = join(packageRoot, "package.json");
|
|
383
|
+
const packageJson = existsSync(packageJsonPath)
|
|
384
|
+
? JSON.parse(readFileSync(packageJsonPath, "utf8"))
|
|
385
|
+
: {};
|
|
386
|
+
let git_commit;
|
|
387
|
+
try {
|
|
388
|
+
git_commit = execFileSync("git", ["-C", packageRoot, "rev-parse", "--short", "HEAD"], {
|
|
389
|
+
encoding: "utf8",
|
|
390
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
391
|
+
}).trim();
|
|
392
|
+
}
|
|
393
|
+
catch {
|
|
394
|
+
git_commit = undefined;
|
|
395
|
+
}
|
|
396
|
+
const entrypoint = new URL(import.meta.url).pathname;
|
|
397
|
+
return {
|
|
398
|
+
entrypoint,
|
|
399
|
+
git_commit,
|
|
400
|
+
mode: entrypoint.includes("/dist/") ? "dist" : "source",
|
|
401
|
+
package_name: packageJson.name ?? "@llblab/pi-actors",
|
|
402
|
+
package_root: packageRoot,
|
|
403
|
+
recipe_root: Paths.getRecipeRoot(),
|
|
404
|
+
packaged_recipe_root: packagedRecipeRoot,
|
|
405
|
+
version: packageJson.version ?? "unknown",
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
function compactPiActorsRuntimeStatus(status) {
|
|
409
|
+
return `\npi-actors version=${String(status.version)} mode=${String(status.mode)} path=${String(status.package_root)} entrypoint=${String(status.entrypoint)}${status.git_commit ? ` git=${String(status.git_commit)}` : ""}`;
|
|
410
|
+
}
|
|
365
411
|
function compactToolActor(name, tool) {
|
|
366
412
|
const parameters = asRecord(tool.parameters);
|
|
367
413
|
const required = Array.isArray(parameters.required)
|
|
@@ -660,7 +706,13 @@ function assertRunAccessibleToContext(runId, ctx) {
|
|
|
660
706
|
const status = AsyncRuns.getRunStatus(runId);
|
|
661
707
|
const sessionId = getContextSessionId(ctx);
|
|
662
708
|
if (sessionId && status.ownerId && status.ownerId !== sessionId) {
|
|
663
|
-
throw new Error(`run:${runId}
|
|
709
|
+
throw Object.assign(new Error(`run:${runId} reason=session_mismatch owner_session=${status.ownerId} current_session=${sessionId} hint=inspect_session:${status.ownerId}`), {
|
|
710
|
+
current_session: sessionId,
|
|
711
|
+
hint: `inspect target=session:${status.ownerId} view=status`,
|
|
712
|
+
owner_session: status.ownerId,
|
|
713
|
+
reason: "session_mismatch",
|
|
714
|
+
run: runId,
|
|
715
|
+
});
|
|
664
716
|
}
|
|
665
717
|
return status;
|
|
666
718
|
}
|
|
@@ -716,37 +768,54 @@ export function createInspectToolDefinition(deps = {}) {
|
|
|
716
768
|
throw new Error("inspect coordinator supports view=status or view=runs.");
|
|
717
769
|
}
|
|
718
770
|
const session = requireContextSessionId(ctx, "inspect coordinator");
|
|
719
|
-
const
|
|
720
|
-
|
|
721
|
-
|
|
771
|
+
const allRuns = AsyncRuns.listRuns(undefined, typeof input.status === "string" ? input.status : undefined).map((run) => AsyncRuns.getRunStatus(String(run.state_dir)));
|
|
772
|
+
const runs = allRuns.filter((run) => run.ownerId === session);
|
|
773
|
+
const sessionSummary = summarizeOtherSessions(session, allRuns);
|
|
722
774
|
return {
|
|
723
775
|
content: [
|
|
724
776
|
{
|
|
725
777
|
type: "text",
|
|
726
|
-
text: maybeJsonText({ session, runs }, input.verbose === true, compactSessionRuns(session, runs)),
|
|
778
|
+
text: maybeJsonText({ session, runs, ...sessionSummary }, input.verbose === true, compactSessionRuns(session, runs, sessionSummary)),
|
|
727
779
|
},
|
|
728
780
|
],
|
|
729
|
-
details: { session, runs },
|
|
781
|
+
details: { session, runs, ...sessionSummary },
|
|
730
782
|
};
|
|
731
783
|
}
|
|
732
784
|
if (address.kind === "session") {
|
|
733
785
|
if (view !== "status" && view !== "runs") {
|
|
734
786
|
throw new Error("inspect session:<id> supports view=status or view=runs.");
|
|
735
787
|
}
|
|
736
|
-
const
|
|
737
|
-
|
|
738
|
-
|
|
788
|
+
const allRuns = AsyncRuns.listRuns(undefined, typeof input.status === "string" ? input.status : undefined).map((run) => AsyncRuns.getRunStatus(String(run.state_dir)));
|
|
789
|
+
const runs = allRuns.filter((run) => address.value === "all" || run.ownerId === address.value);
|
|
790
|
+
const sessionSummary = address.value === "all"
|
|
791
|
+
? {}
|
|
792
|
+
: summarizeOtherSessions(address.value || "", allRuns);
|
|
739
793
|
return {
|
|
740
794
|
content: [
|
|
741
795
|
{
|
|
742
796
|
type: "text",
|
|
743
|
-
text: maybeJsonText({ session: address.value, runs }, input.verbose === true, compactSessionRuns(address.value || "", runs)),
|
|
797
|
+
text: maybeJsonText({ session: address.value, runs, ...sessionSummary }, input.verbose === true, compactSessionRuns(address.value || "", runs, sessionSummary)),
|
|
744
798
|
},
|
|
745
799
|
],
|
|
746
|
-
details: { session: address.value, runs },
|
|
800
|
+
details: { session: address.value, runs, ...sessionSummary },
|
|
747
801
|
};
|
|
748
802
|
}
|
|
749
803
|
if (address.kind === "tool" && address.value) {
|
|
804
|
+
if (address.value === "pi-actors") {
|
|
805
|
+
if (view !== "status") {
|
|
806
|
+
throw new Error("inspect tool:pi-actors supports view=status.");
|
|
807
|
+
}
|
|
808
|
+
const details = getPiActorsRuntimeStatus();
|
|
809
|
+
return {
|
|
810
|
+
content: [
|
|
811
|
+
{
|
|
812
|
+
type: "text",
|
|
813
|
+
text: maybeJsonText(details, input.verbose === true, compactPiActorsRuntimeStatus(details)),
|
|
814
|
+
},
|
|
815
|
+
],
|
|
816
|
+
details,
|
|
817
|
+
};
|
|
818
|
+
}
|
|
750
819
|
if (view !== "status" && view !== "schema") {
|
|
751
820
|
throw new Error("inspect tool:<name> supports view=status or view=schema.");
|
|
752
821
|
}
|
|
@@ -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.27.0
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Actors (pi-actors)
|
|
@@ -41,7 +41,7 @@ Trusted local capability
|
|
|
41
41
|
- **Recipe**: saved JSON definition wrapping a template with args, defaults, imports, mailbox, artifacts, metadata, and optional `async: true`.
|
|
42
42
|
- **Run actor**: one detached execution instance addressable as `run:<id>` with status, logs, messages, mailbox metadata, files, communication snapshot, and artifacts.
|
|
43
43
|
- **Room actor**: shared timeline + roster endpoint addressable as `room:<run>`; every spawned run gets `room:<run>`.
|
|
44
|
-
- **Tool actor**: registered persistent capability addressable as `tool:<name>` and callable through the generated tool or `message`.
|
|
44
|
+
- **Tool actor**: registered persistent capability addressable as `tool:<name>` and callable through the generated tool or `message`. `tool:pi-actors` is reserved for runtime/status inspection.
|
|
45
45
|
- **Coordinator/session**: the current pi session endpoint that receives bounded actor follow-ups.
|
|
46
46
|
- **Mailbox**: public interaction contract: message types the actor accepts/emits.
|
|
47
47
|
- **Artifact**: named durable output path declared by a recipe/run.
|
|
@@ -102,6 +102,7 @@ Check `inspect view=mailbox` before domain-specific messages.
|
|
|
102
102
|
{ "target": "room:repo-health", "view": "roster" }
|
|
103
103
|
{ "target": "room:repo-health", "view": "contacts" }
|
|
104
104
|
{ "target": "room:repo-health", "view": "previews" }
|
|
105
|
+
{ "target": "tool:pi-actors", "view": "status" }
|
|
105
106
|
{ "target": "tool:music_player", "view": "status" }
|
|
106
107
|
{ "target": "recipes", "view": "status" }
|
|
107
108
|
{ "target": "coordinator", "view": "status" }
|
package/docs/tool-registry.md
CHANGED
|
@@ -19,15 +19,18 @@ Because the user recipe directory is sticky agent muscle memory, runtime launche
|
|
|
19
19
|
|
|
20
20
|
`register_tool` is the preferred agent-facing mutation API. It creates, updates, and deletes recipe files in `~/.pi/agent/recipes`; agents do not need to edit the files directly for normal registration. Direct file edits are still valid for operators and advanced agents. Runtime behavior is reactive: file creation, deletion, or edits in the user recipe root trigger validation and tool-set refresh, with invalid recipes surfaced as diagnostics rather than silently ignored.
|
|
21
21
|
|
|
22
|
-
Inspect the discovered registry with:
|
|
22
|
+
Inspect the loaded pi-actors runtime and discovered registry with:
|
|
23
23
|
|
|
24
24
|
```text
|
|
25
|
+
inspect target=tool:pi-actors view=status
|
|
25
26
|
inspect target=recipes view=status
|
|
26
27
|
inspect target=recipes view=doctor
|
|
27
28
|
inspect target=recipes view=summary verbose=true
|
|
28
29
|
```
|
|
29
30
|
|
|
30
|
-
|
|
31
|
+
`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.
|
|
32
|
+
|
|
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.
|
|
31
34
|
|
|
32
35
|
## Registering Tools
|
|
33
36
|
|
package/lib/tools.ts
CHANGED
|
@@ -4,6 +4,10 @@
|
|
|
4
4
|
* Owns generated runtime tool schemas and the register_tool management tool schema
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import { execFileSync } from "node:child_process";
|
|
8
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
9
|
+
import { dirname, join } from "node:path";
|
|
10
|
+
|
|
7
11
|
import * as ActorMessages from "./actor-messages.ts";
|
|
8
12
|
import * as ActorRooms from "./actor-rooms.ts";
|
|
9
13
|
import * as AsyncRuns from "./async-runs.ts";
|
|
@@ -424,12 +428,30 @@ function compactActorFiles(status: Record<string, unknown>): string {
|
|
|
424
428
|
return `\nrun=${run}${artifactText}${files.length ? ` files=${files.join(",")}` : ""}`;
|
|
425
429
|
}
|
|
426
430
|
|
|
431
|
+
function summarizeOtherSessions(
|
|
432
|
+
currentSession: string,
|
|
433
|
+
allRuns: Array<Record<string, unknown>>,
|
|
434
|
+
): Record<string, unknown> {
|
|
435
|
+
const otherRuns = allRuns.filter(
|
|
436
|
+
(run) => run.ownerId && run.ownerId !== currentSession,
|
|
437
|
+
);
|
|
438
|
+
return {
|
|
439
|
+
other_runs: otherRuns.length,
|
|
440
|
+
other_sessions: new Set(otherRuns.map((run) => run.ownerId)).size,
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
|
|
427
444
|
function compactSessionRuns(
|
|
428
445
|
session: string,
|
|
429
446
|
runs: Array<Record<string, unknown>>,
|
|
447
|
+
summary: Record<string, unknown> = {},
|
|
430
448
|
): string {
|
|
431
|
-
|
|
432
|
-
|
|
449
|
+
const suffix = [
|
|
450
|
+
summary.other_sessions !== undefined ? `other_sessions=${String(summary.other_sessions)}` : "",
|
|
451
|
+
summary.other_runs !== undefined ? `other_runs=${String(summary.other_runs)}` : "",
|
|
452
|
+
].filter(Boolean).join(" ");
|
|
453
|
+
if (runs.length === 0) return `\nsession=${session} runs=0${suffix ? ` ${suffix}` : ""}`;
|
|
454
|
+
return `\nsession=${session} runs=${runs.length}${suffix ? ` ${suffix}` : ""}\n${runs
|
|
433
455
|
.map((run) => {
|
|
434
456
|
const tokens = [
|
|
435
457
|
`run=${String(run.run ?? "")}`,
|
|
@@ -443,6 +465,39 @@ function compactSessionRuns(
|
|
|
443
465
|
.join("\n")}`;
|
|
444
466
|
}
|
|
445
467
|
|
|
468
|
+
function getPiActorsRuntimeStatus(): Record<string, unknown> {
|
|
469
|
+
const packagedRecipeRoot = Paths.getPackagedRecipeRoot();
|
|
470
|
+
const packageRoot = dirname(packagedRecipeRoot);
|
|
471
|
+
const packageJsonPath = join(packageRoot, "package.json");
|
|
472
|
+
const packageJson = existsSync(packageJsonPath)
|
|
473
|
+
? JSON.parse(readFileSync(packageJsonPath, "utf8")) as Record<string, unknown>
|
|
474
|
+
: {};
|
|
475
|
+
let git_commit: string | undefined;
|
|
476
|
+
try {
|
|
477
|
+
git_commit = execFileSync("git", ["-C", packageRoot, "rev-parse", "--short", "HEAD"], {
|
|
478
|
+
encoding: "utf8",
|
|
479
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
480
|
+
}).trim();
|
|
481
|
+
} catch {
|
|
482
|
+
git_commit = undefined;
|
|
483
|
+
}
|
|
484
|
+
const entrypoint = new URL(import.meta.url).pathname;
|
|
485
|
+
return {
|
|
486
|
+
entrypoint,
|
|
487
|
+
git_commit,
|
|
488
|
+
mode: entrypoint.includes("/dist/") ? "dist" : "source",
|
|
489
|
+
package_name: packageJson.name ?? "@llblab/pi-actors",
|
|
490
|
+
package_root: packageRoot,
|
|
491
|
+
recipe_root: Paths.getRecipeRoot(),
|
|
492
|
+
packaged_recipe_root: packagedRecipeRoot,
|
|
493
|
+
version: packageJson.version ?? "unknown",
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
function compactPiActorsRuntimeStatus(status: Record<string, unknown>): string {
|
|
498
|
+
return `\npi-actors version=${String(status.version)} mode=${String(status.mode)} path=${String(status.package_root)} entrypoint=${String(status.entrypoint)}${status.git_commit ? ` git=${String(status.git_commit)}` : ""}`;
|
|
499
|
+
}
|
|
500
|
+
|
|
446
501
|
function compactToolActor(name: string, tool: Record<string, unknown>): string {
|
|
447
502
|
const parameters = asRecord(tool.parameters);
|
|
448
503
|
const required = Array.isArray(parameters.required)
|
|
@@ -871,8 +926,17 @@ function assertRunAccessibleToContext(
|
|
|
871
926
|
const status = AsyncRuns.getRunStatus(runId);
|
|
872
927
|
const sessionId = getContextSessionId(ctx);
|
|
873
928
|
if (sessionId && status.ownerId && status.ownerId !== sessionId) {
|
|
874
|
-
throw
|
|
875
|
-
|
|
929
|
+
throw Object.assign(
|
|
930
|
+
new Error(
|
|
931
|
+
`run:${runId} reason=session_mismatch owner_session=${status.ownerId} current_session=${sessionId} hint=inspect_session:${status.ownerId}`,
|
|
932
|
+
),
|
|
933
|
+
{
|
|
934
|
+
current_session: sessionId,
|
|
935
|
+
hint: `inspect target=session:${status.ownerId} view=status`,
|
|
936
|
+
owner_session: status.ownerId,
|
|
937
|
+
reason: "session_mismatch",
|
|
938
|
+
run: runId,
|
|
939
|
+
},
|
|
876
940
|
);
|
|
877
941
|
}
|
|
878
942
|
return status;
|
|
@@ -961,24 +1025,24 @@ export function createInspectToolDefinition<TContext = unknown>(
|
|
|
961
1025
|
);
|
|
962
1026
|
}
|
|
963
1027
|
const session = requireContextSessionId(ctx, "inspect coordinator");
|
|
964
|
-
const
|
|
1028
|
+
const allRuns = AsyncRuns.listRuns(
|
|
965
1029
|
undefined,
|
|
966
1030
|
typeof input.status === "string" ? input.status : undefined,
|
|
967
|
-
)
|
|
968
|
-
|
|
969
|
-
|
|
1031
|
+
).map((run) => AsyncRuns.getRunStatus(String(run.state_dir)));
|
|
1032
|
+
const runs = allRuns.filter((run) => run.ownerId === session);
|
|
1033
|
+
const sessionSummary = summarizeOtherSessions(session, allRuns);
|
|
970
1034
|
return {
|
|
971
1035
|
content: [
|
|
972
1036
|
{
|
|
973
1037
|
type: "text" as const,
|
|
974
1038
|
text: maybeJsonText(
|
|
975
|
-
{ session, runs },
|
|
1039
|
+
{ session, runs, ...sessionSummary },
|
|
976
1040
|
input.verbose === true,
|
|
977
|
-
compactSessionRuns(session, runs),
|
|
1041
|
+
compactSessionRuns(session, runs, sessionSummary),
|
|
978
1042
|
),
|
|
979
1043
|
},
|
|
980
1044
|
],
|
|
981
|
-
details: { session, runs },
|
|
1045
|
+
details: { session, runs, ...sessionSummary },
|
|
982
1046
|
};
|
|
983
1047
|
}
|
|
984
1048
|
if (address.kind === "session") {
|
|
@@ -987,29 +1051,50 @@ export function createInspectToolDefinition<TContext = unknown>(
|
|
|
987
1051
|
"inspect session:<id> supports view=status or view=runs.",
|
|
988
1052
|
);
|
|
989
1053
|
}
|
|
990
|
-
const
|
|
1054
|
+
const allRuns = AsyncRuns.listRuns(
|
|
991
1055
|
undefined,
|
|
992
1056
|
typeof input.status === "string" ? input.status : undefined,
|
|
993
|
-
)
|
|
994
|
-
|
|
995
|
-
.
|
|
996
|
-
|
|
997
|
-
|
|
1057
|
+
).map((run) => AsyncRuns.getRunStatus(String(run.state_dir)));
|
|
1058
|
+
const runs = allRuns.filter(
|
|
1059
|
+
(run) => address.value === "all" || run.ownerId === address.value,
|
|
1060
|
+
);
|
|
1061
|
+
const sessionSummary = address.value === "all"
|
|
1062
|
+
? {}
|
|
1063
|
+
: summarizeOtherSessions(address.value || "", allRuns);
|
|
998
1064
|
return {
|
|
999
1065
|
content: [
|
|
1000
1066
|
{
|
|
1001
1067
|
type: "text" as const,
|
|
1002
1068
|
text: maybeJsonText(
|
|
1003
|
-
{ session: address.value, runs },
|
|
1069
|
+
{ session: address.value, runs, ...sessionSummary },
|
|
1004
1070
|
input.verbose === true,
|
|
1005
|
-
compactSessionRuns(address.value || "", runs),
|
|
1071
|
+
compactSessionRuns(address.value || "", runs, sessionSummary),
|
|
1006
1072
|
),
|
|
1007
1073
|
},
|
|
1008
1074
|
],
|
|
1009
|
-
details: { session: address.value, runs },
|
|
1075
|
+
details: { session: address.value, runs, ...sessionSummary },
|
|
1010
1076
|
};
|
|
1011
1077
|
}
|
|
1012
1078
|
if (address.kind === "tool" && address.value) {
|
|
1079
|
+
if (address.value === "pi-actors") {
|
|
1080
|
+
if (view !== "status") {
|
|
1081
|
+
throw new Error("inspect tool:pi-actors supports view=status.");
|
|
1082
|
+
}
|
|
1083
|
+
const details = getPiActorsRuntimeStatus();
|
|
1084
|
+
return {
|
|
1085
|
+
content: [
|
|
1086
|
+
{
|
|
1087
|
+
type: "text" as const,
|
|
1088
|
+
text: maybeJsonText(
|
|
1089
|
+
details,
|
|
1090
|
+
input.verbose === true,
|
|
1091
|
+
compactPiActorsRuntimeStatus(details),
|
|
1092
|
+
),
|
|
1093
|
+
},
|
|
1094
|
+
],
|
|
1095
|
+
details,
|
|
1096
|
+
};
|
|
1097
|
+
}
|
|
1013
1098
|
if (view !== "status" && view !== "schema") {
|
|
1014
1099
|
throw new Error(
|
|
1015
1100
|
"inspect tool:<name> supports view=status or view=schema.",
|
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.27.0
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Actors (pi-actors)
|
|
@@ -41,7 +41,7 @@ Trusted local capability
|
|
|
41
41
|
- **Recipe**: saved JSON definition wrapping a template with args, defaults, imports, mailbox, artifacts, metadata, and optional `async: true`.
|
|
42
42
|
- **Run actor**: one detached execution instance addressable as `run:<id>` with status, logs, messages, mailbox metadata, files, communication snapshot, and artifacts.
|
|
43
43
|
- **Room actor**: shared timeline + roster endpoint addressable as `room:<run>`; every spawned run gets `room:<run>`.
|
|
44
|
-
- **Tool actor**: registered persistent capability addressable as `tool:<name>` and callable through the generated tool or `message`.
|
|
44
|
+
- **Tool actor**: registered persistent capability addressable as `tool:<name>` and callable through the generated tool or `message`. `tool:pi-actors` is reserved for runtime/status inspection.
|
|
45
45
|
- **Coordinator/session**: the current pi session endpoint that receives bounded actor follow-ups.
|
|
46
46
|
- **Mailbox**: public interaction contract: message types the actor accepts/emits.
|
|
47
47
|
- **Artifact**: named durable output path declared by a recipe/run.
|
|
@@ -102,6 +102,7 @@ Check `inspect view=mailbox` before domain-specific messages.
|
|
|
102
102
|
{ "target": "room:repo-health", "view": "roster" }
|
|
103
103
|
{ "target": "room:repo-health", "view": "contacts" }
|
|
104
104
|
{ "target": "room:repo-health", "view": "previews" }
|
|
105
|
+
{ "target": "tool:pi-actors", "view": "status" }
|
|
105
106
|
{ "target": "tool:music_player", "view": "status" }
|
|
106
107
|
{ "target": "recipes", "view": "status" }
|
|
107
108
|
{ "target": "coordinator", "view": "status" }
|
package/skills/swarm/SKILL.md
CHANGED