@ours.network/fleet 1.2.0-nightly.7 → 1.2.0-nightly.71

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 (423) hide show
  1. package/README.md +279 -68
  2. package/dist/account-origin.d.ts +2 -0
  3. package/dist/account-origin.js +6 -0
  4. package/dist/agent-ours/bridge.js +26 -9
  5. package/dist/agent-ours/correspondence.d.ts +10 -0
  6. package/dist/agent-ours/correspondence.js +1 -0
  7. package/dist/agent-ours/file-rpc.d.ts +12 -0
  8. package/dist/agent-ours/file-rpc.js +18 -4
  9. package/dist/agent-ours/harness.js +9 -1
  10. package/dist/agent-ours/mcp-endpoint.d.ts +19 -0
  11. package/dist/agent-ours/mcp-endpoint.js +98 -25
  12. package/dist/agent-ours/runtime.d.ts +9 -1
  13. package/dist/agent-ours/runtime.js +50 -2
  14. package/dist/agent-ours/service.d.ts +24 -1
  15. package/dist/agent-ours/service.js +97 -13
  16. package/dist/application/codex-models.d.ts +15 -0
  17. package/dist/application/codex-models.js +56 -0
  18. package/dist/application/conversation-history.d.ts +34 -0
  19. package/dist/application/conversation-history.js +280 -0
  20. package/dist/application/conversation-role-observer.d.ts +62 -0
  21. package/dist/application/conversation-role-observer.js +206 -0
  22. package/dist/application/fleet-query-service.d.ts +1 -1
  23. package/dist/application/fleet-query-service.js +3 -3
  24. package/dist/application/log-service.js +8 -8
  25. package/dist/application/model-catalog.d.ts +1 -1
  26. package/dist/application/model-catalog.js +5 -2
  27. package/dist/application/preset-provenance.d.ts +19 -0
  28. package/dist/application/preset-provenance.js +59 -0
  29. package/dist/application/resource-observer.d.ts +40 -0
  30. package/dist/application/resource-observer.js +278 -0
  31. package/dist/application/role-creation-service.js +3 -2
  32. package/dist/application/role-removal-service.js +17 -3
  33. package/dist/application/role-repository.d.ts +1 -1
  34. package/dist/application/role-repository.js +30 -7
  35. package/dist/application/room-layout-definitions.d.ts +45 -0
  36. package/dist/application/room-layout-definitions.js +130 -0
  37. package/dist/application/session-control.d.ts +61 -17
  38. package/dist/application/session-control.js +207 -23
  39. package/dist/application/supervisor-ours-tools.js +5 -11
  40. package/dist/application/task-layouts.d.ts +91 -0
  41. package/dist/application/task-layouts.js +261 -0
  42. package/dist/application/task-room-service.d.ts +101 -3
  43. package/dist/application/task-room-service.js +286 -33
  44. package/dist/application/types.d.ts +2 -0
  45. package/dist/atomic-file.d.ts +34 -1
  46. package/dist/atomic-file.js +161 -18
  47. package/dist/briefing.d.ts +3 -1
  48. package/dist/briefing.js +97 -31
  49. package/dist/build-info.json +9 -4
  50. package/dist/capabilities.d.ts +9 -1
  51. package/dist/capabilities.js +13 -0
  52. package/dist/cli.js +400 -37
  53. package/dist/client-profile.d.ts +5 -17
  54. package/dist/client-profile.js +4 -127
  55. package/dist/config.d.ts +45 -0
  56. package/dist/config.js +57 -6
  57. package/dist/creation.d.ts +1 -1
  58. package/dist/creation.js +4 -4
  59. package/dist/daemon-recovery.d.ts +14 -9
  60. package/dist/daemon-recovery.js +124 -115
  61. package/dist/docs.d.ts +1 -1
  62. package/dist/docs.js +207 -19
  63. package/dist/doctor.d.ts +4 -1
  64. package/dist/doctor.js +75 -60
  65. package/dist/erased-resources.d.ts +9 -0
  66. package/dist/erased-resources.js +87 -0
  67. package/dist/file-delivery/reader.d.ts +15 -0
  68. package/dist/file-delivery/reader.js +63 -0
  69. package/dist/file-delivery/store.d.ts +15 -0
  70. package/dist/file-delivery/store.js +124 -0
  71. package/dist/file-delivery/types.d.ts +19 -0
  72. package/dist/file-delivery/types.js +1 -0
  73. package/dist/fleet-command-audit.d.ts +7 -0
  74. package/dist/fleet-command-audit.js +24 -8
  75. package/dist/harness/agent-session.d.ts +4 -0
  76. package/dist/harness/claude-code-session.js +1 -1
  77. package/dist/harness/claude-code.js +21 -11
  78. package/dist/harness/claude-runtime.d.ts +12 -0
  79. package/dist/harness/claude-runtime.js +43 -0
  80. package/dist/harness/codex-app-server-proxy.d.ts +4 -1
  81. package/dist/harness/codex-app-server-proxy.js +41 -5
  82. package/dist/harness/codex-reasoning.d.ts +5 -0
  83. package/dist/harness/codex-reasoning.js +12 -0
  84. package/dist/harness/codex-runtime.d.ts +11 -1
  85. package/dist/harness/codex-runtime.js +31 -4
  86. package/dist/harness/codex-session.js +2 -2
  87. package/dist/harness/codex.js +24 -12
  88. package/dist/init-wizard.d.ts +2 -0
  89. package/dist/init-wizard.js +38 -19
  90. package/dist/isolation/policy.js +6 -1
  91. package/dist/isolation/types.d.ts +5 -0
  92. package/dist/managed-cli.d.ts +253 -0
  93. package/dist/managed-cli.js +923 -0
  94. package/dist/managed-recovery.d.ts +13 -0
  95. package/dist/managed-recovery.js +31 -0
  96. package/dist/minimal-setup.d.ts +2 -0
  97. package/dist/minimal-setup.js +44 -0
  98. package/dist/monitor.d.ts +9 -34
  99. package/dist/monitor.js +90 -153
  100. package/dist/notifications/fleet-producer.d.ts +22 -0
  101. package/dist/notifications/fleet-producer.js +237 -0
  102. package/dist/notifications/outbox.d.ts +46 -0
  103. package/dist/notifications/outbox.js +158 -0
  104. package/dist/notifications/target-cleanup.d.ts +11 -0
  105. package/dist/notifications/target-cleanup.js +63 -0
  106. package/dist/notifications/task-producer.d.ts +20 -0
  107. package/dist/notifications/task-producer.js +100 -0
  108. package/dist/ops.d.ts +2 -0
  109. package/dist/ops.js +20 -3
  110. package/dist/owner-channel/channel.js +17 -6
  111. package/dist/owner-channel/commands.d.ts +4 -0
  112. package/dist/owner-channel/commands.js +8 -0
  113. package/dist/owner-channel/lifecycle-outbox.js +4 -3
  114. package/dist/owner-channel/ours-client.js +2 -17
  115. package/dist/paths.d.ts +2 -0
  116. package/dist/paths.js +2 -0
  117. package/dist/preset-migration.js +43 -0
  118. package/dist/process-incarnation.d.ts +23 -0
  119. package/dist/process-incarnation.js +42 -0
  120. package/dist/reminders/manager.d.ts +75 -0
  121. package/dist/reminders/manager.js +278 -0
  122. package/dist/reminders/runtime.d.ts +27 -0
  123. package/dist/reminders/runtime.js +32 -0
  124. package/dist/reminders/schedule.d.ts +37 -0
  125. package/dist/reminders/schedule.js +104 -0
  126. package/dist/reminders/store.d.ts +341 -0
  127. package/dist/reminders/store.js +762 -0
  128. package/dist/reminders/tools.d.ts +20 -0
  129. package/dist/reminders/tools.js +114 -0
  130. package/dist/resolved-plan.js +1 -0
  131. package/dist/rooms-tasks/add-member.d.ts +48 -0
  132. package/dist/rooms-tasks/add-member.js +84 -0
  133. package/dist/rooms-tasks/archived-absence.d.ts +8 -0
  134. package/dist/rooms-tasks/archived-absence.js +48 -0
  135. package/dist/rooms-tasks/cli.js +65 -5
  136. package/dist/rooms-tasks/close.d.ts +6 -2
  137. package/dist/rooms-tasks/close.js +246 -37
  138. package/dist/rooms-tasks/config.d.ts +1 -1
  139. package/dist/rooms-tasks/config.js +11 -4
  140. package/dist/rooms-tasks/cowork-adapter.d.ts +12 -5
  141. package/dist/rooms-tasks/cowork-adapter.js +15 -121
  142. package/dist/rooms-tasks/cowork-http.js +56 -6
  143. package/dist/rooms-tasks/deletion.d.ts +1 -1
  144. package/dist/rooms-tasks/deletion.js +84 -5
  145. package/dist/rooms-tasks/erasure.d.ts +6 -0
  146. package/dist/rooms-tasks/erasure.js +173 -0
  147. package/dist/rooms-tasks/external-worker.d.ts +1 -1
  148. package/dist/rooms-tasks/external-worker.js +3 -4
  149. package/dist/rooms-tasks/layout-binding-client.d.ts +11 -0
  150. package/dist/rooms-tasks/layout-binding-client.js +56 -0
  151. package/dist/rooms-tasks/layout-binding-grants.d.ts +21 -0
  152. package/dist/rooms-tasks/layout-binding-grants.js +128 -0
  153. package/dist/rooms-tasks/layout-cli.d.ts +4 -0
  154. package/dist/rooms-tasks/layout-cli.js +107 -0
  155. package/dist/rooms-tasks/layout-config.d.ts +11 -0
  156. package/dist/rooms-tasks/layout-config.js +89 -0
  157. package/dist/rooms-tasks/layout-control.d.ts +21 -0
  158. package/dist/rooms-tasks/layout-control.js +52 -0
  159. package/dist/rooms-tasks/layout-member-retirement.d.ts +10 -0
  160. package/dist/rooms-tasks/layout-member-retirement.js +90 -0
  161. package/dist/rooms-tasks/layout-service.d.ts +43 -0
  162. package/dist/rooms-tasks/layout-service.js +203 -0
  163. package/dist/rooms-tasks/layout.d.ts +121 -0
  164. package/dist/rooms-tasks/layout.js +286 -0
  165. package/dist/rooms-tasks/legacy-task-member.d.ts +19 -0
  166. package/dist/rooms-tasks/legacy-task-member.js +116 -0
  167. package/dist/rooms-tasks/live-readiness.d.ts +18 -0
  168. package/dist/rooms-tasks/live-readiness.js +85 -0
  169. package/dist/rooms-tasks/member-ownership.d.ts +5 -0
  170. package/dist/rooms-tasks/member-ownership.js +40 -0
  171. package/dist/rooms-tasks/member-startup.d.ts +1 -0
  172. package/dist/rooms-tasks/member-startup.js +2 -0
  173. package/dist/rooms-tasks/provision.d.ts +3 -0
  174. package/dist/rooms-tasks/provision.js +101 -49
  175. package/dist/rooms-tasks/recovery.d.ts +19 -0
  176. package/dist/rooms-tasks/recovery.js +110 -0
  177. package/dist/rooms-tasks/room-state.d.ts +4 -2
  178. package/dist/rooms-tasks/room-state.js +47 -8
  179. package/dist/rooms-tasks/task-service-retirement.d.ts +11 -0
  180. package/dist/rooms-tasks/task-service-retirement.js +21 -0
  181. package/dist/rooms-tasks/task-state.d.ts +52 -8
  182. package/dist/rooms-tasks/task-state.js +251 -26
  183. package/dist/rooms-tasks/types.d.ts +40 -0
  184. package/dist/rooms-tasks/workspace-artifacts.d.ts +6 -0
  185. package/dist/rooms-tasks/workspace-artifacts.js +93 -0
  186. package/dist/rooms-tasks/workspace.d.ts +35 -0
  187. package/dist/rooms-tasks/workspace.js +325 -0
  188. package/dist/runner.d.ts +33 -4
  189. package/dist/runner.js +410 -62
  190. package/dist/session/acp.d.ts +75 -2
  191. package/dist/session/acp.js +345 -82
  192. package/dist/session/arbiter.d.ts +98 -0
  193. package/dist/session/arbiter.js +272 -3
  194. package/dist/session/codex-app-server.d.ts +45 -1
  195. package/dist/session/codex-app-server.js +225 -32
  196. package/dist/session/control.d.ts +17 -3
  197. package/dist/session/control.js +213 -23
  198. package/dist/session/conversation-normalizer.d.ts +2 -1
  199. package/dist/session/conversation-normalizer.js +1 -1
  200. package/dist/session/conversation-observation.d.ts +25 -0
  201. package/dist/session/conversation-observation.js +100 -0
  202. package/dist/session/conversation-replay.d.ts +50 -0
  203. package/dist/session/conversation-replay.js +203 -0
  204. package/dist/session/conversation-store.d.ts +15 -1
  205. package/dist/session/conversation-store.js +206 -26
  206. package/dist/session/conversation-types.d.ts +57 -4
  207. package/dist/session/prompt-batch.d.ts +79 -0
  208. package/dist/session/prompt-batch.js +120 -0
  209. package/dist/session/task-notice.d.ts +14 -0
  210. package/dist/session/task-notice.js +13 -0
  211. package/dist/session/types.d.ts +42 -1
  212. package/dist/setup-tunnel-preflight.d.ts +34 -0
  213. package/dist/setup-tunnel-preflight.js +62 -0
  214. package/dist/socket-path.d.ts +2 -0
  215. package/dist/socket-path.js +22 -0
  216. package/dist/spawn.d.ts +5 -1
  217. package/dist/spawn.js +40 -4
  218. package/dist/startup-prompt.d.ts +15 -0
  219. package/dist/startup-prompt.js +71 -0
  220. package/dist/startup-readiness.d.ts +32 -0
  221. package/dist/startup-readiness.js +101 -0
  222. package/dist/startup-telemetry.d.ts +16 -0
  223. package/dist/startup-telemetry.js +46 -0
  224. package/dist/subscriptions/cli.d.ts +91 -0
  225. package/dist/subscriptions/cli.js +223 -0
  226. package/dist/subscriptions/launch.d.ts +37 -0
  227. package/dist/subscriptions/launch.js +93 -0
  228. package/dist/subscriptions/login.d.ts +77 -0
  229. package/dist/subscriptions/login.js +215 -0
  230. package/dist/subscriptions/observed.d.ts +7 -0
  231. package/dist/subscriptions/observed.js +44 -0
  232. package/dist/subscriptions/probe.d.ts +10 -0
  233. package/dist/subscriptions/probe.js +73 -0
  234. package/dist/subscriptions/service.d.ts +102 -0
  235. package/dist/subscriptions/service.js +364 -0
  236. package/dist/subscriptions/store.d.ts +71 -0
  237. package/dist/subscriptions/store.js +143 -0
  238. package/dist/supervisor/adoption.d.ts +8 -0
  239. package/dist/supervisor/adoption.js +78 -0
  240. package/dist/supervisor/catalog.d.ts +62 -0
  241. package/dist/supervisor/catalog.js +254 -0
  242. package/dist/supervisor/fleet.d.ts +8 -0
  243. package/dist/supervisor/fleet.js +89 -0
  244. package/dist/supervisor/index.js +3 -6
  245. package/dist/supervisor/launchd.d.ts +1 -1
  246. package/dist/supervisor/launchd.js +26 -13
  247. package/dist/supervisor/legacy.d.ts +35 -0
  248. package/dist/supervisor/legacy.js +524 -0
  249. package/dist/supervisor/manager.d.ts +15 -0
  250. package/dist/supervisor/manager.js +270 -0
  251. package/dist/supervisor/registration-refresh.d.ts +45 -0
  252. package/dist/supervisor/registration-refresh.js +266 -0
  253. package/dist/supervisor/scope.d.ts +4 -0
  254. package/dist/supervisor/scope.js +10 -0
  255. package/dist/supervisor/systemd.d.ts +2 -2
  256. package/dist/supervisor/systemd.js +19 -33
  257. package/dist/task-supervision.d.ts +9 -0
  258. package/dist/task-supervision.js +145 -0
  259. package/dist/temp-idle.d.ts +17 -0
  260. package/dist/temp-idle.js +42 -0
  261. package/dist/temp-lifecycle.d.ts +23 -8
  262. package/dist/temp-lifecycle.js +123 -66
  263. package/dist/temp-supervisor-recovery.d.ts +1 -1
  264. package/dist/temp-supervisor-recovery.js +2 -2
  265. package/dist/tunnel-setup-completion.d.ts +7 -0
  266. package/dist/tunnel-setup-completion.js +13 -0
  267. package/dist/tunnel-setup-input.d.ts +19 -0
  268. package/dist/tunnel-setup-input.js +124 -0
  269. package/dist/watchdog/query.d.ts +3 -1
  270. package/dist/watchdog/query.js +2 -1
  271. package/dist/web/agent-attachments.d.ts +29 -0
  272. package/dist/web/agent-attachments.js +162 -0
  273. package/dist/web/auth.d.ts +65 -2
  274. package/dist/web/auth.js +201 -11
  275. package/dist/web/available-port.d.ts +2 -0
  276. package/dist/web/available-port.js +23 -0
  277. package/dist/web/control.d.ts +3 -3
  278. package/dist/web/control.js +10 -8
  279. package/dist/web/conversation-stream-codec.d.ts +23 -0
  280. package/dist/web/conversation-stream-codec.js +110 -0
  281. package/dist/web/conversation-stream.d.ts +25 -0
  282. package/dist/web/conversation-stream.js +231 -0
  283. package/dist/web/events.d.ts +2 -0
  284. package/dist/web/events.js +9 -0
  285. package/dist/web/fleet-config-service.d.ts +4 -1
  286. package/dist/web/fleet-config-service.js +46 -15
  287. package/dist/web/prefix-gateway.d.ts +32 -0
  288. package/dist/web/prefix-gateway.js +248 -0
  289. package/dist/web/resource-views.d.ts +124 -0
  290. package/dist/web/resource-views.js +424 -0
  291. package/dist/web/runtime.d.ts +3 -1
  292. package/dist/web/runtime.js +132 -34
  293. package/dist/web/server.d.ts +19 -0
  294. package/dist/web/server.js +802 -67
  295. package/dist/web/service.d.ts +11 -2
  296. package/dist/web/service.js +30 -4
  297. package/dist/web/workspace-devices.d.ts +107 -0
  298. package/dist/web/workspace-devices.js +277 -0
  299. package/dist/web/workspace-tunnel.d.ts +1 -0
  300. package/dist/web/workspace-tunnel.js +34 -0
  301. package/dist/workspace-enrollment.d.ts +83 -0
  302. package/dist/workspace-enrollment.js +580 -0
  303. package/dist/workspace-replacement.d.ts +66 -0
  304. package/dist/workspace-replacement.js +220 -0
  305. package/docs/acp-files.md +146 -0
  306. package/docs/conversation-stream.md +196 -0
  307. package/docs/fleet-migration.md +54 -0
  308. package/docs/live-voice.md +13 -0
  309. package/docs/room-layouts.md +227 -0
  310. package/docs/self-reminders.md +374 -0
  311. package/docs/task-workspaces.md +225 -0
  312. package/docs/validation/managed-cli-permissions.md +421 -0
  313. package/examples/room-layouts/fresh.yaml +27 -0
  314. package/examples/room-layouts/scoped.yaml +21 -0
  315. package/examples/room-layouts/shared.yaml +19 -0
  316. package/package.json +24 -20
  317. package/presets/README.md +78 -0
  318. package/presets/brain-catalog.json +8 -2
  319. package/presets/fleet/agent_templates/Critic.yaml +1 -1
  320. package/presets/fleet/agent_templates/Developer.yaml +1 -1
  321. package/presets/fleet/agent_templates/Engineer.yaml +5 -0
  322. package/presets/fleet/agent_templates/LocalCoordinator.yaml +4 -2
  323. package/presets/fleet/agents/FleetCoordinator.yaml +1 -1
  324. package/presets/fleet/brains/claude-fable-5-1-high.yaml +5 -0
  325. package/presets/fleet/brains/claude-fable-5-1-low.yaml +5 -0
  326. package/presets/fleet/brains/claude-fable-5-1-max.yaml +5 -0
  327. package/presets/fleet/brains/claude-fable-5-1-medium.yaml +5 -0
  328. package/presets/fleet/brains/claude-fable-5-1-xhigh.yaml +5 -0
  329. package/presets/fleet/brains/claude-fable-5-high.yaml +1 -1
  330. package/presets/fleet/brains/claude-fable-5-low.yaml +1 -1
  331. package/presets/fleet/brains/claude-fable-5-max.yaml +1 -1
  332. package/presets/fleet/brains/claude-fable-5-medium.yaml +1 -1
  333. package/presets/fleet/brains/claude-fable-5-xhigh.yaml +1 -1
  334. package/presets/fleet/brains/claude-haiku-4-5-20251001-default.yaml +4 -0
  335. package/presets/fleet/brains/claude-opus-5-5-high.yaml +5 -0
  336. package/presets/fleet/brains/claude-opus-5-5-low.yaml +5 -0
  337. package/presets/fleet/brains/claude-opus-5-5-max.yaml +5 -0
  338. package/presets/fleet/brains/claude-opus-5-5-medium.yaml +5 -0
  339. package/presets/fleet/brains/claude-opus-5-5-xhigh.yaml +5 -0
  340. package/presets/fleet/brains/claude-opus-5-high.yaml +1 -1
  341. package/presets/fleet/brains/claude-opus-5-low.yaml +1 -1
  342. package/presets/fleet/brains/claude-opus-5-max.yaml +1 -1
  343. package/presets/fleet/brains/claude-opus-5-medium.yaml +1 -1
  344. package/presets/fleet/brains/claude-opus-5-xhigh.yaml +1 -1
  345. package/presets/fleet/brains/claude-sonnet-5-high.yaml +1 -1
  346. package/presets/fleet/brains/claude-sonnet-5-low.yaml +1 -1
  347. package/presets/fleet/brains/claude-sonnet-5-max.yaml +1 -1
  348. package/presets/fleet/brains/claude-sonnet-5-medium.yaml +1 -1
  349. package/presets/fleet/brains/claude-sonnet-5-xhigh.yaml +1 -1
  350. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-high.yaml +1 -1
  351. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-low.yaml +1 -1
  352. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-medium.yaml +1 -1
  353. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-xhigh.yaml +1 -1
  354. package/presets/fleet/brains/codex-gpt-5-4-high.yaml +1 -1
  355. package/presets/fleet/brains/codex-gpt-5-4-low.yaml +1 -1
  356. package/presets/fleet/brains/codex-gpt-5-4-medium.yaml +1 -1
  357. package/presets/fleet/brains/codex-gpt-5-4-mini-high.yaml +1 -1
  358. package/presets/fleet/brains/codex-gpt-5-4-mini-low.yaml +1 -1
  359. package/presets/fleet/brains/codex-gpt-5-4-mini-medium.yaml +1 -1
  360. package/presets/fleet/brains/codex-gpt-5-4-mini-xhigh.yaml +1 -1
  361. package/presets/fleet/brains/codex-gpt-5-4-xhigh.yaml +1 -1
  362. package/presets/fleet/brains/codex-gpt-5-5-high.yaml +1 -1
  363. package/presets/fleet/brains/codex-gpt-5-5-low.yaml +1 -1
  364. package/presets/fleet/brains/codex-gpt-5-5-medium.yaml +1 -1
  365. package/presets/fleet/brains/codex-gpt-5-5-xhigh.yaml +1 -1
  366. package/presets/fleet/brains/codex-gpt-5-6-luna-high.yaml +1 -1
  367. package/presets/fleet/brains/codex-gpt-5-6-luna-low.yaml +1 -1
  368. package/presets/fleet/brains/codex-gpt-5-6-luna-max.yaml +1 -1
  369. package/presets/fleet/brains/codex-gpt-5-6-luna-medium.yaml +1 -1
  370. package/presets/fleet/brains/codex-gpt-5-6-luna-xhigh.yaml +1 -1
  371. package/presets/fleet/brains/codex-gpt-5-6-sol-high.yaml +1 -1
  372. package/presets/fleet/brains/codex-gpt-5-6-sol-low.yaml +1 -1
  373. package/presets/fleet/brains/codex-gpt-5-6-sol-max.yaml +1 -1
  374. package/presets/fleet/brains/codex-gpt-5-6-sol-medium.yaml +1 -1
  375. package/presets/fleet/brains/codex-gpt-5-6-sol-ultra.yaml +1 -1
  376. package/presets/fleet/brains/codex-gpt-5-6-sol-xhigh.yaml +1 -1
  377. package/presets/fleet/brains/codex-gpt-5-6-terra-high.yaml +1 -1
  378. package/presets/fleet/brains/codex-gpt-5-6-terra-low.yaml +1 -1
  379. package/presets/fleet/brains/codex-gpt-5-6-terra-max.yaml +1 -1
  380. package/presets/fleet/brains/codex-gpt-5-6-terra-medium.yaml +1 -1
  381. package/presets/fleet/brains/codex-gpt-5-6-terra-ultra.yaml +1 -1
  382. package/presets/fleet/brains/codex-gpt-5-6-terra-xhigh.yaml +1 -1
  383. package/presets/fleet/brains/codex-gpt-6-1-sol-high.yaml +5 -0
  384. package/presets/fleet/brains/codex-gpt-6-1-sol-low.yaml +5 -0
  385. package/presets/fleet/brains/codex-gpt-6-1-sol-max.yaml +5 -0
  386. package/presets/fleet/brains/codex-gpt-6-1-sol-medium.yaml +5 -0
  387. package/presets/fleet/brains/codex-gpt-6-1-sol-ultra.yaml +5 -0
  388. package/presets/fleet/brains/codex-gpt-6-1-sol-xhigh.yaml +5 -0
  389. package/presets/fleet/brains/codex-gpt-6-astra-high.yaml +1 -1
  390. package/presets/fleet/brains/codex-gpt-6-astra-low.yaml +1 -1
  391. package/presets/fleet/brains/codex-gpt-6-astra-max.yaml +1 -1
  392. package/presets/fleet/brains/codex-gpt-6-astra-medium.yaml +1 -1
  393. package/presets/fleet/brains/codex-gpt-6-astra-ultra.yaml +1 -1
  394. package/presets/fleet/brains/codex-gpt-6-astra-xhigh.yaml +1 -1
  395. package/presets/fleet/brains/codex-gpt-6-luna-high.yaml +5 -0
  396. package/presets/fleet/brains/codex-gpt-6-luna-low.yaml +5 -0
  397. package/presets/fleet/brains/codex-gpt-6-luna-max.yaml +5 -0
  398. package/presets/fleet/brains/codex-gpt-6-luna-medium.yaml +5 -0
  399. package/presets/fleet/brains/codex-gpt-6-luna-xhigh.yaml +5 -0
  400. package/presets/fleet/brains/codex-gpt-6-sol-high.yaml +5 -0
  401. package/presets/fleet/brains/codex-gpt-6-sol-low.yaml +5 -0
  402. package/presets/fleet/brains/codex-gpt-6-sol-max.yaml +5 -0
  403. package/presets/fleet/brains/codex-gpt-6-sol-medium.yaml +5 -0
  404. package/presets/fleet/brains/codex-gpt-6-sol-ultra.yaml +5 -0
  405. package/presets/fleet/brains/codex-gpt-6-sol-xhigh.yaml +5 -0
  406. package/presets/fleet/roles/Coordinator.yaml +12 -11
  407. package/presets/fleet/roles/Critic.yaml +13 -0
  408. package/presets/fleet/roles/Developer.yaml +13 -0
  409. package/presets/fleet/roles/Engineer.yaml +53 -0
  410. package/presets/fleet/room_layouts/engineering.yaml +25 -0
  411. package/presets/fleet/room_layouts/pair.yaml +24 -0
  412. package/presets/fleet/room_layouts/single.yaml +17 -0
  413. package/presets/fleet/room_layouts/team.yaml +30 -0
  414. package/presets/fleet/room_templates/engineering.yaml +19 -0
  415. package/presets/manifest.json +2 -2
  416. package/dist/web-app/assets/index-59GF-gsZ.css +0 -1
  417. package/dist/web-app/assets/index-C0h3ALvs.js +0 -10
  418. package/dist/web-app/icons/ours-fleet-maskable.svg +0 -4
  419. package/dist/web-app/icons/ours-fleet.svg +0 -4
  420. package/dist/web-app/index.html +0 -17
  421. package/dist/web-app/manifest.webmanifest +0 -15
  422. package/dist/web-app/offline.html +0 -18
  423. package/dist/web-app/sw.js +0 -51
@@ -1,8 +1,12 @@
1
1
  import { appendFileSync, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, statSync, } from 'node:fs';
2
+ import { parse } from 'yaml';
3
+ import { removeReminderState } from './reminders/store.js';
2
4
  import { randomUUID } from 'node:crypto';
3
5
  import { basename, join } from 'node:path';
4
- import { replaceFileAtomically, withFileLock } from './atomic-file.js';
6
+ import { replaceFileAtomically, withFileLock, withSynchronousFileLock } from './atomic-file.js';
5
7
  import { realExec } from './exec.js';
8
+ import { captureMemberEnvironment, catalogLiveness, memberKey, readMember, registerMember, unregisterMember, unregisterTaskMember } from './supervisor/catalog.js';
9
+ import { ensureFleetParent } from './supervisor/fleet.js';
6
10
  import { stateRoot, tmpRoot } from './paths.js';
7
11
  export const TEMP_SUPERVISOR_FILE = '.temp-supervisor.json';
8
12
  export const TEMP_TERMINATION_FILE = 'termination.jsonl';
@@ -13,26 +17,40 @@ export const TEMP_LAUNCH_GRACE_MS = 60_000;
13
17
  const metadataPath = (dir) => join(dir, TEMP_SUPERVISOR_FILE);
14
18
  export const tempSystemdUnit = (name) => `ours-fleet-temp-${name}.service`;
15
19
  export const tempLaunchdLabel = (name) => `network.ours.fleet.temp.${name}`;
16
- export function prepareTempSupervisor(dir, role) {
20
+ export function prepareTempSupervisor(dir, role, taskOwner) {
17
21
  const record = {
18
- version: 1, role, launchId: randomUUID(), createdAt: new Date().toISOString(), phase: 'launching',
22
+ version: 1, role, launchId: randomUUID(), createdAt: new Date().toISOString(), phase: 'launching', ...(taskOwner ? { taskOwner } : {}),
19
23
  };
20
24
  replaceFileAtomically(metadataPath(dir), JSON.stringify(record, null, 2) + '\n');
21
25
  return record;
22
26
  }
23
27
  export function readTempSupervisor(dir) {
28
+ let value;
24
29
  try {
25
- const value = JSON.parse(readFileSync(metadataPath(dir), 'utf8'));
26
- if (value.version !== 1 || typeof value.role !== 'string' || typeof value.launchId !== 'string')
27
- return undefined;
28
- return value;
30
+ value = JSON.parse(readFileSync(metadataPath(dir), 'utf8'));
29
31
  }
30
32
  catch {
31
33
  return undefined;
32
34
  }
35
+ if (!value || typeof value !== 'object' || value.version !== 1 || typeof value.role !== 'string' || typeof value.launchId !== 'string')
36
+ return undefined;
37
+ if (value.kind !== undefined && !['fleet-managed', 'systemd-transient', 'launchd-transient', 'detached'].includes(value.kind))
38
+ throw Error('TEMP_SUPERVISOR_KIND_UNSUPPORTED');
39
+ return value;
40
+ }
41
+ /** Broad scans retain unsupported entries instead of blocking other members. */
42
+ export function readTempSupervisorForScan(dir) {
43
+ try {
44
+ return readTempSupervisor(dir);
45
+ }
46
+ catch (error) {
47
+ if (error instanceof Error && error.message === 'TEMP_SUPERVISOR_KIND_UNSUPPORTED')
48
+ return undefined;
49
+ throw error;
50
+ }
33
51
  }
34
52
  const metadataLockPath = (dir) => join(stateRoot(), 'locks', 'temp-supervisors', encodeURIComponent(basename(dir)));
35
- async function updateTempSupervisor(dir, update) {
53
+ export async function updateTempSupervisor(dir, update) {
36
54
  // The launcher and the just-started supervisor are separate processes. Lock
37
55
  // their read/merge/write updates so neither can discard the other's kind,
38
56
  // target, pid or phase. The lock lives outside the role directory so an
@@ -48,54 +66,22 @@ async function updateTempSupervisor(dir, update) {
48
66
  return next;
49
67
  });
50
68
  }
51
- /**
52
- * Launch a temp supervisor outside the caller's service-manager ownership
53
- * boundary. `detached: true` creates a new process group but does not escape a
54
- * systemd cgroup; a transient unit does, and is not enabled across reboot.
55
- */
69
+ /** Register managed members under Fleet; explicit manual mode uses a detached child. */
56
70
  export function makeTempSupervisorLauncher(options = {}) {
57
71
  const exec = options.exec ?? realExec;
58
72
  const platform = options.platform ?? process.platform;
59
73
  const supervisor = options.supervisor ?? process.env.OURS_FLEET_SUPERVISOR;
60
74
  return async (binPath, args, dir) => {
61
- const inherited = [
62
- 'HOME', 'PATH', 'XDG_RUNTIME_DIR', 'OURS_FLEET_HOME', 'CODEX_HOME',
63
- // Preserve runtime selection across the service-manager boundary, including
64
- // an explicit empty value which selects the bundle over manager defaults.
65
- 'CODEX_PATH',
66
- // The child supervisor performs daemon identity and wake probes itself;
67
- // it must resolve the same ours profile as the spawning supervisor.
68
- 'OURS_PORT', 'OURS_STATE_DIR', 'OURS_API_TOKEN', 'OURS_CONFIG',
69
- ]
70
- .flatMap(key => process.env[key] !== undefined ? [`${key}=${process.env[key]}`] : []);
71
75
  const role = args.at(-1);
72
76
  if (!role)
73
77
  throw new Error('temporary supervisor launch requires a role name');
74
- const log = join(dir, 'supervisor.log');
75
- if (supervisor !== 'none' && platform === 'linux') {
76
- const target = tempSystemdUnit(role);
77
- await updateTempSupervisor(dir, { phase: 'launching', kind: 'systemd-transient', target, binPath });
78
- const result = await exec('systemd-run', [
79
- '--user', '--quiet', '--collect', `--unit=${target}`,
80
- '--property=Type=exec', '--property=KillMode=control-group', '--property=TimeoutStopSec=15s',
81
- `--property=StandardOutput=append:${log}`, `--property=StandardError=append:${log}`,
82
- ...inherited.map(value => `--setenv=${value}`),
83
- process.execPath, binPath, ...args,
84
- ]);
85
- if (result.code !== 0)
86
- throw new Error(`systemd-run ${target} failed: ${result.stderr.trim() || `exit ${result.code}`}`);
87
- await updateTempSupervisor(dir, { phase: 'active' });
88
- return;
89
- }
90
- if (supervisor !== 'none' && platform === 'darwin') {
91
- const target = tempLaunchdLabel(role);
92
- await updateTempSupervisor(dir, { phase: 'launching', kind: 'launchd-transient', target, binPath });
93
- const result = await exec('launchctl', [
94
- 'submit', '-l', target, '-o', log, '-e', log, '--',
95
- '/usr/bin/env', ...inherited, process.execPath, binPath, ...args,
96
- ]);
97
- if (result.code !== 0)
98
- throw new Error(`launchctl submit ${target} failed: ${result.stderr.trim() || `exit ${result.code}`}`);
78
+ const metadata = readTempSupervisor(dir);
79
+ if (supervisor !== 'none') {
80
+ const kind = metadata?.taskOwner ? 'task' : 'temporary';
81
+ await updateTempSupervisor(dir, { phase: 'launching', kind: 'fleet-managed', target: memberKey(role, kind), binPath });
82
+ await ensureFleetParent(exec, platform);
83
+ await registerMember({ name: role, kind, dir, environment: await captureMemberEnvironment(),
84
+ ...(metadata?.taskOwner ? { taskOwner: metadata.taskOwner, launchId: metadata.launchId } : {}) });
99
85
  await updateTempSupervisor(dir, { phase: 'active' });
100
86
  return;
101
87
  }
@@ -133,18 +119,34 @@ function appendTermination(dir, record) {
133
119
  replaceFileAtomically(join(dir, TEMP_GLOBAL_TERMINATION_MARKER), line);
134
120
  }
135
121
  function appendGlobalTermination(line, checkExisting = false) {
136
- mkdirSync(archiveRoot(), { recursive: true, mode: 0o700 });
137
- const path = join(archiveRoot(), 'terminations.jsonl');
138
- // Recovery may revisit a .retiring directory after a crash between the
139
- // global append and final rename. Avoid duplicating that exact event.
140
- if (checkExisting) {
141
- try {
142
- if (readFileSync(path, 'utf8').split('\n').includes(line.trimEnd()))
143
- return;
122
+ withSynchronousFileLock(join(stateRoot(), 'locks', 'termination-journal'), () => {
123
+ mkdirSync(archiveRoot(), { recursive: true, mode: 0o700 });
124
+ const path = join(archiveRoot(), 'terminations.jsonl');
125
+ // Recovery may revisit a .retiring directory after a crash between the
126
+ // global append and final rename. Avoid duplicating that exact event.
127
+ if (checkExisting) {
128
+ try {
129
+ if (readFileSync(path, 'utf8').split('\n').includes(line.trimEnd()))
130
+ return;
131
+ }
132
+ catch { /* the journal does not exist yet */ }
144
133
  }
145
- catch { /* the journal does not exist yet */ }
146
- }
147
- appendFileSync(path, line, { mode: 0o600 });
134
+ appendFileSync(path, line, { mode: 0o600 });
135
+ });
136
+ }
137
+ /** Explicit erasure removes only exact retired role/launch events. */
138
+ export function eraseTerminationEvents(launches) {
139
+ withSynchronousFileLock(join(stateRoot(), 'locks', 'termination-journal'), () => {
140
+ const path = join(archiveRoot(), 'terminations.jsonl');
141
+ if (!existsSync(path))
142
+ return;
143
+ const lines = readFileSync(path, 'utf8').split('\n').filter(Boolean);
144
+ const kept = lines.filter(line => {
145
+ const row = JSON.parse(line);
146
+ return !launches.some(launch => launch.role === row.role && launch.launchId === row.launchId);
147
+ });
148
+ replaceFileAtomically(path, kept.length ? kept.join('\n') + '\n' : '');
149
+ });
148
150
  }
149
151
  /** Pick a sibling path without overwriting evidence from an earlier attempt. */
150
152
  function collisionSafeArchivePaths(targetBase, retiringBase) {
@@ -186,16 +188,24 @@ export function archiveTempState(role, reason, outcome, detail, now = new Date()
186
188
  return undefined;
187
189
  throw error;
188
190
  }
191
+ // Archived evidence is inert: no runner will ever load it again. Scheduling
192
+ // state is not evidence, so the retired agent's reminders (and their private
193
+ // notes) are removed rather than archived with it.
189
194
  try {
190
195
  appendTermination(paths.retiring, record);
191
196
  }
192
197
  finally {
193
- renameSync(paths.retiring, paths.target);
198
+ try {
199
+ removeReminderState(paths.retiring);
200
+ }
201
+ finally {
202
+ renameSync(paths.retiring, paths.target);
203
+ }
194
204
  }
195
205
  return paths.target;
196
206
  }
197
207
  function isArchiveForLaunch(path, role, launchId) {
198
- const supervisor = readTempSupervisor(path);
208
+ const supervisor = readTempSupervisorForScan(path);
199
209
  if (supervisor?.role !== role || supervisor.launchId !== launchId)
200
210
  return false;
201
211
  try {
@@ -240,7 +250,7 @@ export function tempArchiveForCreationAction(role, creationActionId) {
240
250
  return undefined;
241
251
  }
242
252
  const matches = entries.flatMap(path => {
243
- const supervisor = readTempSupervisor(path);
253
+ const supervisor = readTempSupervisorForScan(path);
244
254
  if (!supervisor || supervisor.role !== role || !isArchiveForLaunch(path, role, supervisor.launchId))
245
255
  return [];
246
256
  try {
@@ -301,7 +311,15 @@ function recoverInterruptedArchives(now) {
301
311
  const recovered = [];
302
312
  for (const name of names) {
303
313
  const source = join(archiveRoot(), name);
304
- const supervisor = readTempSupervisor(source);
314
+ let supervisor;
315
+ try {
316
+ supervisor = readTempSupervisor(source);
317
+ }
318
+ catch (error) {
319
+ if (error instanceof Error && error.message === 'TEMP_SUPERVISOR_KIND_UNSUPPORTED')
320
+ continue;
321
+ throw error;
322
+ }
305
323
  let line;
306
324
  try {
307
325
  line = readFileSync(join(source, TEMP_TERMINATION_FILE), 'utf8')
@@ -377,11 +395,13 @@ export async function tempSupervisorLiveness(dir, deps = {}) {
377
395
  // A concurrent spawn writes the target before systemd-run/launchctl has
378
396
  // registered it. A not-yet-created unit looks exactly like an inactive one,
379
397
  // so launch phase plus its bounded grace is not cleanup authority.
380
- if (record.phase === 'launching'
398
+ if (record.phase === 'launching' && !requestedTempStopReason(dir)
381
399
  && (!Number.isFinite(age) || age < TEMP_LAUNCH_GRACE_MS))
382
400
  return 'unknown';
383
401
  // Older writers could lose `kind` in an unlocked metadata RMW while retaining
384
402
  // the supervisor pid. Exact argv ownership is stronger than the missing tag.
403
+ if (record.kind === 'fleet-managed')
404
+ return (await catalogLiveness(record.target ?? memberKey(record.role, record.taskOwner ? 'task' : 'temporary'), exec)).state;
385
405
  if (record.pid && (!record.kind || record.kind === 'detached'))
386
406
  return detachedProcessLiveness(record, deps);
387
407
  if (record.kind === 'systemd-transient' && record.target) {
@@ -445,6 +465,13 @@ export async function stopTempSupervisor(role, deps = {}) {
445
465
  replaceFileAtomically(join(dir, TEMP_STOP_REQUEST_FILE), JSON.stringify({
446
466
  version: 1, role, reason: 'operator-stop', requestedAt: new Date().toISOString(),
447
467
  }) + '\n');
468
+ if (record.kind === 'fleet-managed') {
469
+ if (record.taskOwner)
470
+ await unregisterTaskMember(role, { ...record.taskOwner, launchId: record.launchId }, { exec, sleep: deps.sleep });
471
+ else
472
+ await unregisterMember(record.target ?? memberKey(role, 'temporary'), { exec, sleep: deps.sleep });
473
+ return 'stopped';
474
+ }
448
475
  if (record.kind === 'systemd-transient' && record.target) {
449
476
  const result = await exec('systemctl', ['--user', 'stop', record.target]);
450
477
  if (result.code !== 0
@@ -495,6 +522,26 @@ export async function stopTempSupervisor(role, deps = {}) {
495
522
  (deps.kill ?? process.kill)(record.pid, 'SIGTERM');
496
523
  return 'stopped';
497
524
  }
525
+ /** Legacy task snapshots remain recovery evidence even before explicit migration. */
526
+ function hasRetainedTaskReference(dir) {
527
+ try {
528
+ if (basename(dir).startsWith('layout-')) {
529
+ const provenance = JSON.parse(readFileSync(join(dir, 'creation.json'), 'utf8'));
530
+ if (typeof provenance.creationActionId === 'string' && provenance.creationActionId.startsWith('task-'))
531
+ return true;
532
+ }
533
+ const startup = parse(readFileSync(join(dir, 'role.yaml'), 'utf8'))?.roomMemberStartup;
534
+ if (!startup?.room_id || !/^[A-Za-z0-9_-]{1,120}$/.test(startup.room_id))
535
+ return false;
536
+ const room = JSON.parse(readFileSync(join(stateRoot(), 'rooms', `${startup.room_id}.json`), 'utf8'));
537
+ return typeof room.task_id === 'string'
538
+ && room.member_seats.some((seat) => seat.role_name === basename(dir));
539
+ }
540
+ catch (error) {
541
+ // An unreadable role/room is uncertain ownership, never permission to reclaim.
542
+ return error.code !== 'ENOENT';
543
+ }
544
+ }
498
545
  /**
499
546
  * Move a bounded batch of definitely-dead temp state into the evidence archive.
500
547
  * Unknown/legacy/live entries are preserved; absence of proof is never cleanup authority.
@@ -516,8 +563,18 @@ export async function reclaimStaleTempState(deps = {}) {
516
563
  const archived = [...recovered];
517
564
  for (const entry of entries) {
518
565
  const dir = join(tmpRoot(), entry.name);
519
- if (!readTempSupervisor(dir))
520
- continue; // legacy evidence has no safe ownership proof
566
+ const supervisor = readTempSupervisorForScan(dir);
567
+ if (!supervisor || supervisor.taskOwner || hasRetainedTaskReference(dir))
568
+ continue;
569
+ if (supervisor?.kind === 'fleet-managed' && supervisor.target) {
570
+ try {
571
+ if (readMember(supervisor.target)?.desired === 'running')
572
+ continue;
573
+ }
574
+ catch {
575
+ continue;
576
+ } // uncertain catalog intent is never reclamation authority
577
+ }
521
578
  if (await tempSupervisorLiveness(dir, deps) !== 'stopped')
522
579
  continue;
523
580
  const target = archiveTempState(entry.name, 'stale-supervisor', 'reclaimed', 'supervisor is definitively stopped; state moved from the live roster without deletion', now);
@@ -1,3 +1,3 @@
1
1
  export declare const TEMP_RECYCLE_EXIT = 75;
2
2
  /** Keep the service's main process alive while replacing a PID-scoped worker. */
3
- export declare function runTempSupervisor(name: string, entrypoint: string, initial?: () => Promise<void>): Promise<void>;
3
+ export declare function runTempSupervisor(name: string, entrypoint: string, initial?: () => Promise<void>, suspendOnStop?: boolean): Promise<void>;
@@ -2,7 +2,7 @@ import { spawn } from 'node:child_process';
2
2
  import { runTemp, SupervisorRecycleRequiredError } from './runner.js';
3
3
  export const TEMP_RECYCLE_EXIT = 75;
4
4
  /** Keep the service's main process alive while replacing a PID-scoped worker. */
5
- export async function runTempSupervisor(name, entrypoint, initial = () => runTemp(name)) {
5
+ export async function runTempSupervisor(name, entrypoint, initial = () => runTemp(name), suspendOnStop = false) {
6
6
  try {
7
7
  await initial();
8
8
  return;
@@ -12,7 +12,7 @@ export async function runTempSupervisor(name, entrypoint, initial = () => runTem
12
12
  throw error;
13
13
  }
14
14
  for (let attempt = 0; attempt < 3; attempt++) {
15
- const child = spawn(process.execPath, [entrypoint, '_run-temp-worker', name], {
15
+ const child = spawn(process.execPath, [entrypoint, '_run-temp-worker', name, ...(suspendOnStop ? ['--suspend-on-stop'] : [])], {
16
16
  stdio: 'inherit',
17
17
  env: process.env,
18
18
  });
@@ -0,0 +1,7 @@
1
+ /** Keep the resumable record until both the QR and ordinary connection code were emitted. */
2
+ export declare function completeTunnelSetup(effects: {
3
+ link: () => Promise<unknown>;
4
+ qr: (code: string) => Promise<string>;
5
+ write: (text: string) => unknown;
6
+ clear: () => void;
7
+ }): Promise<void>;
@@ -0,0 +1,13 @@
1
+ /** Keep the resumable record until both the QR and ordinary connection code were emitted. */
2
+ export async function completeTunnelSetup(effects) {
3
+ try {
4
+ const link = await effects.link();
5
+ const code = Buffer.from(JSON.stringify(link)).toString('base64url');
6
+ const qr = await effects.qr(code);
7
+ await effects.write('Tunnel configured. Scan the QR or paste this private single-use connection code into the App.\n' + qr + '\n' + code + '\n');
8
+ effects.clear();
9
+ }
10
+ catch {
11
+ throw Error('Tunnel is configured but its connection code could not be printed. Run ours-fleet setup-tunnel --resume to retry.');
12
+ }
13
+ }
@@ -0,0 +1,19 @@
1
+ import { type WorkspacePayload } from './workspace-enrollment.js';
2
+ /** Read the same private-file transport as enrollment, without consuming the grant. */
3
+ export declare function readTunnelSetupFile(file: string): string;
4
+ export declare function readTunnelSetupStdin(input?: AsyncIterable<Buffer | string>): Promise<string>;
5
+ /** Only a minimal one-use v2 grant is allowed in shell arguments. */
6
+ export declare function readTunnelSetupArgument(input: string): string;
7
+ /** Redeem the grant by HTTPS POST; long-lived connector credentials remain off argv and URLs. */
8
+ export declare function redeemTunnelSetup(input: string, request?: typeof fetch): Promise<WorkspacePayload>;
9
+ /** Inspect only public destination metadata; this never consumes a one-use grant. */
10
+ export declare function inspectTunnelSetup(input: string, request?: typeof fetch): Promise<{
11
+ workspaceId: string;
12
+ serverCid: string;
13
+ appOrigin: string;
14
+ } | undefined>;
15
+ export declare function verifyTunnelSetupTarget(target: {
16
+ workspaceId: string;
17
+ serverCid: string;
18
+ appOrigin: string;
19
+ } | undefined, payload: WorkspacePayload): void;
@@ -0,0 +1,124 @@
1
+ import { lstatSync, readFileSync } from 'node:fs';
2
+ import { decodeWorkspacePayload } from './workspace-enrollment.js';
3
+ /** Read the same private-file transport as enrollment, without consuming the grant. */
4
+ export function readTunnelSetupFile(file) {
5
+ const stat = lstatSync(file);
6
+ if (!stat.isFile() || stat.isSymbolicLink() || stat.nlink !== 1 || stat.uid !== process.getuid?.() || (stat.mode & 0o077) !== 0 || stat.size > 32768)
7
+ throw Error('Setup payload requires an owned private regular file (chmod 600)');
8
+ return readFileSync(file, 'utf8');
9
+ }
10
+ export async function readTunnelSetupStdin(input = process.stdin) {
11
+ let value = '';
12
+ for await (const chunk of input) {
13
+ value += chunk.toString();
14
+ if (Buffer.byteLength(value) > 32768)
15
+ throw Error('Tunnel setup payload is too large');
16
+ }
17
+ return value;
18
+ }
19
+ /** Only a minimal one-use v2 grant is allowed in shell arguments. */
20
+ export function readTunnelSetupArgument(input) {
21
+ decodeTunnelSetupGrant(input);
22
+ return input;
23
+ }
24
+ function decodeTunnelSetupGrant(input) {
25
+ if (!/^[A-Za-z0-9_-]{1,32768}$/.test(input.trim()))
26
+ throw Error('Invalid tunnel setup payload');
27
+ let grant;
28
+ try {
29
+ grant = JSON.parse(Buffer.from(input.trim(), 'base64url').toString());
30
+ }
31
+ catch {
32
+ throw Error('Invalid tunnel setup payload');
33
+ }
34
+ if (grant?.version !== 2 || Object.keys(grant).sort().join(',') !== 'appOrigin,code,expiresAt,version'
35
+ || !['https://app.ours.network', 'https://app.ours-tunnel.com'].includes(grant.appOrigin)
36
+ || !/^[A-Za-z0-9_-]{43}$/.test(grant.code) || !Number.isSafeInteger(grant.expiresAt)
37
+ || grant.expiresAt <= Date.now() || grant.expiresAt > Date.now() + 16 * 60000)
38
+ throw Error('Tunnel setup grant is invalid or expired; get a fresh command from the App');
39
+ return grant;
40
+ }
41
+ /** Redeem the grant by HTTPS POST; long-lived connector credentials remain off argv and URLs. */
42
+ export async function redeemTunnelSetup(input, request = fetch) {
43
+ if (!/^[A-Za-z0-9_-]{1,32768}$/.test(input.trim()))
44
+ throw Error('Invalid tunnel setup payload');
45
+ // v1 remains supported exclusively by the private file/stdin CLI transports.
46
+ let version;
47
+ try {
48
+ version = JSON.parse(Buffer.from(input.trim(), 'base64url').toString())?.version;
49
+ }
50
+ catch { }
51
+ if (version === 1)
52
+ return decodeWorkspacePayload(input);
53
+ const grant = decodeTunnelSetupGrant(input);
54
+ let response;
55
+ try {
56
+ response = await request(grant.appOrigin + '/account-api/workspace-install-redeem', {
57
+ method: 'POST', redirect: 'error', credentials: 'omit', signal: AbortSignal.timeout(30000),
58
+ headers: { 'Content-Type': 'application/json', Origin: grant.appOrigin }, body: JSON.stringify({ code: grant.code }),
59
+ });
60
+ }
61
+ catch {
62
+ throw Error('Could not reach the account service; setup grant redemption may be unconfirmed. Check the App before requesting a fresh command');
63
+ }
64
+ if (!response.ok) {
65
+ await response.body?.cancel();
66
+ throw Error(response.status === 410 ? 'Tunnel setup grant expired or was already used; get a fresh command from the App'
67
+ : `Tunnel setup grant redemption failed (HTTP ${response.status}); check the App before retrying`);
68
+ }
69
+ let result;
70
+ try {
71
+ result = await response.json();
72
+ }
73
+ catch {
74
+ throw Error('Invalid tunnel setup response');
75
+ }
76
+ if (typeof result?.payload !== 'string' || !/^[A-Za-z0-9_-]{1,32768}$/.test(result.payload))
77
+ throw Error('Invalid tunnel setup response');
78
+ const payload = decodeWorkspacePayload(result.payload);
79
+ if (payload.appOrigin !== grant.appOrigin)
80
+ throw Error('Tunnel setup response origin mismatch');
81
+ return payload;
82
+ }
83
+ /** Inspect only public destination metadata; this never consumes a one-use grant. */
84
+ export async function inspectTunnelSetup(input, request = fetch) {
85
+ let version;
86
+ try {
87
+ version = JSON.parse(Buffer.from(input.trim(), 'base64url').toString()).version;
88
+ }
89
+ catch { }
90
+ if (version === 1) {
91
+ const p = decodeWorkspacePayload(input);
92
+ return { workspaceId: p.challenge.workspaceId, serverCid: p.serverCid, appOrigin: p.appOrigin };
93
+ }
94
+ const grant = decodeTunnelSetupGrant(input);
95
+ let response;
96
+ try {
97
+ response = await request(grant.appOrigin + '/account-api/workspace-install-inspect', { method: 'POST', redirect: 'error', credentials: 'omit', signal: AbortSignal.timeout(15000), headers: { Origin: grant.appOrigin, 'Content-Type': 'application/json' }, body: JSON.stringify({ code: grant.code }) });
98
+ }
99
+ catch {
100
+ throw Error('Could not inspect the account setup command; existing setup and grant preserved');
101
+ }
102
+ if (response.status === 404 || response.status === 405) {
103
+ await response.body?.cancel();
104
+ return;
105
+ }
106
+ if (!response.ok) {
107
+ await response.body?.cancel();
108
+ throw Error('Setup command inspection failed; existing setup and grant preserved');
109
+ }
110
+ let target;
111
+ try {
112
+ target = await response.json();
113
+ }
114
+ catch {
115
+ throw Error('Invalid setup command inspection response');
116
+ }
117
+ if (!target || Object.keys(target).sort().join(',') !== 'serverCid,workspaceId' || typeof target.workspaceId !== 'string' || !/^[A-Za-z0-9_-]{43}$/.test(target.workspaceId) || typeof target.serverCid !== 'string' || !/^[a-f0-9]{64}$/i.test(target.serverCid))
118
+ throw Error('Invalid setup command inspection response');
119
+ return { ...target, appOrigin: grant.appOrigin };
120
+ }
121
+ export function verifyTunnelSetupTarget(target, payload) {
122
+ if (target && (target.workspaceId !== payload.challenge.workspaceId || target.serverCid.toUpperCase() !== payload.serverCid.toUpperCase() || target.appOrigin !== payload.appOrigin))
123
+ throw Error('Redeemed setup command differs from inspected destination; existing setup preserved');
124
+ }
@@ -39,7 +39,9 @@ export declare function buildWatchdogFindings(cfg: {
39
39
  * polling. `now` is injectable (defaults to Date.now) so the TTL boundary is
40
40
  * unit-testable without real timers.
41
41
  */
42
- export declare function cachedWatchdogFindingsProvider(build: () => Map<string, WatchdogRoleFinding>, ttlMs: number, now?: () => number): () => Map<string, WatchdogRoleFinding>;
42
+ export declare function cachedWatchdogFindingsProvider(build: () => Map<string, WatchdogRoleFinding>, ttlMs: number, now?: () => number): (() => Map<string, WatchdogRoleFinding>) & {
43
+ invalidate: () => void;
44
+ };
43
45
  export interface WatchdogSummary {
44
46
  name: string;
45
47
  enabled: boolean;
@@ -59,12 +59,13 @@ export function buildWatchdogFindings(cfg, latestReport) {
59
59
  */
60
60
  export function cachedWatchdogFindingsProvider(build, ttlMs, now = Date.now) {
61
61
  let cached;
62
- return () => {
62
+ const read = () => {
63
63
  const at = now();
64
64
  if (!cached || at - cached.at >= ttlMs)
65
65
  cached = { at, value: build() };
66
66
  return cached.value;
67
67
  };
68
+ return Object.assign(read, { invalidate: () => { cached = undefined; } });
68
69
  }
69
70
  const DEFAULT_REPORTS_LIMIT = 50;
70
71
  const MAX_REPORTS_LIMIT = 500;
@@ -0,0 +1,29 @@
1
+ export declare const MAX_ATTACHMENT_BYTES: number;
2
+ export declare const MAX_ATTACHMENT_BODY: number;
3
+ export interface AgentAttachment {
4
+ id: string;
5
+ name: string;
6
+ mimeType: string;
7
+ size: number;
8
+ sha256: string;
9
+ generation: string;
10
+ }
11
+ export declare function storeAgentAttachment(stateDir: string, generation: string, input: unknown): AgentAttachment;
12
+ export declare function attachmentPrompt(stateDir: string, generation: string, ids: unknown, text: string): string;
13
+ export declare function readAgentAttachment(stateDir: string, id: string): {
14
+ record: AgentAttachment;
15
+ bytes: Buffer;
16
+ };
17
+ interface AttachmentPresentation {
18
+ generation: string;
19
+ commandId: string;
20
+ text: string;
21
+ transportDigest: string;
22
+ ids: string[];
23
+ }
24
+ /** Written before admission so a lost HTTP response cannot lose the visible message. */
25
+ export declare function storeAttachmentPresentation(stateDir: string, value: AttachmentPresentation): void;
26
+ export declare function prepareAttachmentPresentation(stateDir: string, generation: string, commandId: string, ids: string[], text: string, transport: string): void;
27
+ /** Never infer attachments by stripping user text or interpreting a marker. */
28
+ export declare function presentAttachmentEvent<T>(stateDir: string | undefined, roleId: string, event: T): T;
29
+ export {};