@llblab/pi-actors 0.42.3 → 0.43.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.
Files changed (252) hide show
  1. package/AGENTS.md +121 -175
  2. package/CHANGELOG.md +12 -0
  3. package/README.md +113 -276
  4. package/dist/fixtures/protocol/control-endpoint.json +6 -0
  5. package/dist/fixtures/protocol/control-record.json +9 -0
  6. package/dist/fixtures/protocol/recipe-summary.json +4 -12
  7. package/dist/fixtures/protocol/trace-event.json +9 -0
  8. package/dist/lib/async-runs.d.ts +14 -38
  9. package/dist/lib/async-runs.js +158 -108
  10. package/dist/lib/control.d.ts +12 -0
  11. package/dist/lib/control.js +84 -0
  12. package/dist/lib/execution-sessions.d.ts +17 -0
  13. package/dist/lib/execution-sessions.js +85 -0
  14. package/dist/lib/file-state.d.ts +1 -0
  15. package/dist/lib/file-state.js +17 -5
  16. package/dist/lib/inspector-actions.d.ts +2 -2
  17. package/dist/lib/inspector-actions.js +2 -2
  18. package/dist/lib/inspector-command.js +3 -3
  19. package/dist/lib/inspector-overlay.d.ts +52 -70
  20. package/dist/lib/inspector-overlay.js +532 -905
  21. package/dist/lib/inspector.d.ts +3 -71
  22. package/dist/lib/inspector.js +19 -665
  23. package/dist/lib/limits.d.ts +4 -2
  24. package/dist/lib/limits.js +4 -2
  25. package/dist/lib/observability.d.ts +16 -16
  26. package/dist/lib/observability.js +43 -82
  27. package/dist/lib/prompts.d.ts +1 -1
  28. package/dist/lib/prompts.js +2 -2
  29. package/dist/lib/recipe-control.d.ts +7 -0
  30. package/dist/lib/recipe-control.js +39 -0
  31. package/dist/lib/recipes-discovery.js +2 -0
  32. package/dist/lib/recipes-references.d.ts +1 -14
  33. package/dist/lib/recipes-references.js +6 -21
  34. package/dist/lib/review-projection.js +1 -5
  35. package/dist/lib/run-ui-runtime.js +2 -2
  36. package/dist/lib/runs-control-delivery.d.ts +21 -0
  37. package/dist/lib/runs-control-delivery.js +127 -0
  38. package/dist/lib/runs-controls.d.ts +35 -0
  39. package/dist/lib/runs-controls.js +144 -0
  40. package/dist/lib/runs-retention.d.ts +7 -0
  41. package/dist/lib/runs-retention.js +27 -3
  42. package/dist/lib/runs-start.js +4 -2
  43. package/dist/lib/runs-status.js +11 -6
  44. package/dist/lib/runs-trace.d.ts +24 -0
  45. package/dist/lib/runs-trace.js +98 -0
  46. package/dist/lib/runtime-notifier.d.ts +1 -1
  47. package/dist/lib/runtime-notifier.js +1 -1
  48. package/dist/lib/tools-inspect.d.ts +3 -3
  49. package/dist/lib/tools-inspect.js +203 -708
  50. package/dist/lib/tools-local.js +2 -10
  51. package/dist/lib/tools-message.d.ts +7 -7
  52. package/dist/lib/tools-message.js +95 -396
  53. package/dist/lib/tools-response.d.ts +1 -4
  54. package/dist/lib/tools-response.js +5 -39
  55. package/dist/lib/tools-spawn.js +16 -28
  56. package/dist/lib/tools.js +1 -2
  57. package/dist/lib/trace-projection.d.ts +22 -0
  58. package/dist/lib/trace-projection.js +165 -0
  59. package/dist/recipes/draft-review.json +0 -10
  60. package/dist/recipes/lens-swarm.json +0 -14
  61. package/dist/recipes/music-player.json +10 -19
  62. package/dist/recipes/pipeline-architect-coordinator.json +0 -11
  63. package/dist/recipes/pipeline-artifact-bundle.json +1 -22
  64. package/dist/recipes/pipeline-artifact-report.json +1 -18
  65. package/dist/recipes/pipeline-artifact-write.json +1 -18
  66. package/dist/recipes/pipeline-async-run-ops.json +0 -12
  67. package/dist/recipes/pipeline-checkpoint-continuation.json +0 -14
  68. package/dist/recipes/pipeline-development-tasking.json +0 -12
  69. package/dist/recipes/pipeline-docs-maintenance.json +0 -12
  70. package/dist/recipes/pipeline-media-library.json +0 -12
  71. package/dist/recipes/pipeline-quorum-review.json +0 -12
  72. package/dist/recipes/pipeline-release-readiness.json +0 -12
  73. package/dist/recipes/pipeline-release-summary.json +0 -12
  74. package/dist/recipes/pipeline-repo-health.json +0 -12
  75. package/dist/recipes/pipeline-research-synthesis.json +0 -11
  76. package/dist/recipes/pipeline-review-readiness.json +0 -12
  77. package/dist/recipes/resource-locker.json +27 -0
  78. package/dist/recipes/subagent-artifact.json +0 -9
  79. package/dist/recipes/subagent-checkpoint.json +0 -10
  80. package/dist/recipes/subagent-conflict-report.json +0 -11
  81. package/dist/recipes/subagent-contradiction-map.json +0 -11
  82. package/dist/recipes/subagent-critic.json +0 -11
  83. package/dist/recipes/subagent-evidence-map.json +0 -11
  84. package/dist/recipes/subagent-followup.json +0 -10
  85. package/dist/recipes/subagent-judge.json +0 -11
  86. package/dist/recipes/subagent-merge.json +0 -11
  87. package/dist/recipes/subagent-normalize.json +0 -11
  88. package/dist/recipes/subagent-plan.json +0 -11
  89. package/dist/recipes/subagent-preflight.json +0 -11
  90. package/dist/recipes/subagent-prompt.json +0 -10
  91. package/dist/recipes/subagent-quorum.json +0 -10
  92. package/dist/recipes/subagent-review-coordinator.json +0 -14
  93. package/dist/recipes/subagent-review.json +0 -11
  94. package/dist/recipes/subagent-task-card.json +0 -11
  95. package/dist/recipes/subagent-tools.json +0 -10
  96. package/dist/recipes/subagent-verify.json +0 -11
  97. package/dist/recipes/subagents-prompts.json +0 -10
  98. package/dist/recipes/tool-review.json +0 -10
  99. package/dist/scripts/async-runner.mjs +25 -25
  100. package/dist/scripts/conformance.mjs +4 -2
  101. package/dist/scripts/locker.mjs +200 -66
  102. package/dist/scripts/music-player.mjs +159 -150
  103. package/dist/scripts/recipe-utils.mjs +6 -96
  104. package/dist/scripts/release-gates.mjs +60 -0
  105. package/dist/scripts/validate-recipe.mjs +3 -53
  106. package/dist/skills/actors/SKILL.md +53 -266
  107. package/dist/skills/swarm/SKILL.md +11 -33
  108. package/docs/0.43-baseline.md +44 -0
  109. package/docs/README.md +3 -3
  110. package/docs/actor-inspector.md +26 -64
  111. package/docs/actors-deep-reference.md +92 -50
  112. package/docs/async-runs.md +81 -328
  113. package/docs/command-templates.md +2 -2
  114. package/docs/component-recipes.md +30 -133
  115. package/docs/recipe-library.md +57 -182
  116. package/docs/task-first-recipes.md +10 -12
  117. package/docs/template-recipes.md +76 -289
  118. package/docs/tool-registry.md +41 -161
  119. package/fixtures/protocol/control-endpoint.json +6 -0
  120. package/fixtures/protocol/control-record.json +9 -0
  121. package/fixtures/protocol/recipe-summary.json +4 -12
  122. package/fixtures/protocol/trace-event.json +9 -0
  123. package/lib/async-runs.ts +202 -201
  124. package/lib/control.ts +102 -0
  125. package/lib/execution-sessions.ts +111 -0
  126. package/lib/file-state.ts +17 -4
  127. package/lib/inspector-actions.ts +2 -2
  128. package/lib/inspector-command.ts +3 -3
  129. package/lib/inspector-overlay.ts +577 -1121
  130. package/lib/inspector.ts +46 -979
  131. package/lib/limits.ts +4 -2
  132. package/lib/observability.ts +60 -101
  133. package/lib/prompts.ts +2 -2
  134. package/lib/recipe-control.ts +45 -0
  135. package/lib/recipes-discovery.ts +2 -0
  136. package/lib/recipes-references.ts +9 -45
  137. package/lib/review-projection.ts +1 -5
  138. package/lib/run-ui-runtime.ts +2 -2
  139. package/lib/runs-control-delivery.ts +181 -0
  140. package/lib/runs-controls.ts +204 -0
  141. package/lib/runs-retention.ts +38 -3
  142. package/lib/runs-start.ts +4 -2
  143. package/lib/runs-status.ts +11 -6
  144. package/lib/runs-trace.ts +132 -0
  145. package/lib/runtime-notifier.ts +1 -1
  146. package/lib/tools-inspect.ts +240 -901
  147. package/lib/tools-local.ts +2 -12
  148. package/lib/tools-message.ts +112 -519
  149. package/lib/tools-response.ts +5 -52
  150. package/lib/tools-spawn.ts +16 -32
  151. package/lib/tools.ts +1 -2
  152. package/lib/trace-projection.ts +221 -0
  153. package/package.json +2 -1
  154. package/recipes/draft-review.json +0 -10
  155. package/recipes/lens-swarm.json +0 -14
  156. package/recipes/music-player.json +10 -19
  157. package/recipes/pipeline-architect-coordinator.json +0 -11
  158. package/recipes/pipeline-artifact-bundle.json +1 -22
  159. package/recipes/pipeline-artifact-report.json +1 -18
  160. package/recipes/pipeline-artifact-write.json +1 -18
  161. package/recipes/pipeline-async-run-ops.json +0 -12
  162. package/recipes/pipeline-checkpoint-continuation.json +0 -14
  163. package/recipes/pipeline-development-tasking.json +0 -12
  164. package/recipes/pipeline-docs-maintenance.json +0 -12
  165. package/recipes/pipeline-media-library.json +0 -12
  166. package/recipes/pipeline-quorum-review.json +0 -12
  167. package/recipes/pipeline-release-readiness.json +0 -12
  168. package/recipes/pipeline-release-summary.json +0 -12
  169. package/recipes/pipeline-repo-health.json +0 -12
  170. package/recipes/pipeline-research-synthesis.json +0 -11
  171. package/recipes/pipeline-review-readiness.json +0 -12
  172. package/recipes/resource-locker.json +27 -0
  173. package/recipes/subagent-artifact.json +0 -9
  174. package/recipes/subagent-checkpoint.json +0 -10
  175. package/recipes/subagent-conflict-report.json +0 -11
  176. package/recipes/subagent-contradiction-map.json +0 -11
  177. package/recipes/subagent-critic.json +0 -11
  178. package/recipes/subagent-evidence-map.json +0 -11
  179. package/recipes/subagent-followup.json +0 -10
  180. package/recipes/subagent-judge.json +0 -11
  181. package/recipes/subagent-merge.json +0 -11
  182. package/recipes/subagent-normalize.json +0 -11
  183. package/recipes/subagent-plan.json +0 -11
  184. package/recipes/subagent-preflight.json +0 -11
  185. package/recipes/subagent-prompt.json +0 -10
  186. package/recipes/subagent-quorum.json +0 -10
  187. package/recipes/subagent-review-coordinator.json +0 -14
  188. package/recipes/subagent-review.json +0 -11
  189. package/recipes/subagent-task-card.json +0 -11
  190. package/recipes/subagent-tools.json +0 -10
  191. package/recipes/subagent-verify.json +0 -11
  192. package/recipes/subagents-prompts.json +0 -10
  193. package/recipes/tool-review.json +0 -10
  194. package/scripts/async-runner.mjs +25 -25
  195. package/scripts/conformance.mjs +4 -2
  196. package/scripts/locker.mjs +200 -66
  197. package/scripts/music-player.mjs +159 -150
  198. package/scripts/recipe-utils.mjs +6 -96
  199. package/scripts/release-gates.mjs +60 -0
  200. package/scripts/validate-recipe.mjs +3 -53
  201. package/skills/actors/SKILL.md +53 -266
  202. package/skills/swarm/SKILL.md +11 -33
  203. package/dist/fixtures/protocol/actor-message-branch.json +0 -13
  204. package/dist/fixtures/protocol/mailbox-contract.json +0 -15
  205. package/dist/fixtures/protocol/room-message.json +0 -11
  206. package/dist/fixtures/protocol/room-roster.json +0 -11
  207. package/dist/fixtures/protocol/run-inbox-message.json +0 -9
  208. package/dist/fixtures/protocol/run-outbox-event.json +0 -9
  209. package/dist/lib/mailbox-loop.d.ts +0 -41
  210. package/dist/lib/mailbox-loop.js +0 -60
  211. package/dist/lib/messages.d.ts +0 -25
  212. package/dist/lib/messages.js +0 -122
  213. package/dist/lib/rooms.d.ts +0 -104
  214. package/dist/lib/rooms.js +0 -647
  215. package/dist/lib/runs-mailbox.d.ts +0 -25
  216. package/dist/lib/runs-mailbox.js +0 -146
  217. package/dist/lib/runs-messages.d.ts +0 -15
  218. package/dist/lib/runs-messages.js +0 -179
  219. package/dist/lib/runs-outbox.d.ts +0 -41
  220. package/dist/lib/runs-outbox.js +0 -87
  221. package/dist/lib/tools-mailbox.d.ts +0 -8
  222. package/dist/lib/tools-mailbox.js +0 -48
  223. package/dist/recipes/actor-worker.json +0 -39
  224. package/dist/recipes/coordinator-locker.json +0 -45
  225. package/dist/recipes/locker.json +0 -45
  226. package/dist/recipes/pipeline-room-swarm.json +0 -50
  227. package/dist/recipes/subagent-message.json +0 -32
  228. package/dist/recipes/utility-actor-message.json +0 -23
  229. package/dist/scripts/actor-worker.mjs +0 -214
  230. package/dist/scripts/coordinator.mjs +0 -799
  231. package/docs/actor-messages.md +0 -225
  232. package/fixtures/protocol/actor-message-branch.json +0 -13
  233. package/fixtures/protocol/mailbox-contract.json +0 -15
  234. package/fixtures/protocol/room-message.json +0 -11
  235. package/fixtures/protocol/room-roster.json +0 -11
  236. package/fixtures/protocol/run-inbox-message.json +0 -9
  237. package/fixtures/protocol/run-outbox-event.json +0 -9
  238. package/lib/mailbox-loop.ts +0 -144
  239. package/lib/messages.ts +0 -151
  240. package/lib/rooms.ts +0 -939
  241. package/lib/runs-mailbox.ts +0 -208
  242. package/lib/runs-messages.ts +0 -252
  243. package/lib/runs-outbox.ts +0 -144
  244. package/lib/tools-mailbox.ts +0 -56
  245. package/recipes/actor-worker.json +0 -39
  246. package/recipes/coordinator-locker.json +0 -45
  247. package/recipes/locker.json +0 -45
  248. package/recipes/pipeline-room-swarm.json +0 -50
  249. package/recipes/subagent-message.json +0 -32
  250. package/recipes/utility-actor-message.json +0 -23
  251. package/scripts/actor-worker.mjs +0 -214
  252. package/scripts/coordinator.mjs +0 -799
@@ -5,7 +5,6 @@
5
5
  */
6
6
 
7
7
  import * as Limits from "./limits.ts";
8
- import * as ToolsMailbox from "./tools-mailbox.ts";
9
8
 
10
9
  export function withLeadingLineBreak(text: string): string {
11
10
  return `\n${text.replace(/^\n+/, "")}`;
@@ -91,13 +90,13 @@ function compactModelPolicy(value: unknown): string[] {
91
90
  ].filter((token): token is string => Boolean(token));
92
91
  }
93
92
 
94
- export function actorRunNextActions(run: unknown): string[] {
93
+ export function runNextActions(run: unknown): string[] {
95
94
  const id = String(run ?? "").trim();
96
95
  if (!id) return [];
97
96
  return [
98
- `inspect target=run:${id} view=status`,
99
- `inspect target=run:${id} view=messages`,
100
- `message to=run:${id} type=<actor.action>`,
97
+ `inspect target=run:${id} view=recipe`,
98
+ `inspect target=run:${id} view=trace`,
99
+ `inspect target=run:${id} view=control`,
101
100
  ];
102
101
  }
103
102
 
@@ -126,7 +125,7 @@ export function compactAsyncRunStatus(value: unknown): string {
126
125
  if (result.killed === true) tokens.push("killed=true");
127
126
  const draftRecipe = status.draft_recipe;
128
127
  if (draftRecipe) tokens.push(`draft_recipe=${String(draftRecipe)}`);
129
- const nextActions = actorRunNextActions(run);
128
+ const nextActions = runNextActions(run);
130
129
  if (nextActions.length > 0)
131
130
  tokens.push(
132
131
  `next=${nextActions.map((action) => action.replaceAll(/\s+/g, "_")).join("|")}`,
@@ -134,52 +133,6 @@ export function compactAsyncRunStatus(value: unknown): string {
134
133
  return `\n${tokens.join(" ")}`;
135
134
  }
136
135
 
137
- export function compactInboxMessages(
138
- messages: Array<Record<string, unknown>>,
139
- emptyLabel: string,
140
- ): string {
141
- if (messages.length === 0) return `\n(no ${emptyLabel} messages)`;
142
- return `\n${messages
143
- .map((message) =>
144
- [
145
- ...(message.id ? [`id=${String(message.id)}`] : []),
146
- `status=${String(message.status ?? "")}`,
147
- `type=${String(message.type ?? "")}`,
148
- `from=${String(message.from ?? "")}`,
149
- `to=${String(message.to ?? "")}`,
150
- ...(message.queued_at
151
- ? [`queued_at=${String(message.queued_at)}`]
152
- : []),
153
- ...(message.sent_at ? [`sent_at=${String(message.sent_at)}`] : []),
154
- ...(message.claimed_at
155
- ? [`claimed_at=${String(message.claimed_at)}`]
156
- : []),
157
- ...(message.handled_at
158
- ? [`handled_at=${String(message.handled_at)}`]
159
- : []),
160
- ...(message.failed_at
161
- ? [`failed_at=${String(message.failed_at)}`]
162
- : []),
163
- ].join(" "),
164
- )
165
- .join("\n")}`;
166
- }
167
-
168
- export function compactBranchInbox(
169
- messages: Array<Record<string, unknown>>,
170
- ): string {
171
- return compactInboxMessages(messages, "branch inbox");
172
- }
173
-
174
- export function compactRunMailbox(
175
- run: string,
176
- mailbox: Record<string, unknown>,
177
- messages: Array<Record<string, unknown>>,
178
- ): string {
179
- const normalized = ToolsMailbox.normalizeMailboxContracts(mailbox);
180
- return `\nrun=${run} accepts=${ToolsMailbox.mailboxTypes(normalized.accepts).join(",")} emits=${ToolsMailbox.mailboxTypes(normalized.emits).join(",")}${compactInboxMessages(messages, "run inbox")}`;
181
- }
182
-
183
136
  export function artifactNextActions(
184
137
  run: unknown,
185
138
  artifacts: Record<string, unknown>,
@@ -9,12 +9,10 @@ import { join } from "node:path";
9
9
 
10
10
  import * as AsyncRuns from "./async-runs.ts";
11
11
  import { withFileMutationLock } from "./file-state.ts";
12
- import * as Messages from "./messages.ts";
13
12
  import * as ModelContext from "./model-context.ts";
14
13
  import * as Paths from "./paths.ts";
15
14
  import * as RecipesDiscovery from "./recipes-discovery.ts";
16
15
  import * as RecipesUsage from "./recipes-usage.ts";
17
- import * as Rooms from "./rooms.ts";
18
16
  import * as Schema from "./schema.ts";
19
17
  import * as ToolsResponse from "./tools-response.ts";
20
18
 
@@ -53,13 +51,7 @@ function draftRecipeName(run: string): string {
53
51
  function draftRecipeDefaults(
54
52
  values: Record<string, unknown>,
55
53
  ): Record<string, unknown> | undefined {
56
- const ignored = new Set([
57
- "actor_address",
58
- "communication_file",
59
- "default_room",
60
- "run_id",
61
- "state_dir",
62
- ]);
54
+ const ignored = new Set(["run_id", "state_dir", "trace_file"]);
63
55
  const defaults = Object.fromEntries(
64
56
  Object.entries(values).filter(([key]) => !ignored.has(key)),
65
57
  );
@@ -117,15 +109,16 @@ function enhanceSpawnRecipeError(error: unknown, recipe: unknown): Error {
117
109
  );
118
110
  }
119
111
 
120
- function runIdFromActorAddress(
121
- address: string | undefined,
122
- ): string | undefined {
123
- if (!address) return undefined;
124
- const parsed = Messages.parseActorAddress(address);
125
- if (parsed.kind !== "run" || !parsed.value) {
126
- throw new Error(`Expected run:<id> actor address, received: ${address}`);
112
+ function runIdFromTarget(target: string | undefined): string | undefined {
113
+ if (!target) return undefined;
114
+ if (!target.startsWith("run:")) {
115
+ throw new Error(`spawn.as accepts only run:<id>; received: ${target}`);
127
116
  }
128
- return parsed.value;
117
+ const run = target.slice(4);
118
+ if (!run || !/^[A-Za-z0-9_.-]+$/.test(run)) {
119
+ throw new Error(`invalid spawn Run target: ${target}`);
120
+ }
121
+ return run;
129
122
  }
130
123
 
131
124
  export function createSpawnToolDefinition<
@@ -135,17 +128,14 @@ export function createSpawnToolDefinition<
135
128
  name: "spawn",
136
129
  label: "Spawn",
137
130
  description:
138
- "Create an addressable actor from a recipe file or inline command template. Use instead of ad hoc shell backgrounding for work that may outlive this turn, needs steering/follow-up/artifacts, runs as a service, fans out, or should be inspected later. Currently spawns run:<id> actors backed by async runs.",
131
+ "Create one controllable Run from a Recipe file or inline command template.",
139
132
  parameters: Schema.objectSchema(
140
133
  {
141
134
  artifacts: Schema.looseObjectSchema(
142
- "Optional named artifact paths for the spawned actor.",
135
+ "Optional named artifact paths for the spawned Run.",
143
136
  ),
144
137
  as: Schema.stringSchema(
145
- "Optional actor address for the spawned run, e.g. run:<id>.",
146
- ),
147
- correlation_id: Schema.stringSchema(
148
- "Optional workflow correlation id preserved in terminal follow-up delivery.",
138
+ "Optional Run identity in exact run:<id> form.",
149
139
  ),
150
140
  file: Schema.stringSchema(
151
141
  "Optional template recipe JSON file. Bare names resolve under ~/.pi/agent/recipes.",
@@ -187,7 +177,7 @@ export function createSpawnToolDefinition<
187
177
  "spawn.state_dir is not supported; run state is runtime-owned so run:<id> remains addressable and retention-safe.",
188
178
  );
189
179
  }
190
- const runId = runIdFromActorAddress(
180
+ const runId = runIdFromTarget(
191
181
  typeof input.as === "string" ? input.as : undefined,
192
182
  );
193
183
  const recipe =
@@ -202,11 +192,7 @@ export function createSpawnToolDefinition<
202
192
  {
203
193
  file: recipe,
204
194
  launch_source: "spawn",
205
- launch_correlation: {
206
- ...(typeof input.correlation_id === "string"
207
- ? { correlation_id: input.correlation_id } : {}),
208
- tool_call_id: toolCallId,
209
- },
195
+ launch_correlation: { tool_call_id: toolCallId },
210
196
  ownerId: getRunOwnerId(ctx),
211
197
  ...(input.transport_context
212
198
  ? { transport_context: asRecord(input.transport_context) } : {}),
@@ -238,14 +224,12 @@ export function createSpawnToolDefinition<
238
224
  throw enhanceSpawnRecipeError(error, recipe);
239
225
  }
240
226
  const draftRecipe = writeSpawnDraftRecipe(input, meta);
241
- const nextActions = ToolsResponse.actorRunNextActions(meta.run);
227
+ const nextActions = ToolsResponse.runNextActions(meta.run);
242
228
  const details = {
243
229
  ...meta,
244
230
  ...(draftRecipe ? { draft_recipe: draftRecipe } : {}),
245
231
  next_actions: nextActions,
246
232
  };
247
- Rooms.ensureDefaultRoom(meta.state_dir, String(meta.run));
248
- Rooms.writeCommunicationSnapshot(meta.state_dir, String(meta.run));
249
233
  return {
250
234
  content: [
251
235
  {
package/lib/tools.ts CHANGED
@@ -73,8 +73,7 @@ export function createCoreActorToolDefinitions<
73
73
  setActiveTools: deps.setActiveTools,
74
74
  }),
75
75
  ToolsSpawn.createSpawnToolDefinition<TContext>(),
76
- ToolsMessage.createActorMessageToolDefinition<TContext>({
77
- getTool: (name) => deps.getRuntimeTool(name),
76
+ ToolsMessage.createControlToolDefinition<TContext>({
78
77
  handleRuntimeMessage: deps.handleRuntimeMessage,
79
78
  }),
80
79
  ToolsInspect.createInspectToolDefinition<TContext>({
@@ -0,0 +1,221 @@
1
+ /**
2
+ * Unified bounded Run Trace projection.
3
+ * Zones: causal evidence merge, source filtering, redaction, deterministic ordering
4
+ * Owns public Trace items; source journals and session parsing remain in their domains.
5
+ */
6
+
7
+ import { existsSync, statSync } from "node:fs";
8
+ import { join, relative } from "node:path";
9
+
10
+ import * as ExecutionSessions from "./execution-sessions.ts";
11
+ import * as Limits from "./limits.ts";
12
+ import {
13
+ resolveArtifactManifest,
14
+ type RunArtifactDeclaration,
15
+ } from "./runs-artifacts.ts";
16
+ import type { RunControlRecord } from "./runs-controls.ts";
17
+ import { tailFile } from "./runs-status.ts";
18
+ import type { TraceEvent } from "./runs-trace.ts";
19
+ import * as SessionEvidence from "./session-evidence.ts";
20
+ import {
21
+ readJsonFileResilient,
22
+ readJsonlFileResilient,
23
+ type StateReadDiagnostic,
24
+ } from "./state-readers.ts";
25
+
26
+ export type TraceSource =
27
+ | "lifecycle"
28
+ | "control"
29
+ | "process"
30
+ | "agent"
31
+ | "artifact"
32
+ | "runtime";
33
+
34
+ export type TraceSourceFilter = TraceSource | "all";
35
+
36
+ export interface TraceItem {
37
+ id: string;
38
+ ts: string;
39
+ source: TraceSource;
40
+ kind: string;
41
+ summary: string;
42
+ level?: "info" | "warning" | "error";
43
+ detail: unknown;
44
+ }
45
+
46
+ const LIFECYCLE_PREFIXES = ["run.", "command."];
47
+ const SOURCE_LIMIT = 100;
48
+
49
+ function redact(value: unknown): unknown {
50
+ return SessionEvidence.redactSessionEvidenceValue(value);
51
+ }
52
+
53
+ function sourceForEvent(event: TraceEvent): TraceSource {
54
+ return LIFECYCLE_PREFIXES.some((prefix) => event.kind.startsWith(prefix))
55
+ ? "lifecycle"
56
+ : "runtime";
57
+ }
58
+
59
+ function latestControlTimestamp(control: RunControlRecord): string {
60
+ return (
61
+ control.failed_at ??
62
+ control.handled_at ??
63
+ control.claimed_at ??
64
+ control.delivered_at ??
65
+ control.queued_at
66
+ );
67
+ }
68
+
69
+ function diagnosticItems(diagnostics: StateReadDiagnostic[]): TraceItem[] {
70
+ return diagnostics.slice(-SOURCE_LIMIT).map((diagnostic, index) => ({
71
+ id: `diagnostic:${diagnostic.path}:${diagnostic.line ?? 0}:${index}`,
72
+ ts: new Date(0).toISOString(),
73
+ source: "runtime",
74
+ kind: "state.read_error",
75
+ summary: "Malformed or unreadable Run evidence",
76
+ level: "warning",
77
+ detail: redact(diagnostic),
78
+ }));
79
+ }
80
+
81
+ function traceEventItems(stateDir: string): TraceItem[] {
82
+ const read = readJsonlFileResilient<TraceEvent>(join(stateDir, "trace.jsonl"));
83
+ return [
84
+ ...read.records.slice(-SOURCE_LIMIT).map((event) => ({
85
+ id: event.id,
86
+ ts: event.ts,
87
+ source: sourceForEvent(event),
88
+ kind: event.kind,
89
+ summary: event.summary ?? event.kind,
90
+ ...(event.level ? { level: event.level } : {}),
91
+ detail: redact({
92
+ ...(event.attention ? { attention: event.attention } : {}),
93
+ ...(event.data !== undefined ? { data: event.data } : {}),
94
+ }),
95
+ } satisfies TraceItem)),
96
+ ...diagnosticItems(read.diagnostics),
97
+ ];
98
+ }
99
+
100
+ function controlItems(stateDir: string): TraceItem[] {
101
+ const read = readJsonlFileResilient<RunControlRecord>(
102
+ join(stateDir, "controls.jsonl"),
103
+ );
104
+ return [
105
+ ...read.records.slice(-SOURCE_LIMIT).map((control) => ({
106
+ id: `control:${control.id}`,
107
+ ts: latestControlTimestamp(control),
108
+ source: "control" as const,
109
+ kind: `control.${control.status}`,
110
+ summary: `${control.action} ${control.status}`,
111
+ ...(control.status === "failed" ? { level: "error" as const } : {}),
112
+ detail: redact(control),
113
+ })),
114
+ ...diagnosticItems(read.diagnostics),
115
+ ];
116
+ }
117
+
118
+ function agentItems(stateDir: string): TraceItem[] {
119
+ return ExecutionSessions.readExecutionTurns(stateDir)
120
+ .slice(-SOURCE_LIMIT)
121
+ .map((turn) => {
122
+ const text = turn.assistantText ?? turn.userText ?? "Agent turn";
123
+ const summary = text.replaceAll(/\s+/g, " ").trim().slice(0, 160);
124
+ return {
125
+ id: `agent:${turn.commandId}:${turn.sessionFile}:${turn.index}`,
126
+ ts: turn.timestamp ?? new Date(0).toISOString(),
127
+ source: "agent" as const,
128
+ kind: "agent.turn",
129
+ summary: summary || "Agent turn",
130
+ ...(turn.error ? { level: "error" as const } : {}),
131
+ detail: redact(turn),
132
+ };
133
+ });
134
+ }
135
+
136
+ function processItems(stateDir: string): TraceItem[] {
137
+ return ["stdout", "stderr"].flatMap((stream) => {
138
+ const path = join(stateDir, `${stream}.log`);
139
+ if (!existsSync(path)) return [];
140
+ const stat = statSync(path);
141
+ const tail = tailFile(path, Limits.DEFAULT_INSPECT_LINES);
142
+ if (!tail && stat.size === 0) return [];
143
+ return [{
144
+ id: `process:${stream}`,
145
+ ts: stat.mtime.toISOString(),
146
+ source: "process" as const,
147
+ kind: `process.${stream}`,
148
+ summary: `${stream} (${stat.size} bytes)`,
149
+ ...(stream === "stderr" ? { level: "warning" as const } : {}),
150
+ detail: redact({ bytes: stat.size, path, tail }),
151
+ }];
152
+ });
153
+ }
154
+
155
+ function resultItems(stateDir: string): TraceItem[] {
156
+ const path = join(stateDir, "result.json");
157
+ const read = readJsonFileResilient<Record<string, unknown>>(path, {});
158
+ if (Object.keys(read.value).length === 0) return diagnosticItems(read.diagnostics);
159
+ const stat = statSync(path);
160
+ const failed = Number(read.value.code ?? 0) !== 0;
161
+ return [{
162
+ id: "process:result",
163
+ ts:
164
+ typeof read.value.completedAt === "string"
165
+ ? read.value.completedAt
166
+ : stat.mtime.toISOString(),
167
+ source: "process",
168
+ kind: failed ? "process.failed" : "process.result",
169
+ summary: failed ? "Run process failed" : "Run process completed",
170
+ ...(failed ? { level: "error" as const } : {}),
171
+ detail: redact(read.value),
172
+ }, ...diagnosticItems(read.diagnostics)];
173
+ }
174
+
175
+ function artifactItems(
176
+ stateDir: string,
177
+ declarations: Record<string, RunArtifactDeclaration> | undefined,
178
+ ): TraceItem[] {
179
+ const manifest = resolveArtifactManifest(declarations);
180
+ if (!manifest) return [];
181
+ return Object.entries(manifest).slice(0, SOURCE_LIMIT).map(([name, artifact]) => ({
182
+ id: `artifact:${name}`,
183
+ ts: artifact.exists && existsSync(artifact.path)
184
+ ? statSync(artifact.path).mtime.toISOString()
185
+ : new Date(0).toISOString(),
186
+ source: "artifact",
187
+ kind: artifact.exists ? "artifact.ready" : "artifact.missing",
188
+ summary: `${name}: ${artifact.exists ? "ready" : "missing"}`,
189
+ ...(artifact.required && !artifact.exists ? { level: "warning" as const } : {}),
190
+ detail: redact({
191
+ exists: artifact.exists,
192
+ path: relative(stateDir, artifact.path).replaceAll("\\", "/"),
193
+ required: artifact.required,
194
+ ...(artifact.size !== undefined ? { size: artifact.size } : {}),
195
+ }),
196
+ }));
197
+ }
198
+
199
+ export function projectRunTrace(
200
+ stateDir: string,
201
+ options: {
202
+ artifacts?: Record<string, RunArtifactDeclaration>;
203
+ limit?: number;
204
+ source?: TraceSourceFilter;
205
+ } = {},
206
+ ): TraceItem[] {
207
+ const source = options.source ?? "all";
208
+ const items = [
209
+ ...traceEventItems(stateDir),
210
+ ...controlItems(stateDir),
211
+ ...agentItems(stateDir),
212
+ ...processItems(stateDir),
213
+ ...resultItems(stateDir),
214
+ ...artifactItems(stateDir, options.artifacts),
215
+ ]
216
+ .filter((item) => source === "all" || item.source === source)
217
+ .sort((left, right) =>
218
+ right.ts.localeCompare(left.ts) || right.id.localeCompare(left.id),
219
+ );
220
+ return items.slice(0, Math.max(1, Math.min(options.limit ?? 100, 200)));
221
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@llblab/pi-actors",
3
- "version": "0.42.3",
3
+ "version": "0.43.0",
4
4
  "private": false,
5
5
  "description": "Local Actor Kernel for Pi",
6
6
  "keywords": [
@@ -28,6 +28,7 @@
28
28
  "check": "node --experimental-strip-types -e \"await import('./index.ts'); console.log('pi-actors: extension import ok')\"",
29
29
  "conformance": "node scripts/conformance.mjs",
30
30
  "test": "node --experimental-strip-types --test --test-concurrency=1 tests/*.test.ts",
31
+ "test:preservation": "node --experimental-strip-types --test --test-concurrency=1 tests/async-runs.test.ts tests/observability.test.ts tests/session-evidence.test.ts tests/inspector-security.test.ts tests/inspector-overlay.test.ts tests/review-control.test.ts",
31
32
  "pack:dry": "npm pack --dry-run",
32
33
  "recipes:qa": "node scripts/validate-recipe.mjs recipes --all --qa --summary",
33
34
  "release:validate": "npm run validate && node scripts/release-gates.mjs",
@@ -10,15 +10,5 @@
10
10
  "thinking": "{current_thinking}"
11
11
  },
12
12
  "description": "Read-only background reviewer for one immutable automatic draft-memory batch.",
13
- "mailbox": {
14
- "accepts": [
15
- "control.kill"
16
- ],
17
- "emits": [
18
- "command.done",
19
- "run.done",
20
- "run.failed"
21
- ]
22
- },
23
13
  "template": "pi -p --model {model} --thinking {thinking} --no-tools @{input_path} Review the attached immutable pi-actors draft batch. Evaluate every draft independently using launch history, universality, flexibility, parameterization, duplication, safety, and likely future usefulness. Treat draft and sha256 fields as batch-local opaque occurrence/content-group identifiers and return them verbatim. Choose exactly one promote or discard decision per draft; there is no selection quota. Promotion requires a unique absent snake_case target and targetSha256 null. Never return recipe content: the executor derives the exact immutable captured source. Invalid or secret-touching drafts must be discarded. Include assessment with launches, universality, flexibility, futureUsefulness, and safety. Do not mutate, move, register, or delete recipes. End stdout with DRAFT_REVIEW_RESULT on its own line followed by exactly one JSON object with batchId, createdAt, and decisions."
24
14
  }
@@ -37,20 +37,6 @@
37
37
  "risk_policy": "Preserve minority high-impact risks and ideas; separate confirmed issues from hypotheses.",
38
38
  "output_format": "Markdown sections: Status (complete/degraded/insufficient_data), Summary, Consensus Findings, Minority Findings, Verification, Judge Notes, Risks, Next Actions."
39
39
  },
40
- "mailbox": {
41
- "accepts": [
42
- "control.kill"
43
- ],
44
- "emits": [
45
- "review.completed",
46
- "verification.completed",
47
- "merge.completed",
48
- "judge.completed",
49
- "command.done",
50
- "run.done",
51
- "run.failed"
52
- ]
53
- },
54
40
  "template": [
55
41
  {
56
42
  "name": "coordinator",
@@ -16,24 +16,15 @@
16
16
  "volume": "70",
17
17
  "player": "auto"
18
18
  },
19
- "mailbox": {
20
- "accepts": [
21
- "control.stop",
22
- "control.cancel",
23
- "control.kill",
24
- "player.play",
25
- "player.pause",
26
- "player.resume",
27
- "player.toggle",
28
- "player.next",
29
- "player.previous",
30
- "player.stop",
31
- "player.status"
32
- ],
33
- "emits": [
34
- "player.track",
35
- "player.stopped"
36
- ]
37
- },
19
+ "control": [
20
+ "play",
21
+ "pause",
22
+ "resume",
23
+ "toggle",
24
+ "next",
25
+ "previous",
26
+ "stop",
27
+ "status"
28
+ ],
38
29
  "template": "{repo}/scripts/music-player.mjs {command} {source} {loop} {volume} {player} {state_dir}"
39
30
  }
@@ -24,17 +24,6 @@
24
24
  "constraints": "Prefer small composable pieces, explicit contracts, and no broad workflow DSL.",
25
25
  "tools": ""
26
26
  },
27
- "mailbox": {
28
- "accepts": [
29
- "control.kill"
30
- ],
31
- "emits": [
32
- "pipeline.completed",
33
- "command.done",
34
- "run.done",
35
- "run.failed"
36
- ]
37
- },
38
27
  "template": [
39
28
  {
40
29
  "parallel": true,
@@ -4,8 +4,7 @@
4
4
  "artifactWrite": "pipeline-artifact-write.json",
5
5
  "manifest": "utility-artifact-manifest.json",
6
6
  "manifestWrite": "utility-artifact-write.json",
7
- "validation": "utility-validation-wrapper.json",
8
- "message": "utility-actor-message.json"
7
+ "validation": "utility-validation-wrapper.json"
9
8
  },
10
9
  "args": [
11
10
  "input:string",
@@ -33,18 +32,6 @@
33
32
  "validation_timeout_ms": "300000",
34
33
  "tools": ""
35
34
  },
36
- "mailbox": {
37
- "accepts": [
38
- "control.kill"
39
- ],
40
- "emits": [
41
- "artifact.bundle_ready",
42
- "artifact.written",
43
- "artifact.blocked",
44
- "run.done",
45
- "run.failed"
46
- ]
47
- },
48
35
  "artifacts": {
49
36
  "artifact": "{artifact_path}",
50
37
  "manifest": "{manifest_path}"
@@ -85,14 +72,6 @@
85
72
  "artifact_path": "{manifest_path}",
86
73
  "mode": "{write_mode}"
87
74
  }
88
- },
89
- {
90
- "name": "message",
91
- "values": {
92
- "type": "artifact.bundle_ready",
93
- "summary": "Artifact bundle ready: {artifact_path} with manifest {manifest_path}.",
94
- "metadata": "{\"artifact\":\"{artifact_path}\",\"manifest\":\"{manifest_path}\"}"
95
- }
96
75
  }
97
76
  ]
98
77
  }
@@ -2,8 +2,7 @@
2
2
  "async": true,
3
3
  "imports": {
4
4
  "normalizer": "subagent-normalize.json",
5
- "artifact": "subagent-artifact.json",
6
- "message": "utility-actor-message.json"
5
+ "artifact": "subagent-artifact.json"
7
6
  },
8
7
  "args": [
9
8
  "input:string",
@@ -16,15 +15,6 @@
16
15
  "artifact_format": "Markdown sections: Summary, Findings, Evidence, Risks, Next Actions.",
17
16
  "tools": ""
18
17
  },
19
- "mailbox": {
20
- "accepts": [
21
- "control.kill"
22
- ],
23
- "emits": [
24
- "artifact.prepared",
25
- "artifact.blocked"
26
- ]
27
- },
28
18
  "template": [
29
19
  {
30
20
  "name": "normalizer",
@@ -44,13 +34,6 @@
44
34
  "model": "{model}",
45
35
  "tools": "{tools}"
46
36
  }
47
- },
48
- {
49
- "name": "message",
50
- "values": {
51
- "type": "artifact.prepared",
52
- "summary": "Artifact prepared for path {artifact_path}."
53
- }
54
37
  }
55
38
  ]
56
39
  }
@@ -3,8 +3,7 @@
3
3
  "imports": {
4
4
  "normalizer": "subagent-normalize.json",
5
5
  "artifact": "subagent-artifact.json",
6
- "writer": "utility-artifact-write.json",
7
- "message": "utility-actor-message.json"
6
+ "writer": "utility-artifact-write.json"
8
7
  },
9
8
  "args": [
10
9
  "input:string",
@@ -19,15 +18,6 @@
19
18
  "write_mode": "create",
20
19
  "tools": ""
21
20
  },
22
- "mailbox": {
23
- "accepts": [
24
- "control.kill"
25
- ],
26
- "emits": [
27
- "artifact.written",
28
- "artifact.blocked"
29
- ]
30
- },
31
21
  "artifacts": {
32
22
  "artifact": "{artifact_path}"
33
23
  },
@@ -58,13 +48,6 @@
58
48
  "artifact_path": "{artifact_path}",
59
49
  "mode": "{write_mode}"
60
50
  }
61
- },
62
- {
63
- "name": "message",
64
- "values": {
65
- "type": "artifact.written",
66
- "summary": "Artifact written to path {artifact_path} using mode {write_mode}."
67
- }
68
51
  }
69
52
  ]
70
53
  }
@@ -22,18 +22,6 @@
22
22
  "artifact_path": "./async-run-ops.md",
23
23
  "tools": ""
24
24
  },
25
- "mailbox": {
26
- "accepts": [
27
- "control.kill"
28
- ],
29
- "emits": [
30
- "operations.completed",
31
- "artifact.prepared",
32
- "command.done",
33
- "run.done",
34
- "run.failed"
35
- ]
36
- },
37
25
  "template": [
38
26
  {
39
27
  "name": "snapshot",