@ours.network/fleet 1.2.0-nightly.9 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (429) hide show
  1. package/README.md +280 -68
  2. package/dist/account-origin.d.ts +5 -0
  3. package/dist/account-origin.js +31 -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 +22 -0
  11. package/dist/agent-ours/mcp-endpoint.js +101 -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 +27 -1
  15. package/dist/agent-ours/service.js +107 -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 +31 -0
  19. package/dist/application/conversation-history.js +339 -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 +43 -0
  30. package/dist/application/resource-observer.js +323 -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 +3 -1
  34. package/dist/application/role-repository.js +89 -54
  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 +63 -17
  38. package/dist/application/session-control.js +221 -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 +120 -31
  49. package/dist/build-info.json +10 -5
  50. package/dist/capabilities.d.ts +9 -1
  51. package/dist/capabilities.js +13 -0
  52. package/dist/cli.js +391 -38
  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 +214 -20
  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 +13 -0
  74. package/dist/fleet-command-audit.js +31 -9
  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 +31 -0
  101. package/dist/notifications/fleet-producer.js +351 -0
  102. package/dist/notifications/outbox.d.ts +59 -0
  103. package/dist/notifications/outbox.js +199 -0
  104. package/dist/notifications/target-cleanup.d.ts +16 -0
  105. package/dist/notifications/target-cleanup.js +75 -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 +5 -0
  116. package/dist/paths.js +5 -0
  117. package/dist/preset-migration.js +55 -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 +4 -0
  134. package/dist/rooms-tasks/archived-absence.js +11 -0
  135. package/dist/rooms-tasks/cli.js +182 -5
  136. package/dist/rooms-tasks/close.d.ts +5 -2
  137. package/dist/rooms-tasks/close.js +224 -36
  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 +66 -10
  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-review.d.ts +113 -0
  180. package/dist/rooms-tasks/task-review.js +561 -0
  181. package/dist/rooms-tasks/task-service-retirement.d.ts +11 -0
  182. package/dist/rooms-tasks/task-service-retirement.js +21 -0
  183. package/dist/rooms-tasks/task-state.d.ts +51 -5
  184. package/dist/rooms-tasks/task-state.js +358 -50
  185. package/dist/rooms-tasks/task-tools.d.ts +35 -0
  186. package/dist/rooms-tasks/task-tools.js +134 -0
  187. package/dist/rooms-tasks/types.d.ts +78 -0
  188. package/dist/rooms-tasks/workspace-artifacts.d.ts +6 -0
  189. package/dist/rooms-tasks/workspace-artifacts.js +93 -0
  190. package/dist/rooms-tasks/workspace.d.ts +35 -0
  191. package/dist/rooms-tasks/workspace.js +325 -0
  192. package/dist/runner.d.ts +23 -4
  193. package/dist/runner.js +409 -61
  194. package/dist/session/acp.d.ts +75 -2
  195. package/dist/session/acp.js +345 -82
  196. package/dist/session/arbiter.d.ts +98 -0
  197. package/dist/session/arbiter.js +272 -3
  198. package/dist/session/codex-app-server.d.ts +45 -1
  199. package/dist/session/codex-app-server.js +225 -32
  200. package/dist/session/control.d.ts +17 -3
  201. package/dist/session/control.js +219 -23
  202. package/dist/session/conversation-normalizer.d.ts +2 -1
  203. package/dist/session/conversation-normalizer.js +1 -1
  204. package/dist/session/conversation-observation.d.ts +25 -0
  205. package/dist/session/conversation-observation.js +100 -0
  206. package/dist/session/conversation-replay.d.ts +50 -0
  207. package/dist/session/conversation-replay.js +203 -0
  208. package/dist/session/conversation-store.d.ts +15 -1
  209. package/dist/session/conversation-store.js +213 -26
  210. package/dist/session/conversation-types.d.ts +57 -4
  211. package/dist/session/prompt-batch.d.ts +79 -0
  212. package/dist/session/prompt-batch.js +120 -0
  213. package/dist/session/task-notice.d.ts +14 -0
  214. package/dist/session/task-notice.js +13 -0
  215. package/dist/session/types.d.ts +42 -1
  216. package/dist/setup-tunnel-preflight.d.ts +34 -0
  217. package/dist/setup-tunnel-preflight.js +62 -0
  218. package/dist/socket-path.d.ts +2 -0
  219. package/dist/socket-path.js +22 -0
  220. package/dist/spawn.d.ts +5 -1
  221. package/dist/spawn.js +40 -4
  222. package/dist/startup-prompt.d.ts +15 -0
  223. package/dist/startup-prompt.js +71 -0
  224. package/dist/startup-readiness.d.ts +20 -1
  225. package/dist/startup-readiness.js +66 -3
  226. package/dist/startup-telemetry.d.ts +16 -0
  227. package/dist/startup-telemetry.js +46 -0
  228. package/dist/subscriptions/cli.d.ts +91 -0
  229. package/dist/subscriptions/cli.js +223 -0
  230. package/dist/subscriptions/launch.d.ts +37 -0
  231. package/dist/subscriptions/launch.js +93 -0
  232. package/dist/subscriptions/login.d.ts +77 -0
  233. package/dist/subscriptions/login.js +215 -0
  234. package/dist/subscriptions/observed.d.ts +7 -0
  235. package/dist/subscriptions/observed.js +44 -0
  236. package/dist/subscriptions/probe.d.ts +10 -0
  237. package/dist/subscriptions/probe.js +73 -0
  238. package/dist/subscriptions/service.d.ts +102 -0
  239. package/dist/subscriptions/service.js +364 -0
  240. package/dist/subscriptions/store.d.ts +71 -0
  241. package/dist/subscriptions/store.js +143 -0
  242. package/dist/supervisor/adoption.d.ts +8 -0
  243. package/dist/supervisor/adoption.js +78 -0
  244. package/dist/supervisor/catalog.d.ts +62 -0
  245. package/dist/supervisor/catalog.js +254 -0
  246. package/dist/supervisor/fleet.d.ts +8 -0
  247. package/dist/supervisor/fleet.js +89 -0
  248. package/dist/supervisor/index.js +3 -6
  249. package/dist/supervisor/launchd.d.ts +1 -1
  250. package/dist/supervisor/launchd.js +26 -13
  251. package/dist/supervisor/legacy.d.ts +35 -0
  252. package/dist/supervisor/legacy.js +524 -0
  253. package/dist/supervisor/manager.d.ts +15 -0
  254. package/dist/supervisor/manager.js +270 -0
  255. package/dist/supervisor/registration-refresh.d.ts +45 -0
  256. package/dist/supervisor/registration-refresh.js +266 -0
  257. package/dist/supervisor/scope.d.ts +4 -0
  258. package/dist/supervisor/scope.js +10 -0
  259. package/dist/supervisor/systemd.d.ts +2 -2
  260. package/dist/supervisor/systemd.js +18 -37
  261. package/dist/task-supervision.d.ts +9 -0
  262. package/dist/task-supervision.js +145 -0
  263. package/dist/temp-idle.d.ts +17 -0
  264. package/dist/temp-idle.js +42 -0
  265. package/dist/temp-lifecycle.d.ts +23 -8
  266. package/dist/temp-lifecycle.js +123 -66
  267. package/dist/temp-supervisor-recovery.d.ts +1 -1
  268. package/dist/temp-supervisor-recovery.js +2 -2
  269. package/dist/tunnel-setup-completion.d.ts +7 -0
  270. package/dist/tunnel-setup-completion.js +13 -0
  271. package/dist/tunnel-setup-input.d.ts +19 -0
  272. package/dist/tunnel-setup-input.js +130 -0
  273. package/dist/watchdog/query.d.ts +3 -1
  274. package/dist/watchdog/query.js +2 -1
  275. package/dist/web/agent-attachments.d.ts +29 -0
  276. package/dist/web/agent-attachments.js +162 -0
  277. package/dist/web/auth.d.ts +65 -2
  278. package/dist/web/auth.js +201 -11
  279. package/dist/web/available-port.d.ts +2 -0
  280. package/dist/web/available-port.js +23 -0
  281. package/dist/web/control.d.ts +3 -3
  282. package/dist/web/control.js +10 -8
  283. package/dist/web/conversation-stream-codec.d.ts +23 -0
  284. package/dist/web/conversation-stream-codec.js +110 -0
  285. package/dist/web/conversation-stream.d.ts +26 -0
  286. package/dist/web/conversation-stream.js +239 -0
  287. package/dist/web/events.d.ts +2 -0
  288. package/dist/web/events.js +9 -0
  289. package/dist/web/fleet-config-service.d.ts +7 -1
  290. package/dist/web/fleet-config-service.js +61 -16
  291. package/dist/web/prefix-gateway.d.ts +32 -0
  292. package/dist/web/prefix-gateway.js +248 -0
  293. package/dist/web/resource-views.d.ts +138 -0
  294. package/dist/web/resource-views.js +519 -0
  295. package/dist/web/runtime.d.ts +3 -1
  296. package/dist/web/runtime.js +132 -34
  297. package/dist/web/server.d.ts +19 -0
  298. package/dist/web/server.js +893 -71
  299. package/dist/web/service.d.ts +11 -2
  300. package/dist/web/service.js +30 -4
  301. package/dist/web/workspace-devices.d.ts +107 -0
  302. package/dist/web/workspace-devices.js +277 -0
  303. package/dist/web/workspace-tunnel.d.ts +1 -0
  304. package/dist/web/workspace-tunnel.js +34 -0
  305. package/dist/workspace-enrollment.d.ts +83 -0
  306. package/dist/workspace-enrollment.js +605 -0
  307. package/dist/workspace-replacement.d.ts +66 -0
  308. package/dist/workspace-replacement.js +238 -0
  309. package/docs/acp-files.md +146 -0
  310. package/docs/conversation-stream.md +216 -0
  311. package/docs/fleet-migration.md +54 -0
  312. package/docs/live-voice.md +13 -0
  313. package/docs/room-layouts.md +227 -0
  314. package/docs/self-reminders.md +374 -0
  315. package/docs/task-progress-review.md +143 -0
  316. package/docs/task-workspaces.md +225 -0
  317. package/docs/validation/managed-cli-permissions.md +421 -0
  318. package/examples/room-layouts/fresh.yaml +27 -0
  319. package/examples/room-layouts/scoped.yaml +21 -0
  320. package/examples/room-layouts/shared.yaml +19 -0
  321. package/package.json +25 -20
  322. package/presets/README.md +78 -0
  323. package/presets/brain-catalog.json +8 -2
  324. package/presets/fleet/agent_templates/Critic.yaml +1 -1
  325. package/presets/fleet/agent_templates/Developer.yaml +1 -1
  326. package/presets/fleet/agent_templates/Engineer.yaml +5 -0
  327. package/presets/fleet/agent_templates/LocalCoordinator.yaml +6 -3
  328. package/presets/fleet/agents/FleetCoordinator.yaml +1 -1
  329. package/presets/fleet/brains/claude-fable-5-1-high.yaml +5 -0
  330. package/presets/fleet/brains/claude-fable-5-1-low.yaml +5 -0
  331. package/presets/fleet/brains/claude-fable-5-1-max.yaml +5 -0
  332. package/presets/fleet/brains/claude-fable-5-1-medium.yaml +5 -0
  333. package/presets/fleet/brains/claude-fable-5-1-xhigh.yaml +5 -0
  334. package/presets/fleet/brains/claude-fable-5-high.yaml +1 -1
  335. package/presets/fleet/brains/claude-fable-5-low.yaml +1 -1
  336. package/presets/fleet/brains/claude-fable-5-max.yaml +1 -1
  337. package/presets/fleet/brains/claude-fable-5-medium.yaml +1 -1
  338. package/presets/fleet/brains/claude-fable-5-xhigh.yaml +1 -1
  339. package/presets/fleet/brains/claude-haiku-4-5-20251001-default.yaml +4 -0
  340. package/presets/fleet/brains/claude-opus-5-5-high.yaml +5 -0
  341. package/presets/fleet/brains/claude-opus-5-5-low.yaml +5 -0
  342. package/presets/fleet/brains/claude-opus-5-5-max.yaml +5 -0
  343. package/presets/fleet/brains/claude-opus-5-5-medium.yaml +5 -0
  344. package/presets/fleet/brains/claude-opus-5-5-xhigh.yaml +5 -0
  345. package/presets/fleet/brains/claude-opus-5-high.yaml +1 -1
  346. package/presets/fleet/brains/claude-opus-5-low.yaml +1 -1
  347. package/presets/fleet/brains/claude-opus-5-max.yaml +1 -1
  348. package/presets/fleet/brains/claude-opus-5-medium.yaml +1 -1
  349. package/presets/fleet/brains/claude-opus-5-xhigh.yaml +1 -1
  350. package/presets/fleet/brains/claude-sonnet-5-high.yaml +1 -1
  351. package/presets/fleet/brains/claude-sonnet-5-low.yaml +1 -1
  352. package/presets/fleet/brains/claude-sonnet-5-max.yaml +1 -1
  353. package/presets/fleet/brains/claude-sonnet-5-medium.yaml +1 -1
  354. package/presets/fleet/brains/claude-sonnet-5-xhigh.yaml +1 -1
  355. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-high.yaml +1 -1
  356. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-low.yaml +1 -1
  357. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-medium.yaml +1 -1
  358. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-xhigh.yaml +1 -1
  359. package/presets/fleet/brains/codex-gpt-5-4-high.yaml +1 -1
  360. package/presets/fleet/brains/codex-gpt-5-4-low.yaml +1 -1
  361. package/presets/fleet/brains/codex-gpt-5-4-medium.yaml +1 -1
  362. package/presets/fleet/brains/codex-gpt-5-4-mini-high.yaml +1 -1
  363. package/presets/fleet/brains/codex-gpt-5-4-mini-low.yaml +1 -1
  364. package/presets/fleet/brains/codex-gpt-5-4-mini-medium.yaml +1 -1
  365. package/presets/fleet/brains/codex-gpt-5-4-mini-xhigh.yaml +1 -1
  366. package/presets/fleet/brains/codex-gpt-5-4-xhigh.yaml +1 -1
  367. package/presets/fleet/brains/codex-gpt-5-5-high.yaml +1 -1
  368. package/presets/fleet/brains/codex-gpt-5-5-low.yaml +1 -1
  369. package/presets/fleet/brains/codex-gpt-5-5-medium.yaml +1 -1
  370. package/presets/fleet/brains/codex-gpt-5-5-xhigh.yaml +1 -1
  371. package/presets/fleet/brains/codex-gpt-5-6-luna-high.yaml +1 -1
  372. package/presets/fleet/brains/codex-gpt-5-6-luna-low.yaml +1 -1
  373. package/presets/fleet/brains/codex-gpt-5-6-luna-max.yaml +1 -1
  374. package/presets/fleet/brains/codex-gpt-5-6-luna-medium.yaml +1 -1
  375. package/presets/fleet/brains/codex-gpt-5-6-luna-xhigh.yaml +1 -1
  376. package/presets/fleet/brains/codex-gpt-5-6-sol-high.yaml +1 -1
  377. package/presets/fleet/brains/codex-gpt-5-6-sol-low.yaml +1 -1
  378. package/presets/fleet/brains/codex-gpt-5-6-sol-max.yaml +1 -1
  379. package/presets/fleet/brains/codex-gpt-5-6-sol-medium.yaml +1 -1
  380. package/presets/fleet/brains/codex-gpt-5-6-sol-ultra.yaml +1 -1
  381. package/presets/fleet/brains/codex-gpt-5-6-sol-xhigh.yaml +1 -1
  382. package/presets/fleet/brains/codex-gpt-5-6-terra-high.yaml +1 -1
  383. package/presets/fleet/brains/codex-gpt-5-6-terra-low.yaml +1 -1
  384. package/presets/fleet/brains/codex-gpt-5-6-terra-max.yaml +1 -1
  385. package/presets/fleet/brains/codex-gpt-5-6-terra-medium.yaml +1 -1
  386. package/presets/fleet/brains/codex-gpt-5-6-terra-ultra.yaml +1 -1
  387. package/presets/fleet/brains/codex-gpt-5-6-terra-xhigh.yaml +1 -1
  388. package/presets/fleet/brains/codex-gpt-6-1-sol-high.yaml +5 -0
  389. package/presets/fleet/brains/codex-gpt-6-1-sol-low.yaml +5 -0
  390. package/presets/fleet/brains/codex-gpt-6-1-sol-max.yaml +5 -0
  391. package/presets/fleet/brains/codex-gpt-6-1-sol-medium.yaml +5 -0
  392. package/presets/fleet/brains/codex-gpt-6-1-sol-ultra.yaml +5 -0
  393. package/presets/fleet/brains/codex-gpt-6-1-sol-xhigh.yaml +5 -0
  394. package/presets/fleet/brains/codex-gpt-6-astra-high.yaml +1 -1
  395. package/presets/fleet/brains/codex-gpt-6-astra-low.yaml +1 -1
  396. package/presets/fleet/brains/codex-gpt-6-astra-max.yaml +1 -1
  397. package/presets/fleet/brains/codex-gpt-6-astra-medium.yaml +1 -1
  398. package/presets/fleet/brains/codex-gpt-6-astra-ultra.yaml +1 -1
  399. package/presets/fleet/brains/codex-gpt-6-astra-xhigh.yaml +1 -1
  400. package/presets/fleet/brains/codex-gpt-6-luna-high.yaml +5 -0
  401. package/presets/fleet/brains/codex-gpt-6-luna-low.yaml +5 -0
  402. package/presets/fleet/brains/codex-gpt-6-luna-max.yaml +5 -0
  403. package/presets/fleet/brains/codex-gpt-6-luna-medium.yaml +5 -0
  404. package/presets/fleet/brains/codex-gpt-6-luna-xhigh.yaml +5 -0
  405. package/presets/fleet/brains/codex-gpt-6-sol-high.yaml +5 -0
  406. package/presets/fleet/brains/codex-gpt-6-sol-low.yaml +5 -0
  407. package/presets/fleet/brains/codex-gpt-6-sol-max.yaml +5 -0
  408. package/presets/fleet/brains/codex-gpt-6-sol-medium.yaml +5 -0
  409. package/presets/fleet/brains/codex-gpt-6-sol-ultra.yaml +5 -0
  410. package/presets/fleet/brains/codex-gpt-6-sol-xhigh.yaml +5 -0
  411. package/presets/fleet/roles/Coordinator.yaml +21 -13
  412. package/presets/fleet/roles/Critic.yaml +24 -0
  413. package/presets/fleet/roles/Developer.yaml +32 -0
  414. package/presets/fleet/roles/Engineer.yaml +72 -0
  415. package/presets/fleet/roles/LocalCoordinator.yaml +17 -0
  416. package/presets/fleet/room_layouts/engineering.yaml +25 -0
  417. package/presets/fleet/room_layouts/pair.yaml +24 -0
  418. package/presets/fleet/room_layouts/single.yaml +17 -0
  419. package/presets/fleet/room_layouts/team.yaml +30 -0
  420. package/presets/fleet/room_templates/engineering.yaml +19 -0
  421. package/presets/manifest.json +2 -2
  422. package/dist/web-app/assets/index-59GF-gsZ.css +0 -1
  423. package/dist/web-app/assets/index-C0h3ALvs.js +0 -10
  424. package/dist/web-app/icons/ours-fleet-maskable.svg +0 -4
  425. package/dist/web-app/icons/ours-fleet.svg +0 -4
  426. package/dist/web-app/index.html +0 -17
  427. package/dist/web-app/manifest.webmanifest +0 -15
  428. package/dist/web-app/offline.html +0 -18
  429. package/dist/web-app/sw.js +0 -51
@@ -1,5 +1,7 @@
1
1
  import { randomUUID } from 'node:crypto';
2
2
  import { MAX_CHUNK_BYTES } from './wire.js';
3
+ /** Bridge refusals of a current-chat file that are safe to relay verbatim. */
4
+ const CHAT_FILE_REFUSAL = /^(INVALID_CHAT_FILE_PATH|CHAT_FILE_[A-Z_]{1,40}|FILE_EXCEEDS_20_MIB)$/;
3
5
  /** Call-scoped callbacks carry no daemon credentials or upload receipts. */
4
6
  export class FileCallbacks {
5
7
  wire;
@@ -16,7 +18,7 @@ export class FileCallbacks {
16
18
  this.pending.delete(frame.id);
17
19
  clearTimeout(p.timer);
18
20
  if (frame.error)
19
- p.reject(Error('AGENT_FILE_OPERATION_FAILED'));
21
+ p.reject(Error(typeof frame.error === 'string' && CHAT_FILE_REFUSAL.test(frame.error) ? frame.error : 'AGENT_FILE_OPERATION_FAILED'));
20
22
  else
21
23
  p.resolve(frame.value);
22
24
  }
@@ -59,14 +61,26 @@ export class FileCallbacks {
59
61
  return (await this.rpc(String(extra.requestId), 'probe', { path }, extra.signal)) === true;
60
62
  }
61
63
  async read(path, extra) {
64
+ return this.openRead(path, extra);
65
+ }
66
+ /** Current-chat source: integrity-checked by the bridge, with its real path. */
67
+ async readChatFile(path, extra) {
68
+ return this.openRead(path, extra, true);
69
+ }
70
+ async openRead(path, extra, chat = false) {
62
71
  const call = String(extra.requestId), handle = randomUUID();
63
- const meta = await this.rpc(call, 'read-open', { path, handle }, extra.signal);
72
+ const meta = await this.rpc(call, chat ? 'export-open' : 'read-open', { path, handle }, extra.signal);
64
73
  if (!meta ||
65
74
  typeof meta.filename !== 'string' ||
66
75
  !Number.isSafeInteger(meta.size) ||
67
- meta.size < 0)
76
+ meta.size < 0 ||
77
+ (chat && typeof meta.path !== 'string'))
68
78
  throw Error('INVALID_FILE_METADATA');
79
+ let closed = false;
69
80
  const close = async () => {
81
+ if (closed)
82
+ return;
83
+ closed = true;
70
84
  await this.rpc(call, 'close', { handle }, new AbortController().signal).catch(() => { });
71
85
  };
72
86
  const body = new ReadableStream({
@@ -91,7 +105,7 @@ export class FileCallbacks {
91
105
  },
92
106
  cancel: close,
93
107
  });
94
- return { filename: meta.filename, size: meta.size, body, close };
108
+ return { filename: meta.filename, size: meta.size, body, close, ...(chat ? { path: meta.path } : {}) };
95
109
  }
96
110
  async write(path, body, extra) {
97
111
  const call = String(extra.requestId), handle = randomUUID();
@@ -13,6 +13,7 @@ export function managedChildEnvironment(input) {
13
13
  }
14
14
  /** Replace ours only; retain the user's ordinary harness configuration and plugins. */
15
15
  export function prepareManagedHarness(role, stateDir, cwd, descriptor, env) {
16
+ const child = managedChildEnvironment({ ...process.env, ...env });
16
17
  const bridge = fileURLToPath(new URL('./bridge.js', import.meta.url));
17
18
  const server = {
18
19
  name: 'ours',
@@ -20,21 +21,28 @@ export function prepareManagedHarness(role, stateDir, cwd, descriptor, env) {
20
21
  args: [bridge],
21
22
  env: [{ name: 'FLEET_OURS_BRIDGE_DESCRIPTOR', value: descriptor }],
22
23
  };
24
+ // Portable allow covers bound identity messaging, not all tools on the server.
25
+ const tools = role.harness === 'codex' && role.permissions?.approval === 'allow'
26
+ ? Object.fromEntries([
27
+ 'current_identity', 'get_messages', 'list_history', 'list_contacts', 'send_message',
28
+ ].map(name => [name, { approval_mode: 'approve' }]))
29
+ : undefined;
23
30
  const native = {
24
31
  mcp_servers: {
25
32
  ours: {
26
33
  command: process.execPath,
27
34
  args: [bridge],
28
35
  env: { FLEET_OURS_BRIDGE_DESCRIPTOR: descriptor },
36
+ ...(tools ? { tools } : {}),
29
37
  },
30
38
  },
31
39
  plugins: { 'ours@ours-codex-marketplace': { enabled: false } },
32
40
  };
33
- const child = managedChildEnvironment({ ...process.env, ...env });
34
41
  if (role.harness === 'codex') {
35
42
  const config = child.CODEX_CONFIG ? JSON.parse(child.CODEX_CONFIG) : {};
36
43
  child.CODEX_CONFIG = JSON.stringify({
37
44
  ...config,
45
+ mcp_servers: { ...config.mcp_servers, ...native.mcp_servers },
38
46
  plugins: { ...config.plugins, ...native.plugins },
39
47
  });
40
48
  // codex-acp otherwise prefers a same-name connector from the user's config.
@@ -1,6 +1,12 @@
1
+ import type { FileDeliveryInput, DeliveredFile } from '../file-delivery/types.js';
2
+ import type { ToolRequestExtra } from '@ours.network/mcp/dist/types/mcp/tool.js';
3
+ import { type Server } from 'node:net';
1
4
  import type { ApplicationIdentityStore } from '@ours.network/mcp/application-identities';
2
5
  import type { OursClient } from '@ours.network/sdk/client';
6
+ import { FileCallbacks } from './file-rpc.js';
3
7
  import type { AgentOursRuntime } from './runtime.js';
8
+ import { type ReminderToolBackend } from '../reminders/tools.js';
9
+ import { type TaskToolBinding } from '../rooms-tasks/task-tools.js';
4
10
  export interface EndpointOptions {
5
11
  socket: string;
6
12
  capability: string;
@@ -9,7 +15,23 @@ export interface EndpointOptions {
9
15
  client: OursClient;
10
16
  identities: ApplicationIdentityStore;
11
17
  remoteDaemonFiles: boolean;
18
+ currentChatFile?: (input: FileDeliveryInput, extra: ToolRequestExtra, files: FileCallbacks) => Promise<DeliveredFile>;
19
+ /** Fleet self-reminders, bound later by the runner; tools refuse until then. */
20
+ reminders?: () => ReminderToolBackend | undefined;
21
+ /** Task Progress / To review tools for the task this agent was launched for. */
22
+ task?: () => TaskToolBinding | undefined;
12
23
  }
24
+ /**
25
+ * Listen on the bridge socket. A close unlinks it, but a predecessor that ended
26
+ * abruptly leaves the file behind, and every later start of the same generation
27
+ * then fails with EADDRINUSE until the role is held down. Only a provably dead
28
+ * socket is removed, once, and only if the path still names that same file;
29
+ * a live listener, a symlink, any other file, an inconclusive probe or a path
30
+ * replaced since the probe still fails as before. `afterProbe` is a test seam.
31
+ */
32
+ export declare function listenOnBridgeSocket(server: Server, path: string, hooks?: {
33
+ afterProbe?: () => Promise<void>;
34
+ }): Promise<void>;
13
35
  export declare function startMcpEndpoint(options: EndpointOptions): Promise<{
14
36
  close(): Promise<void>;
15
37
  }>;
@@ -1,11 +1,85 @@
1
- import { createServer } from 'node:net';
2
- import { chmod, unlink } from 'node:fs/promises';
1
+ import * as managedMcp from '@ours.network/mcp/server';
2
+ import { connect, createServer } from 'node:net';
3
+ import { chmod, lstat, unlink } from 'node:fs/promises';
3
4
  import { timingSafeEqual } from 'node:crypto';
4
5
  import { createManagedOursMcpServer } from '@ours.network/mcp/server';
5
6
  import { JSONRPCMessageSchema } from '@modelcontextprotocol/sdk/types.js';
6
7
  import { FileCallbacks } from './file-rpc.js';
7
8
  import { Wire } from './wire.js';
9
+ import { registerReminderTools } from '../reminders/tools.js';
10
+ import { registerTaskTools } from '../rooms-tasks/task-tools.js';
11
+ const ORPHAN_PROBE_TIMEOUT_MS = 1_000;
12
+ function listenOnce(server, path) {
13
+ return new Promise((resolve, reject) => {
14
+ server.once('error', reject);
15
+ server.listen(path, () => {
16
+ server.removeListener('error', reject);
17
+ resolve();
18
+ });
19
+ });
20
+ }
21
+ const sameFile = (a, b) => a.dev === b.dev && a.ino === b.ino && a.ctimeNs === b.ctimeNs && a.birthtimeNs === b.birthtimeNs;
22
+ /**
23
+ * The identity of a socket file, never a symlink or other file, that refuses
24
+ * connections; undefined when it is not provably dead.
25
+ */
26
+ async function orphanSocket(path) {
27
+ let found;
28
+ try {
29
+ found = await lstat(path, { bigint: true });
30
+ }
31
+ catch {
32
+ return undefined;
33
+ }
34
+ if (!found.isSocket())
35
+ return undefined;
36
+ const dead = await new Promise(resolve => {
37
+ const probe = connect(path);
38
+ const done = (orphan) => { clearTimeout(timer); probe.destroy(); resolve(orphan); };
39
+ const timer = setTimeout(() => done(false), ORPHAN_PROBE_TIMEOUT_MS);
40
+ probe.once('connect', () => done(false));
41
+ probe.once('error', error => done(error.code === 'ECONNREFUSED'));
42
+ });
43
+ return dead ? { dev: found.dev, ino: found.ino, ctimeNs: found.ctimeNs, birthtimeNs: found.birthtimeNs } : undefined;
44
+ }
45
+ /**
46
+ * Listen on the bridge socket. A close unlinks it, but a predecessor that ended
47
+ * abruptly leaves the file behind, and every later start of the same generation
48
+ * then fails with EADDRINUSE until the role is held down. Only a provably dead
49
+ * socket is removed, once, and only if the path still names that same file;
50
+ * a live listener, a symlink, any other file, an inconclusive probe or a path
51
+ * replaced since the probe still fails as before. `afterProbe` is a test seam.
52
+ */
53
+ export async function listenOnBridgeSocket(server, path, hooks = {}) {
54
+ let original;
55
+ try {
56
+ return await listenOnce(server, path);
57
+ }
58
+ catch (error) {
59
+ original = error;
60
+ if (error.code !== 'EADDRINUSE')
61
+ throw error;
62
+ }
63
+ const orphan = await orphanSocket(path);
64
+ if (!orphan)
65
+ throw original;
66
+ await hooks.afterProbe?.();
67
+ let now;
68
+ try {
69
+ now = await lstat(path, { bigint: true });
70
+ }
71
+ catch {
72
+ throw original;
73
+ }
74
+ if (!now.isSocket() || !sameFile(now, orphan))
75
+ throw original;
76
+ await unlink(path);
77
+ await listenOnce(server, path);
78
+ }
8
79
  export async function startMcpEndpoint(options) {
80
+ // Every managed Agent offers current-chat delivery, so a package without the contract is a broken install, not a quiet downgrade.
81
+ if (options.currentChatFile && managedMcp.MANAGED_FILE_DELIVERY_VERSION !== 1)
82
+ throw Error('INCOMPATIBLE_MANAGED_FILE_DELIVERY: installed @ours.network/mcp lacks MANAGED_FILE_DELIVERY_VERSION 1; reinstall Fleet with its pinned @ours.network/mcp');
9
83
  const sockets = new Set();
10
84
  const server = createServer((socket) => {
11
85
  if (sockets.size >= 4) {
@@ -31,26 +105,34 @@ export async function startMcpEndpoint(options) {
31
105
  },
32
106
  close: async () => wire.close(),
33
107
  };
108
+ // One admission path for every tool on this connection: the same
109
+ // authentication, in-flight bound and managed-runtime fence.
110
+ const admit = async () => {
111
+ if (!authenticated || inFlight >= 32)
112
+ throw Error('MCP_ADMISSION_LIMIT');
113
+ inFlight++;
114
+ try {
115
+ const release = await options.runtime.admit();
116
+ return () => {
117
+ inFlight--;
118
+ release();
119
+ };
120
+ }
121
+ catch (error) {
122
+ inFlight--;
123
+ throw error;
124
+ }
125
+ };
34
126
  const mcp = createManagedOursMcpServer(options.client, 'managed-v1', options.identities, {
35
127
  remoteDaemonFiles: options.remoteDaemonFiles,
128
+ ...(options.currentChatFile ? { currentChatFile: (input, extra) => options.currentChatFile(input, extra, files) } : {}),
36
129
  fileContext: files,
37
- admit: async () => {
38
- if (!authenticated || inFlight >= 32)
39
- throw Error('MCP_ADMISSION_LIMIT');
40
- inFlight++;
41
- try {
42
- const release = await options.runtime.admit();
43
- return () => {
44
- inFlight--;
45
- release();
46
- };
47
- }
48
- catch (error) {
49
- inFlight--;
50
- throw error;
51
- }
52
- },
130
+ admit,
53
131
  });
132
+ if (options.reminders)
133
+ registerReminderTools(mcp, options.reminders, admit);
134
+ if (options.task)
135
+ registerTaskTools(mcp, options.task, admit);
54
136
  wire.onClose = () => {
55
137
  clearTimeout(deadline);
56
138
  sockets.delete(wire);
@@ -89,13 +171,7 @@ export async function startMcpEndpoint(options) {
89
171
  transport.onmessage?.(message);
90
172
  };
91
173
  });
92
- await new Promise((resolve, reject) => {
93
- server.once('error', reject);
94
- server.listen(options.socket, () => {
95
- server.removeListener('error', reject);
96
- resolve();
97
- });
98
- });
174
+ await listenOnBridgeSocket(server, options.socket);
99
175
  await chmod(options.socket, 0o600);
100
176
  return {
101
177
  close: async () => {
@@ -1,3 +1,4 @@
1
+ import type { AgentHistoryQuery } from './correspondence.js';
1
2
  import type { OursClient } from '@ours.network/sdk/client';
2
3
  import { RuntimeJournal, type RuntimeState } from './state.js';
3
4
  export interface AssignedIdentity {
@@ -9,7 +10,9 @@ export interface AssignedIdentity {
9
10
  action: string;
10
11
  expectedCid?: string;
11
12
  allowCreate: boolean;
12
- bio: string;
13
+ /** Omitted fields preserve an existing identity's custom profile. */
14
+ bio?: string;
15
+ persona?: string;
13
16
  }
14
17
  export interface RoomAdmission {
15
18
  id: string;
@@ -47,6 +50,11 @@ export declare class AgentOursRuntime {
47
50
  prepare(room?: RoomAdmission, timeoutMs?: number): Promise<void>;
48
51
  /** Admission covers SDK calls, response rendering and agent filesystem callbacks. */
49
52
  admit(): Promise<() => void>;
53
+ /** Trusted supervisor-only admission for a later room; keeps the live harness
54
+ * and original startup assignment. Not exposed as an agent lifecycle tool. */
55
+ joinAdditionalRoom(invite: string, expectedRoomCid: string): Promise<void>;
56
+ readContacts(): Promise<unknown>;
57
+ readHistory(request: AgentHistoryQuery): Promise<unknown>;
50
58
  startHarness<T>(start: () => Promise<T>): Promise<T>;
51
59
  suspend(): Promise<void>;
52
60
  terminal(): Promise<void>;
@@ -62,6 +62,7 @@ export class AgentOursRuntime {
62
62
  actual.isRoot)
63
63
  throw Error('IDENTITY_PROOF_MISMATCH');
64
64
  this.deps.assertFence();
65
+ return actual;
65
66
  }
66
67
  async prepare(room, timeoutMs = 30_000) {
67
68
  if (['TERMINAL_INTENT', 'CLEANUP_PENDING', 'RELEASED', 'FAILED', 'QUIESCING'].includes(this.state.phase))
@@ -92,7 +93,7 @@ export class AgentOursRuntime {
92
93
  this.transition('RECOVERING');
93
94
  const args = {
94
95
  name: this.state.name,
95
- bio: this.assignment.bio,
96
+ bio: this.assignment.bio ?? '',
96
97
  exposeLocal: false,
97
98
  localAutoAccept: true,
98
99
  };
@@ -104,7 +105,18 @@ export class AgentOursRuntime {
104
105
  this.transition('OWNED', { cid: created.info.cid });
105
106
  }
106
107
  }
107
- await this.verify();
108
+ const profile = await this.verify();
109
+ // The supervisor owns profile setup, just like identity binding. Apply only
110
+ // explicitly configured fields, under the verified owner, before publishing
111
+ // readiness or admitting the harness. Repeating a prepare is idempotent.
112
+ if (this.assignment.bio !== undefined && profile.bio !== this.assignment.bio) {
113
+ this.deps.assertFence();
114
+ await this.deps.client.setBio({ bio: this.assignment.bio });
115
+ }
116
+ if (this.assignment.persona !== undefined && profile.persona !== this.assignment.persona) {
117
+ this.deps.assertFence();
118
+ await this.deps.client.setPersona({ persona: this.assignment.persona });
119
+ }
108
120
  if (!room && (this.state.room || this.state.admissionIntent))
109
121
  throw Error('ROOM_DESCRIPTOR_REQUIRED');
110
122
  if (room) {
@@ -201,6 +213,42 @@ export class AgentOursRuntime {
201
213
  throw error;
202
214
  }
203
215
  }
216
+ /** Trusted supervisor-only admission for a later room; keeps the live harness
217
+ * and original startup assignment. Not exposed as an agent lifecycle tool. */
218
+ async joinAdditionalRoom(invite, expectedRoomCid) {
219
+ const release = await this.admit();
220
+ try {
221
+ const contact = await this.deps.client.addContact({ invite });
222
+ if (contact.cid !== expectedRoomCid)
223
+ throw Error('ADDITIONAL_ROOM_CID_MISMATCH');
224
+ await this.verify();
225
+ }
226
+ finally {
227
+ release();
228
+ }
229
+ }
230
+ async readContacts() {
231
+ const release = await this.admit();
232
+ try {
233
+ const data = await this.deps.client.listContacts();
234
+ await this.verify();
235
+ return { identity: { name: this.state.name, cid: this.state.cid, generation: this.state.generation }, ...data };
236
+ }
237
+ finally {
238
+ release();
239
+ }
240
+ }
241
+ async readHistory(request) {
242
+ const release = await this.admit();
243
+ try {
244
+ const data = await this.deps.client.listHistory(request);
245
+ await this.verify();
246
+ return { identity: { name: this.state.name, cid: this.state.cid, generation: this.state.generation }, ...data };
247
+ }
248
+ finally {
249
+ release();
250
+ }
251
+ }
204
252
  async startHarness(start) {
205
253
  if (!this.accepting || this.state.phase !== 'READY')
206
254
  throw Error('RUNTIME_NOT_READY');
@@ -1,12 +1,35 @@
1
+ import type { AgentSession } from '../session/types.js';
2
+ import type { ReminderToolBackend } from '../reminders/tools.js';
3
+ import { ArtifactStore } from '../file-delivery/store.js';
1
4
  import type { ResolvedRole } from '../config.js';
2
5
  import { AgentOursRuntime } from './runtime.js';
6
+ import { startMcpEndpoint } from './mcp-endpoint.js';
7
+ import type { TaskToolBinding } from '../rooms-tasks/task-tools.js';
3
8
  export declare const privateRuntimeRoot: () => string;
4
9
  export interface ManagedAgentService {
5
10
  runtime: AgentOursRuntime;
6
11
  descriptor: string;
7
12
  privatePaths: string[];
13
+ setFileDelivery?(sender: AgentSession['sendFileToChat']): void;
14
+ /** Bind (or unbind) this agent's self-reminders behind its own MCP tools. */
15
+ setReminders?(backend: ReminderToolBackend | undefined): void;
8
16
  close(terminal: boolean): Promise<void>;
9
17
  }
18
+ /**
19
+ * `send_file` without a contact, offered to every managed Agent with no Agent
20
+ * setting and no export directory. A backend without a tracked ACP turn gets a
21
+ * precise refusal instead of a missing tool.
22
+ */
23
+ export declare function currentChatFileHandler(options: {
24
+ role: Pick<ResolvedRole, 'harness' | 'session' | 'permissions'>;
25
+ cwd: string;
26
+ store: Pick<ArtifactStore, 'copy'>;
27
+ sender: () => AgentSession['sendFileToChat'];
28
+ /** Fleet's own credentials and stored attachments are never chat content. */
29
+ privateState: string[];
30
+ }): NonNullable<Parameters<typeof startMcpEndpoint>[0]['currentChatFile']>;
31
+ /** Task membership comes from supervisor launch records, re-read per call so a restart cannot leave it stale. */
32
+ export declare function taskToolBinding(role: ResolvedRole, stateDir: string, cid: string | undefined): TaskToolBinding | undefined;
10
33
  /** Only trusted launch orchestration may write this descriptor, never the child. */
11
34
  export declare function storeRoomSecret(role: ResolvedRole): void;
12
35
  export declare function storeTemporaryLaunch(role: ResolvedRole, action: string): void;
@@ -20,4 +43,7 @@ export declare function readRoomReadiness(roomCid: string, name: string): {
20
43
  generation: number;
21
44
  } | undefined;
22
45
  /** Explicit logical-instance termination, independent of harness/bridge transport. */
23
- export declare function releaseManagedAgent(role: ResolvedRole): Promise<void>;
46
+ export declare function releaseManagedAgent(role: ResolvedRole, expected?: {
47
+ cid: string;
48
+ action: string;
49
+ }): Promise<void>;
@@ -1,8 +1,13 @@
1
+ import { chatFilePath } from '../file-delivery/reader.js';
2
+ import { ArtifactStore } from '../file-delivery/store.js';
3
+ import { resolve, basename, extname } from 'node:path';
4
+ import { socketPath as privateSocketPath } from '../socket-path.js';
1
5
  import { randomBytes, randomUUID } from 'node:crypto';
2
- import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';
6
+ import { existsSync, lstatSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';
3
7
  import { join } from 'node:path';
4
8
  import { attachOursClient } from '@ours.network/sdk/client';
5
9
  import { ApplicationIdentityStore } from '@ours.network/mcp/application-identities';
10
+ import { assertSafeAncestors } from '../rooms-tasks/workspace.js';
6
11
  import { readClientProfile } from '../client-profile.js';
7
12
  import { stateRoot } from '../paths.js';
8
13
  import { createCoworkAdapter } from '../rooms-tasks/cowork-adapter.js';
@@ -10,7 +15,52 @@ import { RuntimeController } from './controller.js';
10
15
  import { atomicPrivateWrite, binderKey } from './state.js';
11
16
  import { AgentOursRuntime } from './runtime.js';
12
17
  import { startMcpEndpoint } from './mcp-endpoint.js';
18
+ import { readTempSupervisorForScan } from '../temp-lifecycle.js';
13
19
  export const privateRuntimeRoot = () => join(stateRoot(), 'private-ours');
20
+ /**
21
+ * `send_file` without a contact, offered to every managed Agent with no Agent
22
+ * setting and no export directory. A backend without a tracked ACP turn gets a
23
+ * precise refusal instead of a missing tool.
24
+ */
25
+ export function currentChatFileHandler(options) {
26
+ const { role, cwd, store } = options, privateState = options.privateState.map(p => resolve(p));
27
+ const isPrivate = (path) => privateState.some(p => path === p || path.startsWith(p + '/'));
28
+ return async (input, extra, files) => {
29
+ if (role.session !== 'acp' || !['codex', 'claude-code'].includes(role.harness))
30
+ throw Error(`CURRENT_CHAT_DELIVERY_UNAVAILABLE: sending a file to this chat requires a Codex or Claude ACP session; this Agent runs ${role.harness}/${role.session}. Pass contact to send through Ours instead`);
31
+ const sender = options.sender();
32
+ if (!sender)
33
+ throw Error('CURRENT_CHAT_DELIVERY_UNAVAILABLE: this session has no ACP chat to attach to');
34
+ if (isPrivate(chatFilePath(cwd, input.path)))
35
+ throw Error('PRIVATE_STATE_CANNOT_BE_SENT'); // before approval/read
36
+ const name = input.filename ?? basename(input.path);
37
+ if (!name || Buffer.byteLength(name) > 240 || /[\/\\\x00-\x1f]/.test(name) || name === '.' || name === '..')
38
+ throw Error('INVALID_FILENAME');
39
+ const mime = input.mime ?? { '.txt': 'text/plain', '.md': 'text/markdown', '.pdf': 'application/pdf', '.json': 'application/json', '.html': 'text/html', '.png': 'image/png' }[extname(name).toLowerCase()] ?? 'application/octet-stream';
40
+ if (mime.length > 200 || !/^[A-Za-z0-9.+-]+\/[A-Za-z0-9.+-]+$/.test(mime))
41
+ throw Error('INVALID_MIME');
42
+ return sender(input, extra.signal, async (binding) => {
43
+ const source = await files.readChatFile(input.path, extra);
44
+ try {
45
+ // The bridge reports the opened file's real path, so a symlinked ancestor cannot reach private state.
46
+ if (isPrivate(source.path))
47
+ throw Error('PRIVATE_STATE_CANNOT_BE_SENT');
48
+ return await store.copy(source, name, mime, binding, extra.signal);
49
+ }
50
+ finally {
51
+ await source.close();
52
+ }
53
+ }, role.permissions);
54
+ };
55
+ }
56
+ /** Task membership comes from supervisor launch records, re-read per call so a restart cannot leave it stale. */
57
+ export function taskToolBinding(role, stateDir, cid) {
58
+ const owned = readTempSupervisorForScan(stateDir)?.taskOwner?.taskId;
59
+ const taskId = owned ?? role.roomMemberStartup?.task_id;
60
+ if (!taskId)
61
+ return undefined;
62
+ return { taskId, role: role.name, ...(cid ? { identityCid: cid } : {}), taskOwned: !!owned };
63
+ }
14
64
  function roomSecretPath(role) {
15
65
  const startup = role.roomMemberStartup;
16
66
  return join(privateRuntimeRoot(), 'room-inputs', binderKey(startup.room_identity_cid, JSON.stringify([role.name, startup.invite_id])) + '.json');
@@ -93,6 +143,8 @@ export async function prepareManagedAgent(role, stateDir, temporary) {
93
143
  }
94
144
  const controller = await RuntimeController.acquire(root, profile.expectedInstanceId, role.identity, instance);
95
145
  let closeClient;
146
+ let fileSender;
147
+ let reminders;
96
148
  let partialEndpoint;
97
149
  try {
98
150
  let prior = controller.journal.read();
@@ -145,7 +197,8 @@ export async function prepareManagedAgent(role, stateDir, temporary) {
145
197
  action: launch?.action ?? prior?.action ?? randomUUID(),
146
198
  expectedCid: pin?.cid,
147
199
  allowCreate: true,
148
- bio: role.bio ?? '',
200
+ bio: role.bio,
201
+ persona: role.persona,
149
202
  }, {
150
203
  client,
151
204
  journal: controller.journal,
@@ -207,7 +260,8 @@ export async function prepareManagedAgent(role, stateDir, temporary) {
207
260
  await identities.add(role.identity);
208
261
  const bridgeDir = join(stateDir, '.ours-bridge');
209
262
  mkdirSync(bridgeDir, { recursive: true, mode: 0o700 });
210
- const capability = randomBytes(32).toString('hex'), socket = join(bridgeDir, `g${generation}.sock`);
263
+ const capability = randomBytes(32).toString('hex'), socket = privateSocketPath(join(bridgeDir, `g${generation}.sock`));
264
+ const cwd = role.cwd && existsSync(role.cwd) ? resolve(role.cwd) : resolve(stateDir);
211
265
  const endpoint = await startMcpEndpoint({
212
266
  socket,
213
267
  capability,
@@ -216,6 +270,12 @@ export async function prepareManagedAgent(role, stateDir, temporary) {
216
270
  client,
217
271
  identities,
218
272
  remoteDaemonFiles: true,
273
+ reminders: () => reminders,
274
+ task: () => taskToolBinding(role, stateDir, runtime.snapshot.cid),
275
+ currentChatFile: currentChatFileHandler({
276
+ role, cwd, store: new ArtifactStore(stateDir), sender: () => fileSender,
277
+ privateState: [privateRuntimeRoot(), join(stateRoot(), 'private-file-delivery'), profile.configPath, profile.credentialPath],
278
+ }),
219
279
  });
220
280
  partialEndpoint = endpoint;
221
281
  const descriptor = join(bridgeDir, 'descriptor.json');
@@ -223,9 +283,13 @@ export async function prepareManagedAgent(role, stateDir, temporary) {
223
283
  return {
224
284
  runtime,
225
285
  descriptor,
226
- privatePaths: [root, profile.configPath, profile.credentialPath],
286
+ privatePaths: [root, profile.configPath, profile.credentialPath, join(stateRoot(), 'private-file-delivery')],
287
+ setFileDelivery: sender => { fileSender = sender; },
288
+ setReminders: backend => { reminders = backend; },
227
289
  close: async (terminal) => {
228
290
  try {
291
+ fileSender = undefined;
292
+ reminders = undefined;
229
293
  await endpoint.close();
230
294
  if (terminal)
231
295
  await runtime.terminal();
@@ -267,8 +331,10 @@ export async function preparePermanentAssignment(role) {
267
331
  endpoint: profile.endpoint,
268
332
  expectedInstanceId: profile.expectedInstanceId,
269
333
  credentialPath: profile.credentialPath,
270
- sessionMode: 'local',
271
- requiredCapabilities: ['local-pid-v1', 'root-first-identities-v1'],
334
+ sessionMode: 'external',
335
+ leaseToken: randomUUID(),
336
+ requiredCapabilities: ['external-sessions-v1', 'root-first-identities-v1'],
337
+ env: {},
272
338
  });
273
339
  try {
274
340
  const rows = await client.listIdentities(), row = rows.find((r) => r.name === role.identity);
@@ -295,7 +361,12 @@ export async function preparePermanentAssignment(role) {
295
361
  return 'verified';
296
362
  }
297
363
  finally {
298
- await client.close();
364
+ try {
365
+ await client.releaseLease();
366
+ }
367
+ finally {
368
+ await client.close();
369
+ }
299
370
  }
300
371
  }
301
372
  export function readRoomReadiness(roomCid, name) {
@@ -309,24 +380,47 @@ export function readRoomReadiness(roomCid, name) {
309
380
  }
310
381
  }
311
382
  /** Explicit logical-instance termination, independent of harness/bridge transport. */
312
- export async function releaseManagedAgent(role) {
383
+ export async function releaseManagedAgent(role, expected) {
313
384
  const profile = readClientProfile({ ...process.env, ...role.env });
314
385
  if (!profile)
315
386
  throw Error('MANAGED_OURS_REQUIRES_EXPLICIT_DAEMON_PROFILE');
316
387
  const root = privateRuntimeRoot(), dir = join(root, binderKey(profile.expectedInstanceId, role.identity));
317
- if (!existsSync(join(dir, 'instance.json')))
388
+ if (!existsSync(join(dir, 'instance.json'))) {
389
+ if (expected)
390
+ throw Error('MEMBER_RUNTIME_RETIREMENT_MISSING');
318
391
  return;
319
- const instance = JSON.parse(readFileSync(join(dir, 'instance.json'), 'utf8'));
392
+ }
393
+ const read = (file) => {
394
+ const path = join(dir, file);
395
+ if (expected) {
396
+ assertSafeAncestors(dir);
397
+ const stat = lstatSync(path);
398
+ if (!stat.isFile() || stat.isSymbolicLink() || stat.size > 256 * 1024)
399
+ throw Error('MEMBER_RUNTIME_UNSAFE_PROOF');
400
+ }
401
+ return JSON.parse(readFileSync(path, 'utf8'));
402
+ };
403
+ const instance = read('instance.json');
404
+ if (expected) {
405
+ read('state.json');
406
+ read('owner.json');
407
+ }
320
408
  if (instance.role !== role.name)
321
409
  throw Error('IDENTITY_ASSIGNED_TO_OTHER_ROLE');
322
410
  const controller = await RuntimeController.acquire(root, profile.expectedInstanceId, role.identity, instance.instance);
323
411
  try {
324
412
  const state = controller.journal.read();
325
- if (!state || state.phase === 'RELEASED')
413
+ if (expected && (!state || state.name !== role.identity || state.daemon !== profile.expectedInstanceId
414
+ || state.instance !== instance.instance || state.lifetime !== 'temporary' || instance.temporary !== true
415
+ || state.cid !== expected.cid || state.action !== expected.action))
416
+ throw Error('MEMBER_RUNTIME_RETIREMENT_MISMATCH');
417
+ if (!state)
326
418
  return;
327
- const owner = JSON.parse(readFileSync(join(dir, 'owner.json'), 'utf8'));
328
- if (owner.instance !== state.instance)
419
+ const owner = read('owner.json');
420
+ if (owner.instance !== state.instance || typeof owner.token !== 'string' || !owner.token)
329
421
  throw Error('OWNER_RECORD_MISMATCH');
422
+ if (state.phase === 'RELEASED')
423
+ return;
330
424
  const client = await attachOursClient({
331
425
  endpoint: profile.endpoint,
332
426
  expectedInstanceId: profile.expectedInstanceId,
@@ -0,0 +1,15 @@
1
+ import { type OfferedModels } from '../subscriptions/cli.js';
2
+ export interface CodexModelDiscoveryDeps {
3
+ /** The Codex executable ACP sessions will run, and its version. */
4
+ runtime(): Promise<{
5
+ executable: string;
6
+ version: string;
7
+ }>;
8
+ ask(executable: string, env: NodeJS.ProcessEnv): Promise<OfferedModels>;
9
+ now(): number;
10
+ }
11
+ /**
12
+ * The models offered to the active Codex account by the runtime that sessions will actually use.
13
+ * An answer is reused only for the same executable, version, profile and sign-in.
14
+ */
15
+ export declare function createCodexModelDiscovery(overrides?: Partial<CodexModelDiscoveryDeps>): () => Promise<OfferedModels>;