@ours.network/fleet 1.2.0-nightly.4 → 1.2.0-nightly.41

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 (311) hide show
  1. package/README.md +165 -53
  2. package/dist/account-origin.d.ts +2 -0
  3. package/dist/account-origin.js +6 -0
  4. package/dist/agent-ours/bridge.js +9 -0
  5. package/dist/agent-ours/correspondence.d.ts +10 -0
  6. package/dist/agent-ours/correspondence.js +1 -0
  7. package/dist/agent-ours/harness.js +8 -0
  8. package/dist/agent-ours/runtime.d.ts +6 -0
  9. package/dist/agent-ours/runtime.js +36 -0
  10. package/dist/agent-ours/service.js +33 -13
  11. package/dist/application/codex-models.d.ts +15 -0
  12. package/dist/application/codex-models.js +56 -0
  13. package/dist/application/conversation-history.d.ts +25 -0
  14. package/dist/application/conversation-history.js +189 -0
  15. package/dist/application/fleet-query-service.d.ts +1 -1
  16. package/dist/application/fleet-query-service.js +2 -2
  17. package/dist/application/legacy-supervisor-identity.d.ts +7 -0
  18. package/dist/application/legacy-supervisor-identity.js +43 -0
  19. package/dist/application/model-catalog.d.ts +1 -1
  20. package/dist/application/model-catalog.js +5 -2
  21. package/dist/application/preset-provenance.d.ts +19 -0
  22. package/dist/application/preset-provenance.js +59 -0
  23. package/dist/application/role-creation-service.js +1 -1
  24. package/dist/application/role-repository.js +2 -2
  25. package/dist/application/room-layout-definitions.d.ts +45 -0
  26. package/dist/application/room-layout-definitions.js +130 -0
  27. package/dist/application/session-control.d.ts +18 -0
  28. package/dist/application/session-control.js +54 -3
  29. package/dist/application/supervisor-ours-tools.d.ts +159 -0
  30. package/dist/application/supervisor-ours-tools.js +113 -0
  31. package/dist/application/task-layouts.d.ts +91 -0
  32. package/dist/application/task-layouts.js +249 -0
  33. package/dist/application/task-room-service.d.ts +99 -3
  34. package/dist/application/task-room-service.js +296 -31
  35. package/dist/atomic-file.d.ts +5 -0
  36. package/dist/atomic-file.js +62 -1
  37. package/dist/build-info.json +6 -4
  38. package/dist/capabilities.d.ts +3 -1
  39. package/dist/capabilities.js +4 -0
  40. package/dist/cli.js +186 -34
  41. package/dist/client-profile.d.ts +5 -16
  42. package/dist/client-profile.js +4 -112
  43. package/dist/config.d.ts +18 -0
  44. package/dist/config.js +28 -4
  45. package/dist/creation.js +2 -2
  46. package/dist/daemon-recovery.d.ts +1 -8
  47. package/dist/daemon-recovery.js +8 -113
  48. package/dist/docs.d.ts +3 -3
  49. package/dist/docs.js +44 -22
  50. package/dist/doctor.d.ts +1 -1
  51. package/dist/doctor.js +46 -59
  52. package/dist/erased-resources.d.ts +9 -0
  53. package/dist/erased-resources.js +87 -0
  54. package/dist/fleet-command-audit.d.ts +1 -0
  55. package/dist/fleet-command-audit.js +11 -6
  56. package/dist/harness/claude-code.js +9 -1
  57. package/dist/harness/claude-runtime.d.ts +12 -0
  58. package/dist/harness/claude-runtime.js +43 -0
  59. package/dist/harness/codex-app-server-proxy.d.ts +4 -1
  60. package/dist/harness/codex-app-server-proxy.js +41 -5
  61. package/dist/harness/codex-runtime.d.ts +11 -1
  62. package/dist/harness/codex-runtime.js +31 -4
  63. package/dist/harness/codex.d.ts +3 -2
  64. package/dist/harness/codex.js +8 -5
  65. package/dist/init-wizard.d.ts +2 -0
  66. package/dist/init-wizard.js +38 -19
  67. package/dist/isolation/policy.js +2 -0
  68. package/dist/isolation/types.d.ts +5 -0
  69. package/dist/minimal-setup.d.ts +2 -0
  70. package/dist/minimal-setup.js +44 -0
  71. package/dist/monitor.d.ts +1 -34
  72. package/dist/monitor.js +24 -151
  73. package/dist/notifications/fleet-producer.d.ts +18 -0
  74. package/dist/notifications/fleet-producer.js +129 -0
  75. package/dist/notifications/outbox.d.ts +42 -0
  76. package/dist/notifications/outbox.js +149 -0
  77. package/dist/owner-channel/channel.d.ts +3 -0
  78. package/dist/owner-channel/channel.js +34 -6
  79. package/dist/owner-channel/commands.d.ts +4 -0
  80. package/dist/owner-channel/commands.js +8 -0
  81. package/dist/owner-channel/lifecycle-outbox.js +4 -3
  82. package/dist/owner-channel/ours-client.js +2 -17
  83. package/dist/paths.d.ts +2 -0
  84. package/dist/paths.js +2 -0
  85. package/dist/preset-migration.js +25 -0
  86. package/dist/rooms-tasks/add-member.d.ts +48 -0
  87. package/dist/rooms-tasks/add-member.js +83 -0
  88. package/dist/rooms-tasks/archived-absence.d.ts +8 -0
  89. package/dist/rooms-tasks/archived-absence.js +48 -0
  90. package/dist/rooms-tasks/cli.js +34 -5
  91. package/dist/rooms-tasks/close.d.ts +5 -2
  92. package/dist/rooms-tasks/close.js +208 -28
  93. package/dist/rooms-tasks/config.d.ts +1 -1
  94. package/dist/rooms-tasks/config.js +11 -4
  95. package/dist/rooms-tasks/cowork-adapter.d.ts +6 -4
  96. package/dist/rooms-tasks/cowork-adapter.js +11 -117
  97. package/dist/rooms-tasks/cowork-http.d.ts +3 -0
  98. package/dist/rooms-tasks/cowork-http.js +70 -0
  99. package/dist/rooms-tasks/deletion.d.ts +1 -1
  100. package/dist/rooms-tasks/deletion.js +74 -5
  101. package/dist/rooms-tasks/erasure.d.ts +6 -0
  102. package/dist/rooms-tasks/erasure.js +169 -0
  103. package/dist/rooms-tasks/external-worker.d.ts +1 -1
  104. package/dist/rooms-tasks/external-worker.js +3 -4
  105. package/dist/rooms-tasks/layout-binding-client.d.ts +11 -0
  106. package/dist/rooms-tasks/layout-binding-client.js +56 -0
  107. package/dist/rooms-tasks/layout-binding-grants.d.ts +21 -0
  108. package/dist/rooms-tasks/layout-binding-grants.js +128 -0
  109. package/dist/rooms-tasks/layout-cli.d.ts +4 -0
  110. package/dist/rooms-tasks/layout-cli.js +107 -0
  111. package/dist/rooms-tasks/layout-config.d.ts +11 -0
  112. package/dist/rooms-tasks/layout-config.js +89 -0
  113. package/dist/rooms-tasks/layout-control.d.ts +21 -0
  114. package/dist/rooms-tasks/layout-control.js +52 -0
  115. package/dist/rooms-tasks/layout-service.d.ts +42 -0
  116. package/dist/rooms-tasks/layout-service.js +185 -0
  117. package/dist/rooms-tasks/layout.d.ts +114 -0
  118. package/dist/rooms-tasks/layout.js +250 -0
  119. package/dist/rooms-tasks/live-readiness.d.ts +18 -0
  120. package/dist/rooms-tasks/live-readiness.js +85 -0
  121. package/dist/rooms-tasks/member-startup.d.ts +1 -0
  122. package/dist/rooms-tasks/member-startup.js +2 -0
  123. package/dist/rooms-tasks/provision.d.ts +14 -0
  124. package/dist/rooms-tasks/provision.js +173 -46
  125. package/dist/rooms-tasks/room-state.d.ts +4 -2
  126. package/dist/rooms-tasks/room-state.js +53 -8
  127. package/dist/rooms-tasks/task-state.d.ts +42 -7
  128. package/dist/rooms-tasks/task-state.js +153 -23
  129. package/dist/rooms-tasks/types.d.ts +28 -0
  130. package/dist/rooms-tasks/workspace-artifacts.d.ts +6 -0
  131. package/dist/rooms-tasks/workspace-artifacts.js +93 -0
  132. package/dist/rooms-tasks/workspace.d.ts +26 -0
  133. package/dist/rooms-tasks/workspace.js +195 -0
  134. package/dist/runner.d.ts +19 -3
  135. package/dist/runner.js +86 -29
  136. package/dist/session/acp.d.ts +7 -1
  137. package/dist/session/acp.js +21 -6
  138. package/dist/session/arbiter.d.ts +3 -0
  139. package/dist/session/arbiter.js +20 -2
  140. package/dist/session/codex-app-server.d.ts +3 -0
  141. package/dist/session/codex-app-server.js +26 -3
  142. package/dist/session/control.d.ts +10 -1
  143. package/dist/session/control.js +37 -2
  144. package/dist/session/conversation-normalizer.d.ts +2 -1
  145. package/dist/session/conversation-normalizer.js +1 -1
  146. package/dist/session/conversation-types.d.ts +5 -3
  147. package/dist/session/task-notice.d.ts +14 -0
  148. package/dist/session/task-notice.js +13 -0
  149. package/dist/session/types.d.ts +3 -0
  150. package/dist/setup-tunnel-preflight.d.ts +32 -0
  151. package/dist/setup-tunnel-preflight.js +58 -0
  152. package/dist/socket-path.d.ts +2 -0
  153. package/dist/socket-path.js +22 -0
  154. package/dist/spawn.js +7 -0
  155. package/dist/startup-readiness.d.ts +13 -0
  156. package/dist/startup-readiness.js +38 -0
  157. package/dist/subscriptions/cli.d.ts +91 -0
  158. package/dist/subscriptions/cli.js +223 -0
  159. package/dist/subscriptions/launch.d.ts +37 -0
  160. package/dist/subscriptions/launch.js +93 -0
  161. package/dist/subscriptions/login.d.ts +75 -0
  162. package/dist/subscriptions/login.js +182 -0
  163. package/dist/subscriptions/observed.d.ts +7 -0
  164. package/dist/subscriptions/observed.js +44 -0
  165. package/dist/subscriptions/probe.d.ts +10 -0
  166. package/dist/subscriptions/probe.js +73 -0
  167. package/dist/subscriptions/service.d.ts +102 -0
  168. package/dist/subscriptions/service.js +364 -0
  169. package/dist/subscriptions/store.d.ts +71 -0
  170. package/dist/subscriptions/store.js +140 -0
  171. package/dist/supervisor/launchd.js +2 -1
  172. package/dist/supervisor/systemd.js +9 -23
  173. package/dist/temp-idle.d.ts +17 -0
  174. package/dist/temp-idle.js +42 -0
  175. package/dist/temp-lifecycle.d.ts +6 -1
  176. package/dist/temp-lifecycle.js +29 -13
  177. package/dist/web/agent-attachments.d.ts +29 -0
  178. package/dist/web/agent-attachments.js +162 -0
  179. package/dist/web/auth.d.ts +29 -1
  180. package/dist/web/auth.js +81 -7
  181. package/dist/web/available-port.d.ts +2 -0
  182. package/dist/web/available-port.js +23 -0
  183. package/dist/web/control.d.ts +3 -3
  184. package/dist/web/control.js +10 -8
  185. package/dist/web/fleet-config-service.d.ts +4 -1
  186. package/dist/web/fleet-config-service.js +46 -15
  187. package/dist/web/prefix-gateway.d.ts +29 -0
  188. package/dist/web/prefix-gateway.js +206 -0
  189. package/dist/web/runtime.d.ts +3 -1
  190. package/dist/web/runtime.js +116 -31
  191. package/dist/web/server.d.ts +16 -0
  192. package/dist/web/server.js +501 -53
  193. package/dist/web/service.d.ts +11 -2
  194. package/dist/web/service.js +30 -4
  195. package/dist/web/workspace-devices.d.ts +29 -0
  196. package/dist/web/workspace-devices.js +84 -0
  197. package/dist/web/workspace-tunnel.d.ts +1 -0
  198. package/dist/web/workspace-tunnel.js +32 -0
  199. package/dist/workspace-enrollment.d.ts +53 -0
  200. package/dist/workspace-enrollment.js +370 -0
  201. package/docs/room-layouts.md +227 -0
  202. package/docs/task-workspaces.md +130 -0
  203. package/examples/room-layouts/fresh.yaml +27 -0
  204. package/examples/room-layouts/scoped.yaml +21 -0
  205. package/examples/room-layouts/shared.yaml +19 -0
  206. package/package.json +13 -18
  207. package/presets/README.md +78 -0
  208. package/presets/brain-catalog.json +8 -2
  209. package/presets/fleet/agent_templates/Critic.yaml +1 -1
  210. package/presets/fleet/agent_templates/Developer.yaml +1 -1
  211. package/presets/fleet/agent_templates/Engineer.yaml +5 -0
  212. package/presets/fleet/agent_templates/LocalCoordinator.yaml +1 -1
  213. package/presets/fleet/agents/FleetCoordinator.yaml +1 -1
  214. package/presets/fleet/brains/claude-fable-5-1-high.yaml +5 -0
  215. package/presets/fleet/brains/claude-fable-5-1-low.yaml +5 -0
  216. package/presets/fleet/brains/claude-fable-5-1-max.yaml +5 -0
  217. package/presets/fleet/brains/claude-fable-5-1-medium.yaml +5 -0
  218. package/presets/fleet/brains/claude-fable-5-1-xhigh.yaml +5 -0
  219. package/presets/fleet/brains/claude-fable-5-high.yaml +1 -1
  220. package/presets/fleet/brains/claude-fable-5-low.yaml +1 -1
  221. package/presets/fleet/brains/claude-fable-5-max.yaml +1 -1
  222. package/presets/fleet/brains/claude-fable-5-medium.yaml +1 -1
  223. package/presets/fleet/brains/claude-fable-5-xhigh.yaml +1 -1
  224. package/presets/fleet/brains/claude-haiku-4-5-20251001-default.yaml +4 -0
  225. package/presets/fleet/brains/claude-opus-5-5-high.yaml +5 -0
  226. package/presets/fleet/brains/claude-opus-5-5-low.yaml +5 -0
  227. package/presets/fleet/brains/claude-opus-5-5-max.yaml +5 -0
  228. package/presets/fleet/brains/claude-opus-5-5-medium.yaml +5 -0
  229. package/presets/fleet/brains/claude-opus-5-5-xhigh.yaml +5 -0
  230. package/presets/fleet/brains/claude-opus-5-high.yaml +1 -1
  231. package/presets/fleet/brains/claude-opus-5-low.yaml +1 -1
  232. package/presets/fleet/brains/claude-opus-5-max.yaml +1 -1
  233. package/presets/fleet/brains/claude-opus-5-medium.yaml +1 -1
  234. package/presets/fleet/brains/claude-opus-5-xhigh.yaml +1 -1
  235. package/presets/fleet/brains/claude-sonnet-5-high.yaml +1 -1
  236. package/presets/fleet/brains/claude-sonnet-5-low.yaml +1 -1
  237. package/presets/fleet/brains/claude-sonnet-5-max.yaml +1 -1
  238. package/presets/fleet/brains/claude-sonnet-5-medium.yaml +1 -1
  239. package/presets/fleet/brains/claude-sonnet-5-xhigh.yaml +1 -1
  240. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-high.yaml +1 -1
  241. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-low.yaml +1 -1
  242. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-medium.yaml +1 -1
  243. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-xhigh.yaml +1 -1
  244. package/presets/fleet/brains/codex-gpt-5-4-high.yaml +1 -1
  245. package/presets/fleet/brains/codex-gpt-5-4-low.yaml +1 -1
  246. package/presets/fleet/brains/codex-gpt-5-4-medium.yaml +1 -1
  247. package/presets/fleet/brains/codex-gpt-5-4-mini-high.yaml +1 -1
  248. package/presets/fleet/brains/codex-gpt-5-4-mini-low.yaml +1 -1
  249. package/presets/fleet/brains/codex-gpt-5-4-mini-medium.yaml +1 -1
  250. package/presets/fleet/brains/codex-gpt-5-4-mini-xhigh.yaml +1 -1
  251. package/presets/fleet/brains/codex-gpt-5-4-xhigh.yaml +1 -1
  252. package/presets/fleet/brains/codex-gpt-5-5-high.yaml +1 -1
  253. package/presets/fleet/brains/codex-gpt-5-5-low.yaml +1 -1
  254. package/presets/fleet/brains/codex-gpt-5-5-medium.yaml +1 -1
  255. package/presets/fleet/brains/codex-gpt-5-5-xhigh.yaml +1 -1
  256. package/presets/fleet/brains/codex-gpt-5-6-luna-high.yaml +1 -1
  257. package/presets/fleet/brains/codex-gpt-5-6-luna-low.yaml +1 -1
  258. package/presets/fleet/brains/codex-gpt-5-6-luna-max.yaml +1 -1
  259. package/presets/fleet/brains/codex-gpt-5-6-luna-medium.yaml +1 -1
  260. package/presets/fleet/brains/codex-gpt-5-6-luna-xhigh.yaml +1 -1
  261. package/presets/fleet/brains/codex-gpt-5-6-sol-high.yaml +1 -1
  262. package/presets/fleet/brains/codex-gpt-5-6-sol-low.yaml +1 -1
  263. package/presets/fleet/brains/codex-gpt-5-6-sol-max.yaml +1 -1
  264. package/presets/fleet/brains/codex-gpt-5-6-sol-medium.yaml +1 -1
  265. package/presets/fleet/brains/codex-gpt-5-6-sol-ultra.yaml +1 -1
  266. package/presets/fleet/brains/codex-gpt-5-6-sol-xhigh.yaml +1 -1
  267. package/presets/fleet/brains/codex-gpt-5-6-terra-high.yaml +1 -1
  268. package/presets/fleet/brains/codex-gpt-5-6-terra-low.yaml +1 -1
  269. package/presets/fleet/brains/codex-gpt-5-6-terra-max.yaml +1 -1
  270. package/presets/fleet/brains/codex-gpt-5-6-terra-medium.yaml +1 -1
  271. package/presets/fleet/brains/codex-gpt-5-6-terra-ultra.yaml +1 -1
  272. package/presets/fleet/brains/codex-gpt-5-6-terra-xhigh.yaml +1 -1
  273. package/presets/fleet/brains/codex-gpt-6-1-sol-high.yaml +5 -0
  274. package/presets/fleet/brains/codex-gpt-6-1-sol-low.yaml +5 -0
  275. package/presets/fleet/brains/codex-gpt-6-1-sol-max.yaml +5 -0
  276. package/presets/fleet/brains/codex-gpt-6-1-sol-medium.yaml +5 -0
  277. package/presets/fleet/brains/codex-gpt-6-1-sol-ultra.yaml +5 -0
  278. package/presets/fleet/brains/codex-gpt-6-1-sol-xhigh.yaml +5 -0
  279. package/presets/fleet/brains/codex-gpt-6-astra-high.yaml +1 -1
  280. package/presets/fleet/brains/codex-gpt-6-astra-low.yaml +1 -1
  281. package/presets/fleet/brains/codex-gpt-6-astra-max.yaml +1 -1
  282. package/presets/fleet/brains/codex-gpt-6-astra-medium.yaml +1 -1
  283. package/presets/fleet/brains/codex-gpt-6-astra-ultra.yaml +1 -1
  284. package/presets/fleet/brains/codex-gpt-6-astra-xhigh.yaml +1 -1
  285. package/presets/fleet/brains/codex-gpt-6-luna-high.yaml +5 -0
  286. package/presets/fleet/brains/codex-gpt-6-luna-low.yaml +5 -0
  287. package/presets/fleet/brains/codex-gpt-6-luna-max.yaml +5 -0
  288. package/presets/fleet/brains/codex-gpt-6-luna-medium.yaml +5 -0
  289. package/presets/fleet/brains/codex-gpt-6-luna-xhigh.yaml +5 -0
  290. package/presets/fleet/brains/codex-gpt-6-sol-high.yaml +5 -0
  291. package/presets/fleet/brains/codex-gpt-6-sol-low.yaml +5 -0
  292. package/presets/fleet/brains/codex-gpt-6-sol-max.yaml +5 -0
  293. package/presets/fleet/brains/codex-gpt-6-sol-medium.yaml +5 -0
  294. package/presets/fleet/brains/codex-gpt-6-sol-ultra.yaml +5 -0
  295. package/presets/fleet/brains/codex-gpt-6-sol-xhigh.yaml +5 -0
  296. package/presets/fleet/roles/Coordinator.yaml +34 -11
  297. package/presets/fleet/roles/Engineer.yaml +40 -0
  298. package/presets/fleet/room_layouts/engineering.yaml +25 -0
  299. package/presets/fleet/room_layouts/pair.yaml +24 -0
  300. package/presets/fleet/room_layouts/single.yaml +17 -0
  301. package/presets/fleet/room_layouts/team.yaml +30 -0
  302. package/presets/fleet/room_templates/engineering.yaml +19 -0
  303. package/presets/manifest.json +2 -2
  304. package/dist/web-app/assets/index-59GF-gsZ.css +0 -1
  305. package/dist/web-app/assets/index-C0h3ALvs.js +0 -10
  306. package/dist/web-app/icons/ours-fleet-maskable.svg +0 -4
  307. package/dist/web-app/icons/ours-fleet.svg +0 -4
  308. package/dist/web-app/index.html +0 -17
  309. package/dist/web-app/manifest.webmanifest +0 -15
  310. package/dist/web-app/offline.html +0 -18
  311. package/dist/web-app/sw.js +0 -51
@@ -6,7 +6,7 @@ import { realExec } from '../exec.js';
6
6
  import { registerAdapter } from './registry.js';
7
7
  import { harnessRuntimeDir } from '../isolation/policy.js';
8
8
  import { resolveBundledAcpAgent, } from './acp-agent.js';
9
- import { VERIFIED_CODEX_ACP_VERSIONS, probeCodexRuntime, codexVersionAtLeast } from './codex-runtime.js';
9
+ import { VERIFIED_CODEX_ACP_VERSIONS, probeCodexRuntime, codexVersionAtLeast, hostCodex } from './codex-runtime.js';
10
10
  import { CodexAgentSessionAdapter } from './codex-session.js';
11
11
  const OPTION_KEYS = [
12
12
  'launcher', 'sandbox', 'approval', 'permission_mode', 'search', 'profile', 'config', 'add_dirs',
@@ -25,12 +25,13 @@ const CODEX_PROXY_REAL_PATH_ENV = 'OURS_FLEET_REAL_CODEX_PATH';
25
25
  const CODEX_PROXY_MANIFEST_ENV = 'OURS_FLEET_CODEX_ACP_MANIFEST';
26
26
  /**
27
27
  * What an unattended role can actually do under Codex's native settings.
28
- * `on-request` and `untrusted` stop to ask, and with no console attached that
29
- * request is refused rather than answered — so the role can only read.
28
+ * `on-request` and `untrusted` can ask even for startup file reads (for example,
29
+ * shell reads or files outside the sandbox). Without a controller the request
30
+ * waits or is denied according to policy. Neither guarantees `read-state`.
30
31
  */
31
32
  export function codexCapabilities(approval, sandbox) {
32
33
  if (approval !== 'never')
33
- return ['read-state'];
34
+ return [];
34
35
  const caps = ['read-state', 'messaging', 'monitor', 'status-commands'];
35
36
  if (sandbox !== 'read-only')
36
37
  caps.push('write-state', 'workspace-edit');
@@ -265,7 +266,9 @@ function codexAcpEnvironment(role, dirs) {
265
266
  [CODEX_PROXY_APPROVAL_ENV]: approvalPolicy(role) ?? 'on-request',
266
267
  [CODEX_PROXY_SANDBOX_ENV]: acpRuntimeSandbox(role),
267
268
  [CODEX_PROXY_MANIFEST_ENV]: resolution.manifestPath,
268
- [CODEX_PROXY_REAL_PATH_ENV]: role.env?.CODEX_PATH ?? process.env.CODEX_PATH ?? '',
269
+ // Same choice as probeCodexRuntime: an explicit CODEX_PATH, else a host Codex at least as new as the bundled one.
270
+ [CODEX_PROXY_REAL_PATH_ENV]: role.env?.CODEX_PATH ?? process.env.CODEX_PATH
271
+ ?? hostCodex(resolution.manifestPath, { ...process.env, ...role.env }, dirs.runCwd)?.path ?? '',
269
272
  };
270
273
  }
271
274
  function encodeTomlValue(value) {
@@ -53,6 +53,8 @@ export declare function validateCatalog(value: BrainCatalog, path?: string): Bra
53
53
  export declare function readInitSettings(path: string): InitAnswers;
54
54
  export declare function askInitQuestions(prompter: InitPrompter, configuration: string): Promise<InitAnswers | undefined>;
55
55
  export declare function formatSetupSummary(answers: InitAnswers, configuration: string): string;
56
+ /** The packaged Brain preset for a catalog model at one effort; the same naming the presets are generated with. */
57
+ export declare function brainPresetId(model: Pick<CatalogModel, 'harness' | 'model'>, effort: string): string;
56
58
  /** Build the exact, deterministic default experience without writing to disk. */
57
59
  export declare function generateSetup(answers: InitAnswers): GeneratedSetup;
58
60
  /** Run the mutation boundary only after the complete questionnaire is accepted. */
@@ -7,13 +7,19 @@ import { packagedPresetRoot } from './preset-bootstrap.js';
7
7
  import { listTemplates } from './rooms-tasks/templates.js';
8
8
  const WORK_KINDS = ['development', 'review', 'coordination'];
9
9
  const ROLE_WORK = {
10
- Developer: 'development', Critic: 'review',
10
+ Developer: 'development', Engineer: 'development', Critic: 'review',
11
11
  LocalCoordinator: 'coordination', Coordinator: 'coordination', FleetCoordinator: 'coordination',
12
12
  };
13
13
  const REASONING_EFFORT = {
14
14
  quick: 'low', balanced: 'medium', thorough: 'high',
15
15
  };
16
16
  const MODEL_LABELS = {
17
+ 'gpt-6.1-sol': 'GPT-6.1 Sol',
18
+ 'gpt-6-sol': 'GPT-6 Sol',
19
+ 'gpt-6-luna': 'GPT-6 Luna',
20
+ 'claude-fable-5-1': 'Claude Fable 5.1',
21
+ 'claude-opus-5-5': 'Claude Opus 5.5',
22
+ 'claude-haiku-4-5-20251001': 'Claude Haiku 4.5',
17
23
  'gpt-6-astra': 'GPT-6 Astra',
18
24
  'gpt-5.6-sol': 'GPT-5.6 Sol',
19
25
  'gpt-5.6-terra': 'GPT-5.6 Terra',
@@ -33,10 +39,10 @@ export function validateCatalog(value, path = 'supported model catalog') {
33
39
  for (const model of value.models) {
34
40
  if (!['codex', 'claude-code'].includes(model.harness) || model.session !== 'acp'
35
41
  || typeof model.model !== 'string' || !MODEL_LABELS[model.model]
36
- || !Array.isArray(model.efforts) || model.efforts.length === 0
42
+ || !Array.isArray(model.efforts)
37
43
  || new Set(model.efforts).size !== model.efforts.length
38
44
  || model.efforts.some(effort => !['low', 'medium', 'high', 'xhigh', 'max', 'ultra'].includes(effort))
39
- || !['low', 'medium', 'high'].every(effort => model.efforts.includes(effort)))
45
+ || (model.efforts.length > 0 && !['low', 'medium', 'high'].every(effort => model.efforts.includes(effort))))
40
46
  throw new Error(`unsupported model state in ${path}`);
41
47
  const tuple = `${model.harness}\0${model.session}\0${model.model}`;
42
48
  if (tuples.has(tuple))
@@ -90,7 +96,9 @@ export async function askInitQuestions(prompter, configuration) {
90
96
  return undefined;
91
97
  if (subscriptions.length === 0)
92
98
  throw new Error('Select at least one subscription.');
93
- const available = catalog().models.filter(model => subscriptions.includes(subscriptionFor(model)));
99
+ // The wizard assigns low/medium/high; no-effort models remain available as named presets.
100
+ const available = catalog().models.filter(model => subscriptions.includes(subscriptionFor(model))
101
+ && model.efforts.length > 0);
94
102
  if (!available.length)
95
103
  throw new Error('None of the selected subscriptions has a supported model.');
96
104
  prompter.note([
@@ -163,7 +171,9 @@ export function formatSetupSummary(answers, configuration) {
163
171
  ' One-agent work: one Developer',
164
172
  ' Reviewed pair: Developer + independent Critic',
165
173
  ' Team: LocalCoordinator + Developer + Critic',
174
+ ' Gated engineering: Engineer (gate pipeline) + independent Critic',
166
175
  ' FleetCoordinator: coordination model',
176
+ ' Brains: the packaged presets; each prepared agent references the preset of its chosen model',
167
177
  '',
168
178
  'After the final Yes, Fleet performs host service setup before publishing the complete configuration.',
169
179
  'A hard termination after that Yes can leave host integration or private stage/recovery evidence to inspect.',
@@ -172,6 +182,11 @@ export function formatSetupSummary(answers, configuration) {
172
182
  function preset(relative) {
173
183
  return readFileSync(join(packagedPresetRoot(), 'fleet', relative), 'utf8');
174
184
  }
185
+ /** The packaged Brain preset for a catalog model at one effort; the same naming the presets are generated with. */
186
+ export function brainPresetId(model, effort) {
187
+ return `${model.harness === 'claude-code' ? 'claude' : 'codex'}-${model.model
188
+ .replace(/^claude-/, '').replace(/\./g, '-').toLowerCase()}-${effort}`;
189
+ }
175
190
  /** Build the exact, deterministic default experience without writing to disk. */
176
191
  export function generateSetup(answers) {
177
192
  const effort = REASONING_EFFORT[answers.reasoning];
@@ -192,6 +207,7 @@ export function generateSetup(answers) {
192
207
  ' permissions: { approval: ask, filesystem: workspace, unattended: deny }',
193
208
  '',
194
209
  ].join('\n'));
210
+ const assigned = {};
195
211
  for (const work of WORK_KINDS) {
196
212
  const proposed = answers.models[work];
197
213
  if (!proposed || !answers.subscriptions.includes(subscriptionFor(proposed)))
@@ -205,14 +221,14 @@ export function generateSetup(answers) {
205
221
  throw new Error(`${work} contains tampered model capabilities`);
206
222
  if (!model.efforts.includes(effort))
207
223
  throw new Error(`${MODEL_LABELS[model.model]} does not support ${answers.reasoning} reasoning`);
208
- files.set(`brains/${work}.yaml`, [
209
- `harness: ${model.harness}`,
210
- `session: ${model.session}`,
211
- `model: ${model.model}`,
212
- `effort: ${effort}`,
213
- '',
214
- ].join('\n'));
224
+ // The chosen model is assigned by reference to its packaged Brain preset; setup defines no Brain of its own.
225
+ const id = brainPresetId(model, effort);
226
+ if (!existsSync(join(packagedPresetRoot(), 'fleet', 'brains', `${id}.yaml`)))
227
+ throw new Error(`no packaged Brain preset for ${MODEL_LABELS[model.model]} at ${effort} reasoning`);
228
+ assigned[work] = id;
215
229
  }
230
+ for (const name of readdirSync(join(packagedPresetRoot(), 'fleet', 'brains')).filter(name => name.endsWith('.yaml')).sort())
231
+ files.set(`brains/${name}`, preset(`brains/${name}`));
216
232
  if (answers.assignmentStrategy === 'one-model') {
217
233
  const tuples = new Set(WORK_KINDS.map(work => {
218
234
  const model = answers.models[work];
@@ -221,23 +237,26 @@ export function generateSetup(answers) {
221
237
  if (tuples.size !== 1)
222
238
  throw new Error('one-model assignment requires the same exact model for development, review, and coordination');
223
239
  }
224
- for (const role of ['Coordinator', 'LocalCoordinator', 'Developer', 'Critic']) {
240
+ for (const role of ['Coordinator', 'LocalCoordinator', 'Developer', 'Engineer', 'Critic']) {
225
241
  files.set(`roles/${role}.yaml`, preset(`roles/${role}.yaml`));
226
242
  if (role === 'Coordinator')
227
243
  continue;
228
244
  files.set(`agent_templates/${role}.yaml`, [
229
245
  `role: { ref: ${role} }`,
230
- `brain: { ref: ${ROLE_WORK[role]} }`,
246
+ `brain: { ref: ${assigned[ROLE_WORK[role]]} }`,
231
247
  'coordinator: FleetCoordinator',
232
- 'permissions: { approval: ask, filesystem: workspace, unattended: deny }',
248
+ // Temporary default while the product is being tested: prepared agents work without asking.
249
+ 'permissions: { approval: allow, filesystem: unrestricted, unattended: deny }',
233
250
  'monitor: { mode: fleet, interrupt: after_tool }',
234
251
  '',
235
252
  ].join('\n'));
236
253
  }
237
254
  files.set('agents/FleetCoordinator.yaml', preset('agents/FleetCoordinator.yaml')
238
- .replace('brain: { ref: claude-default }', 'brain: { ref: coordination }'));
239
- for (const name of ['single', 'pair', 'team'])
255
+ .replace('brain: { ref: claude-default }', `brain: { ref: ${assigned.coordination} }`));
256
+ for (const name of ['single', 'pair', 'team', 'engineering']) {
240
257
  files.set(`room_templates/${name}.yaml`, preset(`room_templates/${name}.yaml`));
258
+ files.set(`room_layouts/${name}.yaml`, preset(`room_layouts/${name}.yaml`));
259
+ }
241
260
  return { files, answers };
242
261
  }
243
262
  /** Run the mutation boundary only after the complete questionnaire is accepted. */
@@ -385,7 +404,7 @@ function writeStaged(stageManifest, setup) {
385
404
  replaceFileAtomically(target, contents, 0o600);
386
405
  fsyncPath(target);
387
406
  }
388
- for (const kind of ['agents', 'agent_templates', 'roles', 'brains', 'room_templates'])
407
+ for (const kind of ['agents', 'agent_templates', 'roles', 'brains', 'room_templates', 'room_layouts'])
389
408
  fsyncPath(join(stageRoot, kind));
390
409
  fsyncPath(stageRoot);
391
410
  fsyncPath(dirname(stageManifest));
@@ -394,8 +413,8 @@ function validateStaged(stageManifest) {
394
413
  const config = loadConfig(stageManifest, { yamlMode: 'strict' });
395
414
  const templates = listTemplates(config.roomTemplates ?? {});
396
415
  const names = new Set(templates.map(template => template.name));
397
- if (!['pair', 'single', 'team'].every(name => names.has(name)))
398
- throw new Error('generated setup did not resolve the default single, pair, and team experiences');
416
+ if (!['pair', 'single', 'team', 'engineering'].every(name => names.has(name)))
417
+ throw new Error('generated setup did not resolve the default single, pair, team, and engineering experiences');
399
418
  for (const template of templates)
400
419
  for (const member of template.members)
401
420
  if (!config.agentTemplates?.[member.agent_template])
@@ -168,6 +168,8 @@ export function resolveIsolation(cfg, ctx) {
168
168
  }
169
169
  for (const dir of ctx.additionalWriteDirs ?? [])
170
170
  addRw(dir);
171
+ if (ctx.subscriptionHome)
172
+ addRw(ctx.subscriptionHome);
171
173
  // The selected harness/session command may live outside the system allowlist
172
174
  // (for example Node and a bundled ACP adapter under ~/.local). The runner
173
175
  // resolves its exact executable/package closure; keep every such bind RO.
@@ -63,6 +63,11 @@ export interface WrapContext {
63
63
  * them for itself or for its peers.
64
64
  */
65
65
  harnessSharedPaths?: string[];
66
+ /**
67
+ * The pinned subscription profile home (CLAUDE_CONFIG_DIR / CODEX_HOME), mounted
68
+ * read-write so the CLI can refresh that profile's login. Absent for the default.
69
+ */
70
+ subscriptionHome?: string;
66
71
  /** Exact launcher/interpreter/module closure required by the selected command. */
67
72
  runtimeReadPaths?: string[];
68
73
  brokerEndpoint?: string;
@@ -0,0 +1,2 @@
1
+ /** Prepare only the web configuration; App onboarding chooses agents and brains later. */
2
+ export declare function ensureMinimalSetup(configuration: string): Promise<void>;
@@ -0,0 +1,44 @@
1
+ import { existsSync, linkSync, mkdirSync, mkdtempSync, renameSync, rmSync, unlinkSync, writeFileSync } from 'node:fs';
2
+ import { basename, join } from 'node:path';
3
+ import { withFileLock } from './atomic-file.js';
4
+ import { loadConfig, splitRootFor } from './config.js';
5
+ import { preflightInitPaths } from './init-wizard.js';
6
+ import { workspaceOwnerInvite } from './paths.js';
7
+ /** Prepare only the web configuration; App onboarding chooses agents and brains later. */
8
+ export async function ensureMinimalSetup(configuration) {
9
+ const initial = preflightInitPaths(configuration);
10
+ await withFileLock(join(initial.parent, `.${basename(initial.splitRoot)}.init.lock`), async () => {
11
+ const selected = preflightInitPaths(configuration);
12
+ if (selected.manifestExisted) {
13
+ loadConfig(selected.configPath, { yamlMode: 'strict', deferredOwnerInviteFile: workspaceOwnerInvite() });
14
+ return;
15
+ }
16
+ if (selected.rootExisted)
17
+ throw new Error('Fleet split configuration exists without its manifest; retain it and recover the existing setup before setting up a tunnel');
18
+ const stage = mkdtempSync(join(selected.parent, '.fleet-web-setup-'));
19
+ const manifest = join(stage, 'fleet.yaml');
20
+ const root = splitRootFor(manifest);
21
+ let publishedRoot = false;
22
+ try {
23
+ mkdirSync(join(root, 'agents'), { recursive: true, mode: 0o700 });
24
+ writeFileSync(manifest, 'api_version: ours.network/fleet/v2\n', { mode: 0o600, flag: 'wx' });
25
+ loadConfig(manifest, { yamlMode: 'strict' });
26
+ if (existsSync(selected.splitRoot) || existsSync(selected.configPath))
27
+ throw new Error('Fleet setup targets changed; refusing replacement');
28
+ renameSync(root, selected.splitRoot);
29
+ publishedRoot = true;
30
+ // Same-filesystem link publishes without replacing an unexpected manifest.
31
+ linkSync(manifest, selected.configPath);
32
+ unlinkSync(manifest);
33
+ loadConfig(selected.configPath, { yamlMode: 'strict' });
34
+ }
35
+ catch (cause) {
36
+ if (publishedRoot)
37
+ throw new Error('Minimal Fleet setup stopped after publishing its empty split directory; existing files were retained for recovery', { cause });
38
+ throw cause;
39
+ }
40
+ finally {
41
+ rmSync(stage, { recursive: true, force: true });
42
+ }
43
+ });
44
+ }
package/dist/monitor.d.ts CHANGED
@@ -88,33 +88,6 @@ export interface MonitorDeps {
88
88
  * turns they trigger keep dying.
89
89
  */
90
90
  export type StatusCause = 'connectivity' | 'delivery' | 'modal' | 'offline' | 'turns-failing' | 'safe-boundary' | 'auth';
91
- /** Best-effort daemon config (issue #17): the fields the MCP client reads. */
92
- interface DaemonConfig {
93
- apiToken?: string;
94
- port?: number;
95
- stateDir?: string;
96
- }
97
- /**
98
- * Read the daemon config the way the MCP client does — best-effort. Any missing,
99
- * malformed, or unreadable config yields `{}` so token resolution falls through
100
- * (issue #17). Only the well-typed fields we consume are surfaced.
101
- */
102
- export declare function readDaemonConfig(env: NodeJS.ProcessEnv): DaemonConfig;
103
- /**
104
- * Resolve the daemon API token exactly like the MCP client (issue #17), a 3-step
105
- * chain: `OURS_API_TOKEN` (trimmed) → config `apiToken` (trimmed) → the 0600 owner
106
- * token at `<stateDir>/daemon-token`. Never generates a token; a failed read of
107
- * any source (missing/unreadable) silently falls through to the next.
108
- */
109
- export declare function resolveApiToken(env: NodeJS.ProcessEnv, file?: DaemonConfig): string | undefined;
110
- export interface DaemonEndpoint {
111
- origin: string;
112
- port: number;
113
- configPath: string;
114
- stateDir: string;
115
- url(name: string): string;
116
- headers: Record<string, string>;
117
- }
118
91
  export type IdentityPresence = {
119
92
  state: 'present';
120
93
  temporary: boolean;
@@ -125,16 +98,12 @@ export type IdentityPresence = {
125
98
  state: 'unknown';
126
99
  detail: string;
127
100
  };
128
- /** Resolve the daemon endpoint + auth header from env → config → defaults. */
129
- export declare function resolveEndpoint(env: NodeJS.ProcessEnv, includeToken?: boolean): DaemonEndpoint;
130
101
  /**
131
102
  * Ask the daemon's authoritative identity index. The notifications endpoint is
132
103
  * intentionally unsuitable for lifecycle: it serves an empty 200 page for a
133
104
  * valid but missing identity, which made a closed temp identity look healthy.
134
105
  */
135
106
  export declare function probeIdentityPresence(name: string, fetch: FetchLike, env: NodeJS.ProcessEnv, deps?: IdentityProbeDeps): Promise<IdentityPresence>;
136
- /** Actionable, secret-free description of every token source for this profile. */
137
- export declare function authResolutionHint(ep: DaemonEndpoint): string;
138
107
  /** Keep only the events whose type the role asked to wake on. */
139
108
  export declare function filterEvents(events: NotifyEvent[], wakeSources: string[]): NotifyEvent[];
140
109
  /**
@@ -196,8 +165,7 @@ export declare class Monitor {
196
165
  private readonly identity;
197
166
  private readonly cfg;
198
167
  private readonly deps;
199
- private readonly ep?;
200
- private readonly profile?;
168
+ private readonly profile;
201
169
  private readonly profileKey;
202
170
  private readonly monitorLeaseToken;
203
171
  private profileClientPromise?;
@@ -263,4 +231,3 @@ export declare class Monitor {
263
231
  private writeStatus;
264
232
  }
265
233
  export declare function createMonitor(o: MonitorOpts): Monitor;
266
- export {};
package/dist/monitor.js CHANGED
@@ -1,6 +1,5 @@
1
1
  import { existsSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
2
2
  import { randomUUID } from 'node:crypto';
3
- import { homedir } from 'node:os';
4
3
  import { join } from 'node:path';
5
4
  import { attachOursClient } from '@ours.network/sdk/client';
6
5
  import { clientProfileKey, readClientProfile, } from './client-profile.js';
@@ -42,124 +41,28 @@ const TURN_OBSERVE_INTERVAL_MS = 1_500;
42
41
  const DEFAULT_TURN_FAIL_THRESHOLD = 3; // fallback when the resolved config omits it
43
42
  class AuthError extends Error {
44
43
  }
45
- /** Path to the daemon config the MCP client uses: OURS_CONFIG ?? real ~/.ours/config.json. */
46
- const daemonConfigPath = (env) => env.OURS_CONFIG ?? join(homedir(), '.ours', 'config.json');
47
- /** Preserve the daemon's legacy env integer semantics: parseInt, invalid → absent. */
48
- function envInt(env, name) {
49
- const raw = env[name];
50
- if (raw === undefined)
51
- return undefined;
52
- const n = parseInt(raw, 10);
53
- return Number.isNaN(n) ? undefined : n;
54
- }
55
- /**
56
- * Read the daemon config the way the MCP client does — best-effort. Any missing,
57
- * malformed, or unreadable config yields `{}` so token resolution falls through
58
- * (issue #17). Only the well-typed fields we consume are surfaced.
59
- */
60
- export function readDaemonConfig(env) {
61
- try {
62
- const p = JSON.parse(readFileSync(daemonConfigPath(env), 'utf8'));
63
- const o = {};
64
- if (typeof p.apiToken === 'string' && p.apiToken.trim())
65
- o.apiToken = p.apiToken.trim();
66
- if (typeof p.port === 'number' && Number.isFinite(p.port))
67
- o.port = p.port;
68
- if (typeof p.stateDir === 'string')
69
- o.stateDir = p.stateDir;
70
- return o;
71
- }
72
- catch {
73
- return {};
74
- }
75
- }
76
- /**
77
- * Resolve the daemon API token exactly like the MCP client (issue #17), a 3-step
78
- * chain: `OURS_API_TOKEN` (trimmed) → config `apiToken` (trimmed) → the 0600 owner
79
- * token at `<stateDir>/daemon-token`. Never generates a token; a failed read of
80
- * any source (missing/unreadable) silently falls through to the next.
81
- */
82
- export function resolveApiToken(env, file = readDaemonConfig(env)) {
83
- const e = env.OURS_API_TOKEN?.trim();
84
- if (e)
85
- return e;
86
- if (file.apiToken)
87
- return file.apiToken;
88
- const sd = env.OURS_STATE_DIR ?? file.stateDir ?? join(homedir(), '.ours');
89
- try {
90
- const t = readFileSync(join(sd, 'daemon-token'), 'utf8').trim();
91
- if (t)
92
- return t;
93
- }
94
- catch { /* missing/unreadable (e.g. cross-user 0600) → fall through */ }
95
- return undefined;
96
- }
97
- /** Resolve the daemon endpoint + auth header from env → config → defaults. */
98
- export function resolveEndpoint(env, includeToken = true) {
99
- const file = readDaemonConfig(env);
100
- const port = envInt(env, 'OURS_PORT') ?? file.port ?? DEFAULT_PORT;
101
- const configPath = daemonConfigPath(env);
102
- const stateDir = env.OURS_STATE_DIR ?? file.stateDir ?? join(homedir(), '.ours');
103
- const token = includeToken ? resolveApiToken(env, file) : undefined;
104
- const origin = `http://127.0.0.1:${port}`;
105
- return {
106
- origin,
107
- port,
108
- configPath,
109
- stateDir,
110
- url: (name) => `${origin}/identities/${encodeURIComponent(name)}/notifications`,
111
- headers: token ? { 'x-ours-api-token': token } : {},
112
- };
113
- }
114
44
  /**
115
45
  * Ask the daemon's authoritative identity index. The notifications endpoint is
116
46
  * intentionally unsuitable for lifecycle: it serves an empty 200 page for a
117
47
  * valid but missing identity, which made a closed temp identity look healthy.
118
48
  */
119
49
  export async function probeIdentityPresence(name, fetch, env, deps = {}) {
120
- const profile = readClientProfile(env);
121
- if (profile) {
122
- let client;
123
- try {
124
- client = await (deps.attachClient?.({
125
- endpoint: profile.endpoint, expectedInstanceId: profile.expectedInstanceId,
126
- credentialPath: profile.credentialPath,
127
- sessionMode: 'external', leaseToken: randomUUID(), env: {},
128
- }) ?? attachOursClient({
129
- endpoint: profile.endpoint, expectedInstanceId: profile.expectedInstanceId,
130
- credentialPath: profile.credentialPath,
131
- sessionMode: 'external', leaseToken: randomUUID(), env: {},
132
- }));
133
- return classifyIdentityPresence(name, await client.identities());
134
- }
135
- catch {
136
- return { state: 'unknown', detail: 'selected identity index is unavailable' };
137
- }
138
- finally {
139
- await client?.close().catch(() => undefined);
140
- }
141
- }
142
- const ep = resolveEndpoint(env);
143
- let response;
50
+ let client;
144
51
  try {
145
- response = await fetch(`${ep.origin}/identities`, { headers: ep.headers });
146
- }
147
- catch (error) {
148
- return { state: 'unknown', detail: `identity index unreachable (${msg(error)})` };
149
- }
150
- if (!response.ok)
151
- return {
152
- state: 'unknown',
153
- detail: response.status === 401
154
- ? `daemon rejected the API token (401) — ${authResolutionHint(ep)}`
155
- : `identity index returned HTTP ${response.status}`,
52
+ const profile = readClientProfile(env);
53
+ const options = {
54
+ endpoint: profile.endpoint, expectedInstanceId: profile.expectedInstanceId,
55
+ credentialPath: profile.credentialPath,
56
+ sessionMode: 'external', leaseToken: randomUUID(), env: {},
156
57
  };
157
- try {
158
- const body = await response.json();
159
- return classifyIdentityPresence(name, body.identities);
58
+ client = await (deps.attachClient?.(options) ?? attachOursClient(options));
59
+ return classifyIdentityPresence(name, await client.identities());
160
60
  }
161
- catch (error) {
162
- return { state: 'unknown', detail: `identity index response is unreadable (${msg(error)})` };
61
+ catch {
62
+ return { state: 'unknown', detail: 'gateway identity index unavailable; check the shared client profile and issued credential' };
63
+ }
64
+ finally {
65
+ await client?.close().catch(() => undefined);
163
66
  }
164
67
  }
165
68
  function classifyIdentityPresence(name, identities) {
@@ -186,12 +89,6 @@ function classifyIdentityPresence(name, identities) {
186
89
  stale: found.stale === true,
187
90
  };
188
91
  }
189
- /** Actionable, secret-free description of every token source for this profile. */
190
- export function authResolutionHint(ep) {
191
- const tokenPath = join(ep.stateDir, 'daemon-token');
192
- return `set OURS_API_TOKEN, set apiToken in ${JSON.stringify(ep.configPath)}, or ensure ` +
193
- `${JSON.stringify(tokenPath)} is readable by the fleet supervisor`;
194
- }
195
92
  /** Keep only the events whose type the role asked to wake on. */
196
93
  export function filterEvents(events, wakeSources) {
197
94
  const set = new Set(wakeSources);
@@ -414,7 +311,6 @@ export class Monitor {
414
311
  identity;
415
312
  cfg;
416
313
  deps;
417
- ep;
418
314
  profile;
419
315
  profileKey;
420
316
  monitorLeaseToken = `ours-fleet-monitor-${process.pid}-${randomUUID()}`;
@@ -441,8 +337,7 @@ export class Monitor {
441
337
  this.cfg = o.cfg;
442
338
  this.deps = o.deps;
443
339
  this.profile = readClientProfile(o.deps.env);
444
- this.ep = this.profile ? undefined : resolveEndpoint(o.deps.env);
445
- this.profileKey = this.profile ? clientProfileKey(this.profile) : this.ep.origin;
340
+ this.profileKey = clientProfileKey(this.profile);
446
341
  this.statusPath = join(o.agentDir, '.monitor-status');
447
342
  this.cursorPath = join(o.agentDir, '.notify-cursor');
448
343
  this.statePath = join(o.agentDir, '.monitor-state.json');
@@ -470,14 +365,8 @@ export class Monitor {
470
365
  }
471
366
  catch (e) {
472
367
  if (e instanceof AuthError) {
473
- if (this.profile) {
474
- this.cursor = null;
475
- this.degrade('auth', e.message);
476
- }
477
- else {
478
- this.fatal = true;
479
- this.degrade('auth', e.message, 'failed');
480
- }
368
+ this.cursor = null;
369
+ this.degrade('auth', e.message);
481
370
  }
482
371
  else {
483
372
  this.cursor = null;
@@ -510,11 +399,6 @@ export class Monitor {
510
399
  catch (e) {
511
400
  if (this.stopped)
512
401
  return;
513
- if (e instanceof AuthError && !this.profile) {
514
- this.fatal = true;
515
- this.degrade('auth', e.message, 'failed');
516
- return;
517
- }
518
402
  backoff = Math.min(backoff + BACKOFF_STEP_MS, BACKOFF_MAX_MS);
519
403
  if (e instanceof AuthError)
520
404
  this.degrade('auth', e.message);
@@ -630,23 +514,19 @@ export class Monitor {
630
514
  this.currentAbort = ctrl;
631
515
  let timedOut = false;
632
516
  const timer = this.deps.timers.set(() => { timedOut = true; ctrl.abort(); }, timeoutMs);
633
- let resp;
634
517
  try {
635
- if (this.profile) {
636
- const client = await this.profileClient();
637
- const page = await client.readNotificationPage(this.identity, {
638
- since: since === 'tip' ? 'tip' : Number.parseInt(since, 10),
639
- signal: ctrl.signal,
640
- requestTimeoutMs: timeoutMs,
641
- });
642
- return { cursor: page.cursor, events: page.events };
643
- }
644
- resp = await this.deps.fetch(`${this.ep.url(this.identity)}?since=${since}`, { headers: this.ep.headers, signal: ctrl.signal });
518
+ const client = await this.profileClient();
519
+ const page = await client.readNotificationPage(this.identity, {
520
+ since: since === 'tip' ? 'tip' : Number.parseInt(since, 10),
521
+ signal: ctrl.signal,
522
+ requestTimeoutMs: timeoutMs,
523
+ });
524
+ return { cursor: page.cursor, events: page.events };
645
525
  }
646
526
  catch (error) {
647
527
  if (timedOut && timeoutKind === 'stall')
648
528
  throw new Error(`notification stream stalled for ${Math.round(timeoutMs / 1000)}s`);
649
- if (this.profile && /(?:HTTP\s*401|unauthori[sz]ed|API token)/i.test(msg(error)))
529
+ if (/(?:HTTP\s*401|unauthori[sz]ed|API token)/i.test(msg(error)))
650
530
  throw new AuthError(`daemon rejected the API token (401) — verify the credential file `
651
531
  + `${JSON.stringify(this.profile.credentialPath)} selected by `
652
532
  + `${JSON.stringify(this.profile.configPath)}`);
@@ -656,15 +536,8 @@ export class Monitor {
656
536
  this.deps.timers.clear(timer);
657
537
  this.currentAbort = null;
658
538
  }
659
- if (resp.status === 401)
660
- throw new AuthError(`daemon rejected the API token (401) — ${authResolutionHint(this.ep)}`);
661
- if (!resp.ok)
662
- throw new Error(`daemon returned HTTP ${resp.status}`);
663
- return resp.json();
664
539
  }
665
540
  profileClient() {
666
- if (!this.profile)
667
- throw new Error('explicit client profile is not selected');
668
541
  if (!this.profileClientPromise) {
669
542
  const { endpoint, expectedInstanceId, credentialPath } = this.profile;
670
543
  const options = {
@@ -0,0 +1,18 @@
1
+ import { type ProducerConfig } from './outbox.js';
2
+ /** Read durable supervisor ledgers without restarting agents or attaching a
3
+ * conversation controller. Each tick reads at most 256 KiB per role, from its
4
+ * checkpoint; it never loads a complete transcript or requests model history. */
5
+ export declare class FleetNotificationProducer {
6
+ private readonly roots;
7
+ private readonly stateDir;
8
+ private readonly config;
9
+ private readonly warn;
10
+ private readonly queues;
11
+ private readonly timer;
12
+ private readonly existing;
13
+ constructor(roots: string[], stateDir: string, config: ProducerConfig, warn?: (line: string) => void);
14
+ private names;
15
+ poll(): void;
16
+ drain(): Promise<void>;
17
+ close(): Promise<void>;
18
+ }