@cluesmith/codev 3.2.4 → 3.3.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 (394) hide show
  1. package/dashboard-dist/assets/index-BZRTEMtH.css +32 -0
  2. package/dashboard-dist/assets/{index-BZ1x7x1X.js → index-D3XaKpZW.js} +46 -44
  3. package/dashboard-dist/assets/index-D3XaKpZW.js.map +1 -0
  4. package/dashboard-dist/index.html +2 -2
  5. package/dist/agent-farm/cli.d.ts.map +1 -1
  6. package/dist/agent-farm/cli.js +154 -0
  7. package/dist/agent-farm/cli.js.map +1 -1
  8. package/dist/agent-farm/commands/cleanup.d.ts.map +1 -1
  9. package/dist/agent-farm/commands/cleanup.js +16 -0
  10. package/dist/agent-farm/commands/cleanup.js.map +1 -1
  11. package/dist/agent-farm/commands/inbox.d.ts +42 -0
  12. package/dist/agent-farm/commands/inbox.d.ts.map +1 -0
  13. package/dist/agent-farm/commands/inbox.js +129 -0
  14. package/dist/agent-farm/commands/inbox.js.map +1 -0
  15. package/dist/agent-farm/commands/interrupt.d.ts +20 -0
  16. package/dist/agent-farm/commands/interrupt.d.ts.map +1 -0
  17. package/dist/agent-farm/commands/interrupt.js +65 -0
  18. package/dist/agent-farm/commands/interrupt.js.map +1 -0
  19. package/dist/agent-farm/commands/reset/constants.d.ts +52 -0
  20. package/dist/agent-farm/commands/reset/constants.d.ts.map +1 -0
  21. package/dist/agent-farm/commands/reset/constants.js +52 -0
  22. package/dist/agent-farm/commands/reset/constants.js.map +1 -0
  23. package/dist/agent-farm/commands/reset/context.d.ts +230 -0
  24. package/dist/agent-farm/commands/reset/context.d.ts.map +1 -0
  25. package/dist/agent-farm/commands/reset/context.js +428 -0
  26. package/dist/agent-farm/commands/reset/context.js.map +1 -0
  27. package/dist/agent-farm/commands/reset/index.d.ts +192 -0
  28. package/dist/agent-farm/commands/reset/index.d.ts.map +1 -0
  29. package/dist/agent-farm/commands/reset/index.js +488 -0
  30. package/dist/agent-farm/commands/reset/index.js.map +1 -0
  31. package/dist/agent-farm/commands/reset/receipt.d.ts +97 -0
  32. package/dist/agent-farm/commands/reset/receipt.d.ts.map +1 -0
  33. package/dist/agent-farm/commands/reset/receipt.js +149 -0
  34. package/dist/agent-farm/commands/reset/receipt.js.map +1 -0
  35. package/dist/agent-farm/commands/reset/reorient.d.ts +117 -0
  36. package/dist/agent-farm/commands/reset/reorient.d.ts.map +1 -0
  37. package/dist/agent-farm/commands/reset/reorient.js +313 -0
  38. package/dist/agent-farm/commands/reset/reorient.js.map +1 -0
  39. package/dist/agent-farm/commands/reset.d.ts +34 -0
  40. package/dist/agent-farm/commands/reset.d.ts.map +1 -0
  41. package/dist/agent-farm/commands/reset.js +309 -0
  42. package/dist/agent-farm/commands/reset.js.map +1 -0
  43. package/dist/agent-farm/commands/send.d.ts.map +1 -1
  44. package/dist/agent-farm/commands/send.js +49 -8
  45. package/dist/agent-farm/commands/send.js.map +1 -1
  46. package/dist/agent-farm/commands/spawn-worktree.d.ts +105 -6
  47. package/dist/agent-farm/commands/spawn-worktree.d.ts.map +1 -1
  48. package/dist/agent-farm/commands/spawn-worktree.js +289 -66
  49. package/dist/agent-farm/commands/spawn-worktree.js.map +1 -1
  50. package/dist/agent-farm/commands/spawn.d.ts.map +1 -1
  51. package/dist/agent-farm/commands/spawn.js +13 -1
  52. package/dist/agent-farm/commands/spawn.js.map +1 -1
  53. package/dist/agent-farm/commands/status.d.ts.map +1 -1
  54. package/dist/agent-farm/commands/status.js +80 -8
  55. package/dist/agent-farm/commands/status.js.map +1 -1
  56. package/dist/agent-farm/commands/tower.d.ts.map +1 -1
  57. package/dist/agent-farm/commands/tower.js +13 -0
  58. package/dist/agent-farm/commands/tower.js.map +1 -1
  59. package/dist/agent-farm/db/index.d.ts +1 -1
  60. package/dist/agent-farm/db/index.d.ts.map +1 -1
  61. package/dist/agent-farm/db/index.js +80 -1
  62. package/dist/agent-farm/db/index.js.map +1 -1
  63. package/dist/agent-farm/db/mailbox.d.ts +222 -0
  64. package/dist/agent-farm/db/mailbox.d.ts.map +1 -0
  65. package/dist/agent-farm/db/mailbox.js +304 -0
  66. package/dist/agent-farm/db/mailbox.js.map +1 -0
  67. package/dist/agent-farm/db/schema.d.ts +1 -1
  68. package/dist/agent-farm/db/schema.d.ts.map +1 -1
  69. package/dist/agent-farm/db/schema.js +35 -0
  70. package/dist/agent-farm/db/schema.js.map +1 -1
  71. package/dist/agent-farm/db/types.d.ts +45 -0
  72. package/dist/agent-farm/db/types.d.ts.map +1 -1
  73. package/dist/agent-farm/db/types.js.map +1 -1
  74. package/dist/agent-farm/lib/builder-lookup.d.ts +1 -1
  75. package/dist/agent-farm/lib/builder-lookup.js +2 -2
  76. package/dist/agent-farm/lib/builder-lookup.js.map +1 -1
  77. package/dist/agent-farm/lib/reconnect-backoff.d.ts +64 -0
  78. package/dist/agent-farm/lib/reconnect-backoff.d.ts.map +1 -0
  79. package/dist/agent-farm/lib/reconnect-backoff.js +57 -0
  80. package/dist/agent-farm/lib/reconnect-backoff.js.map +1 -0
  81. package/dist/agent-farm/lib/tower-client.d.ts +16 -6
  82. package/dist/agent-farm/lib/tower-client.d.ts.map +1 -1
  83. package/dist/agent-farm/lib/tower-client.js +37 -6
  84. package/dist/agent-farm/lib/tower-client.js.map +1 -1
  85. package/dist/agent-farm/lib/tunnel-client.d.ts +69 -1
  86. package/dist/agent-farm/lib/tunnel-client.d.ts.map +1 -1
  87. package/dist/agent-farm/lib/tunnel-client.js +225 -30
  88. package/dist/agent-farm/lib/tunnel-client.js.map +1 -1
  89. package/dist/agent-farm/servers/canvas-relay.d.ts +60 -0
  90. package/dist/agent-farm/servers/canvas-relay.d.ts.map +1 -0
  91. package/dist/agent-farm/servers/canvas-relay.js +376 -0
  92. package/dist/agent-farm/servers/canvas-relay.js.map +1 -0
  93. package/dist/agent-farm/servers/cron-delivery.d.ts +72 -0
  94. package/dist/agent-farm/servers/cron-delivery.d.ts.map +1 -0
  95. package/dist/agent-farm/servers/cron-delivery.js +80 -0
  96. package/dist/agent-farm/servers/cron-delivery.js.map +1 -0
  97. package/dist/agent-farm/servers/delayed-send.d.ts +85 -0
  98. package/dist/agent-farm/servers/delayed-send.d.ts.map +1 -0
  99. package/dist/agent-farm/servers/delayed-send.js +165 -0
  100. package/dist/agent-farm/servers/delayed-send.js.map +1 -0
  101. package/dist/agent-farm/servers/gate-profiles.d.ts +67 -0
  102. package/dist/agent-farm/servers/gate-profiles.d.ts.map +1 -0
  103. package/dist/agent-farm/servers/gate-profiles.js +113 -0
  104. package/dist/agent-farm/servers/gate-profiles.js.map +1 -0
  105. package/dist/agent-farm/servers/mailbox-delivery.d.ts +370 -0
  106. package/dist/agent-farm/servers/mailbox-delivery.d.ts.map +1 -0
  107. package/dist/agent-farm/servers/mailbox-delivery.js +617 -0
  108. package/dist/agent-farm/servers/mailbox-delivery.js.map +1 -0
  109. package/dist/agent-farm/servers/mailbox-wiring.d.ts +125 -0
  110. package/dist/agent-farm/servers/mailbox-wiring.d.ts.map +1 -0
  111. package/dist/agent-farm/servers/mailbox-wiring.js +425 -0
  112. package/dist/agent-farm/servers/mailbox-wiring.js.map +1 -0
  113. package/dist/agent-farm/servers/message-write.d.ts +53 -1
  114. package/dist/agent-farm/servers/message-write.d.ts.map +1 -1
  115. package/dist/agent-farm/servers/message-write.js +63 -0
  116. package/dist/agent-farm/servers/message-write.js.map +1 -1
  117. package/dist/agent-farm/servers/overview.d.ts +19 -0
  118. package/dist/agent-farm/servers/overview.d.ts.map +1 -1
  119. package/dist/agent-farm/servers/overview.js +100 -4
  120. package/dist/agent-farm/servers/overview.js.map +1 -1
  121. package/dist/agent-farm/servers/render-gate.d.ts +141 -0
  122. package/dist/agent-farm/servers/render-gate.d.ts.map +1 -0
  123. package/dist/agent-farm/servers/render-gate.js +251 -0
  124. package/dist/agent-farm/servers/render-gate.js.map +1 -0
  125. package/dist/agent-farm/servers/session-submit.d.ts +90 -0
  126. package/dist/agent-farm/servers/session-submit.d.ts.map +1 -0
  127. package/dist/agent-farm/servers/session-submit.js +127 -0
  128. package/dist/agent-farm/servers/session-submit.js.map +1 -0
  129. package/dist/agent-farm/servers/tower-cron.d.ts +10 -7
  130. package/dist/agent-farm/servers/tower-cron.d.ts.map +1 -1
  131. package/dist/agent-farm/servers/tower-cron.js +58 -53
  132. package/dist/agent-farm/servers/tower-cron.js.map +1 -1
  133. package/dist/agent-farm/servers/tower-instances.d.ts +14 -2
  134. package/dist/agent-farm/servers/tower-instances.d.ts.map +1 -1
  135. package/dist/agent-farm/servers/tower-instances.js +78 -16
  136. package/dist/agent-farm/servers/tower-instances.js.map +1 -1
  137. package/dist/agent-farm/servers/tower-messages.d.ts +38 -1
  138. package/dist/agent-farm/servers/tower-messages.d.ts.map +1 -1
  139. package/dist/agent-farm/servers/tower-messages.js +106 -1
  140. package/dist/agent-farm/servers/tower-messages.js.map +1 -1
  141. package/dist/agent-farm/servers/tower-routes.d.ts +16 -4
  142. package/dist/agent-farm/servers/tower-routes.d.ts.map +1 -1
  143. package/dist/agent-farm/servers/tower-routes.js +826 -145
  144. package/dist/agent-farm/servers/tower-routes.js.map +1 -1
  145. package/dist/agent-farm/servers/tower-server.js +188 -34
  146. package/dist/agent-farm/servers/tower-server.js.map +1 -1
  147. package/dist/agent-farm/servers/tower-terminals.d.ts +23 -2
  148. package/dist/agent-farm/servers/tower-terminals.d.ts.map +1 -1
  149. package/dist/agent-farm/servers/tower-terminals.js +134 -127
  150. package/dist/agent-farm/servers/tower-terminals.js.map +1 -1
  151. package/dist/agent-farm/servers/tower-tunnel.d.ts.map +1 -1
  152. package/dist/agent-farm/servers/tower-tunnel.js +63 -4
  153. package/dist/agent-farm/servers/tower-tunnel.js.map +1 -1
  154. package/dist/agent-farm/servers/tower-types.d.ts +2 -1
  155. package/dist/agent-farm/servers/tower-types.d.ts.map +1 -1
  156. package/dist/agent-farm/servers/tower-utils.d.ts +64 -1
  157. package/dist/agent-farm/servers/tower-utils.d.ts.map +1 -1
  158. package/dist/agent-farm/servers/tower-utils.js +168 -1
  159. package/dist/agent-farm/servers/tower-utils.js.map +1 -1
  160. package/dist/agent-farm/servers/tower-websocket.d.ts +6 -1
  161. package/dist/agent-farm/servers/tower-websocket.d.ts.map +1 -1
  162. package/dist/agent-farm/servers/tower-websocket.js +95 -57
  163. package/dist/agent-farm/servers/tower-websocket.js.map +1 -1
  164. package/dist/agent-farm/servers/write-queue.d.ts +35 -0
  165. package/dist/agent-farm/servers/write-queue.d.ts.map +1 -0
  166. package/dist/agent-farm/servers/write-queue.js +56 -0
  167. package/dist/agent-farm/servers/write-queue.js.map +1 -0
  168. package/dist/agent-farm/types.d.ts +39 -0
  169. package/dist/agent-farm/types.d.ts.map +1 -1
  170. package/dist/agent-farm/utils/agent-names.d.ts +2 -2
  171. package/dist/agent-farm/utils/agent-names.d.ts.map +1 -1
  172. package/dist/agent-farm/utils/agent-names.js +3 -3
  173. package/dist/agent-farm/utils/agent-names.js.map +1 -1
  174. package/dist/agent-farm/utils/architect-name.d.ts +2 -2
  175. package/dist/agent-farm/utils/architect-name.d.ts.map +1 -1
  176. package/dist/agent-farm/utils/architect-name.js +3 -3
  177. package/dist/agent-farm/utils/architect-name.js.map +1 -1
  178. package/dist/agent-farm/utils/config.d.ts +13 -0
  179. package/dist/agent-farm/utils/config.d.ts.map +1 -1
  180. package/dist/agent-farm/utils/config.js +33 -1
  181. package/dist/agent-farm/utils/config.js.map +1 -1
  182. package/dist/agent-farm/utils/harness.d.ts +103 -7
  183. package/dist/agent-farm/utils/harness.d.ts.map +1 -1
  184. package/dist/agent-farm/utils/harness.js +126 -19
  185. package/dist/agent-farm/utils/harness.js.map +1 -1
  186. package/dist/agent-farm/utils/message-format.d.ts +8 -0
  187. package/dist/agent-farm/utils/message-format.d.ts.map +1 -1
  188. package/dist/agent-farm/utils/message-format.js +20 -0
  189. package/dist/agent-farm/utils/message-format.js.map +1 -1
  190. package/dist/agent-farm/utils/server-utils.d.ts +80 -4
  191. package/dist/agent-farm/utils/server-utils.d.ts.map +1 -1
  192. package/dist/agent-farm/utils/server-utils.js +286 -5
  193. package/dist/agent-farm/utils/server-utils.js.map +1 -1
  194. package/dist/cli.d.ts.map +1 -1
  195. package/dist/cli.js +6 -32
  196. package/dist/cli.js.map +1 -1
  197. package/dist/commands/consult/cli-options.d.ts +34 -0
  198. package/dist/commands/consult/cli-options.d.ts.map +1 -0
  199. package/dist/commands/consult/cli-options.js +67 -0
  200. package/dist/commands/consult/cli-options.js.map +1 -0
  201. package/dist/commands/consult/index.d.ts +56 -2
  202. package/dist/commands/consult/index.d.ts.map +1 -1
  203. package/dist/commands/consult/index.js +272 -28
  204. package/dist/commands/consult/index.js.map +1 -1
  205. package/dist/commands/consult/metrics.d.ts +42 -0
  206. package/dist/commands/consult/metrics.d.ts.map +1 -1
  207. package/dist/commands/consult/metrics.js +129 -9
  208. package/dist/commands/consult/metrics.js.map +1 -1
  209. package/dist/commands/doctor.d.ts.map +1 -1
  210. package/dist/commands/doctor.js +71 -19
  211. package/dist/commands/doctor.js.map +1 -1
  212. package/dist/commands/porch/checks.d.ts +23 -0
  213. package/dist/commands/porch/checks.d.ts.map +1 -1
  214. package/dist/commands/porch/checks.js +60 -0
  215. package/dist/commands/porch/checks.js.map +1 -1
  216. package/dist/commands/porch/config.d.ts +20 -0
  217. package/dist/commands/porch/config.d.ts.map +1 -1
  218. package/dist/commands/porch/config.js +20 -0
  219. package/dist/commands/porch/config.js.map +1 -1
  220. package/dist/commands/porch/index.js +21 -28
  221. package/dist/commands/porch/index.js.map +1 -1
  222. package/dist/commands/porch/next.d.ts.map +1 -1
  223. package/dist/commands/porch/next.js +2 -41
  224. package/dist/commands/porch/next.js.map +1 -1
  225. package/dist/lib/config.d.ts +69 -3
  226. package/dist/lib/config.d.ts.map +1 -1
  227. package/dist/lib/config.js +67 -3
  228. package/dist/lib/config.js.map +1 -1
  229. package/dist/lib/consult-lanes.d.ts +149 -0
  230. package/dist/lib/consult-lanes.d.ts.map +1 -0
  231. package/dist/lib/consult-lanes.js +360 -0
  232. package/dist/lib/consult-lanes.js.map +1 -0
  233. package/dist/lib/forge-contracts.d.ts +9 -0
  234. package/dist/lib/forge-contracts.d.ts.map +1 -1
  235. package/dist/lib/forge-contracts.js.map +1 -1
  236. package/dist/lib/github.d.ts +15 -1
  237. package/dist/lib/github.d.ts.map +1 -1
  238. package/dist/lib/github.js +18 -1
  239. package/dist/lib/github.js.map +1 -1
  240. package/dist/lib/prompt-behavior-metrics.d.ts +67 -0
  241. package/dist/lib/prompt-behavior-metrics.d.ts.map +1 -0
  242. package/dist/lib/prompt-behavior-metrics.js +290 -0
  243. package/dist/lib/prompt-behavior-metrics.js.map +1 -0
  244. package/dist/lib/skeleton.d.ts +22 -1
  245. package/dist/lib/skeleton.d.ts.map +1 -1
  246. package/dist/lib/skeleton.js +120 -0
  247. package/dist/lib/skeleton.js.map +1 -1
  248. package/dist/lib/test-env.d.ts +74 -0
  249. package/dist/lib/test-env.d.ts.map +1 -0
  250. package/dist/lib/test-env.js +92 -0
  251. package/dist/lib/test-env.js.map +1 -0
  252. package/dist/terminal/attach-replay.d.ts +39 -0
  253. package/dist/terminal/attach-replay.d.ts.map +1 -0
  254. package/dist/terminal/attach-replay.js +76 -0
  255. package/dist/terminal/attach-replay.js.map +1 -0
  256. package/dist/terminal/pty-manager.d.ts +2 -0
  257. package/dist/terminal/pty-manager.d.ts.map +1 -1
  258. package/dist/terminal/pty-manager.js +51 -23
  259. package/dist/terminal/pty-manager.js.map +1 -1
  260. package/dist/terminal/pty-session.d.ts +201 -1
  261. package/dist/terminal/pty-session.d.ts.map +1 -1
  262. package/dist/terminal/pty-session.js +327 -46
  263. package/dist/terminal/pty-session.js.map +1 -1
  264. package/dist/terminal/ring-buffer.d.ts +39 -6
  265. package/dist/terminal/ring-buffer.d.ts.map +1 -1
  266. package/dist/terminal/ring-buffer.js +74 -6
  267. package/dist/terminal/ring-buffer.js.map +1 -1
  268. package/dist/terminal/session-manager.d.ts +53 -0
  269. package/dist/terminal/session-manager.d.ts.map +1 -1
  270. package/dist/terminal/session-manager.js +94 -9
  271. package/dist/terminal/session-manager.js.map +1 -1
  272. package/dist/terminal/session-screen.d.ts +116 -0
  273. package/dist/terminal/session-screen.d.ts.map +1 -0
  274. package/dist/terminal/session-screen.js +180 -0
  275. package/dist/terminal/session-screen.js.map +1 -0
  276. package/dist/terminal/shellper-main.js +2 -1
  277. package/dist/terminal/shellper-main.js.map +1 -1
  278. package/dist/terminal/shellper-process.d.ts +1 -1
  279. package/dist/terminal/shellper-process.d.ts.map +1 -1
  280. package/dist/terminal/shellper-process.js +17 -8
  281. package/dist/terminal/shellper-process.js.map +1 -1
  282. package/dist/terminal/shellper-replay-buffer.d.ts +69 -7
  283. package/dist/terminal/shellper-replay-buffer.d.ts.map +1 -1
  284. package/dist/terminal/shellper-replay-buffer.js +167 -31
  285. package/dist/terminal/shellper-replay-buffer.js.map +1 -1
  286. package/package.json +13 -8
  287. package/scripts/forge/gitea/pr-view.sh +4 -1
  288. package/scripts/forge/github/pr-view.sh +1 -1
  289. package/scripts/forge/gitlab/pr-view.sh +4 -1
  290. package/skeleton/.claude/skills/afx/SKILL.md +68 -1
  291. package/skeleton/.claude/skills/arch-init/SKILL.md +22 -9
  292. package/skeleton/.claude/skills/arch-save/SKILL.md +183 -0
  293. package/skeleton/.claude/skills/codev/SKILL.md +31 -0
  294. package/skeleton/.claude/skills/runnable-worktrees/SKILL.md +119 -0
  295. package/skeleton/.codex/skills/afx/SKILL.md +68 -1
  296. package/skeleton/.codex/skills/arch-init/SKILL.md +22 -9
  297. package/skeleton/.codex/skills/arch-save/SKILL.md +183 -0
  298. package/skeleton/.codex/skills/codev/SKILL.md +31 -0
  299. package/skeleton/.codex/skills/runnable-worktrees/SKILL.md +119 -0
  300. package/skeleton/protocols/air/builder-prompt.md +28 -47
  301. package/skeleton/protocols/air/consult-types/impl-review.md +17 -34
  302. package/skeleton/protocols/air/consult-types/pr-review.md +14 -37
  303. package/skeleton/protocols/air/prompts/implement.md +20 -73
  304. package/skeleton/protocols/air/prompts/pr.md +22 -54
  305. package/skeleton/protocols/air/protocol.md +40 -71
  306. package/skeleton/protocols/aspir/builder-prompt.md +29 -66
  307. package/skeleton/protocols/aspir/consult-types/impl-review.md +17 -44
  308. package/skeleton/protocols/aspir/consult-types/phase-review.md +17 -44
  309. package/skeleton/protocols/aspir/consult-types/plan-review.md +14 -38
  310. package/skeleton/protocols/aspir/consult-types/pr-review.md +13 -39
  311. package/skeleton/protocols/aspir/consult-types/spec-review.md +13 -33
  312. package/skeleton/protocols/aspir/prompts/implement.md +27 -196
  313. package/skeleton/protocols/aspir/prompts/plan.md +21 -85
  314. package/skeleton/protocols/aspir/prompts/review.md +29 -228
  315. package/skeleton/protocols/aspir/prompts/specify.md +25 -111
  316. package/skeleton/protocols/aspir/protocol.json +10 -0
  317. package/skeleton/protocols/aspir/protocol.md +36 -82
  318. package/skeleton/protocols/bugfix/builder-prompt.md +23 -45
  319. package/skeleton/protocols/bugfix/consult-types/impl-review.md +21 -45
  320. package/skeleton/protocols/bugfix/consult-types/pr-review.md +18 -52
  321. package/skeleton/protocols/bugfix/prompts/fix.md +16 -61
  322. package/skeleton/protocols/bugfix/prompts/investigate.md +18 -62
  323. package/skeleton/protocols/bugfix/prompts/pr.md +22 -57
  324. package/skeleton/protocols/bugfix/protocol.md +44 -50
  325. package/skeleton/protocols/experiment/builder-prompt.md +25 -58
  326. package/skeleton/protocols/experiment/protocol.md +22 -188
  327. package/skeleton/protocols/experiment/templates/notes.md +11 -71
  328. package/skeleton/protocols/maintain/builder-prompt.md +24 -45
  329. package/skeleton/protocols/maintain/consult-types/impl-review.md +17 -44
  330. package/skeleton/protocols/maintain/consult-types/pr-review.md +13 -39
  331. package/skeleton/protocols/maintain/prompts/maintain.md +1 -1
  332. package/skeleton/protocols/maintain/protocol.md +32 -266
  333. package/skeleton/protocols/maintain/templates/maintenance-run.md +11 -0
  334. package/skeleton/protocols/pir/builder-prompt.md +31 -64
  335. package/skeleton/protocols/pir/consult-types/impl-review.md +17 -50
  336. package/skeleton/protocols/pir/consult-types/pr-review.md +11 -40
  337. package/skeleton/protocols/pir/prompts/implement.md +2 -3
  338. package/skeleton/protocols/pir/prompts/plan.md +1 -4
  339. package/skeleton/protocols/pir/prompts/review.md +2 -2
  340. package/skeleton/protocols/pir/protocol.md +54 -177
  341. package/skeleton/protocols/research/builder-prompt.md +27 -58
  342. package/skeleton/protocols/research/protocol.md +28 -153
  343. package/skeleton/protocols/spike/builder-prompt.md +30 -48
  344. package/skeleton/protocols/spike/protocol.md +24 -107
  345. package/skeleton/protocols/spike/templates/findings.md +9 -39
  346. package/skeleton/protocols/spir/builder-prompt.md +33 -62
  347. package/skeleton/protocols/spir/consult-types/impl-review.md +17 -44
  348. package/skeleton/protocols/spir/consult-types/phase-review.md +17 -44
  349. package/skeleton/protocols/spir/consult-types/plan-review.md +14 -38
  350. package/skeleton/protocols/spir/consult-types/pr-review.md +13 -39
  351. package/skeleton/protocols/spir/consult-types/spec-review.md +13 -33
  352. package/skeleton/protocols/spir/prompts/implement.md +26 -195
  353. package/skeleton/protocols/spir/prompts/plan.md +20 -84
  354. package/skeleton/protocols/spir/prompts/review.md +28 -227
  355. package/skeleton/protocols/spir/prompts/specify.md +24 -110
  356. package/skeleton/protocols/spir/protocol.json +10 -0
  357. package/skeleton/protocols/spir/protocol.md +84 -633
  358. package/skeleton/protocols/spir/templates/plan.md +28 -147
  359. package/skeleton/protocols/spir/templates/review.md +21 -80
  360. package/skeleton/protocols/spir/templates/spec.md +24 -118
  361. package/skeleton/resources/commands/agent-farm.md +262 -0
  362. package/skeleton/resources/commands/consult.md +236 -8
  363. package/skeleton/resources/commands/overview.md +1 -0
  364. package/skeleton/roles/architect.md +67 -317
  365. package/skeleton/roles/builder.md +76 -191
  366. package/skeleton/templates/AGENTS.md +2 -0
  367. package/skeleton/templates/CLAUDE.md +2 -0
  368. package/templates/3d-viewer.html +32 -4
  369. package/templates/open.html +58 -12
  370. package/templates/tower.html +65 -9
  371. package/templates/vendor/three-3MFLoader.js +1478 -0
  372. package/templates/vendor/three-STLLoader.js +410 -0
  373. package/templates/vendor/three-TrackballControls.js +828 -0
  374. package/templates/vendor/three-fflate.module.js +2474 -0
  375. package/templates/vendor/three.module.js +53044 -0
  376. package/dashboard-dist/assets/index-BZ1x7x1X.js.map +0 -1
  377. package/dashboard-dist/assets/index-DB1KBSNE.css +0 -32
  378. package/dist/agent-farm/servers/send-buffer.d.ts +0 -59
  379. package/dist/agent-farm/servers/send-buffer.d.ts.map +0 -1
  380. package/dist/agent-farm/servers/send-buffer.js +0 -116
  381. package/dist/agent-farm/servers/send-buffer.js.map +0 -1
  382. package/skeleton/porch/prompts/defend.md +0 -108
  383. package/skeleton/porch/prompts/diagnose.md +0 -70
  384. package/skeleton/porch/prompts/evaluate.md +0 -132
  385. package/skeleton/porch/prompts/fix.md +0 -59
  386. package/skeleton/porch/prompts/implement.md +0 -88
  387. package/skeleton/porch/prompts/plan.md +0 -74
  388. package/skeleton/porch/prompts/pr.md +0 -84
  389. package/skeleton/porch/prompts/review.md +0 -249
  390. package/skeleton/porch/prompts/specify.md +0 -53
  391. package/skeleton/porch/prompts/test.md +0 -63
  392. package/skeleton/protocols/aspir/templates/plan.md +0 -184
  393. package/skeleton/protocols/aspir/templates/review.md +0 -120
  394. package/skeleton/protocols/aspir/templates/spec.md +0 -154
@@ -0,0 +1,376 @@
1
+ /**
2
+ * Canvas command relay + live view registry for Tower (spec 1401).
3
+ *
4
+ * Lets any CONTROLLER (a control device, a companion app, a test harness) drive an open
5
+ * artifact-canvas view over Tower's existing channels:
6
+ * - host -> Tower: REST on `/api/canvas/views` (register / heartbeat / unregister).
7
+ * - controller -> Tower: REST POST `/api/canvas/command`.
8
+ * - Tower -> host: SSE `canvas-command` at /api/events, addressed to one resolved view.
9
+ *
10
+ * Why this is not just another verb on `command-relay.ts`: that relay is a fire-and-forget
11
+ * broadcast which answers `{ok:true}` whether or not anything is listening. It structurally
12
+ * cannot say "no canvas is open", and it cannot pick one view among several. Both are hard
13
+ * requirements here, so Tower keeps a registry of live views, resolves exactly ONE target, and
14
+ * reports which one it drove.
15
+ *
16
+ * The registry is deliberately in-memory and lease-based: a view is a live UI surface, not
17
+ * durable state, and a host that dies without unregistering must age out rather than linger as
18
+ * a ghost that silently swallows commands.
19
+ */
20
+ import * as path from 'node:path';
21
+ import * as fs from 'node:fs';
22
+ import { randomUUID } from 'node:crypto';
23
+ import { parseJsonBody } from '../utils/server-utils.js';
24
+ // Wire names — the canonical contract lives in @cluesmith/codev-types
25
+ // (CANVAS_COMMAND_ROUTE / CANVAS_VIEWS_ROUTE / CANVAS_COMMAND_EVENT) and must match these. They
26
+ // are re-declared locally because codev runs UNBUNDLED from dist/ and codev-types is a
27
+ // compile-time-only (type) dependency with no runtime dist on the codev side, so a runtime value
28
+ // import from it would not resolve. Same reason command-relay.ts re-declares COMMAND_ROUTE.
29
+ export const CANVAS_COMMAND_ROUTE = '/api/canvas/command';
30
+ export const CANVAS_VIEWS_ROUTE = '/api/canvas/views';
31
+ const CANVAS_COMMAND_EVENT = 'canvas-command';
32
+ /** Everything under this prefix belongs to this module. */
33
+ export const CANVAS_ROUTE_PREFIX = '/api/canvas/';
34
+ /** How often a host should heartbeat to keep its lease alive. */
35
+ export const CANVAS_VIEW_HEARTBEAT_MS = 30_000;
36
+ /**
37
+ * How long a view survives without a heartbeat. Three missed beats: long enough to ride out a
38
+ * busy event loop or a paused debugger, short enough that a killed host's ghost view stops
39
+ * absorbing commands quickly.
40
+ */
41
+ export const CANVAS_VIEW_LEASE_MS = 90_000;
42
+ /**
43
+ * The closed command vocabulary as runtime data, so an unknown command can be rejected. The
44
+ * assertion below fails to compile if `CanvasCommand` gains a member that is missing here, which
45
+ * is what keeps Tower's validation from silently falling behind the contract.
46
+ */
47
+ const CANVAS_COMMANDS = [
48
+ 'block-next',
49
+ 'block-prev',
50
+ 'comment-next',
51
+ 'comment-prev',
52
+ 'heading-next',
53
+ 'heading-prev',
54
+ 'column-forward',
55
+ 'column-back',
56
+ 'viewport-down',
57
+ 'viewport-up',
58
+ 'doc-start',
59
+ 'doc-end',
60
+ 'composer-open',
61
+ 'composer-submit',
62
+ 'composer-cancel',
63
+ 'composer-open-or-submit',
64
+ 'reading-mode-toggle',
65
+ ];
66
+ /** The commands `count` is valid on. Same drift protection as above. */
67
+ const TRAVERSAL_COMMANDS = [
68
+ 'block-next',
69
+ 'block-prev',
70
+ 'comment-next',
71
+ 'comment-prev',
72
+ 'heading-next',
73
+ 'heading-prev',
74
+ 'column-forward',
75
+ 'column-back',
76
+ 'viewport-down',
77
+ 'viewport-up',
78
+ ];
79
+ const views = new Map();
80
+ let sequence = 0;
81
+ let deps = null;
82
+ let inited = false;
83
+ /** Wire the module's dependencies. */
84
+ export function initCanvasRelay(d) {
85
+ deps = d;
86
+ inited = true;
87
+ }
88
+ /** Tear down state (used by tests and shutdown). */
89
+ export function shutdownCanvasRelay() {
90
+ views.clear();
91
+ sequence = 0;
92
+ deps = null;
93
+ inited = false;
94
+ }
95
+ /**
96
+ * Resolve a path the same way the file-tab registry does, so the two agree on what "the same
97
+ * file" means: follow symlinks, fall back to resolving the parent when the file itself does not
98
+ * exist, and finally to a plain absolute path.
99
+ */
100
+ function canonicalPath(raw) {
101
+ try {
102
+ return fs.realpathSync(raw);
103
+ }
104
+ catch {
105
+ try {
106
+ return path.join(fs.realpathSync(path.dirname(raw)), path.basename(raw));
107
+ }
108
+ catch {
109
+ return path.resolve(raw);
110
+ }
111
+ }
112
+ }
113
+ /** Drop views whose lease has lapsed. Called on every access, so no timer is needed. */
114
+ function sweep(nowMs) {
115
+ for (const [id, view] of views) {
116
+ if (nowMs - view.lastSeenAt > CANVAS_VIEW_LEASE_MS)
117
+ views.delete(id);
118
+ }
119
+ }
120
+ /**
121
+ * Pick the single view a command should go to.
122
+ *
123
+ * Zero matches is the caller's problem to hear about, not something to paper over. More than one
124
+ * match resolves to the most recently active view and NEVER fans out: delivering
125
+ * `composer-submit` to two views of one file would post the comment twice.
126
+ */
127
+ function resolveTarget(workspace, file, nowMs) {
128
+ sweep(nowMs);
129
+ const ws = canonicalPath(workspace);
130
+ let wanted = null;
131
+ if (file !== undefined)
132
+ wanted = canonicalPath(file);
133
+ const matches = [];
134
+ for (const view of views.values()) {
135
+ if (view.workspace !== ws)
136
+ continue;
137
+ if (wanted !== null && view.file !== wanted)
138
+ continue;
139
+ matches.push(view);
140
+ }
141
+ if (matches.length === 0)
142
+ return null;
143
+ matches.sort((a, b) => {
144
+ if (b.lastActiveAt !== a.lastActiveAt)
145
+ return b.lastActiveAt - a.lastActiveAt;
146
+ return b.sequence - a.sequence;
147
+ });
148
+ return matches[0];
149
+ }
150
+ /**
151
+ * Single entry point for `/api/canvas/*`, delegated from tower-routes. Lazily initializes from
152
+ * the RouteContext on first hit, then dispatches.
153
+ */
154
+ export async function handleCanvasRoute(req, res, url, ctx) {
155
+ if (!inited) {
156
+ initCanvasRelay({
157
+ broadcast: (type, body) => ctx.broadcastNotification({ type, title: type, body: JSON.stringify(body) }),
158
+ now: () => Date.now(),
159
+ });
160
+ }
161
+ if (req.method === 'POST' && url.pathname === CANVAS_COMMAND_ROUTE) {
162
+ return handleCanvasCommand(req, res);
163
+ }
164
+ if (req.method === 'POST' && url.pathname === CANVAS_VIEWS_ROUTE) {
165
+ return handleRegisterView(req, res);
166
+ }
167
+ const viewMatch = url.pathname.match(/^\/api\/canvas\/views\/([^/]+)(\/heartbeat)?$/);
168
+ if (viewMatch) {
169
+ const viewId = decodeURIComponent(viewMatch[1]);
170
+ if (req.method === 'POST' && viewMatch[2])
171
+ return handleHeartbeat(req, res, viewId);
172
+ if (req.method === 'DELETE' && !viewMatch[2])
173
+ return handleUnregisterView(res, viewId);
174
+ }
175
+ sendJson(res, 404, { ok: false, error: 'Unknown canvas route' });
176
+ }
177
+ /** POST /api/canvas/views — a host registers one live canvas view. */
178
+ async function handleRegisterView(req, res) {
179
+ const d = requireDeps();
180
+ let parsed;
181
+ try {
182
+ parsed = await parseJsonBody(req);
183
+ }
184
+ catch {
185
+ return sendJson(res, 400, { ok: false, error: 'Invalid JSON' });
186
+ }
187
+ const body = asObject(parsed);
188
+ if (!body)
189
+ return sendJson(res, 400, { ok: false, error: 'Invalid JSON' });
190
+ if (!isNonEmptyString(body.workspace)) {
191
+ return sendJson(res, 400, { ok: false, error: 'Missing workspace' });
192
+ }
193
+ if (!isNonEmptyString(body.file)) {
194
+ return sendJson(res, 400, { ok: false, error: 'Missing file' });
195
+ }
196
+ const nowMs = d.now();
197
+ sweep(nowMs);
198
+ sequence += 1;
199
+ const viewId = `canvas-${randomUUID()}`;
200
+ const file = canonicalPath(body.file);
201
+ // One registration per VIEW, never per file: two panels showing the same document are two
202
+ // views with distinct ids, which is precisely the case the MRU rule exists to disambiguate.
203
+ views.set(viewId, {
204
+ viewId,
205
+ workspace: canonicalPath(body.workspace),
206
+ file,
207
+ lastActiveAt: nowMs,
208
+ lastSeenAt: nowMs,
209
+ sequence,
210
+ });
211
+ const result = { ok: true, viewId, file };
212
+ return sendJson(res, 200, result);
213
+ }
214
+ /**
215
+ * POST /api/canvas/views/:viewId/heartbeat — keep the lease alive.
216
+ *
217
+ * `focused: true` additionally makes this the most recently active view. Tower stamps both times
218
+ * itself; host clocks are never trusted, so MRU stays deterministic across hosts whose clocks
219
+ * disagree.
220
+ */
221
+ async function handleHeartbeat(req, res, viewId) {
222
+ const d = requireDeps();
223
+ let parsed;
224
+ try {
225
+ parsed = await parseJsonBody(req);
226
+ }
227
+ catch {
228
+ // A bodyless heartbeat is valid and already parses as `{}` ("still here, not necessarily
229
+ // focused"), so reaching here means the body was genuinely malformed. Renewing the lease on
230
+ // a payload we could not read would be extending liveness on the strength of a broken
231
+ // request.
232
+ return sendJson(res, 400, { ok: false, error: 'Invalid JSON' });
233
+ }
234
+ const body = asObject(parsed);
235
+ if (!body)
236
+ return sendJson(res, 400, { ok: false, error: 'Invalid JSON' });
237
+ // `focused` is optional but typed: a non-boolean violates `CanvasViewHeartbeat`. Reject before
238
+ // touching the lease, matching how the command route treats a bad `count` — a payload that does
239
+ // not meet the contract should not buy liveness just because the rest of it parsed.
240
+ if (body.focused !== undefined && typeof body.focused !== 'boolean') {
241
+ return sendJson(res, 400, { ok: false, error: 'focused must be a boolean' });
242
+ }
243
+ const nowMs = d.now();
244
+ sweep(nowMs);
245
+ const view = views.get(viewId);
246
+ // 404 rather than a silent re-create: a host holding an id Tower has forgotten (a restart, an
247
+ // expired lease) must learn that and re-register, or it would heartbeat into the void forever.
248
+ if (!view)
249
+ return sendJson(res, 404, { ok: false, error: 'Unknown view' });
250
+ view.lastSeenAt = nowMs;
251
+ if (body.focused === true)
252
+ view.lastActiveAt = nowMs;
253
+ return sendJson(res, 200, { ok: true });
254
+ }
255
+ /** DELETE /api/canvas/views/:viewId — the host's view is gone. */
256
+ function handleUnregisterView(res, viewId) {
257
+ const existed = views.delete(viewId);
258
+ if (!existed)
259
+ return sendJson(res, 404, { ok: false, error: 'Unknown view' });
260
+ return sendJson(res, 200, { ok: true });
261
+ }
262
+ /**
263
+ * POST /api/canvas/command — run one command against the resolved target view.
264
+ *
265
+ * Answers explicitly in every case: `no-canvas` (404) when the selector matches nothing,
266
+ * `invalid-request` (400) when the payload is malformed, and the resolved target on success.
267
+ * Delivery itself rides SSE and is best-effort; what is guaranteed is the ANSWER about targeting.
268
+ */
269
+ async function handleCanvasCommand(req, res) {
270
+ const d = requireDeps();
271
+ let parsed;
272
+ try {
273
+ parsed = await parseJsonBody(req);
274
+ }
275
+ catch {
276
+ return sendJson(res, 400, invalidRequest('Invalid JSON'));
277
+ }
278
+ const raw = asObject(parsed);
279
+ if (!raw)
280
+ return sendJson(res, 400, invalidRequest('Invalid JSON'));
281
+ const invalid = validateCommandRequest(raw);
282
+ if (invalid)
283
+ return sendJson(res, 400, invalidRequest(invalid));
284
+ const workspace = raw.workspace;
285
+ const file = raw.file;
286
+ const command = raw.command;
287
+ const count = raw.count;
288
+ const nowMs = d.now();
289
+ const target = resolveTarget(workspace, file, nowMs);
290
+ if (!target) {
291
+ const noCanvas = {
292
+ ok: false,
293
+ code: 'no-canvas',
294
+ error: 'No canvas view is open for that workspace/file',
295
+ };
296
+ return sendJson(res, 404, noCanvas);
297
+ }
298
+ const event = { viewId: target.viewId, command };
299
+ if (count !== undefined)
300
+ event.count = count;
301
+ d.broadcast(CANVAS_COMMAND_EVENT, event);
302
+ // Driving a view is activity: it keeps that view the MRU target for the follow-up commands a
303
+ // controller is about to send, even though it never took focus in its host.
304
+ //
305
+ // It is deliberately NOT liveness. Delivery is fire-and-forget over SSE and proves nothing
306
+ // about the host still being alive, so refreshing the lease here would mean a ghost view kept
307
+ // renewing itself for as long as a controller kept driving it — exactly when the reviewer most
308
+ // needs to be told there is no canvas. Only a heartbeat, which only a live host can send,
309
+ // extends the lease.
310
+ target.lastActiveAt = nowMs;
311
+ const ok = {
312
+ ok: true,
313
+ target: { viewId: target.viewId, file: target.file },
314
+ };
315
+ return sendJson(res, 200, ok);
316
+ }
317
+ /** Returns an error message when the payload is not a valid command request, else null. */
318
+ function validateCommandRequest(raw) {
319
+ if (!isNonEmptyString(raw.workspace))
320
+ return 'Missing workspace';
321
+ if (raw.file !== undefined && !isNonEmptyString(raw.file))
322
+ return 'Invalid file';
323
+ if (typeof raw.command !== 'string')
324
+ return 'Missing command';
325
+ if (!CANVAS_COMMANDS.includes(raw.command)) {
326
+ return `Unknown command: ${raw.command}`;
327
+ }
328
+ if (raw.count !== undefined) {
329
+ const count = raw.count;
330
+ if (typeof count !== 'number' || !Number.isInteger(count) || count < 1) {
331
+ return 'count must be a positive integer';
332
+ }
333
+ if (!TRAVERSAL_COMMANDS.includes(raw.command)) {
334
+ return `count is not valid for ${raw.command}`;
335
+ }
336
+ }
337
+ return null;
338
+ }
339
+ /** Every command-route failure carries a wire `code`; this keeps that impossible to forget. */
340
+ function invalidRequest(error) {
341
+ return { ok: false, code: 'invalid-request', error };
342
+ }
343
+ function isNonEmptyString(value) {
344
+ return typeof value === 'string' && value.trim().length > 0;
345
+ }
346
+ /**
347
+ * `parseJsonBody` is typed as returning an object but will happily resolve a literal `null`, a
348
+ * bare array, or a number, since those are all valid JSON. Reading a field off `null` throws,
349
+ * which would escape as a 500 with no wire `code` — the one thing the error contract forbids.
350
+ */
351
+ function asObject(value) {
352
+ if (typeof value !== 'object' || value === null || Array.isArray(value))
353
+ return null;
354
+ return value;
355
+ }
356
+ /** Access the wired deps, throwing a clear error if init was skipped. */
357
+ function requireDeps() {
358
+ if (!deps)
359
+ throw new Error('Canvas relay not initialized');
360
+ return deps;
361
+ }
362
+ /** Write a JSON response with the given status code. */
363
+ function sendJson(res, status, body) {
364
+ res.writeHead(status, { 'Content-Type': 'application/json' });
365
+ res.end(JSON.stringify(body));
366
+ }
367
+ /** Live views, for tests and diagnostics. Never part of the wire surface. */
368
+ export function listCanvasViewsForTest() {
369
+ return [...views.values()].map((v) => ({
370
+ viewId: v.viewId,
371
+ workspace: v.workspace,
372
+ file: v.file,
373
+ lastActiveAt: v.lastActiveAt,
374
+ }));
375
+ }
376
+ //# sourceMappingURL=canvas-relay.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canvas-relay.js","sourceRoot":"","sources":["../../../src/agent-farm/servers/canvas-relay.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Cron message delivery through the mailbox + gate (Spec 1313, Phase 6).
3
+ *
4
+ * Cron is today the most unguarded message writer: it wrote straight to the PTY with
5
+ * no idle check and logged "delivered" unconditionally. This phase makes it an
6
+ * ordinary mailbox sender — every cron notification is persisted and then delivered
7
+ * through the SAME single gated path (`deliverAgentMailSerialized`) that `handleSend`
8
+ * and the backstop drainer use, so a cron message can never land mid-draft or fuse
9
+ * with a human's half-typed line. There is no force path: a busy/menu/wrapper screen
10
+ * holds the message for the backstop, exactly like any other send.
11
+ *
12
+ * Two cron-specific twists on top of the shared path:
13
+ * - **Supersede key = task name** (Baked Decision 6): a newer run of a task replaces
14
+ * its own older *held* row instead of queueing a backlog. Supersede keys are
15
+ * cron-only in this project — no non-cron send ever supplies one.
16
+ * - **Honest run outcome**: the caller logs the real fate (`delivered` / `held` /
17
+ * `superseded`) rather than an unconditional "delivered".
18
+ *
19
+ * This module holds the registry-free orchestration core behind the Phase-4
20
+ * {@link DeliveryPorts} seam, so it is unit-testable against a real mailbox DB with
21
+ * fake edges (no live Tower). The identity resolution that needs the live routing
22
+ * registry (`resolveTarget` + the architect reverse-map + the dead-session registry
23
+ * fallback) lives in `tower-routes.ts`'s `deliverCronMessage`, which calls this.
24
+ */
25
+ import type Database from 'better-sqlite3';
26
+ import type { MailboxReason } from '../db/types.js';
27
+ import { type DeliveryPorts } from './mailbox-delivery.js';
28
+ /** The pseudo-agent identity every cron notification is sent as. */
29
+ export declare const CRON_SENDER = "af-cron";
30
+ /** The real fate of one cron run's message (what the run log records). */
31
+ export type CronOutcome = 'delivered' | 'held' | 'superseded' | 'unresolved';
32
+ /** Outcome of routing a cron notification through the mailbox + gate. */
33
+ export interface CronDeliveryResult {
34
+ /**
35
+ * `delivered` — written to a render-verified empty prompt now; `held` — this run's
36
+ * message is held for the backstop (line busy / menu / no profile / no live PTY);
37
+ * `superseded` — held, and it replaced a still-held row from an earlier run of the
38
+ * same task (no backlog); `unresolved` — the target could not be resolved at all
39
+ * (nothing persisted).
40
+ */
41
+ outcome: CronOutcome;
42
+ /** Why held, when `held`/`superseded`; null when `delivered`/`unresolved`. */
43
+ reason: MailboxReason | null;
44
+ /** The persisted row id (audit); null only when `unresolved`. */
45
+ mailboxId: string | null;
46
+ }
47
+ /** A resolved cron recipient plus the bytes to persist. */
48
+ export interface CronTarget {
49
+ workspacePath: string;
50
+ /** Canonical recipient agent id (a builder id or a specific architect name). */
51
+ toAgent: string;
52
+ /** Last-known PTY hint; null when the recipient has no live terminal. */
53
+ terminalId: string | null;
54
+ /** Raw message body (never logged). */
55
+ body: string;
56
+ /** Exact bytes written to the PTY on delivery. */
57
+ formattedMessage: string;
58
+ /** Per-task coalescing key (Baked Decision 6) — the task name. */
59
+ supersedeKey: string;
60
+ }
61
+ /**
62
+ * Persist a cron notification (superseding any still-held row from an earlier run of
63
+ * the same task) and attempt one gated delivery, returning the run's real outcome.
64
+ *
65
+ * The corruption-safety is entirely inherited from {@link deliverAgentMailSerialized}:
66
+ * the body is only ever written to a render-verified empty prompt, and the per-agent
67
+ * serializer means a concurrent send can never interleave with this write. A busy or
68
+ * unclassifiable screen simply leaves the row held for the backstop — there is no
69
+ * force path here, by construction.
70
+ */
71
+ export declare function deliverCronMail(ports: DeliveryPorts, db: Database.Database, target: CronTarget): Promise<CronDeliveryResult>;
72
+ //# sourceMappingURL=cron-delivery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cron-delivery.d.ts","sourceRoot":"","sources":["../../../src/agent-farm/servers/cron-delivery.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAE3C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAA8B,KAAK,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAEvF,oEAAoE;AACpE,eAAO,MAAM,WAAW,YAAY,CAAC;AAErC,0EAA0E;AAC1E,MAAM,MAAM,WAAW,GAAG,WAAW,GAAG,MAAM,GAAG,YAAY,GAAG,YAAY,CAAC;AAE7E,yEAAyE;AACzE,MAAM,WAAW,kBAAkB;IACjC;;;;;;OAMG;IACH,OAAO,EAAE,WAAW,CAAC;IACrB,8EAA8E;IAC9E,MAAM,EAAE,aAAa,GAAG,IAAI,CAAC;IAC7B,iEAAiE;IACjE,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,2DAA2D;AAC3D,MAAM,WAAW,UAAU;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,gFAAgF;IAChF,OAAO,EAAE,MAAM,CAAC;IAChB,yEAAyE;IACzE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,uCAAuC;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,kDAAkD;IAClD,gBAAgB,EAAE,MAAM,CAAC;IACzB,kEAAkE;IAClE,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;;GASG;AACH,wBAAsB,eAAe,CACnC,KAAK,EAAE,aAAa,EACpB,EAAE,EAAE,QAAQ,CAAC,QAAQ,EACrB,MAAM,EAAE,UAAU,GACjB,OAAO,CAAC,kBAAkB,CAAC,CAgD7B"}
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Cron message delivery through the mailbox + gate (Spec 1313, Phase 6).
3
+ *
4
+ * Cron is today the most unguarded message writer: it wrote straight to the PTY with
5
+ * no idle check and logged "delivered" unconditionally. This phase makes it an
6
+ * ordinary mailbox sender — every cron notification is persisted and then delivered
7
+ * through the SAME single gated path (`deliverAgentMailSerialized`) that `handleSend`
8
+ * and the backstop drainer use, so a cron message can never land mid-draft or fuse
9
+ * with a human's half-typed line. There is no force path: a busy/menu/wrapper screen
10
+ * holds the message for the backstop, exactly like any other send.
11
+ *
12
+ * Two cron-specific twists on top of the shared path:
13
+ * - **Supersede key = task name** (Baked Decision 6): a newer run of a task replaces
14
+ * its own older *held* row instead of queueing a backlog. Supersede keys are
15
+ * cron-only in this project — no non-cron send ever supplies one.
16
+ * - **Honest run outcome**: the caller logs the real fate (`delivered` / `held` /
17
+ * `superseded`) rather than an unconditional "delivered".
18
+ *
19
+ * This module holds the registry-free orchestration core behind the Phase-4
20
+ * {@link DeliveryPorts} seam, so it is unit-testable against a real mailbox DB with
21
+ * fake edges (no live Tower). The identity resolution that needs the live routing
22
+ * registry (`resolveTarget` + the architect reverse-map + the dead-session registry
23
+ * fallback) lives in `tower-routes.ts`'s `deliverCronMessage`, which calls this.
24
+ */
25
+ import { supersede, getById, countHeldWithKey } from '../db/mailbox.js';
26
+ import { deliverAgentMailSerialized } from './mailbox-delivery.js';
27
+ /** The pseudo-agent identity every cron notification is sent as. */
28
+ export const CRON_SENDER = 'af-cron';
29
+ /**
30
+ * Persist a cron notification (superseding any still-held row from an earlier run of
31
+ * the same task) and attempt one gated delivery, returning the run's real outcome.
32
+ *
33
+ * The corruption-safety is entirely inherited from {@link deliverAgentMailSerialized}:
34
+ * the body is only ever written to a render-verified empty prompt, and the per-agent
35
+ * serializer means a concurrent send can never interleave with this write. A busy or
36
+ * unclassifiable screen simply leaves the row held for the backstop — there is no
37
+ * force path here, by construction.
38
+ */
39
+ export async function deliverCronMail(ports, db, target) {
40
+ const { workspacePath, toAgent, supersedeKey } = target;
41
+ // Did an earlier run of this task leave a row still held? Read it BEFORE the
42
+ // supersede, with no await between, so the pair is atomic on the synchronous DB
43
+ // handle (see countHeldWithKey). This only informs the log word — the "no backlog"
44
+ // correctness comes from supersede() being atomic regardless.
45
+ const replacedPrior = countHeldWithKey(db, workspacePath, supersedeKey) > 0;
46
+ const row = supersede(db, workspacePath, supersedeKey, {
47
+ workspacePath,
48
+ toAgent,
49
+ terminalId: target.terminalId,
50
+ body: target.body,
51
+ formattedMessage: target.formattedMessage,
52
+ fromAgent: CRON_SENDER,
53
+ fromWorkspace: workspacePath,
54
+ }, ports.now());
55
+ // The held set changed (a new row enqueued, possibly replacing a prior held one) →
56
+ // refresh the indicator count (Spec 1313, Phase 7). A clean delivery below fires it
57
+ // again when the row leaves the set; both are cheap, idempotent refetch triggers.
58
+ ports.onHeldStateChange();
59
+ try {
60
+ await deliverAgentMailSerialized(ports, db, workspacePath, toAgent);
61
+ }
62
+ catch (err) {
63
+ // A gate/write error leaves the row HELD (markDelivered only runs on a completed
64
+ // write); the backstop drainer retries. Mirrors handleSend — never throws upward.
65
+ ports.log(`[cron] delivery attempt errored for ${toAgent} (row ${row.id.slice(0, 8)}… stays held): ${String(err)}`);
66
+ }
67
+ const stored = getById(db, row.id);
68
+ if (stored?.status === 'delivered') {
69
+ return { outcome: 'delivered', reason: null, mailboxId: row.id };
70
+ }
71
+ // Held. `reason` is set by the delivery pass when it holds; default to `busy` for
72
+ // the rare case where an older row for the same agent delivered first and left ours
73
+ // queued behind it (its Enter makes the line busy for the next pass anyway).
74
+ return {
75
+ outcome: replacedPrior ? 'superseded' : 'held',
76
+ reason: stored?.reason ?? 'busy',
77
+ mailboxId: row.id,
78
+ };
79
+ }
80
+ //# sourceMappingURL=cron-delivery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cron-delivery.js","sourceRoot":"","sources":["../../../src/agent-farm/servers/cron-delivery.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAGH,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAExE,OAAO,EAAE,0BAA0B,EAAsB,MAAM,uBAAuB,CAAC;AAEvF,oEAAoE;AACpE,MAAM,CAAC,MAAM,WAAW,GAAG,SAAS,CAAC;AAoCrC;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,KAAoB,EACpB,EAAqB,EACrB,MAAkB;IAElB,MAAM,EAAE,aAAa,EAAE,OAAO,EAAE,YAAY,EAAE,GAAG,MAAM,CAAC;IAExD,6EAA6E;IAC7E,gFAAgF;IAChF,mFAAmF;IACnF,8DAA8D;IAC9D,MAAM,aAAa,GAAG,gBAAgB,CAAC,EAAE,EAAE,aAAa,EAAE,YAAY,CAAC,GAAG,CAAC,CAAC;IAC5E,MAAM,GAAG,GAAG,SAAS,CACnB,EAAE,EACF,aAAa,EACb,YAAY,EACZ;QACE,aAAa;QACb,OAAO;QACP,UAAU,EAAE,MAAM,CAAC,UAAU;QAC7B,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,gBAAgB,EAAE,MAAM,CAAC,gBAAgB;QACzC,SAAS,EAAE,WAAW;QACtB,aAAa,EAAE,aAAa;KAC7B,EACD,KAAK,CAAC,GAAG,EAAE,CACZ,CAAC;IACF,mFAAmF;IACnF,oFAAoF;IACpF,kFAAkF;IAClF,KAAK,CAAC,iBAAiB,EAAE,CAAC;IAE1B,IAAI,CAAC;QACH,MAAM,0BAA0B,CAAC,KAAK,EAAE,EAAE,EAAE,aAAa,EAAE,OAAO,CAAC,CAAC;IACtE,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,iFAAiF;QACjF,kFAAkF;QAClF,KAAK,CAAC,GAAG,CAAC,uCAAuC,OAAO,SAAS,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,kBAAkB,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACtH,CAAC;IAED,MAAM,MAAM,GAAG,OAAO,CAAC,EAAE,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;IACnC,IAAI,MAAM,EAAE,MAAM,KAAK,WAAW,EAAE,CAAC;QACnC,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC;IACnE,CAAC;IACD,kFAAkF;IAClF,oFAAoF;IACpF,6EAA6E;IAC7E,OAAO;QACL,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,MAAM;QAC9C,MAAM,EAAE,MAAM,EAAE,MAAM,IAAI,MAAM;QAChC,SAAS,EAAE,GAAG,CAAC,EAAE;KAClB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Due-time timer for the delayed `afx send --delay --interrupt` path (Spec 1307,
3
+ * reshaped by Spec 1313 round 3).
4
+ *
5
+ * ## What this module does NOW (and no longer does)
6
+ *
7
+ * The message *body* of every `--delay` send is persisted to the durable mailbox
8
+ * at REQUEST time, with a `not_before` due time, by `handleDelayedSend` in
9
+ * tower-routes.ts — NOT here, and NOT at fire time. The gated backstop drainer
10
+ * delivers that row once `not_before` passes, so a plain `--delay` keeps no timer
11
+ * at all and survives a Tower restart by construction.
12
+ *
13
+ * This module holds a due-time timer used by ONE caller: the delayed-`--interrupt`
14
+ * path. When it fires it writes only the Ctrl+C that ends the current turn — no
15
+ * message body, and it marks nothing `delivered`. The body then lands through the
16
+ * same render gate every send uses, after the ^C ends the turn.
17
+ *
18
+ * ## Why the registry exists at all
19
+ *
20
+ * A bare `setTimeout` would work until Tower shuts down, at which point the
21
+ * process would either hang on a pending timer or exit with a ^C half-scheduled
22
+ * and no record of it. The registry makes shutdown explicit.
23
+ *
24
+ * ## Shutdown drops the ^C nudge, never the message
25
+ *
26
+ * A pre-due `--delay` message is a persisted mailbox row: it survives a restart
27
+ * and delivers when the target's prompt is next clean. Only the in-memory ^C
28
+ * nudge is dropped on shutdown — recoverable (a human re-interrupts if it matters),
29
+ * and matching the documented "only the interrupt semantics gracefully degrade"
30
+ * boundary. This is the CONSCIOUS reversal of Spec 1307's original body-drop-on-
31
+ * restart trade (see review 1313): the render gate now supplies the protection that
32
+ * trade wanted — a post-restart delivery still only lands on a render-verified empty
33
+ * prompt, and a stale pending row is visible and cancellable in `afx inbox`.
34
+ */
35
+ /**
36
+ * Upper bound on `--delay`, in seconds.
37
+ *
38
+ * One hour. Not a meaningful workflow limit — it exists so a typo (`--delay
39
+ * 1500` when 15 was meant) cannot park a message far in the future unnoticed.
40
+ * Under Spec 1313 round 3 a parked send IS listable and cancellable via
41
+ * `afx inbox` / `afx inbox dismiss` (it is a durable mailbox row), but the
42
+ * ceiling stays as a cheap guard against the typo case.
43
+ */
44
+ export declare const MAX_DELAY_SECONDS = 3600;
45
+ /**
46
+ * Validate a delay in seconds, returning null when acceptable or an error
47
+ * string naming the problem.
48
+ *
49
+ * `Number.isInteger` rather than a bare comparison chain: `NaN > 0` and
50
+ * `NaN <= 0` are both false, so a NaN slips through any single comparison
51
+ * written the obvious way and yields a `setTimeout` that fires immediately —
52
+ * silently converting a delayed send into an immediate one. Infinity is
53
+ * rejected for the same class of reason.
54
+ */
55
+ export declare function validateDelaySeconds(value: unknown): string | null;
56
+ /**
57
+ * Schedule `deliver` to run after `delaySeconds`.
58
+ *
59
+ * The callback is responsible for re-resolving the session and re-deciding how
60
+ * to deliver; this module guarantees only *when* it is invoked, and that it is
61
+ * invoked at most once.
62
+ */
63
+ export declare function scheduleDelayedSend(delaySeconds: number, terminalId: string,
64
+ /**
65
+ * Invoked when the send comes due. Receives `isStillLive`, which it must
66
+ * re-check at the moment it actually writes (inside the submission lock): a
67
+ * delivery can acquire the lock only AFTER a shutdown that fired while it
68
+ * queued, and the generation check below only guards the moment BEFORE it
69
+ * enters the lock. Return value is ignored.
70
+ */
71
+ deliver: (isStillLive: () => boolean) => unknown): void;
72
+ /**
73
+ * Cancel every pending delayed send without delivering. Returns the count of
74
+ * still-timing sends dropped, so shutdown can log it rather than losing
75
+ * messages silently.
76
+ *
77
+ * Covers two states: sends still on their timer (cleared here) and sends whose
78
+ * timer has fired but whose delivery has not started — invalidated by bumping
79
+ * `generation`, which the delivery callback re-checks. A delivery already
80
+ * writing when this runs is NOT interrupted; see `generation`'s note.
81
+ */
82
+ export declare function shutdownDelayedSends(): number;
83
+ /** Number of pending delayed sends. Diagnostics and tests. */
84
+ export declare function pendingDelayedSendCount(): number;
85
+ //# sourceMappingURL=delayed-send.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delayed-send.d.ts","sourceRoot":"","sources":["../../../src/agent-farm/servers/delayed-send.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAwCH;;;;;;;;GAQG;AACH,eAAO,MAAM,iBAAiB,OAAO,CAAC;AAEtC;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAWlE;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM;AAClB;;;;;;GAMG;AACH,OAAO,EAAE,CAAC,WAAW,EAAE,MAAM,OAAO,KAAK,OAAO,GAC/C,IAAI,CAmCN;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAQ7C;AAED,8DAA8D;AAC9D,wBAAgB,uBAAuB,IAAI,MAAM,CAEhD"}