@llblab/pi-actors 0.42.3 → 0.43.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 (279) hide show
  1. package/AGENTS.md +127 -175
  2. package/BACKLOG.md +189 -1
  3. package/CHANGELOG.md +183 -292
  4. package/README.md +115 -276
  5. package/dist/fixtures/protocol/control-endpoint.json +6 -0
  6. package/dist/fixtures/protocol/control-record.json +9 -0
  7. package/dist/fixtures/protocol/recipe-summary.json +4 -12
  8. package/dist/fixtures/protocol/trace-event.json +9 -0
  9. package/dist/index.js +1 -1
  10. package/dist/lib/async-runs.d.ts +15 -38
  11. package/dist/lib/async-runs.js +173 -111
  12. package/dist/lib/automatic-review-runtime.d.ts +1 -1
  13. package/dist/lib/automatic-review-runtime.js +5 -5
  14. package/dist/lib/command-templates.d.ts +2 -0
  15. package/dist/lib/command-templates.js +38 -4
  16. package/dist/lib/control-projection.d.ts +20 -0
  17. package/dist/lib/control-projection.js +66 -0
  18. package/dist/lib/control.d.ts +15 -0
  19. package/dist/lib/control.js +97 -0
  20. package/dist/lib/draft-sleep.js +3 -3
  21. package/dist/lib/execution-sessions.d.ts +17 -0
  22. package/dist/lib/execution-sessions.js +85 -0
  23. package/dist/lib/file-state.d.ts +2 -0
  24. package/dist/lib/file-state.js +114 -46
  25. package/dist/lib/inspector-actions.d.ts +2 -2
  26. package/dist/lib/inspector-actions.js +2 -2
  27. package/dist/lib/inspector-command.js +3 -3
  28. package/dist/lib/inspector-overlay.d.ts +54 -70
  29. package/dist/lib/inspector-overlay.js +576 -910
  30. package/dist/lib/inspector.d.ts +3 -71
  31. package/dist/lib/inspector.js +19 -665
  32. package/dist/lib/limits.d.ts +7 -3
  33. package/dist/lib/limits.js +7 -3
  34. package/dist/lib/observability.d.ts +16 -16
  35. package/dist/lib/observability.js +43 -82
  36. package/dist/lib/prompts.d.ts +1 -1
  37. package/dist/lib/prompts.js +3 -3
  38. package/dist/lib/recipe-control.d.ts +7 -0
  39. package/dist/lib/recipe-control.js +43 -0
  40. package/dist/lib/recipes-discovery.js +2 -0
  41. package/dist/lib/recipes-references.d.ts +1 -14
  42. package/dist/lib/recipes-references.js +6 -21
  43. package/dist/lib/review-control.d.ts +1 -1
  44. package/dist/lib/review-control.js +4 -5
  45. package/dist/lib/review-projection.js +1 -5
  46. package/dist/lib/run-ui-runtime.js +2 -2
  47. package/dist/lib/runs-control-delivery.d.ts +28 -0
  48. package/dist/lib/runs-control-delivery.js +150 -0
  49. package/dist/lib/runs-controls.d.ts +37 -0
  50. package/dist/lib/runs-controls.js +146 -0
  51. package/dist/lib/runs-retention.d.ts +7 -0
  52. package/dist/lib/runs-retention.js +27 -3
  53. package/dist/lib/runs-start.js +4 -2
  54. package/dist/lib/runs-status.js +11 -6
  55. package/dist/lib/runs-trace.d.ts +24 -0
  56. package/dist/lib/runs-trace.js +102 -0
  57. package/dist/lib/runtime-identity.d.ts +7 -0
  58. package/dist/lib/runtime-identity.js +35 -0
  59. package/dist/lib/runtime-notifier.d.ts +1 -1
  60. package/dist/lib/runtime-notifier.js +1 -1
  61. package/dist/lib/runtime-triage.d.ts +29 -0
  62. package/dist/lib/runtime-triage.js +76 -0
  63. package/dist/lib/tool-review-scheduler.js +7 -7
  64. package/dist/lib/tools-inspect.d.ts +3 -3
  65. package/dist/lib/tools-inspect.js +241 -707
  66. package/dist/lib/tools-local.js +2 -10
  67. package/dist/lib/tools-message.d.ts +6 -7
  68. package/dist/lib/tools-message.js +95 -396
  69. package/dist/lib/tools-response.d.ts +1 -5
  70. package/dist/lib/tools-response.js +5 -48
  71. package/dist/lib/tools-spawn.js +16 -28
  72. package/dist/lib/tools.d.ts +1 -1
  73. package/dist/lib/tools.js +2 -3
  74. package/dist/lib/trace-projection.d.ts +22 -0
  75. package/dist/lib/trace-projection.js +185 -0
  76. package/dist/recipes/draft-review.json +0 -10
  77. package/dist/recipes/lens-swarm.json +0 -14
  78. package/dist/recipes/music-player.json +10 -19
  79. package/dist/recipes/pipeline-architect-coordinator.json +0 -11
  80. package/dist/recipes/pipeline-artifact-bundle.json +1 -22
  81. package/dist/recipes/pipeline-artifact-report.json +1 -18
  82. package/dist/recipes/pipeline-artifact-write.json +1 -18
  83. package/dist/recipes/pipeline-async-run-ops.json +0 -12
  84. package/dist/recipes/pipeline-checkpoint-continuation.json +0 -14
  85. package/dist/recipes/pipeline-development-tasking.json +0 -12
  86. package/dist/recipes/pipeline-docs-maintenance.json +0 -12
  87. package/dist/recipes/pipeline-media-library.json +0 -12
  88. package/dist/recipes/pipeline-quorum-review.json +0 -12
  89. package/dist/recipes/pipeline-release-readiness.json +0 -12
  90. package/dist/recipes/pipeline-release-summary.json +0 -12
  91. package/dist/recipes/pipeline-repo-health.json +0 -12
  92. package/dist/recipes/pipeline-research-synthesis.json +0 -11
  93. package/dist/recipes/pipeline-review-readiness.json +0 -12
  94. package/dist/recipes/resource-locker.json +27 -0
  95. package/dist/recipes/subagent-artifact.json +0 -9
  96. package/dist/recipes/subagent-checkpoint.json +0 -10
  97. package/dist/recipes/subagent-conflict-report.json +0 -11
  98. package/dist/recipes/subagent-contradiction-map.json +0 -11
  99. package/dist/recipes/subagent-critic.json +0 -11
  100. package/dist/recipes/subagent-evidence-map.json +0 -11
  101. package/dist/recipes/subagent-followup.json +0 -10
  102. package/dist/recipes/subagent-judge.json +0 -11
  103. package/dist/recipes/subagent-merge.json +0 -11
  104. package/dist/recipes/subagent-normalize.json +0 -11
  105. package/dist/recipes/subagent-plan.json +0 -11
  106. package/dist/recipes/subagent-preflight.json +0 -11
  107. package/dist/recipes/subagent-prompt.json +0 -10
  108. package/dist/recipes/subagent-quorum.json +0 -10
  109. package/dist/recipes/subagent-review-coordinator.json +0 -14
  110. package/dist/recipes/subagent-review.json +0 -11
  111. package/dist/recipes/subagent-task-card.json +0 -11
  112. package/dist/recipes/subagent-tools.json +0 -10
  113. package/dist/recipes/subagent-verify.json +0 -11
  114. package/dist/recipes/subagents-prompts.json +0 -10
  115. package/dist/recipes/tool-review.json +0 -10
  116. package/dist/scripts/async-runner.mjs +25 -25
  117. package/dist/scripts/conformance.mjs +4 -2
  118. package/dist/scripts/locker.mjs +196 -69
  119. package/dist/scripts/music-player.mjs +162 -159
  120. package/dist/scripts/recipe-utils.mjs +6 -96
  121. package/dist/scripts/release-gates.mjs +91 -0
  122. package/dist/scripts/validate-recipe.mjs +8 -57
  123. package/dist/skills/actors/SKILL.md +57 -265
  124. package/dist/skills/swarm/SKILL.md +10 -34
  125. package/docs/0.43-baseline.md +39 -0
  126. package/docs/README.md +4 -6
  127. package/docs/actor-inspector.md +26 -64
  128. package/docs/async-runs.md +81 -328
  129. package/docs/command-templates.md +8 -118
  130. package/docs/recipe-library.md +55 -182
  131. package/docs/releasing.md +28 -0
  132. package/docs/template-recipes.md +76 -289
  133. package/docs/tool-registry.md +41 -161
  134. package/fixtures/protocol/control-endpoint.json +6 -0
  135. package/fixtures/protocol/control-record.json +9 -0
  136. package/fixtures/protocol/recipe-summary.json +4 -12
  137. package/fixtures/protocol/trace-event.json +9 -0
  138. package/index.ts +1 -1
  139. package/lib/async-runs.ts +218 -204
  140. package/lib/automatic-review-runtime.ts +7 -7
  141. package/lib/command-templates.ts +44 -4
  142. package/lib/control-projection.ts +105 -0
  143. package/lib/control.ts +117 -0
  144. package/lib/draft-sleep.ts +3 -3
  145. package/lib/execution-sessions.ts +111 -0
  146. package/lib/file-state.ts +84 -64
  147. package/lib/inspector-actions.ts +2 -2
  148. package/lib/inspector-command.ts +3 -3
  149. package/lib/inspector-overlay.ts +617 -1126
  150. package/lib/inspector.ts +46 -979
  151. package/lib/limits.ts +7 -3
  152. package/lib/observability.ts +60 -101
  153. package/lib/prompts.ts +3 -3
  154. package/lib/recipe-control.ts +52 -0
  155. package/lib/recipes-discovery.ts +2 -0
  156. package/lib/recipes-references.ts +9 -45
  157. package/lib/review-control.ts +4 -5
  158. package/lib/review-projection.ts +1 -5
  159. package/lib/run-ui-runtime.ts +2 -2
  160. package/lib/runs-control-delivery.ts +209 -0
  161. package/lib/runs-controls.ts +213 -0
  162. package/lib/runs-retention.ts +38 -3
  163. package/lib/runs-start.ts +4 -2
  164. package/lib/runs-status.ts +11 -6
  165. package/lib/runs-trace.ts +136 -0
  166. package/lib/runtime-identity.ts +39 -0
  167. package/lib/runtime-notifier.ts +1 -1
  168. package/lib/runtime-triage.ts +120 -0
  169. package/lib/tool-review-scheduler.ts +7 -7
  170. package/lib/tools-inspect.ts +283 -900
  171. package/lib/tools-local.ts +2 -12
  172. package/lib/tools-message.ts +112 -520
  173. package/lib/tools-response.ts +5 -64
  174. package/lib/tools-spawn.ts +16 -32
  175. package/lib/tools.ts +5 -6
  176. package/lib/trace-projection.ts +244 -0
  177. package/package.json +2 -1
  178. package/recipes/draft-review.json +0 -10
  179. package/recipes/lens-swarm.json +0 -14
  180. package/recipes/music-player.json +10 -19
  181. package/recipes/pipeline-architect-coordinator.json +0 -11
  182. package/recipes/pipeline-artifact-bundle.json +1 -22
  183. package/recipes/pipeline-artifact-report.json +1 -18
  184. package/recipes/pipeline-artifact-write.json +1 -18
  185. package/recipes/pipeline-async-run-ops.json +0 -12
  186. package/recipes/pipeline-checkpoint-continuation.json +0 -14
  187. package/recipes/pipeline-development-tasking.json +0 -12
  188. package/recipes/pipeline-docs-maintenance.json +0 -12
  189. package/recipes/pipeline-media-library.json +0 -12
  190. package/recipes/pipeline-quorum-review.json +0 -12
  191. package/recipes/pipeline-release-readiness.json +0 -12
  192. package/recipes/pipeline-release-summary.json +0 -12
  193. package/recipes/pipeline-repo-health.json +0 -12
  194. package/recipes/pipeline-research-synthesis.json +0 -11
  195. package/recipes/pipeline-review-readiness.json +0 -12
  196. package/recipes/resource-locker.json +27 -0
  197. package/recipes/subagent-artifact.json +0 -9
  198. package/recipes/subagent-checkpoint.json +0 -10
  199. package/recipes/subagent-conflict-report.json +0 -11
  200. package/recipes/subagent-contradiction-map.json +0 -11
  201. package/recipes/subagent-critic.json +0 -11
  202. package/recipes/subagent-evidence-map.json +0 -11
  203. package/recipes/subagent-followup.json +0 -10
  204. package/recipes/subagent-judge.json +0 -11
  205. package/recipes/subagent-merge.json +0 -11
  206. package/recipes/subagent-normalize.json +0 -11
  207. package/recipes/subagent-plan.json +0 -11
  208. package/recipes/subagent-preflight.json +0 -11
  209. package/recipes/subagent-prompt.json +0 -10
  210. package/recipes/subagent-quorum.json +0 -10
  211. package/recipes/subagent-review-coordinator.json +0 -14
  212. package/recipes/subagent-review.json +0 -11
  213. package/recipes/subagent-task-card.json +0 -11
  214. package/recipes/subagent-tools.json +0 -10
  215. package/recipes/subagent-verify.json +0 -11
  216. package/recipes/subagents-prompts.json +0 -10
  217. package/recipes/tool-review.json +0 -10
  218. package/scripts/async-runner.mjs +25 -25
  219. package/scripts/conformance.mjs +4 -2
  220. package/scripts/locker.mjs +196 -69
  221. package/scripts/music-player.mjs +162 -159
  222. package/scripts/recipe-utils.mjs +6 -96
  223. package/scripts/release-gates.mjs +91 -0
  224. package/scripts/validate-recipe.mjs +8 -57
  225. package/skills/actors/SKILL.md +57 -265
  226. package/skills/swarm/SKILL.md +10 -34
  227. package/dist/fixtures/protocol/actor-message-branch.json +0 -13
  228. package/dist/fixtures/protocol/mailbox-contract.json +0 -15
  229. package/dist/fixtures/protocol/room-message.json +0 -11
  230. package/dist/fixtures/protocol/room-roster.json +0 -11
  231. package/dist/fixtures/protocol/run-inbox-message.json +0 -9
  232. package/dist/fixtures/protocol/run-outbox-event.json +0 -9
  233. package/dist/lib/mailbox-loop.d.ts +0 -41
  234. package/dist/lib/mailbox-loop.js +0 -60
  235. package/dist/lib/messages.d.ts +0 -25
  236. package/dist/lib/messages.js +0 -122
  237. package/dist/lib/rooms.d.ts +0 -104
  238. package/dist/lib/rooms.js +0 -647
  239. package/dist/lib/runs-mailbox.d.ts +0 -25
  240. package/dist/lib/runs-mailbox.js +0 -146
  241. package/dist/lib/runs-messages.d.ts +0 -15
  242. package/dist/lib/runs-messages.js +0 -179
  243. package/dist/lib/runs-outbox.d.ts +0 -41
  244. package/dist/lib/runs-outbox.js +0 -87
  245. package/dist/lib/tools-mailbox.d.ts +0 -8
  246. package/dist/lib/tools-mailbox.js +0 -48
  247. package/dist/recipes/actor-worker.json +0 -39
  248. package/dist/recipes/coordinator-locker.json +0 -45
  249. package/dist/recipes/locker.json +0 -45
  250. package/dist/recipes/pipeline-room-swarm.json +0 -50
  251. package/dist/recipes/subagent-message.json +0 -32
  252. package/dist/recipes/utility-actor-message.json +0 -23
  253. package/dist/scripts/actor-worker.mjs +0 -214
  254. package/dist/scripts/coordinator.mjs +0 -799
  255. package/docs/actor-messages.md +0 -225
  256. package/docs/actors-deep-reference.md +0 -66
  257. package/docs/component-recipes.md +0 -148
  258. package/docs/task-first-recipes.md +0 -263
  259. package/fixtures/protocol/actor-message-branch.json +0 -13
  260. package/fixtures/protocol/mailbox-contract.json +0 -15
  261. package/fixtures/protocol/room-message.json +0 -11
  262. package/fixtures/protocol/room-roster.json +0 -11
  263. package/fixtures/protocol/run-inbox-message.json +0 -9
  264. package/fixtures/protocol/run-outbox-event.json +0 -9
  265. package/lib/mailbox-loop.ts +0 -144
  266. package/lib/messages.ts +0 -151
  267. package/lib/rooms.ts +0 -939
  268. package/lib/runs-mailbox.ts +0 -208
  269. package/lib/runs-messages.ts +0 -252
  270. package/lib/runs-outbox.ts +0 -144
  271. package/lib/tools-mailbox.ts +0 -56
  272. package/recipes/actor-worker.json +0 -39
  273. package/recipes/coordinator-locker.json +0 -45
  274. package/recipes/locker.json +0 -45
  275. package/recipes/pipeline-room-swarm.json +0 -50
  276. package/recipes/subagent-message.json +0 -32
  277. package/recipes/utility-actor-message.json +0 -23
  278. package/scripts/actor-worker.mjs +0 -214
  279. package/scripts/coordinator.mjs +0 -799
@@ -1,763 +1,297 @@
1
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
2
+ * Public inspect tool behavior.
3
+ * Zones: Run Recipe/Trace/Control views and runtime/recipe/tool diagnostics
4
+ * Owns exact inspect target/view dispatch; source projection stays in domain modules.
5
5
  */
6
- import { execFileSync } from "node:child_process";
7
- import { existsSync, readFileSync } from "node:fs";
8
- import { dirname, join } from "node:path";
9
- import { fileURLToPath } from "node:url";
6
+ import { join } from "node:path";
10
7
  import * as AsyncRuns from "./async-runs.js";
8
+ import * as ControlProjection from "./control-projection.js";
9
+ import * as Inspector from "./inspector.js";
11
10
  import * as Limits from "./limits.js";
12
- import * as Messages from "./messages.js";
13
11
  import * as Paths from "./paths.js";
14
12
  import * as RecipesDiscovery from "./recipes-discovery.js";
15
13
  import * as ReviewDiagnostics from "./review-diagnostics.js";
16
- import * as Rooms from "./rooms.js";
14
+ import * as RunsControls from "./runs-controls.js";
15
+ import * as RunControlDelivery from "./runs-control-delivery.js";
16
+ import * as RunsTrace from "./runs-trace.js";
17
+ import * as RuntimeIdentity from "./runtime-identity.js";
18
+ import * as RuntimeTriage from "./runtime-triage.js";
17
19
  import * as Schema from "./schema.js";
18
20
  import * as ToolsAccess from "./tools-access.js";
19
- import * as ToolsMailbox from "./tools-mailbox.js";
20
21
  import * as ToolsResponse from "./tools-response.js";
22
+ import * as TraceProjection from "./trace-projection.js";
21
23
  const asRecord = ToolsResponse.asRecord;
22
24
  const maybeJsonText = ToolsResponse.maybeJsonText;
23
- function compactAutomaticReviewDiagnostics(diagnostics) {
24
- const draft = asRecord(diagnostics.draft_review);
25
- const tool = asRecord(diagnostics.tool_review);
26
- return [
27
- `automatic_reviews draft=${String(draft.phase ?? "idle")} tool=${String(tool.phase ?? "idle")}`,
28
- `lineages=${String(diagnostics.lineage_count ?? 0)} revision_snapshots=${String(diagnostics.revision_snapshots ?? 0)}`,
29
- ].join("\n");
30
- }
31
- function compactRunMessages(messages) {
32
- if (messages.length === 0)
33
- return "\n(no actor messages)";
34
- return `\n${messages
35
- .map((message) => [
36
- `run=${message.run}`,
37
- `type=${message.event}`,
38
- `level=${message.level}`,
39
- `summary=${message.summary.replaceAll(/\s+/g, "_")}`,
40
- ].join(" "))
41
- .join("\n")}`;
42
- }
43
- function compactRoomPreviews(previews) {
44
- if (previews.length === 0)
45
- return "\n(no room message previews)";
46
- return `\n${previews
47
- .map((preview) => [
48
- `ts=${preview.timestamp}`,
49
- preview.from ? `from=${preview.from}` : "",
50
- `to=${preview.to}`,
51
- `type=${preview.type}`,
52
- preview.summary
53
- ? `summary=${ToolsResponse.compactPreview(preview.summary)}`
54
- : "",
55
- preview.body_preview
56
- ? `body=${ToolsResponse.compactPreview(preview.body_preview)}`
57
- : "",
58
- ]
59
- .filter(Boolean)
60
- .join(" "))
61
- .join("\n")}`;
62
- }
63
- function compactRoomMessages(messages) {
64
- if (messages.length === 0)
65
- return "\n(no room messages)";
66
- return `\n${messages
67
- .map((message) => [
68
- `ts=${message.received_at}`,
69
- `from=${String(message.from ?? "<unknown>")}`,
70
- `to=${message.to}`,
71
- `type=${message.type}`,
72
- `summary=${String(message.summary ?? "").replaceAll(/\s+/g, "_")}`,
73
- ToolsResponse.compactPreview(message.body)
74
- ? `body=${ToolsResponse.compactPreview(message.body)}`
75
- : "",
76
- ]
77
- .filter(Boolean)
78
- .join(" "))
79
- .join("\n")}`;
80
- }
81
- function compactRoomContacts(contacts) {
82
- if (contacts.length === 0)
83
- return "\n(no room contacts)";
84
- return `\n${contacts
85
- .map((contact) => [
86
- `address=${contact.address}`,
87
- contact.role !== undefined ? `role=${String(contact.role)}` : "",
88
- contact.parent !== undefined ? `parent=${String(contact.parent)}` : "",
89
- contact.caps !== undefined
90
- ? `caps=${Array.isArray(contact.caps) ? contact.caps.join(",") : String(contact.caps)}`
91
- : "",
92
- contact.claim !== undefined
93
- ? `claim=${String(contact.claim).replaceAll(/\s+/g, "_")}`
94
- : "",
95
- contact.status !== undefined ? `status=${String(contact.status)}` : "",
96
- ]
97
- .filter(Boolean)
98
- .join(" "))
99
- .join("\n")}`;
100
- }
101
- function compactRoomRoster(roster) {
102
- const members = Object.values(roster);
103
- if (members.length === 0)
104
- return "\n(no room members)";
105
- return `\n${members
106
- .map((member) => [
107
- `address=${member.address}`,
108
- `role=${String(member.role ?? "")}`,
109
- member.parent !== undefined ? `parent=${String(member.parent)}` : "",
110
- member.caps !== undefined
111
- ? `caps=${Array.isArray(member.caps) ? member.caps.join(",") : String(member.caps)}`
112
- : "",
113
- member.claim !== undefined
114
- ? `claim=${String(member.claim).replaceAll(/\s+/g, "_")}`
115
- : "",
116
- `status=${String(member.status ?? "")}`,
117
- `last_seen=${member.last_seen}`,
118
- ].join(" "))
119
- .join("\n")}`;
120
- }
121
- function compactRoomStatus(status) {
122
- 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)}` : ""}`;
123
- }
124
- function compactCommunicationSnapshot(snapshot) {
125
- if (!snapshot)
126
- return "\n(no communication snapshot)";
127
- return `\nself=${snapshot.self} root=${snapshot.root} rooms=${snapshot.rooms.length} updated_at=${snapshot.updated_at}`;
128
- }
129
- function compactArtifactPath(value) {
130
- if (typeof value === "string")
131
- return value;
132
- const record = asRecord(value);
133
- return String(record.path ?? "<missing>");
134
- }
135
- function readBoundedReviewEvidence(status, limit) {
136
- const evidenceFile = status.evidenceFile;
137
- if (typeof evidenceFile !== "string" || !existsSync(evidenceFile))
138
- return undefined;
139
- try {
140
- const manifest = asRecord(JSON.parse(readFileSync(evidenceFile, "utf8")));
141
- const commands = Array.isArray(manifest.commands) ? manifest.commands : [];
142
- const bounded = commands.slice(-Math.max(1, limit));
143
- return {
144
- ...manifest,
145
- commands: bounded,
146
- commands_total: commands.length,
147
- ...(bounded.length < commands.length ? { commands_truncated: true } : {}),
148
- };
149
- }
150
- catch {
151
- return {
152
- status: "invalid",
153
- error: "review evidence manifest is unreadable",
154
- };
155
- }
156
- }
157
- function compactActorFiles(status) {
158
- const run = String(status.run ?? "<unknown>");
159
- const artifacts = asRecord(status.artifacts);
160
- const files = [
161
- status.stdoutLog,
162
- status.stderrLog,
163
- status.eventsFile,
164
- status.outboxFile,
165
- status.evidenceFile,
166
- status.state_dir
167
- ? `${String(status.state_dir)}/communication.json`
168
- : undefined,
169
- status.state_dir ? `${String(status.state_dir)}/result.json` : undefined,
170
- ].filter((file) => typeof file === "string");
171
- const artifactText = Object.keys(artifacts).length
172
- ? ` artifacts=${Object.entries(artifacts)
173
- .map(([key, value]) => `${key}:${compactArtifactPath(value)}`)
174
- .join(",")}`
175
- : "";
176
- const nextActions = ToolsResponse.artifactNextActions(run, artifacts);
177
- const nextText = nextActions.length
178
- ? ` next=${nextActions.map((action) => action.replaceAll(/\s+/g, "_")).join("|")}`
179
- : "";
180
- return `\nrun=${run}${artifactText}${files.length ? ` files=${files.join(",")}` : ""}${nextText}`;
181
- }
182
- function summarizeOtherSessions(currentSession, allRuns) {
183
- const otherRuns = allRuns.filter((run) => run.ownerId && run.ownerId !== currentSession);
25
+ function runtimeStatus() {
184
26
  return {
185
- other_runs: otherRuns.length,
186
- other_sessions: new Set(otherRuns.map((run) => run.ownerId)).size,
27
+ automatic_review: process.env.PI_ACTORS_AUTOMATIC_REVIEW !== "off",
28
+ run_root: Paths.getRunStateRoot(),
29
+ state_schema: RuntimeIdentity.RUN_STATE_SCHEMA,
30
+ version: RuntimeIdentity.getPackageVersion(),
187
31
  };
188
32
  }
189
- function compactSessionRuns(session, runs, summary = {}) {
190
- const suffix = [
191
- summary.other_sessions !== undefined
192
- ? `other_sessions=${String(summary.other_sessions)}`
193
- : "",
194
- summary.other_runs !== undefined
195
- ? `other_runs=${String(summary.other_runs)}`
196
- : "",
197
- ]
198
- .filter(Boolean)
199
- .join(" ");
200
- if (runs.length === 0)
201
- return `\nsession=${session} runs=0${suffix ? ` ${suffix}` : ""}`;
202
- return `\nsession=${session} runs=${runs.length}${suffix ? ` ${suffix}` : ""}\n${runs
33
+ function runtimeRuns(ctx, deps, status) {
34
+ const session = ToolsAccess.getContextSessionId(ctx);
35
+ const listed = deps.listRuns
36
+ ? deps.listRuns()
37
+ : AsyncRuns.listRuns(undefined, status);
38
+ const runs = listed
203
39
  .map((run) => {
204
- const tokens = [
205
- `run=${String(run.run ?? "")}`,
206
- `status=${String(run.status ?? "")}`,
207
- ];
208
- if (run.recipe)
209
- tokens.push(`recipe=${String(run.recipe)}`);
210
- if (run.retire_when)
211
- tokens.push(`retire_when=${String(run.retire_when)}`);
212
- return tokens.join(" ");
40
+ try {
41
+ return deps.getRunStatus
42
+ ? deps.getRunStatus(String(run.state_dir ?? run.run))
43
+ : AsyncRuns.getRunStatus(String(run.state_dir ?? run.run));
44
+ }
45
+ catch {
46
+ return run;
47
+ }
213
48
  })
214
- .join("\n")}`;
49
+ .filter((run) => !session || !run.ownerId || run.ownerId === session);
50
+ return { ...(session ? { owner_session: session } : {}), runs };
215
51
  }
216
- function getPiActorsRuntimeStatus() {
217
- const packagedRecipeRoot = Paths.getPackagedRecipeRoot();
218
- const packageRoot = dirname(packagedRecipeRoot);
219
- const packageJsonPath = join(packageRoot, "package.json");
220
- const packageJson = existsSync(packageJsonPath)
221
- ? JSON.parse(readFileSync(packageJsonPath, "utf8"))
222
- : {};
223
- let git_commit;
224
- try {
225
- git_commit = execFileSync("git", ["-C", packageRoot, "rev-parse", "--short", "HEAD"], {
226
- encoding: "utf8",
227
- stdio: ["ignore", "pipe", "ignore"],
228
- }).trim();
229
- }
230
- catch {
231
- git_commit = undefined;
232
- }
233
- const entrypoint = fileURLToPath(import.meta.url);
234
- return {
235
- automatic_recipe_review: Paths.isAutomaticRecipeReviewEnabled(),
236
- entrypoint,
237
- git_commit,
238
- mode: entrypoint.includes("/dist/") ? "dist" : "source",
239
- package_name: packageJson.name ?? "@llblab/pi-actors",
240
- package_root: packageRoot,
241
- recipe_root: Paths.getRecipeRoot(),
242
- packaged_recipe_root: packagedRecipeRoot,
243
- version: packageJson.version ?? "unknown",
244
- };
245
- }
246
- function compactPiActorsRuntimeStatus(status) {
247
- return `\npi-actors version=${String(status.version)} mode=${String(status.mode)} automatic_review=${String(status.automatic_recipe_review)} path=${String(status.package_root)} entrypoint=${String(status.entrypoint)}${status.git_commit ? ` git=${String(status.git_commit)}` : ""}`;
248
- }
249
- function isStaleClaim(message, now) {
250
- if (message.status !== "claimed")
251
- return false;
252
- const claimedAt = Date.parse(String(message.claimed_at ?? ""));
253
- return Number.isFinite(claimedAt) && now - claimedAt > 5 * 60 * 1000;
254
- }
255
- function getRunTriageSignals(runs) {
256
- const now = Date.now();
257
- const staleClaims = [];
258
- const attentionMessages = [];
52
+ function runtimeTriage(ctx, deps) {
53
+ const inventory = runtimeRuns(ctx, deps, undefined);
54
+ const runs = inventory.runs;
55
+ const failed = runs.filter((run) => run.status === "failed");
56
+ const pendingControls = [];
57
+ const staleControls = [];
58
+ const controlDiagnostics = [];
59
+ const nowMs = Date.now();
259
60
  for (const run of runs) {
260
- const stateDir = String(run.state_dir ?? "");
261
- const runId = String(run.run ?? "");
61
+ const stateDir = typeof run.state_dir === "string" ? run.state_dir : undefined;
262
62
  if (!stateDir)
263
63
  continue;
264
- try {
265
- for (const message of AsyncRuns.readRunInboxMessages(stateDir, 200)) {
266
- if (!isStaleClaim(message, now))
267
- continue;
268
- staleClaims.push({
269
- run: runId,
270
- id: message.id,
271
- claimed_at: message.claimed_at,
272
- claimed_by: message.claimed_by,
273
- type: message.type,
274
- });
275
- }
276
- }
277
- catch { }
278
- try {
279
- for (const event of AsyncRuns.readRunEvents(stateDir, 80)) {
280
- if (event.metadata?.requires_response !== true)
281
- continue;
282
- attentionMessages.push({
283
- run: runId,
284
- id: event.id,
285
- summary: event.summary,
286
- type: event.type ?? event.event,
287
- });
288
- }
64
+ const runId = String(run.run ?? "unknown");
65
+ const journal = RunsControls.readRunControlJournalFromStateDir(stateDir);
66
+ controlDiagnostics.push(...journal.diagnostics.map((diagnostic) => ({
67
+ ...(diagnostic.line === undefined ? {} : { line: diagnostic.line }),
68
+ reason: diagnostic.line === undefined
69
+ ? "unreadable_control_journal"
70
+ : "invalid_control_json",
71
+ run: runId,
72
+ })));
73
+ for (const control of journal.records) {
74
+ const classified = RuntimeTriage.classifyRuntimeControl({
75
+ run: runId,
76
+ ...(typeof run.run_instance_id === "string"
77
+ ? { runInstanceId: run.run_instance_id }
78
+ : {}),
79
+ status: String(run.status ?? "unknown"),
80
+ }, control, nowMs);
81
+ if (classified.pending)
82
+ pendingControls.push(classified.pending);
83
+ if (classified.stale)
84
+ staleControls.push(classified.stale);
85
+ if (classified.diagnostic)
86
+ controlDiagnostics.push({ ...classified.diagnostic });
289
87
  }
290
- catch { }
291
88
  }
292
- return { attention_messages: attentionMessages, stale_claims: staleClaims };
293
- }
294
- function isTriageHighRiskRecipe(recipe) {
295
- if (recipe.tool !== true)
296
- return false;
297
- const labels = Array.isArray(recipe.risk_labels)
298
- ? recipe.risk_labels.map((label) => String(label))
299
- : [];
300
- return labels.some((label) => label !== "risk.long_running");
301
- }
302
- function getPiActorsTriage(ctx, deps) {
303
- const runtime = getPiActorsRuntimeStatus();
304
- const currentSession = ToolsAccess.getContextSessionId(ctx);
305
- const listRuns = deps.listRuns ?? AsyncRuns.listRuns;
306
- const getRunStatus = deps.getRunStatus ?? AsyncRuns.getRunStatus;
307
- const allRuns = listRuns().flatMap((run) => {
308
- try {
309
- return [getRunStatus(String(run.state_dir))];
310
- }
311
- catch {
89
+ const attentionEvents = runs.flatMap((run) => {
90
+ const stateDir = typeof run.state_dir === "string" ? run.state_dir : undefined;
91
+ if (!stateDir)
312
92
  return [];
313
- }
93
+ return RunsTrace.readRunTraceEvents(stateDir)
94
+ .filter((event) => event.attention === "notify" || event.attention === "followup")
95
+ .map((event) => ({
96
+ run: run.run,
97
+ id: event.id,
98
+ kind: event.kind,
99
+ attention: event.attention,
100
+ summary: event.summary,
101
+ ts: event.ts,
102
+ }));
314
103
  });
315
- const visibleRuns = currentSession
316
- ? allRuns.filter((run) => !run.ownerId || run.ownerId === currentSession)
317
- : allRuns;
318
- const activeRuns = visibleRuns.filter((run) => run.status === "running");
319
- const failedRuns = visibleRuns.filter((run) => run.status === "failed");
320
- const otherRuns = currentSession
321
- ? allRuns.filter((run) => run.ownerId && run.ownerId !== currentSession)
322
- : [];
104
+ const boundedStaleControls = staleControls.slice(0, 40);
105
+ const staleKeys = new Set(boundedStaleControls.map((control) => `${control.run}\0${control.run_instance_id}\0${control.id}`));
106
+ const boundedPendingControls = [
107
+ ...boundedStaleControls,
108
+ ...pendingControls.filter((control) => !staleKeys.has(`${control.run}\0${control.run_instance_id}\0${control.id}`)),
109
+ ].slice(0, 40);
110
+ return {
111
+ ...inventory,
112
+ failed_runs: failed,
113
+ pending_control_count: pendingControls.length,
114
+ pending_controls: boundedPendingControls,
115
+ stale_control_count: staleControls.length,
116
+ stale_controls: boundedStaleControls,
117
+ control_diagnostic_count: controlDiagnostics.length,
118
+ control_diagnostics: controlDiagnostics.slice(0, 40),
119
+ attention_events: attentionEvents.slice(-40).reverse(),
120
+ next_actions: [
121
+ ...(failed.length ? ["inspect target=runtime view=runs status=failed"] : []),
122
+ ...staleControls.slice(0, 3).map((entry) => `inspect target=run:${entry.run} view=control`),
123
+ ...(attentionEvents.length
124
+ ? attentionEvents.slice(-3).map((entry) => `inspect target=run:${String(entry.run)} view=trace source=runtime`)
125
+ : []),
126
+ ],
127
+ };
128
+ }
129
+ function compactRuntime(view, details) {
130
+ if (view === "runs") {
131
+ return `\nruntime runs=${details.runs?.length ?? 0}`;
132
+ }
133
+ if (view === "triage") {
134
+ return `\nruntime failed=${details.failed_runs?.length ?? 0} pending_controls=${Number(details.pending_control_count ?? 0)} stale_controls=${Number(details.stale_control_count ?? 0)} attention=${details.attention_events?.length ?? 0}`;
135
+ }
136
+ return `\nruntime version=${String(details.version)} state_schema=${String(details.state_schema)} automatic_review=${String(details.automatic_review)}`;
137
+ }
138
+ function inspectRuntime(view, input, ctx, deps) {
139
+ if (view === "status")
140
+ return runtimeStatus();
141
+ if (view === "runs") {
142
+ return runtimeRuns(ctx, deps, typeof input.status === "string" ? input.status : undefined);
143
+ }
144
+ if (view === "triage")
145
+ return runtimeTriage(ctx, deps);
146
+ throw new Error("inspect runtime supports view=status, view=runs, or view=triage.");
147
+ }
148
+ function inspectRecipes(view, deps) {
149
+ if (view !== "status" &&
150
+ view !== "summary" &&
151
+ view !== "doctor" &&
152
+ view !== "imports" &&
153
+ view !== "reviews") {
154
+ throw new Error("inspect recipes supports view=status, view=summary, view=doctor, view=imports, or view=reviews.");
155
+ }
323
156
  const recipeRoot = deps.recipeRoot ?? Paths.getRecipeRoot();
157
+ if (view === "reviews") {
158
+ return ReviewDiagnostics.readAutomaticReviewDiagnostics({ recipeRoot });
159
+ }
324
160
  const discovered = RecipesDiscovery.discoverRecipeSources([
325
161
  { root: recipeRoot, defaultTool: true, mutableUsage: true },
326
162
  { root: deps.packagedRecipeRoot ?? Paths.getPackagedRecipeRoot() },
327
163
  ]);
328
- const recipeSummary = {
164
+ const summary = {
329
165
  ...RecipesDiscovery.summarizeDiscovery(discovered),
330
166
  drafts: RecipesDiscovery.listDraftRecipes(join(recipeRoot, "drafts")),
331
167
  };
332
- const activeRecipes = Array.isArray(recipeSummary.active)
333
- ? recipeSummary.active
334
- : [];
335
- const highRiskRecipes = activeRecipes.filter(isTriageHighRiskRecipe);
336
- const signals = getRunTriageSignals(visibleRuns);
337
- const attentionMessages = signals.attention_messages;
338
- const staleClaims = signals.stale_claims;
339
- const invalidRecipes = Array.isArray(recipeSummary.invalid)
340
- ? recipeSummary.invalid
341
- : [];
342
- const remediations = Array.isArray(recipeSummary.remediations)
343
- ? recipeSummary.remediations
344
- : [];
345
- const drafts = Array.isArray(recipeSummary.drafts)
346
- ? recipeSummary.drafts
347
- : [];
348
- const nextActions = [
349
- invalidRecipes.length || remediations.length
350
- ? "inspect target=recipes view=doctor"
351
- : "",
352
- drafts.length ? "inspect target=recipes view=summary verbose=true" : "",
353
- failedRuns[0]?.run
354
- ? `inspect target=run:${String(failedRuns[0].run)} view=tail lines=80`
355
- : "",
356
- attentionMessages[0]?.run
357
- ? `inspect target=run:${String(attentionMessages[0].run)} view=messages`
358
- : "",
359
- activeRuns.length
360
- ? "inspect target=session:all view=runs status=active"
361
- : "",
362
- ].filter(Boolean);
363
168
  return {
364
- runtime,
365
- current_session: currentSession ?? null,
366
- active_runs: activeRuns.map((run) => ({
367
- run: run.run,
368
- ownerId: run.ownerId,
369
- recipe: run.recipe,
370
- status: run.status,
371
- })),
372
- other_session_runs: otherRuns.length,
373
- invalid_recipes: invalidRecipes,
374
- blocking_recipes: remediations.filter((item) => String(item.kind ?? "").startsWith("blocking_")),
375
- high_risk_recipes: highRiskRecipes.map((recipe) => ({
376
- id: recipe.id,
377
- path: recipe.path,
378
- risk_labels: recipe.risk_labels,
379
- })),
380
- draft_recipes: drafts,
381
- stale_claims: staleClaims,
382
- recent_failed_runs: failedRuns.slice(0, 5).map((run) => ({
383
- run: run.run,
384
- recipe: run.recipe,
385
- status: run.status,
386
- })),
387
- attention_messages: attentionMessages.slice(-10),
388
- next_actions: [...new Set(nextActions)].slice(0, 5),
169
+ ...summary,
170
+ next_actions: ToolsResponse.recipeRegistryNextActions(summary, view),
389
171
  };
390
172
  }
391
- function compactPiActorsTriage(summary) {
392
- const runtime = asRecord(summary.runtime);
393
- const activeRuns = Array.isArray(summary.active_runs)
394
- ? summary.active_runs.length
395
- : 0;
396
- const invalidRecipes = Array.isArray(summary.invalid_recipes)
397
- ? summary.invalid_recipes.length
398
- : 0;
399
- const blockingRecipes = Array.isArray(summary.blocking_recipes)
400
- ? summary.blocking_recipes.length
401
- : 0;
402
- const highRiskRecipes = Array.isArray(summary.high_risk_recipes)
403
- ? summary.high_risk_recipes.length
404
- : 0;
405
- const drafts = Array.isArray(summary.draft_recipes)
406
- ? summary.draft_recipes.length
407
- : 0;
408
- const staleClaims = Array.isArray(summary.stale_claims)
409
- ? summary.stale_claims.length
410
- : 0;
411
- const failedRuns = Array.isArray(summary.recent_failed_runs)
412
- ? summary.recent_failed_runs.length
413
- : 0;
414
- const attention = Array.isArray(summary.attention_messages)
415
- ? summary.attention_messages.length
416
- : 0;
417
- const nextActions = Array.isArray(summary.next_actions)
418
- ? summary.next_actions
419
- : [];
420
- return `\ntriage version=${String(runtime.version ?? "unknown")} mode=${String(runtime.mode ?? "unknown")} active_runs=${activeRuns} other_runs=${String(summary.other_session_runs ?? 0)} invalid_recipes=${invalidRecipes} blocking_recipes=${blockingRecipes} high_risk_recipes=${highRiskRecipes} drafts=${drafts} stale_claims=${staleClaims} failed_runs=${failedRuns} attention=${attention}${ToolsResponse.compactNextActions(nextActions)}`;
173
+ function parseRunTarget(target) {
174
+ if (!target.startsWith("run:"))
175
+ return undefined;
176
+ const run = target.slice(4);
177
+ return run && /^[A-Za-z0-9_.-]+$/.test(run) ? run : undefined;
421
178
  }
422
- function compactToolActor(name, tool) {
423
- const parameters = asRecord(tool.parameters);
424
- const required = Array.isArray(parameters.required)
425
- ? parameters.required.join(",")
426
- : "";
427
- const properties = asRecord(parameters.properties);
428
- return `\ntool=${name} description=${String(tool.description ?? "").replaceAll(/\s+/g, "_")} args=${Object.keys(properties).join(",")} required=${required}`;
179
+ function inspectRun(run, view, input, ctx, deps) {
180
+ if (view !== "recipe" && view !== "trace" && view !== "control") {
181
+ throw new Error("inspect run:<id> supports view=recipe, view=trace, or view=control.");
182
+ }
183
+ const status = deps.getRunStatus
184
+ ? deps.getRunStatus(run)
185
+ : ToolsAccess.assertRunAccessibleToContext(run, ctx);
186
+ const stateDir = String(status.state_dir);
187
+ if (view === "recipe") {
188
+ return Inspector.readActorInspectorRecipe(stateDir);
189
+ }
190
+ if (view === "trace") {
191
+ const source = typeof input.source === "string" ? input.source : "all";
192
+ if (source !== "all" &&
193
+ source !== "lifecycle" &&
194
+ source !== "control" &&
195
+ source !== "process" &&
196
+ source !== "agent" &&
197
+ source !== "artifact" &&
198
+ source !== "runtime") {
199
+ throw new Error(`unsupported Trace source: ${source}`);
200
+ }
201
+ return {
202
+ run,
203
+ source,
204
+ items: TraceProjection.projectRunTrace(stateDir, {
205
+ artifacts: status.artifacts,
206
+ limit: Number(input.lines || Limits.DEFAULT_INSPECT_LINES),
207
+ source,
208
+ }),
209
+ };
210
+ }
211
+ const runInstanceId = String(status.run_instance_id ?? "");
212
+ return {
213
+ run,
214
+ run_instance_id: runInstanceId,
215
+ status: status.status,
216
+ actor_actions: Array.isArray(status.control) ? status.control : [],
217
+ runtime_actions: status.status === "running" ? ["kill"] : ["archive", "prune"],
218
+ endpoint: runInstanceId
219
+ ? RunControlDelivery.readRunControlEndpoint(stateDir, runInstanceId)
220
+ : undefined,
221
+ recent_controls: RunsControls.readRunControlsFromStateDir(stateDir)
222
+ .slice(-Math.max(1, Number(input.lines || 20)))
223
+ .reverse()
224
+ .map(ControlProjection.projectRunControl),
225
+ };
226
+ }
227
+ function inspectTool(name, view, deps) {
228
+ if (!name || !/^[A-Za-z0-9_.-]+$/.test(name)) {
229
+ throw new Error(`invalid tool target: tool:${name}`);
230
+ }
231
+ if (view !== "status" && view !== "schema") {
232
+ throw new Error("inspect tool:<name> supports view=status or view=schema.");
233
+ }
234
+ const tool = deps.getTool?.(name);
235
+ if (!tool)
236
+ throw new Error(`registered tool not found: ${name}`);
237
+ return {
238
+ name,
239
+ description: tool.description,
240
+ parameters: tool.parameters,
241
+ promptSnippet: tool.promptSnippet,
242
+ };
243
+ }
244
+ function compactResult(target, view, details) {
245
+ if (target === "runtime")
246
+ return compactRuntime(view, details);
247
+ if (target === "recipes")
248
+ return ToolsResponse.compactRecipeRegistry(details);
249
+ if (target.startsWith("tool:"))
250
+ return `\ntool=${target.slice(5)} view=${view}`;
251
+ return `\nrun=${target.slice(4)} view=${view}`;
429
252
  }
430
253
  export function createInspectToolDefinition(deps = {}) {
431
254
  return {
432
255
  name: "inspect",
433
256
  label: "Inspect",
434
- 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.",
257
+ description: "Inspect a Run's Recipe, Trace, or Control evidence, or runtime/recipe/tool diagnostics.",
435
258
  parameters: Schema.objectSchema({
436
- lines: Schema.stringSchema("Line count for tail/messages views. Default 40."),
437
- status: Schema.stringSchema("Optional session run filter: all, running, active, terminal, done, failed, cancelled, killed, or exited."),
438
- 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."),
439
- verbose: Schema.booleanSchema("Return full JSON instead of compact text where available."),
440
- view: Schema.stringSchema("Inspection view. Core run views: status, tail, messages, artifacts, files, mailbox. Advanced views include communication, roster, and contacts."),
259
+ lines: Schema.stringSchema("Bounded item count for Trace or recent Controls."),
260
+ source: Schema.stringSchema("Optional Trace source: all, lifecycle, control, process, agent, artifact, or runtime."),
261
+ status: Schema.stringSchema("Optional runtime Run status filter."),
262
+ target: Schema.stringSchema("Target: run:<id>, runtime, recipes, or tool:<name>."),
263
+ verbose: Schema.booleanSchema("Return full JSON instead of compact text."),
264
+ view: Schema.stringSchema("Run view recipe|trace|control, or target-specific management view."),
441
265
  }, ["target", "view"]),
442
266
  async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
443
267
  const input = asRecord(params);
444
268
  const target = String(input.target ?? "");
445
269
  const view = String(input.view ?? "");
446
- if (target === "recipes" || target === "recipe-registry") {
447
- if (view !== "status" &&
448
- view !== "summary" &&
449
- view !== "doctor" &&
450
- view !== "imports" &&
451
- view !== "reviews") {
452
- throw new Error("inspect recipes supports view=status, view=summary, view=doctor, view=imports, or view=reviews.");
453
- }
454
- if (view === "reviews") {
455
- const diagnostics = ReviewDiagnostics.readAutomaticReviewDiagnostics({
456
- recipeRoot: deps.recipeRoot ?? Paths.getRecipeRoot(),
457
- });
458
- return {
459
- content: [{
460
- type: "text",
461
- text: maybeJsonText(diagnostics, input.verbose === true, compactAutomaticReviewDiagnostics(diagnostics)),
462
- }],
463
- details: diagnostics,
464
- };
465
- }
466
- const discovered = RecipesDiscovery.discoverRecipeSources([
467
- {
468
- root: deps.recipeRoot ?? Paths.getRecipeRoot(),
469
- defaultTool: true,
470
- mutableUsage: true,
471
- },
472
- { root: deps.packagedRecipeRoot ?? Paths.getPackagedRecipeRoot() },
473
- ]);
474
- const recipeRoot = deps.recipeRoot ?? Paths.getRecipeRoot();
475
- const summaryBase = {
476
- ...RecipesDiscovery.summarizeDiscovery(discovered),
477
- drafts: RecipesDiscovery.listDraftRecipes(join(recipeRoot, "drafts")),
478
- };
479
- const summary = {
480
- ...summaryBase,
481
- next_actions: ToolsResponse.recipeRegistryNextActions(summaryBase, view),
482
- };
483
- return {
484
- content: [
485
- {
486
- type: "text",
487
- text: maybeJsonText(summary, input.verbose === true, view === "doctor"
488
- ? ToolsResponse.compactRecipeDoctor(summary)
489
- : view === "imports"
490
- ? ToolsResponse.compactRecipeImports(summary)
491
- : ToolsResponse.compactRecipeRegistry(summary)),
492
- },
493
- ],
494
- details: summary,
495
- };
496
- }
497
- const address = Messages.parseActorAddress(target);
498
- if (address.kind === "coordinator") {
499
- if (view !== "status" && view !== "runs") {
500
- throw new Error("inspect coordinator supports view=status or view=runs.");
501
- }
502
- const session = ToolsAccess.requireContextSessionId(ctx, "inspect coordinator");
503
- const allRuns = AsyncRuns.listRuns(undefined, typeof input.status === "string" ? input.status : undefined).map((run) => AsyncRuns.getRunStatus(String(run.state_dir)));
504
- const runs = allRuns.filter((run) => run.ownerId === session);
505
- const sessionSummary = summarizeOtherSessions(session, allRuns);
506
- return {
507
- content: [
508
- {
509
- type: "text",
510
- text: maybeJsonText({ session, runs, ...sessionSummary }, input.verbose === true, compactSessionRuns(session, runs, sessionSummary)),
511
- },
512
- ],
513
- details: { session, runs, ...sessionSummary },
514
- };
515
- }
516
- if (address.kind === "session") {
517
- if (view !== "status" && view !== "runs") {
518
- throw new Error("inspect session:<id> supports view=status or view=runs.");
519
- }
520
- const allRuns = AsyncRuns.listRuns(undefined, typeof input.status === "string" ? input.status : undefined).map((run) => AsyncRuns.getRunStatus(String(run.state_dir)));
521
- const runs = allRuns.filter((run) => address.value === "all" || run.ownerId === address.value);
522
- const sessionSummary = address.value === "all"
523
- ? {}
524
- : summarizeOtherSessions(address.value || "", allRuns);
525
- return {
526
- content: [
527
- {
528
- type: "text",
529
- text: maybeJsonText({ session: address.value, runs, ...sessionSummary }, input.verbose === true, compactSessionRuns(address.value || "", runs, sessionSummary)),
530
- },
531
- ],
532
- details: { session: address.value, runs, ...sessionSummary },
533
- };
534
- }
535
- if (address.kind === "tool" && address.value) {
536
- if (address.value === "pi-actors") {
537
- if (view !== "status" && view !== "triage") {
538
- throw new Error("inspect tool:pi-actors supports view=status or view=triage.");
539
- }
540
- const details = view === "triage"
541
- ? getPiActorsTriage(ctx, deps)
542
- : getPiActorsRuntimeStatus();
543
- return {
544
- content: [
545
- {
546
- type: "text",
547
- text: maybeJsonText(details, input.verbose === true, view === "triage"
548
- ? compactPiActorsTriage(details)
549
- : compactPiActorsRuntimeStatus(details)),
550
- },
551
- ],
552
- details,
553
- };
554
- }
555
- if (view !== "status" && view !== "schema") {
556
- throw new Error("inspect tool:<name> supports view=status or view=schema.");
557
- }
558
- const tool = deps.getTool?.(address.value);
559
- if (!tool)
560
- throw new Error(`tool actor not found: ${address.value}`);
561
- const details = {
562
- name: address.value,
563
- description: tool.description,
564
- parameters: tool.parameters,
565
- promptSnippet: tool.promptSnippet,
566
- };
567
- return {
568
- content: [
569
- {
570
- type: "text",
571
- text: maybeJsonText(details, input.verbose === true || view === "schema", compactToolActor(address.value, details)),
572
- },
573
- ],
574
- details,
575
- };
270
+ let details;
271
+ if (target === "runtime") {
272
+ details = inspectRuntime(view, input, ctx, deps);
576
273
  }
577
- if (address.kind === "room" && address.value && address.room) {
578
- const status = ToolsAccess.assertRunAccessibleToContext(address.value, ctx);
579
- const stateDir = String(status.state_dir ?? "");
580
- if (!stateDir)
581
- throw new Error(`room:${address.value} has no run state directory.`);
582
- if (view === "status") {
583
- const status = Rooms.getRoomStatus(stateDir, address.room);
584
- return {
585
- content: [
586
- {
587
- type: "text",
588
- text: maybeJsonText(status, input.verbose === true, compactRoomStatus(status)),
589
- },
590
- ],
591
- details: status,
592
- };
593
- }
594
- if (view === "previews") {
595
- const previews = Rooms.readRoomMessagePreviews(stateDir, address.room, Number(input.lines || Limits.DEFAULT_INSPECT_LINES));
596
- return {
597
- content: [
598
- {
599
- type: "text",
600
- text: maybeJsonText(previews, input.verbose === true, compactRoomPreviews(previews)),
601
- },
602
- ],
603
- details: { previews },
604
- };
605
- }
606
- if (view === "messages") {
607
- const messages = Rooms.readRoomMessages(stateDir, address.room, Number(input.lines || Limits.DEFAULT_INSPECT_LINES));
608
- return {
609
- content: [
610
- {
611
- type: "text",
612
- text: maybeJsonText(messages, input.verbose === true, compactRoomMessages(messages)),
613
- },
614
- ],
615
- details: { messages },
616
- };
617
- }
618
- if (view === "contacts") {
619
- const contacts = Rooms.readRoomContacts(stateDir, address.room);
620
- return {
621
- content: [
622
- {
623
- type: "text",
624
- text: maybeJsonText(contacts, input.verbose === true, compactRoomContacts(contacts)),
625
- },
626
- ],
627
- details: { contacts },
628
- };
629
- }
630
- if (view === "roster") {
631
- const roster = Rooms.readRoomRoster(stateDir, address.room);
632
- return {
633
- content: [
634
- {
635
- type: "text",
636
- text: maybeJsonText(roster, input.verbose === true, compactRoomRoster(roster)),
637
- },
638
- ],
639
- details: { roster },
640
- };
641
- }
642
- throw new Error("inspect room:<run> supports view=status, view=messages, view=previews, view=roster, or view=contacts.");
274
+ else if (target === "recipes") {
275
+ details = inspectRecipes(view, deps);
643
276
  }
644
- const runId = address.kind === "run" || address.kind === "branch"
645
- ? address.value
646
- : undefined;
647
- if (!runId)
648
- throw new Error("inspect target must be run:<id>, branch:<run>/<branch>, coordinator, session:<id>, or tool:<name>.");
649
- if (address.kind === "branch") {
650
- if (view !== "mailbox")
651
- throw new Error("inspect branch:<run>/<branch> supports view=mailbox.");
652
- const status = ToolsAccess.assertRunAccessibleToContext(runId, ctx);
653
- const branchInbox = Rooms.readBranchInboxDiagnostics(String(status.state_dir ?? ""), runId, target, Number(input.lines || Limits.DEFAULT_INSPECT_LINES));
654
- const messages = branchInbox.messages;
655
- return {
656
- content: [
657
- {
658
- type: "text",
659
- text: maybeJsonText(input.verbose === true ? branchInbox : messages, input.verbose === true, `${ToolsResponse.compactBranchInbox(messages.map((message) => ({ ...message })))}${branchInbox.corrupted > 0 ? `\ncorrupted=${branchInbox.corrupted}` : ""}`),
660
- },
661
- ],
662
- details: { corrupted: branchInbox.corrupted, messages },
663
- };
664
- }
665
- switch (view) {
666
- case "status": {
667
- const status = ToolsAccess.assertRunAccessibleToContext(runId, ctx);
668
- return {
669
- content: [
670
- {
671
- type: "text",
672
- text: maybeJsonText(status, input.verbose === true, ToolsResponse.compactAsyncRunStatus(status)),
673
- },
674
- ],
675
- details: status,
676
- };
677
- }
678
- case "tail": {
679
- ToolsAccess.assertRunAccessibleToContext(runId, ctx);
680
- const text = AsyncRuns.tailRun(runId, Number(input.lines || Limits.DEFAULT_INSPECT_LINES));
681
- return {
682
- content: [{ type: "text", text: `\n${text}` }],
683
- details: {},
684
- };
685
- }
686
- case "messages": {
687
- ToolsAccess.assertRunAccessibleToContext(runId, ctx);
688
- const messages = AsyncRuns.readRunEvents(runId, Number(input.lines || Limits.DEFAULT_INSPECT_LINES));
689
- return {
690
- content: [
691
- {
692
- type: "text",
693
- text: maybeJsonText(messages, input.verbose === true, compactRunMessages(messages)),
694
- },
695
- ],
696
- details: { messages },
697
- };
698
- }
699
- case "artifacts":
700
- case "files": {
701
- const status = ToolsAccess.assertRunAccessibleToContext(runId, ctx);
702
- const artifactManifest = AsyncRuns.resolveArtifactManifest(status.artifacts);
703
- const reviewEvidence = readBoundedReviewEvidence(status, Number(input.lines || Limits.DEFAULT_INSPECT_LINES));
704
- const details = artifactManifest
705
- ? {
706
- ...status,
707
- ...(reviewEvidence ? { review_evidence: reviewEvidence } : {}),
708
- artifact_manifest: artifactManifest,
709
- next_actions: ToolsResponse.artifactNextActions(status.run ?? runId, asRecord(status.artifacts)),
710
- }
711
- : {
712
- ...status,
713
- ...(reviewEvidence ? { review_evidence: reviewEvidence } : {}),
714
- };
715
- return {
716
- content: [
717
- {
718
- type: "text",
719
- text: maybeJsonText(details, input.verbose === true, compactActorFiles(status)),
720
- },
721
- ],
722
- details,
723
- };
724
- }
725
- case "mailbox": {
726
- const status = ToolsAccess.assertRunAccessibleToContext(runId, ctx);
727
- const mailbox = asRecord(status.mailbox);
728
- const normalizedMailbox = ToolsMailbox.normalizeMailboxContracts(mailbox);
729
- const messages = AsyncRuns.readRunInboxMessages(runId, Number(input.lines || Limits.DEFAULT_INSPECT_LINES));
730
- const details = {
731
- mailbox,
732
- normalized_mailbox: normalizedMailbox,
733
- messages,
734
- };
735
- return {
736
- content: [
737
- {
738
- type: "text",
739
- text: maybeJsonText(details, input.verbose === true, ToolsResponse.compactRunMailbox(String(status.run ?? runId), mailbox, messages)),
740
- },
741
- ],
742
- details,
743
- };
277
+ else {
278
+ const run = parseRunTarget(target);
279
+ if (run)
280
+ details = inspectRun(run, view, input, ctx, deps);
281
+ else if (target.startsWith("tool:")) {
282
+ details = inspectTool(target.slice(5), view, deps);
744
283
  }
745
- case "communication": {
746
- const status = ToolsAccess.assertRunAccessibleToContext(runId, ctx);
747
- const snapshot = Rooms.readCommunicationSnapshot(String(status.state_dir ?? ""));
748
- return {
749
- content: [
750
- {
751
- type: "text",
752
- text: maybeJsonText(snapshot ?? {}, input.verbose === true, compactCommunicationSnapshot(snapshot)),
753
- },
754
- ],
755
- details: { communication: snapshot },
756
- };
284
+ else {
285
+ throw new Error(`unsupported inspect target: ${target}; use run:<id>, runtime, recipes, or tool:<name>`);
757
286
  }
758
- default:
759
- throw new Error("inspect view must be one of: status, tail, messages, artifacts, files, mailbox, communication; branch targets support mailbox.");
760
287
  }
288
+ return {
289
+ content: [{
290
+ type: "text",
291
+ text: maybeJsonText(details, input.verbose === true || (target.startsWith("tool:") && view === "schema"), compactResult(target, view, details)),
292
+ }],
293
+ details,
294
+ };
761
295
  },
762
296
  };
763
297
  }