@llblab/pi-actors 0.42.2 → 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 (254) hide show
  1. package/AGENTS.md +121 -175
  2. package/CHANGELOG.md +16 -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 +17 -17
  26. package/dist/lib/observability.js +45 -84
  27. package/dist/lib/pi.d.ts +1 -1
  28. package/dist/lib/prompts.d.ts +1 -1
  29. package/dist/lib/prompts.js +2 -2
  30. package/dist/lib/recipe-control.d.ts +7 -0
  31. package/dist/lib/recipe-control.js +39 -0
  32. package/dist/lib/recipes-discovery.js +2 -0
  33. package/dist/lib/recipes-references.d.ts +1 -14
  34. package/dist/lib/recipes-references.js +6 -21
  35. package/dist/lib/review-projection.js +1 -5
  36. package/dist/lib/run-ui-runtime.js +2 -2
  37. package/dist/lib/runs-control-delivery.d.ts +21 -0
  38. package/dist/lib/runs-control-delivery.js +127 -0
  39. package/dist/lib/runs-controls.d.ts +35 -0
  40. package/dist/lib/runs-controls.js +144 -0
  41. package/dist/lib/runs-retention.d.ts +7 -0
  42. package/dist/lib/runs-retention.js +27 -3
  43. package/dist/lib/runs-start.js +4 -2
  44. package/dist/lib/runs-status.js +11 -6
  45. package/dist/lib/runs-trace.d.ts +24 -0
  46. package/dist/lib/runs-trace.js +98 -0
  47. package/dist/lib/runtime-notifier.d.ts +1 -1
  48. package/dist/lib/runtime-notifier.js +1 -1
  49. package/dist/lib/tools-inspect.d.ts +3 -3
  50. package/dist/lib/tools-inspect.js +203 -708
  51. package/dist/lib/tools-local.js +2 -10
  52. package/dist/lib/tools-message.d.ts +7 -7
  53. package/dist/lib/tools-message.js +95 -396
  54. package/dist/lib/tools-response.d.ts +1 -4
  55. package/dist/lib/tools-response.js +5 -39
  56. package/dist/lib/tools-spawn.js +16 -28
  57. package/dist/lib/tools.js +1 -2
  58. package/dist/lib/trace-projection.d.ts +22 -0
  59. package/dist/lib/trace-projection.js +165 -0
  60. package/dist/recipes/draft-review.json +0 -10
  61. package/dist/recipes/lens-swarm.json +0 -14
  62. package/dist/recipes/music-player.json +10 -19
  63. package/dist/recipes/pipeline-architect-coordinator.json +0 -11
  64. package/dist/recipes/pipeline-artifact-bundle.json +1 -22
  65. package/dist/recipes/pipeline-artifact-report.json +1 -18
  66. package/dist/recipes/pipeline-artifact-write.json +1 -18
  67. package/dist/recipes/pipeline-async-run-ops.json +0 -12
  68. package/dist/recipes/pipeline-checkpoint-continuation.json +0 -14
  69. package/dist/recipes/pipeline-development-tasking.json +0 -12
  70. package/dist/recipes/pipeline-docs-maintenance.json +0 -12
  71. package/dist/recipes/pipeline-media-library.json +0 -12
  72. package/dist/recipes/pipeline-quorum-review.json +0 -12
  73. package/dist/recipes/pipeline-release-readiness.json +0 -12
  74. package/dist/recipes/pipeline-release-summary.json +0 -12
  75. package/dist/recipes/pipeline-repo-health.json +0 -12
  76. package/dist/recipes/pipeline-research-synthesis.json +0 -11
  77. package/dist/recipes/pipeline-review-readiness.json +0 -12
  78. package/dist/recipes/resource-locker.json +27 -0
  79. package/dist/recipes/subagent-artifact.json +0 -9
  80. package/dist/recipes/subagent-checkpoint.json +0 -10
  81. package/dist/recipes/subagent-conflict-report.json +0 -11
  82. package/dist/recipes/subagent-contradiction-map.json +0 -11
  83. package/dist/recipes/subagent-critic.json +0 -11
  84. package/dist/recipes/subagent-evidence-map.json +0 -11
  85. package/dist/recipes/subagent-followup.json +0 -10
  86. package/dist/recipes/subagent-judge.json +0 -11
  87. package/dist/recipes/subagent-merge.json +0 -11
  88. package/dist/recipes/subagent-normalize.json +0 -11
  89. package/dist/recipes/subagent-plan.json +0 -11
  90. package/dist/recipes/subagent-preflight.json +0 -11
  91. package/dist/recipes/subagent-prompt.json +0 -10
  92. package/dist/recipes/subagent-quorum.json +0 -10
  93. package/dist/recipes/subagent-review-coordinator.json +0 -14
  94. package/dist/recipes/subagent-review.json +0 -11
  95. package/dist/recipes/subagent-task-card.json +0 -11
  96. package/dist/recipes/subagent-tools.json +0 -10
  97. package/dist/recipes/subagent-verify.json +0 -11
  98. package/dist/recipes/subagents-prompts.json +0 -10
  99. package/dist/recipes/tool-review.json +0 -10
  100. package/dist/scripts/async-runner.mjs +25 -25
  101. package/dist/scripts/conformance.mjs +4 -2
  102. package/dist/scripts/locker.mjs +200 -66
  103. package/dist/scripts/music-player.mjs +159 -150
  104. package/dist/scripts/recipe-utils.mjs +6 -96
  105. package/dist/scripts/release-gates.mjs +60 -0
  106. package/dist/scripts/validate-recipe.mjs +3 -53
  107. package/dist/skills/actors/SKILL.md +53 -266
  108. package/dist/skills/swarm/SKILL.md +11 -33
  109. package/docs/0.43-baseline.md +44 -0
  110. package/docs/README.md +3 -3
  111. package/docs/actor-inspector.md +26 -64
  112. package/docs/actors-deep-reference.md +92 -50
  113. package/docs/async-runs.md +81 -328
  114. package/docs/command-templates.md +2 -2
  115. package/docs/component-recipes.md +30 -133
  116. package/docs/recipe-library.md +57 -182
  117. package/docs/task-first-recipes.md +10 -12
  118. package/docs/template-recipes.md +76 -289
  119. package/docs/tool-registry.md +41 -161
  120. package/fixtures/protocol/control-endpoint.json +6 -0
  121. package/fixtures/protocol/control-record.json +9 -0
  122. package/fixtures/protocol/recipe-summary.json +4 -12
  123. package/fixtures/protocol/trace-event.json +9 -0
  124. package/lib/async-runs.ts +202 -201
  125. package/lib/control.ts +102 -0
  126. package/lib/execution-sessions.ts +111 -0
  127. package/lib/file-state.ts +17 -4
  128. package/lib/inspector-actions.ts +2 -2
  129. package/lib/inspector-command.ts +3 -3
  130. package/lib/inspector-overlay.ts +577 -1121
  131. package/lib/inspector.ts +46 -979
  132. package/lib/limits.ts +4 -2
  133. package/lib/observability.ts +63 -104
  134. package/lib/pi.ts +1 -1
  135. package/lib/prompts.ts +2 -2
  136. package/lib/recipe-control.ts +45 -0
  137. package/lib/recipes-discovery.ts +2 -0
  138. package/lib/recipes-references.ts +9 -45
  139. package/lib/review-projection.ts +1 -5
  140. package/lib/run-ui-runtime.ts +2 -2
  141. package/lib/runs-control-delivery.ts +181 -0
  142. package/lib/runs-controls.ts +204 -0
  143. package/lib/runs-retention.ts +38 -3
  144. package/lib/runs-start.ts +4 -2
  145. package/lib/runs-status.ts +11 -6
  146. package/lib/runs-trace.ts +132 -0
  147. package/lib/runtime-notifier.ts +1 -1
  148. package/lib/tools-inspect.ts +240 -901
  149. package/lib/tools-local.ts +2 -12
  150. package/lib/tools-message.ts +112 -519
  151. package/lib/tools-response.ts +5 -52
  152. package/lib/tools-spawn.ts +16 -32
  153. package/lib/tools.ts +1 -2
  154. package/lib/trace-projection.ts +221 -0
  155. package/package.json +2 -1
  156. package/recipes/draft-review.json +0 -10
  157. package/recipes/lens-swarm.json +0 -14
  158. package/recipes/music-player.json +10 -19
  159. package/recipes/pipeline-architect-coordinator.json +0 -11
  160. package/recipes/pipeline-artifact-bundle.json +1 -22
  161. package/recipes/pipeline-artifact-report.json +1 -18
  162. package/recipes/pipeline-artifact-write.json +1 -18
  163. package/recipes/pipeline-async-run-ops.json +0 -12
  164. package/recipes/pipeline-checkpoint-continuation.json +0 -14
  165. package/recipes/pipeline-development-tasking.json +0 -12
  166. package/recipes/pipeline-docs-maintenance.json +0 -12
  167. package/recipes/pipeline-media-library.json +0 -12
  168. package/recipes/pipeline-quorum-review.json +0 -12
  169. package/recipes/pipeline-release-readiness.json +0 -12
  170. package/recipes/pipeline-release-summary.json +0 -12
  171. package/recipes/pipeline-repo-health.json +0 -12
  172. package/recipes/pipeline-research-synthesis.json +0 -11
  173. package/recipes/pipeline-review-readiness.json +0 -12
  174. package/recipes/resource-locker.json +27 -0
  175. package/recipes/subagent-artifact.json +0 -9
  176. package/recipes/subagent-checkpoint.json +0 -10
  177. package/recipes/subagent-conflict-report.json +0 -11
  178. package/recipes/subagent-contradiction-map.json +0 -11
  179. package/recipes/subagent-critic.json +0 -11
  180. package/recipes/subagent-evidence-map.json +0 -11
  181. package/recipes/subagent-followup.json +0 -10
  182. package/recipes/subagent-judge.json +0 -11
  183. package/recipes/subagent-merge.json +0 -11
  184. package/recipes/subagent-normalize.json +0 -11
  185. package/recipes/subagent-plan.json +0 -11
  186. package/recipes/subagent-preflight.json +0 -11
  187. package/recipes/subagent-prompt.json +0 -10
  188. package/recipes/subagent-quorum.json +0 -10
  189. package/recipes/subagent-review-coordinator.json +0 -14
  190. package/recipes/subagent-review.json +0 -11
  191. package/recipes/subagent-task-card.json +0 -11
  192. package/recipes/subagent-tools.json +0 -10
  193. package/recipes/subagent-verify.json +0 -11
  194. package/recipes/subagents-prompts.json +0 -10
  195. package/recipes/tool-review.json +0 -10
  196. package/scripts/async-runner.mjs +25 -25
  197. package/scripts/conformance.mjs +4 -2
  198. package/scripts/locker.mjs +200 -66
  199. package/scripts/music-player.mjs +159 -150
  200. package/scripts/recipe-utils.mjs +6 -96
  201. package/scripts/release-gates.mjs +60 -0
  202. package/scripts/validate-recipe.mjs +3 -53
  203. package/skills/actors/SKILL.md +53 -266
  204. package/skills/swarm/SKILL.md +11 -33
  205. package/dist/fixtures/protocol/actor-message-branch.json +0 -13
  206. package/dist/fixtures/protocol/mailbox-contract.json +0 -15
  207. package/dist/fixtures/protocol/room-message.json +0 -11
  208. package/dist/fixtures/protocol/room-roster.json +0 -11
  209. package/dist/fixtures/protocol/run-inbox-message.json +0 -9
  210. package/dist/fixtures/protocol/run-outbox-event.json +0 -9
  211. package/dist/lib/mailbox-loop.d.ts +0 -41
  212. package/dist/lib/mailbox-loop.js +0 -60
  213. package/dist/lib/messages.d.ts +0 -25
  214. package/dist/lib/messages.js +0 -122
  215. package/dist/lib/rooms.d.ts +0 -104
  216. package/dist/lib/rooms.js +0 -647
  217. package/dist/lib/runs-mailbox.d.ts +0 -25
  218. package/dist/lib/runs-mailbox.js +0 -146
  219. package/dist/lib/runs-messages.d.ts +0 -15
  220. package/dist/lib/runs-messages.js +0 -179
  221. package/dist/lib/runs-outbox.d.ts +0 -41
  222. package/dist/lib/runs-outbox.js +0 -87
  223. package/dist/lib/tools-mailbox.d.ts +0 -8
  224. package/dist/lib/tools-mailbox.js +0 -48
  225. package/dist/recipes/actor-worker.json +0 -39
  226. package/dist/recipes/coordinator-locker.json +0 -45
  227. package/dist/recipes/locker.json +0 -45
  228. package/dist/recipes/pipeline-room-swarm.json +0 -50
  229. package/dist/recipes/subagent-message.json +0 -32
  230. package/dist/recipes/utility-actor-message.json +0 -23
  231. package/dist/scripts/actor-worker.mjs +0 -214
  232. package/dist/scripts/coordinator.mjs +0 -799
  233. package/docs/actor-messages.md +0 -225
  234. package/fixtures/protocol/actor-message-branch.json +0 -13
  235. package/fixtures/protocol/mailbox-contract.json +0 -15
  236. package/fixtures/protocol/room-message.json +0 -11
  237. package/fixtures/protocol/room-roster.json +0 -11
  238. package/fixtures/protocol/run-inbox-message.json +0 -9
  239. package/fixtures/protocol/run-outbox-event.json +0 -9
  240. package/lib/mailbox-loop.ts +0 -144
  241. package/lib/messages.ts +0 -151
  242. package/lib/rooms.ts +0 -939
  243. package/lib/runs-mailbox.ts +0 -208
  244. package/lib/runs-messages.ts +0 -252
  245. package/lib/runs-outbox.ts +0 -144
  246. package/lib/tools-mailbox.ts +0 -56
  247. package/recipes/actor-worker.json +0 -39
  248. package/recipes/coordinator-locker.json +0 -45
  249. package/recipes/locker.json +0 -45
  250. package/recipes/pipeline-room-swarm.json +0 -50
  251. package/recipes/subagent-message.json +0 -32
  252. package/recipes/utility-actor-message.json +0 -23
  253. package/scripts/actor-worker.mjs +0 -214
  254. package/scripts/coordinator.mjs +0 -799
@@ -1,208 +0,0 @@
1
- /**
2
- * Async run mailbox state.
3
- * Owns: durable run inbox records, claim locks, and handler status transitions.
4
- */
5
-
6
- import { randomUUID } from "node:crypto";
7
- import { mkdirSync, rmSync, statSync, writeFileSync } from "node:fs";
8
- import { join } from "node:path";
9
-
10
- import { readJsonlFileResilient } from "./state-readers.ts";
11
-
12
- const RUN_INBOX_LOCK_TIMEOUT_MS = 5000;
13
- const STALE_INBOX_LOCK_MAX_AGE_MS = 5 * 60 * 1000;
14
-
15
- export type RunInboxStatus =
16
- | "queued"
17
- | "sent"
18
- | "claimed"
19
- | "handled"
20
- | "failed";
21
-
22
- export type RunInboxMessage = Record<string, unknown> & {
23
- id?: string;
24
- status?: RunInboxStatus | string;
25
- };
26
-
27
- export interface ProcessRunInboxResult {
28
- claimed: number;
29
- failed: number;
30
- handled: number;
31
- }
32
-
33
- export function runInboxFile(stateDir: string): string {
34
- return join(stateDir, "inbox.jsonl");
35
- }
36
-
37
- export function parseRunInboxLine(line: string): RunInboxMessage | undefined {
38
- try {
39
- return JSON.parse(line) as RunInboxMessage;
40
- } catch {
41
- return undefined;
42
- }
43
- }
44
-
45
- export function readRunInboxMessagesFromStateDir(
46
- stateDir: string,
47
- ): RunInboxMessage[] {
48
- return readJsonlFileResilient<RunInboxMessage>(runInboxFile(stateDir))
49
- .records;
50
- }
51
-
52
- function sleepSync(ms: number): void {
53
- Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
54
- }
55
-
56
- function acquireRunInboxLock(stateDir: string): () => void {
57
- const lockDir = join(stateDir, ".inbox.lock");
58
- const started = Date.now();
59
- while (true) {
60
- try {
61
- mkdirSync(lockDir, { recursive: false });
62
- writeFileSync(
63
- join(lockDir, "owner.json"),
64
- `${JSON.stringify({ pid: process.pid, created_at: new Date().toISOString() })}\n`,
65
- "utf8",
66
- );
67
- return () => rmSync(lockDir, { recursive: true, force: true });
68
- } catch (error) {
69
- try {
70
- const stat = statSync(lockDir);
71
- if (Date.now() - stat.mtimeMs > STALE_INBOX_LOCK_MAX_AGE_MS) {
72
- rmSync(lockDir, { recursive: true, force: true });
73
- continue;
74
- }
75
- } catch {
76
- continue;
77
- }
78
- if (Date.now() - started > RUN_INBOX_LOCK_TIMEOUT_MS) {
79
- throw new Error("Run inbox lock timed out.", { cause: error });
80
- }
81
- sleepSync(10);
82
- }
83
- }
84
- }
85
-
86
- function writeRunInboxMessages(
87
- stateDir: string,
88
- messages: RunInboxMessage[],
89
- ): void {
90
- writeFileSync(
91
- runInboxFile(stateDir),
92
- messages.length
93
- ? `${messages.map((message) => JSON.stringify(message)).join("\n")}\n`
94
- : "",
95
- "utf8",
96
- );
97
- }
98
-
99
- export function updateRunInboxMessageStatusInStateDir(
100
- stateDir: string,
101
- id: string,
102
- nextStatus: RunInboxStatus,
103
- metadata: Record<string, unknown> = {},
104
- ): boolean {
105
- const releaseLock = acquireRunInboxLock(stateDir);
106
- try {
107
- const messages = readRunInboxMessagesFromStateDir(stateDir);
108
- const timestampKey = `${nextStatus}_at`;
109
- let changed = false;
110
- const updated = messages.map((message) => {
111
- if (message.id !== id) return message;
112
- changed = true;
113
- return {
114
- ...message,
115
- ...metadata,
116
- [timestampKey]: new Date().toISOString(),
117
- status: nextStatus,
118
- };
119
- });
120
- if (changed) writeRunInboxMessages(stateDir, updated);
121
- return changed;
122
- } finally {
123
- releaseLock();
124
- }
125
- }
126
-
127
- export function claimRunInboxMessageInStateDir(
128
- stateDir: string,
129
- owner = "runtime",
130
- statuses: string[] = ["queued"],
131
- ): RunInboxMessage | undefined {
132
- const releaseLock = acquireRunInboxLock(stateDir);
133
- try {
134
- const messages = readRunInboxMessagesFromStateDir(stateDir);
135
- const index = messages.findIndex((message) =>
136
- statuses.includes(String(message.status ?? "queued")),
137
- );
138
- if (index < 0) return undefined;
139
- const claimed = {
140
- ...messages[index],
141
- claimed_at: new Date().toISOString(),
142
- claimed_by: owner,
143
- id:
144
- typeof messages[index].id === "string"
145
- ? messages[index].id
146
- : randomUUID(),
147
- status: "claimed",
148
- } satisfies RunInboxMessage;
149
- messages[index] = claimed;
150
- writeRunInboxMessages(stateDir, messages);
151
- return claimed;
152
- } finally {
153
- releaseLock();
154
- }
155
- }
156
-
157
- export function appendRunInboxMessageInStateDir(
158
- stateDir: string,
159
- record: Record<string, unknown>,
160
- ): string {
161
- const id = randomUUID();
162
- const ts = new Date().toISOString();
163
- const releaseLock = acquireRunInboxLock(stateDir);
164
- try {
165
- writeFileSync(
166
- runInboxFile(stateDir),
167
- `${JSON.stringify({ ...record, id, queued_at: ts, received_at: ts, status: "queued" })}\n`,
168
- { flag: "a" },
169
- );
170
- } finally {
171
- releaseLock();
172
- }
173
- return id;
174
- }
175
-
176
- export async function processRunInboxMessagesInStateDir(
177
- stateDir: string,
178
- handler: (message: RunInboxMessage) => Promise<void> | void,
179
- options: { limit?: number; owner?: string; statuses?: string[] } = {},
180
- ): Promise<ProcessRunInboxResult> {
181
- const result: ProcessRunInboxResult = { claimed: 0, failed: 0, handled: 0 };
182
- const limit = Math.max(1, Number(options.limit ?? 1));
183
- const owner = options.owner ?? "runtime";
184
- for (let index = 0; index < limit; index += 1) {
185
- const message = claimRunInboxMessageInStateDir(
186
- stateDir,
187
- owner,
188
- options.statuses,
189
- );
190
- if (!message?.id) break;
191
- result.claimed += 1;
192
- try {
193
- await handler(message);
194
- if (updateRunInboxMessageStatusInStateDir(stateDir, message.id, "handled")) {
195
- result.handled += 1;
196
- }
197
- } catch (error) {
198
- if (
199
- updateRunInboxMessageStatusInStateDir(stateDir, message.id, "failed", {
200
- error: error instanceof Error ? error.message : String(error),
201
- })
202
- ) {
203
- result.failed += 1;
204
- }
205
- }
206
- }
207
- return result;
208
- }
@@ -1,252 +0,0 @@
1
- /**
2
- * Async run message delivery.
3
- * Owns: run control endpoint resolution, inbox enqueueing, wake notification,
4
- * transport writes, and delivery receipts.
5
- */
6
-
7
- import {
8
- closeSync,
9
- constants,
10
- existsSync,
11
- openSync,
12
- statSync,
13
- writeFileSync,
14
- writeSync,
15
- } from "node:fs";
16
- import { createConnection } from "node:net";
17
- import { join } from "node:path";
18
-
19
- import { markTerminalHandled } from "./runs-control.ts";
20
- import {
21
- appendRunInboxMessageInStateDir,
22
- updateRunInboxMessageStatusInStateDir,
23
- } from "./runs-mailbox.ts";
24
- import { notifyRuntimeWake } from "./runtime-notifier.ts";
25
-
26
- export interface RunControlEndpoint {
27
- path: string;
28
- type: "fifo" | "mailbox" | "named-pipe";
29
- }
30
-
31
- export interface SendRunMessageOptions {
32
- namedPipeSend?: (path: string, payload: string) => Promise<number>;
33
- platform?: NodeJS.Platform;
34
- }
35
-
36
- export function getRunControlEndpoint(
37
- status: Record<string, unknown>,
38
- stateDir: string,
39
- ): RunControlEndpoint {
40
- const control = status.control;
41
- if (control && typeof control === "object" && !Array.isArray(control)) {
42
- const record = control as Record<string, unknown>;
43
- if (
44
- (record.type === "fifo" ||
45
- record.type === "mailbox" ||
46
- record.type === "named-pipe") &&
47
- typeof record.path === "string" &&
48
- record.path.trim()
49
- ) {
50
- return { path: record.path, type: record.type };
51
- }
52
- }
53
- return { path: join(stateDir, "control.fifo"), type: "fifo" };
54
- }
55
-
56
- function appendRunInboxMessage(stateDir: string, message: string): string {
57
- let record: Record<string, unknown>;
58
- try {
59
- const parsed = JSON.parse(message) as unknown;
60
- record =
61
- parsed && typeof parsed === "object" && !Array.isArray(parsed)
62
- ? (parsed as Record<string, unknown>)
63
- : { body: parsed, type: "run.message" };
64
- } catch {
65
- record = { body: message, type: "run.message" };
66
- }
67
- return appendRunInboxMessageInStateDir(stateDir, record);
68
- }
69
-
70
- function writeRunMessageReceipt(
71
- stateDir: string,
72
- message: string,
73
- bytes: number,
74
- inboxId: string,
75
- ): void {
76
- const trimmedMessage = message.trim().toLowerCase();
77
- const terminalMessage = ["stop", "cancel", "quit", "exit"].includes(
78
- trimmedMessage,
79
- );
80
- const ts = new Date().toISOString();
81
- writeFileSync(
82
- join(stateDir, "events.jsonl"),
83
- `${JSON.stringify({ bytes, event: "run.message", inbox_id: inboxId, terminal: terminalMessage || undefined, ts })}\n`,
84
- { flag: "a" },
85
- );
86
- updateRunInboxMessageStatusInStateDir(stateDir, inboxId, "sent", { bytes });
87
- if (terminalMessage) {
88
- markTerminalHandled(stateDir, {
89
- event: "run.message",
90
- message: trimmedMessage,
91
- });
92
- }
93
- }
94
-
95
- function sendRunMessageToFifo(
96
- endpoint: RunControlEndpoint,
97
- payload: string,
98
- ): number {
99
- if (!existsSync(endpoint.path))
100
- throw new Error(`Run control FIFO not found: ${endpoint.path}`);
101
- const stat = statSync(endpoint.path);
102
- if ((stat.mode & constants.S_IFMT) !== constants.S_IFIFO) {
103
- throw new Error(`Run control endpoint is not a FIFO: ${endpoint.path}`);
104
- }
105
- let fd: number | undefined;
106
- try {
107
- fd = openSync(endpoint.path, constants.O_WRONLY | constants.O_NONBLOCK);
108
- return writeSync(fd, payload);
109
- } finally {
110
- if (fd !== undefined) closeSync(fd);
111
- }
112
- }
113
-
114
- function notifyRunMessageWake(
115
- stateDir: string,
116
- run: string,
117
- bytes: number,
118
- endpoint: RunControlEndpoint,
119
- inboxId: string,
120
- ): Record<string, unknown> | undefined {
121
- try {
122
- const event = notifyRuntimeWake(stateDir, {
123
- actor: `run:${run}`,
124
- metadata: { bytes, control_type: endpoint.type, inbox_id: inboxId },
125
- reason: "run.message",
126
- });
127
- return { wake: "wake.jsonl", wake_id: event.id };
128
- } catch {
129
- return undefined;
130
- }
131
- }
132
-
133
- function sendRunMessageToNamedPipe(
134
- endpoint: RunControlEndpoint,
135
- payload: string,
136
- send?: (path: string, payload: string) => Promise<number>,
137
- ): Promise<number> {
138
- if (send) return send(endpoint.path, payload);
139
- return new Promise((resolve, reject) => {
140
- const socket = createConnection(endpoint.path);
141
- let settled = false;
142
- const timeout = setTimeout(() => {
143
- if (settled) return;
144
- settled = true;
145
- socket.destroy();
146
- reject(new Error("named pipe connection timed out"));
147
- }, 5000);
148
- const finish = (error?: Error): void => {
149
- if (settled) return;
150
- settled = true;
151
- clearTimeout(timeout);
152
- if (error) reject(error);
153
- else resolve(Buffer.byteLength(payload));
154
- };
155
- socket.on("error", finish);
156
- socket.on("connect", () => {
157
- socket.end(payload, () => finish());
158
- });
159
- });
160
- }
161
-
162
- export async function deliverRunMessage(
163
- status: Record<string, unknown>,
164
- run: string,
165
- stateDir: string,
166
- message: string,
167
- options: SendRunMessageOptions = {},
168
- ): Promise<Record<string, unknown>> {
169
- const endpoint = getRunControlEndpoint(status, stateDir);
170
- const payload = message.endsWith("\n") ? message : `${message}\n`;
171
- const payloadBytes = Buffer.byteLength(payload);
172
- const inboxId = appendRunInboxMessage(stateDir, message);
173
- const wake = notifyRunMessageWake(
174
- stateDir,
175
- run,
176
- payloadBytes,
177
- endpoint,
178
- inboxId,
179
- );
180
- const runtimePlatform = options.platform ?? process.platform;
181
- if (endpoint.type === "mailbox") {
182
- return {
183
- ...(wake ?? {}),
184
- bytes: payloadBytes,
185
- control: "inbox.jsonl",
186
- control_path: endpoint.path,
187
- control_type: endpoint.type,
188
- inbox_id: inboxId,
189
- queued: true,
190
- run,
191
- sent: true,
192
- state_dir: stateDir,
193
- };
194
- }
195
- try {
196
- if (endpoint.type === "fifo") {
197
- if (runtimePlatform === "win32") {
198
- throw new Error(
199
- "run actor messages on native Windows require a named-pipe control endpoint; this recipe still exposes Unix FIFO control.",
200
- );
201
- }
202
- const bytes = sendRunMessageToFifo(endpoint, payload);
203
- writeRunMessageReceipt(stateDir, message, bytes, inboxId);
204
- return {
205
- ...(wake ?? {}),
206
- bytes,
207
- control: "control.fifo",
208
- control_path: endpoint.path,
209
- control_type: endpoint.type,
210
- inbox_id: inboxId,
211
- run,
212
- sent: true,
213
- state_dir: stateDir,
214
- };
215
- }
216
- const bytes = await sendRunMessageToNamedPipe(
217
- endpoint,
218
- payload,
219
- options.namedPipeSend,
220
- );
221
- writeRunMessageReceipt(stateDir, message, bytes, inboxId);
222
- return {
223
- ...(wake ?? {}),
224
- bytes,
225
- control: endpoint.path,
226
- control_path: endpoint.path,
227
- control_type: endpoint.type,
228
- inbox_id: inboxId,
229
- run,
230
- sent: true,
231
- state_dir: stateDir,
232
- };
233
- } catch (error) {
234
- const deliveryError =
235
- error instanceof Error ? error.message : String(error);
236
- throw Object.assign(
237
- new Error(
238
- `Run control endpoint is not ready: ${endpoint.path}: ${deliveryError}`,
239
- ),
240
- {
241
- control_path: endpoint.path,
242
- control_type: endpoint.type,
243
- delivery_error: deliveryError,
244
- inbox_id: inboxId,
245
- queued: true,
246
- run,
247
- sent: false,
248
- state_dir: stateDir,
249
- },
250
- );
251
- }
252
- }
@@ -1,144 +0,0 @@
1
- /**
2
- * Async run outbox events.
3
- * Owns: outbox event normalization, parsing, and append payload formatting.
4
- */
5
-
6
- export type RunOutboxDelivery = "log" | "notify" | "followup";
7
- export type RunOutboxLevel = "info" | "warning" | "error";
8
-
9
- export interface RunOutboxEvent {
10
- body?: unknown;
11
- correlation_id?: string;
12
- data?: unknown;
13
- delivery: RunOutboxDelivery;
14
- event: string;
15
- from?: string;
16
- id: string;
17
- level: RunOutboxLevel;
18
- metadata?: Record<string, unknown>;
19
- reply_to?: string;
20
- run: string;
21
- state_dir: string;
22
- summary: string;
23
- to?: string;
24
- ts: string;
25
- type?: string;
26
- }
27
-
28
- export function normalizeRunOutboxDelivery(value: unknown): RunOutboxDelivery {
29
- return value === "notify" || value === "followup" ? value : "log";
30
- }
31
-
32
- export function normalizeRunOutboxLevel(value: unknown): RunOutboxLevel {
33
- return value === "warning" || value === "error" ? value : "info";
34
- }
35
-
36
- function normalizeRunOutboxEvent(
37
- raw: unknown,
38
- run: string,
39
- stateDir: string,
40
- index: number,
41
- ): RunOutboxEvent | undefined {
42
- if (!raw || typeof raw !== "object" || Array.isArray(raw)) return undefined;
43
- const record = raw as Record<string, unknown>;
44
- const event =
45
- typeof record.event === "string" && record.event.trim()
46
- ? record.event.trim()
47
- : "run.event";
48
- const summary =
49
- typeof record.summary === "string" && record.summary.trim()
50
- ? record.summary.trim()
51
- : event;
52
- const ts =
53
- typeof record.ts === "string" && record.ts.trim()
54
- ? record.ts.trim()
55
- : new Date(0).toISOString();
56
- const id =
57
- typeof record.id === "string" && record.id.trim()
58
- ? record.id.trim()
59
- : `${run}:${index}`;
60
- return {
61
- ...(record.body !== undefined ? { body: record.body } : {}),
62
- ...(typeof record.correlation_id === "string"
63
- ? { correlation_id: record.correlation_id }
64
- : {}),
65
- ...(record.data !== undefined ? { data: record.data } : {}),
66
- delivery: normalizeRunOutboxDelivery(record.delivery),
67
- ...(record.metadata &&
68
- typeof record.metadata === "object" &&
69
- !Array.isArray(record.metadata)
70
- ? { metadata: record.metadata as Record<string, unknown> }
71
- : {}),
72
- event,
73
- ...(typeof record.from === "string" ? { from: record.from } : {}),
74
- id,
75
- level: normalizeRunOutboxLevel(record.level),
76
- ...(typeof record.reply_to === "string"
77
- ? { reply_to: record.reply_to }
78
- : {}),
79
- run,
80
- state_dir: stateDir,
81
- summary,
82
- ...(typeof record.to === "string" ? { to: record.to } : {}),
83
- ts,
84
- ...(typeof record.type === "string" ? { type: record.type } : {}),
85
- };
86
- }
87
-
88
- export function parseRunOutboxEventLine(
89
- line: string,
90
- run: string,
91
- stateDir: string,
92
- index: number,
93
- ): RunOutboxEvent | undefined {
94
- try {
95
- return normalizeRunOutboxEvent(JSON.parse(line), run, stateDir, index);
96
- } catch {
97
- return undefined;
98
- }
99
- }
100
-
101
- export function buildRunOutboxEventPayload(
102
- run: string,
103
- event: {
104
- body?: unknown;
105
- correlation_id?: string;
106
- data?: unknown;
107
- delivery?: string;
108
- event?: string;
109
- from?: string;
110
- level?: string;
111
- metadata?: Record<string, unknown>;
112
- reply_to?: string;
113
- summary?: string;
114
- to?: string;
115
- type?: string;
116
- },
117
- ): Record<string, unknown> {
118
- const type = event.type || event.event || "run.message";
119
- const to = event.to || "coordinator";
120
- const metadata = event.metadata ?? {};
121
- const requiresResponse = metadata.requires_response === true;
122
- return {
123
- ...(event.body !== undefined ? { body: event.body } : {}),
124
- ...(event.correlation_id ? { correlation_id: event.correlation_id } : {}),
125
- ...(event.data !== undefined ? { data: event.data } : {}),
126
- delivery: normalizeRunOutboxDelivery(
127
- event.delivery ??
128
- (requiresResponse
129
- ? "followup"
130
- : to === "coordinator"
131
- ? "notify"
132
- : "log"),
133
- ),
134
- event: type,
135
- from: event.from || `run:${run}`,
136
- level: normalizeRunOutboxLevel(event.level),
137
- ...(event.metadata ? { metadata: event.metadata } : {}),
138
- ...(event.reply_to ? { reply_to: event.reply_to } : {}),
139
- summary: event.summary || type,
140
- to,
141
- ts: new Date().toISOString(),
142
- type,
143
- };
144
- }
@@ -1,56 +0,0 @@
1
- /**
2
- * Public tool mailbox contract helpers
3
- * Zones: accepted/emitted message type declarations, contract normalization, compact type extraction
4
- * Owns mailbox metadata normalization for public message and inspect tool paths
5
- */
6
-
7
- function asRecord(value: unknown): Record<string, unknown> {
8
- return value && typeof value === "object" && !Array.isArray(value)
9
- ? (value as Record<string, unknown>)
10
- : {};
11
- }
12
-
13
- export function normalizeMailboxEntry(
14
- value: unknown,
15
- ): Record<string, unknown> | undefined {
16
- if (typeof value === "string") return { type: value };
17
- const record = asRecord(value);
18
- if (typeof record.type !== "string" || !record.type.trim()) return undefined;
19
- return {
20
- type: record.type.trim(),
21
- ...(record.body_schema !== undefined
22
- ? { body_schema: record.body_schema }
23
- : {}),
24
- ...(record.ack !== undefined ? { ack: record.ack } : {}),
25
- ...(typeof record.idempotency === "string"
26
- ? { idempotency: record.idempotency }
27
- : {}),
28
- ...(typeof record.level === "string" ? { level: record.level } : {}),
29
- ...(record.requires_response === true ? { requires_response: true } : {}),
30
- ...(typeof record.summary === "string" ? { summary: record.summary } : {}),
31
- };
32
- }
33
-
34
- export function normalizeMailboxContracts(
35
- mailbox: Record<string, unknown>,
36
- ): Record<string, unknown[]> {
37
- const accepts = Array.isArray(mailbox.accepts)
38
- ? mailbox.accepts
39
- .map(normalizeMailboxEntry)
40
- .filter((entry): entry is Record<string, unknown> => Boolean(entry))
41
- : [];
42
- const emits = Array.isArray(mailbox.emits)
43
- ? mailbox.emits
44
- .map(normalizeMailboxEntry)
45
- .filter((entry): entry is Record<string, unknown> => Boolean(entry))
46
- : [];
47
- return { accepts, emits };
48
- }
49
-
50
- export function mailboxTypes(entries: unknown[]): string[] {
51
- return entries.flatMap((entry) =>
52
- typeof asRecord(entry).type === "string"
53
- ? [String(asRecord(entry).type)]
54
- : [],
55
- );
56
- }
@@ -1,39 +0,0 @@
1
- {
2
- "async": true,
3
- "description": "Start a canonical mailbox-backed branch worker demo actor.",
4
- "args": [
5
- "repo:path",
6
- "branch:string",
7
- "poll_ms:int",
8
- "stale_claim_ms:int",
9
- "write_artifacts:bool"
10
- ],
11
- "defaults": {
12
- "repo": "~/.pi/agent/extensions/pi-actors",
13
- "branch": "worker",
14
- "poll_ms": "1000",
15
- "stale_claim_ms": "0",
16
- "write_artifacts": "true"
17
- },
18
- "mailbox": {
19
- "accepts": [
20
- "task.assign",
21
- "control.kill"
22
- ],
23
- "emits": [
24
- "actor.join",
25
- "task.claim",
26
- "task.result",
27
- "awaiting_assignment",
28
- "actor.leave",
29
- "run.done",
30
- "run.failed"
31
- ]
32
- },
33
- "artifacts": {
34
- "journal": "{state_dir}/worker-events.jsonl",
35
- "status": "{state_dir}/worker-status.json",
36
- "results": "{state_dir}/worker-artifacts"
37
- },
38
- "template": "{repo}/scripts/actor-worker.mjs --state-dir {state_dir} --run {run_id} --branch {branch} --poll-ms {poll_ms} --stale-claim-ms {stale_claim_ms} --write-artifacts {write_artifacts}"
39
- }