@llblab/pi-actors 0.33.0 → 0.34.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. package/AGENTS.md +22 -11
  2. package/BACKLOG.md +3 -34
  3. package/CHANGELOG.md +14 -0
  4. package/dist/index.js +10 -10
  5. package/dist/lib/async-runs.d.ts +23 -75
  6. package/dist/lib/async-runs.js +49 -801
  7. package/dist/lib/command-templates.d.ts +3 -3
  8. package/dist/lib/command-templates.js +3 -3
  9. package/dist/lib/config.d.ts +2 -2
  10. package/dist/lib/config.js +5 -5
  11. package/dist/lib/{output.d.ts → execution-output.d.ts} +3 -3
  12. package/dist/lib/{output.js → execution-output.js} +3 -3
  13. package/dist/lib/execution.js +1 -1
  14. package/dist/lib/{actor-inspector-tui.d.ts → inspector.d.ts} +3 -2
  15. package/dist/lib/{actor-inspector-tui.js → inspector.js} +3 -2
  16. package/dist/lib/mailbox-loop.d.ts +3 -3
  17. package/dist/lib/mailbox-loop.js +3 -3
  18. package/dist/lib/{actor-messages.d.ts → messages.d.ts} +3 -3
  19. package/dist/lib/{actor-messages.js → messages.js} +3 -3
  20. package/dist/lib/{actor-recipe-context.d.ts → recipes-context.d.ts} +3 -2
  21. package/dist/lib/{actor-recipe-context.js → recipes-context.js} +2 -1
  22. package/dist/lib/{recipe-discovery.d.ts → recipes-discovery.d.ts} +8 -8
  23. package/dist/lib/{recipe-discovery.js → recipes-discovery.js} +6 -6
  24. package/dist/lib/registry.d.ts +1 -1
  25. package/dist/lib/registry.js +22 -22
  26. package/dist/lib/{actor-rooms.d.ts → rooms.d.ts} +4 -4
  27. package/dist/lib/{actor-rooms.js → rooms.js} +3 -3
  28. package/dist/lib/runs-artifacts.d.ts +21 -0
  29. package/dist/lib/runs-artifacts.js +68 -0
  30. package/dist/lib/runs-control.d.ts +13 -0
  31. package/dist/lib/runs-control.js +59 -0
  32. package/dist/lib/runs-identity.d.ts +5 -0
  33. package/dist/lib/runs-identity.js +11 -0
  34. package/dist/lib/runs-index.d.ts +18 -0
  35. package/dist/lib/runs-index.js +119 -0
  36. package/dist/lib/runs-mailbox.d.ts +25 -0
  37. package/dist/lib/runs-mailbox.js +146 -0
  38. package/dist/lib/runs-messages.d.ts +15 -0
  39. package/dist/lib/runs-messages.js +179 -0
  40. package/dist/lib/runs-outbox.d.ts +41 -0
  41. package/dist/lib/runs-outbox.js +87 -0
  42. package/dist/lib/runs-process.d.ts +7 -0
  43. package/dist/lib/runs-process.js +36 -0
  44. package/dist/lib/runs-retention.d.ts +8 -0
  45. package/dist/lib/runs-retention.js +56 -0
  46. package/dist/lib/runs-start.d.ts +9 -0
  47. package/dist/lib/runs-start.js +67 -0
  48. package/dist/lib/runs-status.d.ts +8 -0
  49. package/dist/lib/runs-status.js +60 -0
  50. package/dist/lib/runtime.d.ts +2 -2
  51. package/dist/lib/runtime.js +5 -5
  52. package/dist/lib/schema.d.ts +9 -0
  53. package/dist/lib/schema.js +40 -0
  54. package/dist/lib/tools-access.d.ts +19 -0
  55. package/dist/lib/tools-access.js +44 -0
  56. package/dist/lib/tools-inspect.d.ts +11 -0
  57. package/dist/lib/tools-inspect.js +534 -0
  58. package/dist/lib/tools-local.d.ts +14 -0
  59. package/dist/lib/tools-local.js +156 -0
  60. package/dist/lib/tools-mailbox.d.ts +8 -0
  61. package/dist/lib/tools-mailbox.js +48 -0
  62. package/dist/lib/tools-message.d.ts +9 -0
  63. package/dist/lib/tools-message.js +428 -0
  64. package/dist/lib/tools-register.d.ts +18 -0
  65. package/dist/lib/tools-register.js +40 -0
  66. package/dist/lib/tools-response.d.ts +21 -0
  67. package/dist/lib/tools-response.js +219 -0
  68. package/dist/lib/tools-spawn.d.ts +12 -0
  69. package/dist/lib/tools-spawn.js +159 -0
  70. package/dist/lib/tools.d.ts +8 -39
  71. package/dist/lib/tools.js +11 -1490
  72. package/dist/recipes/utility-coordinator-lock-snapshot.json +1 -1
  73. package/dist/scripts/actor-worker.mjs +190 -7
  74. package/dist/scripts/async-runner.mjs +171 -8
  75. package/dist/scripts/build-dist.mjs +1 -1
  76. package/dist/scripts/conformance.mjs +3 -3
  77. package/dist/scripts/coordinator.mjs +733 -11
  78. package/dist/scripts/locker.mjs +345 -16
  79. package/dist/scripts/recipe-utils.mjs +478 -14
  80. package/dist/scripts/validate-recipe.mjs +110 -10
  81. package/dist/skills/actors/SKILL.md +1 -1
  82. package/dist/skills/swarm/SKILL.md +1 -1
  83. package/docs/recipe-library.md +1 -1
  84. package/docs/template-recipes.md +1 -1
  85. package/index.ts +11 -18
  86. package/lib/async-runs.ts +124 -1016
  87. package/lib/command-templates.ts +3 -3
  88. package/lib/config.ts +7 -7
  89. package/lib/{output.ts → execution-output.ts} +3 -3
  90. package/lib/execution.ts +5 -1
  91. package/lib/{actor-inspector-tui.ts → inspector.ts} +4 -3
  92. package/lib/mailbox-loop.ts +3 -3
  93. package/lib/{actor-messages.ts → messages.ts} +3 -3
  94. package/lib/{actor-recipe-context.ts → recipes-context.ts} +3 -2
  95. package/lib/{recipe-discovery.ts → recipes-discovery.ts} +19 -19
  96. package/lib/{recipe-usage.ts → recipes-usage.ts} +2 -2
  97. package/lib/registry.ts +37 -24
  98. package/lib/{actor-rooms.ts → rooms.ts} +4 -4
  99. package/lib/runs-artifacts.ts +96 -0
  100. package/lib/runs-control.ts +85 -0
  101. package/lib/runs-identity.ts +14 -0
  102. package/lib/runs-index.ts +156 -0
  103. package/lib/runs-mailbox.ts +208 -0
  104. package/lib/runs-messages.ts +252 -0
  105. package/lib/runs-outbox.ts +144 -0
  106. package/lib/runs-process.ts +51 -0
  107. package/lib/runs-retention.ts +77 -0
  108. package/lib/runs-start.ts +98 -0
  109. package/lib/runs-status.ts +78 -0
  110. package/lib/runtime.ts +6 -6
  111. package/lib/schema.ts +55 -0
  112. package/lib/tools-access.ts +68 -0
  113. package/lib/tools-inspect.ts +735 -0
  114. package/lib/tools-local.ts +234 -0
  115. package/lib/tools-mailbox.ts +56 -0
  116. package/lib/tools-message.ts +569 -0
  117. package/lib/tools-register.ts +64 -0
  118. package/lib/tools-response.ts +267 -0
  119. package/lib/tools-spawn.ts +240 -0
  120. package/lib/tools.ts +20 -2040
  121. package/package.json +1 -1
  122. package/recipes/utility-coordinator-lock-snapshot.json +1 -1
  123. package/scripts/actor-worker.mjs +190 -7
  124. package/scripts/async-runner.mjs +171 -8
  125. package/scripts/build-dist.mjs +1 -1
  126. package/scripts/conformance.mjs +3 -3
  127. package/scripts/coordinator.mjs +733 -11
  128. package/scripts/locker.mjs +345 -16
  129. package/scripts/recipe-utils.mjs +478 -14
  130. package/scripts/validate-recipe.mjs +110 -10
  131. package/skills/actors/SKILL.md +1 -1
  132. package/skills/swarm/SKILL.md +1 -1
  133. package/dist/lib/actor-worker.d.ts +0 -16
  134. package/dist/lib/actor-worker.js +0 -134
  135. package/dist/lib/async-runner.d.ts +0 -5
  136. package/dist/lib/async-runner.js +0 -134
  137. package/dist/lib/coordinator.d.ts +0 -5
  138. package/dist/lib/coordinator.js +0 -660
  139. package/dist/lib/locker.d.ts +0 -5
  140. package/dist/lib/locker.js +0 -310
  141. package/dist/lib/recipe-utils.d.ts +0 -5
  142. package/dist/lib/recipe-utils.js +0 -385
  143. package/dist/lib/validate-recipe.d.ts +0 -6
  144. package/dist/lib/validate-recipe.js +0 -104
  145. package/lib/actor-worker.ts +0 -174
  146. package/lib/async-runner.ts +0 -173
  147. package/lib/coordinator.ts +0 -742
  148. package/lib/locker.ts +0 -340
  149. package/lib/recipe-utils.ts +0 -486
  150. package/lib/validate-recipe.ts +0 -110
  151. /package/dist/lib/{recipe-references.d.ts → recipes-references.d.ts} +0 -0
  152. /package/dist/lib/{recipe-references.js → recipes-references.js} +0 -0
  153. /package/dist/lib/{recipe-usage.d.ts → recipes-usage.d.ts} +0 -0
  154. /package/dist/lib/{recipe-usage.js → recipes-usage.js} +0 -0
  155. /package/lib/{recipe-references.ts → recipes-references.ts} +0 -0
@@ -0,0 +1,534 @@
1
+ /**
2
+ * Public inspect tool behavior
3
+ * Zones: actor observation, recipe registry inspection, room/session/tool/run views
4
+ * Owns the public inspect execution path and compact observation responses
5
+ */
6
+ import { execFileSync } from "node:child_process";
7
+ import { existsSync, readFileSync } from "node:fs";
8
+ import { dirname, join } from "node:path";
9
+ import * as AsyncRuns from "./async-runs.js";
10
+ import * as Limits from "./limits.js";
11
+ import * as Messages from "./messages.js";
12
+ import * as Paths from "./paths.js";
13
+ import * as RecipesDiscovery from "./recipes-discovery.js";
14
+ import * as Rooms from "./rooms.js";
15
+ import * as Schema from "./schema.js";
16
+ import * as ToolsAccess from "./tools-access.js";
17
+ import * as ToolsMailbox from "./tools-mailbox.js";
18
+ import * as ToolsResponse from "./tools-response.js";
19
+ const asRecord = ToolsResponse.asRecord;
20
+ const maybeJsonText = ToolsResponse.maybeJsonText;
21
+ function compactRunMessages(messages) {
22
+ if (messages.length === 0)
23
+ return "\n(no actor messages)";
24
+ return `\n${messages
25
+ .map((message) => [
26
+ `run=${message.run}`,
27
+ `type=${message.event}`,
28
+ `level=${message.level}`,
29
+ `summary=${message.summary.replaceAll(/\s+/g, "_")}`,
30
+ ].join(" "))
31
+ .join("\n")}`;
32
+ }
33
+ function compactRoomPreviews(previews) {
34
+ if (previews.length === 0)
35
+ return "\n(no room message previews)";
36
+ return `\n${previews
37
+ .map((preview) => [
38
+ `ts=${preview.timestamp}`,
39
+ preview.from ? `from=${preview.from}` : "",
40
+ `to=${preview.to}`,
41
+ `type=${preview.type}`,
42
+ preview.summary
43
+ ? `summary=${ToolsResponse.compactPreview(preview.summary)}`
44
+ : "",
45
+ preview.body_preview
46
+ ? `body=${ToolsResponse.compactPreview(preview.body_preview)}`
47
+ : "",
48
+ ]
49
+ .filter(Boolean)
50
+ .join(" "))
51
+ .join("\n")}`;
52
+ }
53
+ function compactRoomMessages(messages) {
54
+ if (messages.length === 0)
55
+ return "\n(no room messages)";
56
+ return `\n${messages
57
+ .map((message) => [
58
+ `ts=${message.received_at}`,
59
+ `from=${String(message.from ?? "<unknown>")}`,
60
+ `to=${message.to}`,
61
+ `type=${message.type}`,
62
+ `summary=${String(message.summary ?? "").replaceAll(/\s+/g, "_")}`,
63
+ ToolsResponse.compactPreview(message.body)
64
+ ? `body=${ToolsResponse.compactPreview(message.body)}`
65
+ : "",
66
+ ]
67
+ .filter(Boolean)
68
+ .join(" "))
69
+ .join("\n")}`;
70
+ }
71
+ function compactRoomContacts(contacts) {
72
+ if (contacts.length === 0)
73
+ return "\n(no room contacts)";
74
+ return `\n${contacts
75
+ .map((contact) => [
76
+ `address=${contact.address}`,
77
+ contact.role !== undefined ? `role=${String(contact.role)}` : "",
78
+ contact.parent !== undefined ? `parent=${String(contact.parent)}` : "",
79
+ contact.caps !== undefined
80
+ ? `caps=${Array.isArray(contact.caps) ? contact.caps.join(",") : String(contact.caps)}`
81
+ : "",
82
+ contact.claim !== undefined
83
+ ? `claim=${String(contact.claim).replaceAll(/\s+/g, "_")}`
84
+ : "",
85
+ contact.status !== undefined ? `status=${String(contact.status)}` : "",
86
+ ]
87
+ .filter(Boolean)
88
+ .join(" "))
89
+ .join("\n")}`;
90
+ }
91
+ function compactRoomRoster(roster) {
92
+ const members = Object.values(roster);
93
+ if (members.length === 0)
94
+ return "\n(no room members)";
95
+ return `\n${members
96
+ .map((member) => [
97
+ `address=${member.address}`,
98
+ `role=${String(member.role ?? "")}`,
99
+ member.parent !== undefined ? `parent=${String(member.parent)}` : "",
100
+ member.caps !== undefined
101
+ ? `caps=${Array.isArray(member.caps) ? member.caps.join(",") : String(member.caps)}`
102
+ : "",
103
+ member.claim !== undefined
104
+ ? `claim=${String(member.claim).replaceAll(/\s+/g, "_")}`
105
+ : "",
106
+ `status=${String(member.status ?? "")}`,
107
+ `last_seen=${member.last_seen}`,
108
+ ].join(" "))
109
+ .join("\n")}`;
110
+ }
111
+ function compactRoomStatus(status) {
112
+ return `\nroom=${status.room} messages=${status.message_count} roster=${status.roster_count}${status.diagnostics_count ? ` diagnostics=${status.diagnostics_count}` : ""}${status.last_message_at ? ` last_message_at=${status.last_message_at}` : ""}${status.last_message_from ? ` last_from=${status.last_message_from}` : ""}${status.last_message_type ? ` last_type=${status.last_message_type}` : ""}${status.last_message_summary ? ` last_summary=${ToolsResponse.compactPreview(status.last_message_summary)}` : ""}`;
113
+ }
114
+ function compactCommunicationSnapshot(snapshot) {
115
+ if (!snapshot)
116
+ return "\n(no communication snapshot)";
117
+ return `\nself=${snapshot.self} root=${snapshot.root} rooms=${snapshot.rooms.length} updated_at=${snapshot.updated_at}`;
118
+ }
119
+ function compactArtifactPath(value) {
120
+ if (typeof value === "string")
121
+ return value;
122
+ const record = asRecord(value);
123
+ return String(record.path ?? "<missing>");
124
+ }
125
+ function compactActorFiles(status) {
126
+ const run = String(status.run ?? "<unknown>");
127
+ const artifacts = asRecord(status.artifacts);
128
+ const files = [
129
+ status.stdoutLog,
130
+ status.stderrLog,
131
+ status.eventsFile,
132
+ status.outboxFile,
133
+ status.state_dir
134
+ ? `${String(status.state_dir)}/communication.json`
135
+ : undefined,
136
+ status.state_dir ? `${String(status.state_dir)}/result.json` : undefined,
137
+ ].filter((file) => typeof file === "string");
138
+ const artifactText = Object.keys(artifacts).length
139
+ ? ` artifacts=${Object.entries(artifacts)
140
+ .map(([key, value]) => `${key}:${compactArtifactPath(value)}`)
141
+ .join(",")}`
142
+ : "";
143
+ const nextActions = ToolsResponse.artifactNextActions(run, artifacts);
144
+ const nextText = nextActions.length
145
+ ? ` next=${nextActions.map((action) => action.replaceAll(/\s+/g, "_")).join("|")}`
146
+ : "";
147
+ return `\nrun=${run}${artifactText}${files.length ? ` files=${files.join(",")}` : ""}${nextText}`;
148
+ }
149
+ function summarizeOtherSessions(currentSession, allRuns) {
150
+ const otherRuns = allRuns.filter((run) => run.ownerId && run.ownerId !== currentSession);
151
+ return {
152
+ other_runs: otherRuns.length,
153
+ other_sessions: new Set(otherRuns.map((run) => run.ownerId)).size,
154
+ };
155
+ }
156
+ function compactSessionRuns(session, runs, summary = {}) {
157
+ const suffix = [
158
+ summary.other_sessions !== undefined
159
+ ? `other_sessions=${String(summary.other_sessions)}`
160
+ : "",
161
+ summary.other_runs !== undefined
162
+ ? `other_runs=${String(summary.other_runs)}`
163
+ : "",
164
+ ]
165
+ .filter(Boolean)
166
+ .join(" ");
167
+ if (runs.length === 0)
168
+ return `\nsession=${session} runs=0${suffix ? ` ${suffix}` : ""}`;
169
+ return `\nsession=${session} runs=${runs.length}${suffix ? ` ${suffix}` : ""}\n${runs
170
+ .map((run) => {
171
+ const tokens = [
172
+ `run=${String(run.run ?? "")}`,
173
+ `status=${String(run.status ?? "")}`,
174
+ ];
175
+ if (run.recipe)
176
+ tokens.push(`recipe=${String(run.recipe)}`);
177
+ if (run.retire_when)
178
+ tokens.push(`retire_when=${String(run.retire_when)}`);
179
+ return tokens.join(" ");
180
+ })
181
+ .join("\n")}`;
182
+ }
183
+ function getPiActorsRuntimeStatus() {
184
+ const packagedRecipeRoot = Paths.getPackagedRecipeRoot();
185
+ const packageRoot = dirname(packagedRecipeRoot);
186
+ const packageJsonPath = join(packageRoot, "package.json");
187
+ const packageJson = existsSync(packageJsonPath)
188
+ ? JSON.parse(readFileSync(packageJsonPath, "utf8"))
189
+ : {};
190
+ let git_commit;
191
+ try {
192
+ git_commit = execFileSync("git", ["-C", packageRoot, "rev-parse", "--short", "HEAD"], {
193
+ encoding: "utf8",
194
+ stdio: ["ignore", "pipe", "ignore"],
195
+ }).trim();
196
+ }
197
+ catch {
198
+ git_commit = undefined;
199
+ }
200
+ const entrypoint = new URL(import.meta.url).pathname;
201
+ return {
202
+ entrypoint,
203
+ git_commit,
204
+ mode: entrypoint.includes("/dist/") ? "dist" : "source",
205
+ package_name: packageJson.name ?? "@llblab/pi-actors",
206
+ package_root: packageRoot,
207
+ recipe_root: Paths.getRecipeRoot(),
208
+ packaged_recipe_root: packagedRecipeRoot,
209
+ version: packageJson.version ?? "unknown",
210
+ };
211
+ }
212
+ function compactPiActorsRuntimeStatus(status) {
213
+ 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)}` : ""}`;
214
+ }
215
+ function compactToolActor(name, tool) {
216
+ const parameters = asRecord(tool.parameters);
217
+ const required = Array.isArray(parameters.required)
218
+ ? parameters.required.join(",")
219
+ : "";
220
+ const properties = asRecord(parameters.properties);
221
+ return `\ntool=${name} description=${String(tool.description ?? "").replaceAll(/\s+/g, "_")} args=${Object.keys(properties).join(",")} required=${required}`;
222
+ }
223
+ export function createInspectToolDefinition(deps = {}) {
224
+ return {
225
+ name: "inspect",
226
+ label: "Inspect",
227
+ description: "Intentionally inspect actors at decision points, after follow-ups, or during diagnosis instead of polling. Core targets are run:<id> and tool:<name>; advanced targets include branch:<run>/<branch>, room:<run>, coordinator, session:<id>, and session:all.",
228
+ parameters: Schema.objectSchema({
229
+ lines: Schema.stringSchema("Line count for tail/messages views. Default 40."),
230
+ status: Schema.stringSchema("Optional session run filter: all, running, active, terminal, done, failed, cancelled, killed, or exited."),
231
+ target: Schema.stringSchema("Actor address to inspect, e.g. run:<id> or tool:<name>; advanced: branch:<run>/<branch>, room:<run>, coordinator, session:<id>, session:all."),
232
+ verbose: Schema.booleanSchema("Return full JSON instead of compact text where available."),
233
+ view: Schema.stringSchema("Inspection view. Core run views: status, tail, messages, artifacts, files, mailbox. Advanced views include communication, roster, and contacts."),
234
+ }, ["target", "view"]),
235
+ async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
236
+ const input = asRecord(params);
237
+ const target = String(input.target ?? "");
238
+ const view = String(input.view ?? "");
239
+ if (target === "recipes" || target === "recipe-registry") {
240
+ if (view !== "status" &&
241
+ view !== "summary" &&
242
+ view !== "doctor" &&
243
+ view !== "imports") {
244
+ throw new Error("inspect recipes supports view=status, view=summary, view=doctor, or view=imports.");
245
+ }
246
+ const discovered = RecipesDiscovery.discoverRecipeSources([
247
+ {
248
+ root: deps.recipeRoot ?? Paths.getRecipeRoot(),
249
+ defaultTool: true,
250
+ mutableUsage: true,
251
+ },
252
+ { root: deps.packagedRecipeRoot ?? Paths.getPackagedRecipeRoot() },
253
+ ]);
254
+ const recipeRoot = deps.recipeRoot ?? Paths.getRecipeRoot();
255
+ const summaryBase = {
256
+ ...RecipesDiscovery.summarizeDiscovery(discovered),
257
+ drafts: RecipesDiscovery.listDraftRecipes(join(recipeRoot, "drafts")),
258
+ };
259
+ const summary = {
260
+ ...summaryBase,
261
+ next_actions: ToolsResponse.recipeRegistryNextActions(summaryBase, view),
262
+ };
263
+ return {
264
+ content: [
265
+ {
266
+ type: "text",
267
+ text: maybeJsonText(summary, input.verbose === true, view === "doctor"
268
+ ? ToolsResponse.compactRecipeDoctor(summary)
269
+ : view === "imports"
270
+ ? ToolsResponse.compactRecipeImports(summary)
271
+ : ToolsResponse.compactRecipeRegistry(summary)),
272
+ },
273
+ ],
274
+ details: summary,
275
+ };
276
+ }
277
+ const address = Messages.parseActorAddress(target);
278
+ if (address.kind === "coordinator") {
279
+ if (view !== "status" && view !== "runs") {
280
+ throw new Error("inspect coordinator supports view=status or view=runs.");
281
+ }
282
+ const session = ToolsAccess.requireContextSessionId(ctx, "inspect coordinator");
283
+ const allRuns = AsyncRuns.listRuns(undefined, typeof input.status === "string" ? input.status : undefined).map((run) => AsyncRuns.getRunStatus(String(run.state_dir)));
284
+ const runs = allRuns.filter((run) => run.ownerId === session);
285
+ const sessionSummary = summarizeOtherSessions(session, allRuns);
286
+ return {
287
+ content: [
288
+ {
289
+ type: "text",
290
+ text: maybeJsonText({ session, runs, ...sessionSummary }, input.verbose === true, compactSessionRuns(session, runs, sessionSummary)),
291
+ },
292
+ ],
293
+ details: { session, runs, ...sessionSummary },
294
+ };
295
+ }
296
+ if (address.kind === "session") {
297
+ if (view !== "status" && view !== "runs") {
298
+ throw new Error("inspect session:<id> supports view=status or view=runs.");
299
+ }
300
+ const allRuns = AsyncRuns.listRuns(undefined, typeof input.status === "string" ? input.status : undefined).map((run) => AsyncRuns.getRunStatus(String(run.state_dir)));
301
+ const runs = allRuns.filter((run) => address.value === "all" || run.ownerId === address.value);
302
+ const sessionSummary = address.value === "all"
303
+ ? {}
304
+ : summarizeOtherSessions(address.value || "", allRuns);
305
+ return {
306
+ content: [
307
+ {
308
+ type: "text",
309
+ text: maybeJsonText({ session: address.value, runs, ...sessionSummary }, input.verbose === true, compactSessionRuns(address.value || "", runs, sessionSummary)),
310
+ },
311
+ ],
312
+ details: { session: address.value, runs, ...sessionSummary },
313
+ };
314
+ }
315
+ if (address.kind === "tool" && address.value) {
316
+ if (address.value === "pi-actors") {
317
+ if (view !== "status") {
318
+ throw new Error("inspect tool:pi-actors supports view=status.");
319
+ }
320
+ const details = getPiActorsRuntimeStatus();
321
+ return {
322
+ content: [
323
+ {
324
+ type: "text",
325
+ text: maybeJsonText(details, input.verbose === true, compactPiActorsRuntimeStatus(details)),
326
+ },
327
+ ],
328
+ details,
329
+ };
330
+ }
331
+ if (view !== "status" && view !== "schema") {
332
+ throw new Error("inspect tool:<name> supports view=status or view=schema.");
333
+ }
334
+ const tool = deps.getTool?.(address.value);
335
+ if (!tool)
336
+ throw new Error(`tool actor not found: ${address.value}`);
337
+ const details = {
338
+ name: address.value,
339
+ description: tool.description,
340
+ parameters: tool.parameters,
341
+ promptSnippet: tool.promptSnippet,
342
+ };
343
+ return {
344
+ content: [
345
+ {
346
+ type: "text",
347
+ text: maybeJsonText(details, input.verbose === true || view === "schema", compactToolActor(address.value, details)),
348
+ },
349
+ ],
350
+ details,
351
+ };
352
+ }
353
+ if (address.kind === "room" && address.value && address.room) {
354
+ const status = ToolsAccess.assertRunAccessibleToContext(address.value, ctx);
355
+ const stateDir = String(status.state_dir ?? "");
356
+ if (!stateDir)
357
+ throw new Error(`room:${address.value} has no run state directory.`);
358
+ if (view === "status") {
359
+ const status = Rooms.getRoomStatus(stateDir, address.room);
360
+ return {
361
+ content: [
362
+ {
363
+ type: "text",
364
+ text: maybeJsonText(status, input.verbose === true, compactRoomStatus(status)),
365
+ },
366
+ ],
367
+ details: status,
368
+ };
369
+ }
370
+ if (view === "previews") {
371
+ const previews = Rooms.readRoomMessagePreviews(stateDir, address.room, Number(input.lines || Limits.DEFAULT_INSPECT_LINES));
372
+ return {
373
+ content: [
374
+ {
375
+ type: "text",
376
+ text: maybeJsonText(previews, input.verbose === true, compactRoomPreviews(previews)),
377
+ },
378
+ ],
379
+ details: { previews },
380
+ };
381
+ }
382
+ if (view === "messages") {
383
+ const messages = Rooms.readRoomMessages(stateDir, address.room, Number(input.lines || Limits.DEFAULT_INSPECT_LINES));
384
+ return {
385
+ content: [
386
+ {
387
+ type: "text",
388
+ text: maybeJsonText(messages, input.verbose === true, compactRoomMessages(messages)),
389
+ },
390
+ ],
391
+ details: { messages },
392
+ };
393
+ }
394
+ if (view === "contacts") {
395
+ const contacts = Rooms.readRoomContacts(stateDir, address.room);
396
+ return {
397
+ content: [
398
+ {
399
+ type: "text",
400
+ text: maybeJsonText(contacts, input.verbose === true, compactRoomContacts(contacts)),
401
+ },
402
+ ],
403
+ details: { contacts },
404
+ };
405
+ }
406
+ if (view === "roster") {
407
+ const roster = Rooms.readRoomRoster(stateDir, address.room);
408
+ return {
409
+ content: [
410
+ {
411
+ type: "text",
412
+ text: maybeJsonText(roster, input.verbose === true, compactRoomRoster(roster)),
413
+ },
414
+ ],
415
+ details: { roster },
416
+ };
417
+ }
418
+ throw new Error("inspect room:<run> supports view=status, view=messages, view=previews, view=roster, or view=contacts.");
419
+ }
420
+ const runId = address.kind === "run" || address.kind === "branch"
421
+ ? address.value
422
+ : undefined;
423
+ if (!runId)
424
+ throw new Error("inspect target must be run:<id>, branch:<run>/<branch>, coordinator, session:<id>, or tool:<name>.");
425
+ if (address.kind === "branch") {
426
+ if (view !== "mailbox")
427
+ throw new Error("inspect branch:<run>/<branch> supports view=mailbox.");
428
+ const status = ToolsAccess.assertRunAccessibleToContext(runId, ctx);
429
+ const branchInbox = Rooms.readBranchInboxDiagnostics(String(status.state_dir ?? ""), runId, target, Number(input.lines || Limits.DEFAULT_INSPECT_LINES));
430
+ const messages = branchInbox.messages;
431
+ return {
432
+ content: [
433
+ {
434
+ type: "text",
435
+ text: maybeJsonText(input.verbose === true ? branchInbox : messages, input.verbose === true, `${ToolsResponse.compactBranchInbox(messages.map((message) => ({ ...message })))}${branchInbox.corrupted > 0 ? `\ncorrupted=${branchInbox.corrupted}` : ""}`),
436
+ },
437
+ ],
438
+ details: { corrupted: branchInbox.corrupted, messages },
439
+ };
440
+ }
441
+ switch (view) {
442
+ case "status": {
443
+ const status = ToolsAccess.assertRunAccessibleToContext(runId, ctx);
444
+ return {
445
+ content: [
446
+ {
447
+ type: "text",
448
+ text: maybeJsonText(status, input.verbose === true, ToolsResponse.compactAsyncRunStatus(status)),
449
+ },
450
+ ],
451
+ details: status,
452
+ };
453
+ }
454
+ case "tail": {
455
+ ToolsAccess.assertRunAccessibleToContext(runId, ctx);
456
+ const text = AsyncRuns.tailRun(runId, Number(input.lines || Limits.DEFAULT_INSPECT_LINES));
457
+ return {
458
+ content: [{ type: "text", text: `\n${text}` }],
459
+ details: {},
460
+ };
461
+ }
462
+ case "messages": {
463
+ ToolsAccess.assertRunAccessibleToContext(runId, ctx);
464
+ const messages = AsyncRuns.readRunEvents(runId, Number(input.lines || Limits.DEFAULT_INSPECT_LINES));
465
+ return {
466
+ content: [
467
+ {
468
+ type: "text",
469
+ text: maybeJsonText(messages, input.verbose === true, compactRunMessages(messages)),
470
+ },
471
+ ],
472
+ details: { messages },
473
+ };
474
+ }
475
+ case "artifacts":
476
+ case "files": {
477
+ const status = ToolsAccess.assertRunAccessibleToContext(runId, ctx);
478
+ const artifactManifest = AsyncRuns.resolveArtifactManifest(status.artifacts);
479
+ const details = artifactManifest
480
+ ? {
481
+ ...status,
482
+ artifact_manifest: artifactManifest,
483
+ next_actions: ToolsResponse.artifactNextActions(status.run ?? runId, asRecord(status.artifacts)),
484
+ }
485
+ : status;
486
+ return {
487
+ content: [
488
+ {
489
+ type: "text",
490
+ text: maybeJsonText(details, input.verbose === true, compactActorFiles(status)),
491
+ },
492
+ ],
493
+ details,
494
+ };
495
+ }
496
+ case "mailbox": {
497
+ const status = ToolsAccess.assertRunAccessibleToContext(runId, ctx);
498
+ const mailbox = asRecord(status.mailbox);
499
+ const normalizedMailbox = ToolsMailbox.normalizeMailboxContracts(mailbox);
500
+ const messages = AsyncRuns.readRunInboxMessages(runId, Number(input.lines || Limits.DEFAULT_INSPECT_LINES));
501
+ const details = {
502
+ mailbox,
503
+ normalized_mailbox: normalizedMailbox,
504
+ messages,
505
+ };
506
+ return {
507
+ content: [
508
+ {
509
+ type: "text",
510
+ text: maybeJsonText(details, input.verbose === true, ToolsResponse.compactRunMailbox(String(status.run ?? runId), mailbox, messages)),
511
+ },
512
+ ],
513
+ details,
514
+ };
515
+ }
516
+ case "communication": {
517
+ const status = ToolsAccess.assertRunAccessibleToContext(runId, ctx);
518
+ const snapshot = Rooms.readCommunicationSnapshot(String(status.state_dir ?? ""));
519
+ return {
520
+ content: [
521
+ {
522
+ type: "text",
523
+ text: maybeJsonText(snapshot ?? {}, input.verbose === true, compactCommunicationSnapshot(snapshot)),
524
+ },
525
+ ],
526
+ details: { communication: snapshot },
527
+ };
528
+ }
529
+ default:
530
+ throw new Error("inspect view must be one of: status, tail, messages, artifacts, files, mailbox, communication; branch targets support mailbox.");
531
+ }
532
+ },
533
+ };
534
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Local tool definition behavior
3
+ * Zones: user recipe tools, generated schemas, async recipe launch
4
+ * Owns wrapping saved local capabilities as executable pi tools
5
+ */
6
+ import type { RegisteredTool } from "./config.ts";
7
+ import * as Execution from "./execution.ts";
8
+ export interface RuntimeToolContext {
9
+ cwd: string;
10
+ sessionManager?: {
11
+ getSessionId?: () => string;
12
+ };
13
+ }
14
+ export declare function createRuntimeToolDefinition(cfg: RegisteredTool, exec: Execution.RegisteredToolExec): any;