@ours.network/fleet 1.2.0-nightly.5 → 1.2.0-nightly.50

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 (330) hide show
  1. package/README.md +263 -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-removal-service.js +2 -0
  25. package/dist/application/role-repository.js +2 -2
  26. package/dist/application/room-layout-definitions.d.ts +45 -0
  27. package/dist/application/room-layout-definitions.js +130 -0
  28. package/dist/application/session-control.d.ts +30 -0
  29. package/dist/application/session-control.js +72 -3
  30. package/dist/application/supervisor-ours-tools.d.ts +159 -0
  31. package/dist/application/supervisor-ours-tools.js +113 -0
  32. package/dist/application/task-layouts.d.ts +91 -0
  33. package/dist/application/task-layouts.js +249 -0
  34. package/dist/application/task-room-service.d.ts +99 -3
  35. package/dist/application/task-room-service.js +296 -31
  36. package/dist/atomic-file.d.ts +5 -0
  37. package/dist/atomic-file.js +62 -1
  38. package/dist/briefing.d.ts +2 -0
  39. package/dist/briefing.js +8 -1
  40. package/dist/build-info.json +7 -4
  41. package/dist/capabilities.d.ts +6 -1
  42. package/dist/capabilities.js +8 -0
  43. package/dist/cli.js +376 -34
  44. package/dist/client-profile.d.ts +5 -17
  45. package/dist/client-profile.js +4 -127
  46. package/dist/config.d.ts +28 -0
  47. package/dist/config.js +41 -6
  48. package/dist/creation.js +2 -2
  49. package/dist/daemon-recovery.d.ts +1 -8
  50. package/dist/daemon-recovery.js +8 -113
  51. package/dist/docs.d.ts +3 -3
  52. package/dist/docs.js +126 -22
  53. package/dist/doctor.d.ts +1 -1
  54. package/dist/doctor.js +55 -60
  55. package/dist/erased-resources.d.ts +9 -0
  56. package/dist/erased-resources.js +87 -0
  57. package/dist/fleet-command-audit.d.ts +7 -0
  58. package/dist/fleet-command-audit.js +26 -9
  59. package/dist/harness/claude-code.js +9 -1
  60. package/dist/harness/claude-runtime.d.ts +12 -0
  61. package/dist/harness/claude-runtime.js +43 -0
  62. package/dist/harness/codex-app-server-proxy.d.ts +4 -1
  63. package/dist/harness/codex-app-server-proxy.js +41 -5
  64. package/dist/harness/codex-reasoning.d.ts +5 -0
  65. package/dist/harness/codex-reasoning.js +12 -0
  66. package/dist/harness/codex-runtime.d.ts +11 -1
  67. package/dist/harness/codex-runtime.js +31 -4
  68. package/dist/harness/codex.d.ts +3 -2
  69. package/dist/harness/codex.js +20 -11
  70. package/dist/init-wizard.d.ts +2 -0
  71. package/dist/init-wizard.js +38 -19
  72. package/dist/isolation/policy.js +2 -0
  73. package/dist/isolation/types.d.ts +5 -0
  74. package/dist/managed-cli.d.ts +253 -0
  75. package/dist/managed-cli.js +923 -0
  76. package/dist/minimal-setup.d.ts +2 -0
  77. package/dist/minimal-setup.js +44 -0
  78. package/dist/monitor.d.ts +1 -34
  79. package/dist/monitor.js +24 -151
  80. package/dist/notifications/fleet-producer.d.ts +19 -0
  81. package/dist/notifications/fleet-producer.js +208 -0
  82. package/dist/notifications/outbox.d.ts +46 -0
  83. package/dist/notifications/outbox.js +158 -0
  84. package/dist/notifications/target-cleanup.d.ts +8 -0
  85. package/dist/notifications/target-cleanup.js +41 -0
  86. package/dist/ops.js +5 -1
  87. package/dist/owner-channel/channel.d.ts +3 -0
  88. package/dist/owner-channel/channel.js +34 -6
  89. package/dist/owner-channel/commands.d.ts +4 -0
  90. package/dist/owner-channel/commands.js +8 -0
  91. package/dist/owner-channel/lifecycle-outbox.js +4 -3
  92. package/dist/owner-channel/ours-client.js +2 -17
  93. package/dist/paths.d.ts +2 -0
  94. package/dist/paths.js +2 -0
  95. package/dist/preset-migration.js +25 -0
  96. package/dist/resolved-plan.js +1 -0
  97. package/dist/rooms-tasks/add-member.d.ts +48 -0
  98. package/dist/rooms-tasks/add-member.js +83 -0
  99. package/dist/rooms-tasks/archived-absence.d.ts +8 -0
  100. package/dist/rooms-tasks/archived-absence.js +48 -0
  101. package/dist/rooms-tasks/cli.js +34 -5
  102. package/dist/rooms-tasks/close.d.ts +5 -2
  103. package/dist/rooms-tasks/close.js +208 -28
  104. package/dist/rooms-tasks/config.d.ts +1 -1
  105. package/dist/rooms-tasks/config.js +11 -4
  106. package/dist/rooms-tasks/cowork-adapter.d.ts +6 -4
  107. package/dist/rooms-tasks/cowork-adapter.js +8 -121
  108. package/dist/rooms-tasks/deletion.d.ts +1 -1
  109. package/dist/rooms-tasks/deletion.js +74 -5
  110. package/dist/rooms-tasks/erasure.d.ts +6 -0
  111. package/dist/rooms-tasks/erasure.js +169 -0
  112. package/dist/rooms-tasks/external-worker.d.ts +1 -1
  113. package/dist/rooms-tasks/external-worker.js +3 -4
  114. package/dist/rooms-tasks/layout-binding-client.d.ts +11 -0
  115. package/dist/rooms-tasks/layout-binding-client.js +56 -0
  116. package/dist/rooms-tasks/layout-binding-grants.d.ts +21 -0
  117. package/dist/rooms-tasks/layout-binding-grants.js +128 -0
  118. package/dist/rooms-tasks/layout-cli.d.ts +4 -0
  119. package/dist/rooms-tasks/layout-cli.js +107 -0
  120. package/dist/rooms-tasks/layout-config.d.ts +11 -0
  121. package/dist/rooms-tasks/layout-config.js +89 -0
  122. package/dist/rooms-tasks/layout-control.d.ts +21 -0
  123. package/dist/rooms-tasks/layout-control.js +52 -0
  124. package/dist/rooms-tasks/layout-service.d.ts +42 -0
  125. package/dist/rooms-tasks/layout-service.js +185 -0
  126. package/dist/rooms-tasks/layout.d.ts +114 -0
  127. package/dist/rooms-tasks/layout.js +250 -0
  128. package/dist/rooms-tasks/live-readiness.d.ts +18 -0
  129. package/dist/rooms-tasks/live-readiness.js +85 -0
  130. package/dist/rooms-tasks/member-startup.d.ts +1 -0
  131. package/dist/rooms-tasks/member-startup.js +2 -0
  132. package/dist/rooms-tasks/provision.d.ts +14 -0
  133. package/dist/rooms-tasks/provision.js +173 -46
  134. package/dist/rooms-tasks/room-state.d.ts +4 -2
  135. package/dist/rooms-tasks/room-state.js +53 -8
  136. package/dist/rooms-tasks/task-state.d.ts +42 -7
  137. package/dist/rooms-tasks/task-state.js +153 -23
  138. package/dist/rooms-tasks/types.d.ts +28 -0
  139. package/dist/rooms-tasks/workspace-artifacts.d.ts +6 -0
  140. package/dist/rooms-tasks/workspace-artifacts.js +93 -0
  141. package/dist/rooms-tasks/workspace.d.ts +26 -0
  142. package/dist/rooms-tasks/workspace.js +195 -0
  143. package/dist/runner.d.ts +19 -3
  144. package/dist/runner.js +128 -45
  145. package/dist/session/acp.d.ts +9 -1
  146. package/dist/session/acp.js +31 -6
  147. package/dist/session/arbiter.d.ts +5 -0
  148. package/dist/session/arbiter.js +28 -2
  149. package/dist/session/codex-app-server.d.ts +5 -0
  150. package/dist/session/codex-app-server.js +36 -3
  151. package/dist/session/control.d.ts +12 -1
  152. package/dist/session/control.js +68 -3
  153. package/dist/session/conversation-normalizer.d.ts +2 -1
  154. package/dist/session/conversation-normalizer.js +1 -1
  155. package/dist/session/conversation-store.d.ts +1 -1
  156. package/dist/session/conversation-store.js +7 -11
  157. package/dist/session/conversation-types.d.ts +7 -3
  158. package/dist/session/task-notice.d.ts +14 -0
  159. package/dist/session/task-notice.js +13 -0
  160. package/dist/session/types.d.ts +6 -0
  161. package/dist/setup-tunnel-preflight.d.ts +34 -0
  162. package/dist/setup-tunnel-preflight.js +62 -0
  163. package/dist/socket-path.d.ts +2 -0
  164. package/dist/socket-path.js +22 -0
  165. package/dist/spawn.js +8 -1
  166. package/dist/startup-readiness.d.ts +13 -0
  167. package/dist/startup-readiness.js +38 -0
  168. package/dist/subscriptions/cli.d.ts +91 -0
  169. package/dist/subscriptions/cli.js +223 -0
  170. package/dist/subscriptions/launch.d.ts +37 -0
  171. package/dist/subscriptions/launch.js +93 -0
  172. package/dist/subscriptions/login.d.ts +75 -0
  173. package/dist/subscriptions/login.js +182 -0
  174. package/dist/subscriptions/observed.d.ts +7 -0
  175. package/dist/subscriptions/observed.js +44 -0
  176. package/dist/subscriptions/probe.d.ts +10 -0
  177. package/dist/subscriptions/probe.js +73 -0
  178. package/dist/subscriptions/service.d.ts +102 -0
  179. package/dist/subscriptions/service.js +364 -0
  180. package/dist/subscriptions/store.d.ts +71 -0
  181. package/dist/subscriptions/store.js +140 -0
  182. package/dist/supervisor/launchd.js +2 -1
  183. package/dist/supervisor/systemd.js +9 -23
  184. package/dist/temp-idle.d.ts +17 -0
  185. package/dist/temp-idle.js +42 -0
  186. package/dist/temp-lifecycle.d.ts +6 -1
  187. package/dist/temp-lifecycle.js +29 -13
  188. package/dist/tunnel-setup-completion.d.ts +7 -0
  189. package/dist/tunnel-setup-completion.js +13 -0
  190. package/dist/tunnel-setup-input.d.ts +19 -0
  191. package/dist/tunnel-setup-input.js +124 -0
  192. package/dist/web/agent-attachments.d.ts +29 -0
  193. package/dist/web/agent-attachments.js +162 -0
  194. package/dist/web/auth.d.ts +29 -1
  195. package/dist/web/auth.js +82 -7
  196. package/dist/web/available-port.d.ts +2 -0
  197. package/dist/web/available-port.js +23 -0
  198. package/dist/web/control.d.ts +3 -3
  199. package/dist/web/control.js +10 -8
  200. package/dist/web/fleet-config-service.d.ts +4 -1
  201. package/dist/web/fleet-config-service.js +46 -15
  202. package/dist/web/prefix-gateway.d.ts +32 -0
  203. package/dist/web/prefix-gateway.js +234 -0
  204. package/dist/web/runtime.d.ts +3 -1
  205. package/dist/web/runtime.js +116 -31
  206. package/dist/web/server.d.ts +16 -0
  207. package/dist/web/server.js +588 -53
  208. package/dist/web/service.d.ts +11 -2
  209. package/dist/web/service.js +30 -4
  210. package/dist/web/workspace-devices.d.ts +36 -0
  211. package/dist/web/workspace-devices.js +101 -0
  212. package/dist/web/workspace-tunnel.d.ts +1 -0
  213. package/dist/web/workspace-tunnel.js +34 -0
  214. package/dist/workspace-enrollment.d.ts +82 -0
  215. package/dist/workspace-enrollment.js +566 -0
  216. package/dist/workspace-replacement.d.ts +66 -0
  217. package/dist/workspace-replacement.js +220 -0
  218. package/docs/live-voice.md +13 -0
  219. package/docs/room-layouts.md +227 -0
  220. package/docs/task-workspaces.md +130 -0
  221. package/docs/validation/managed-cli-permissions.md +421 -0
  222. package/examples/room-layouts/fresh.yaml +27 -0
  223. package/examples/room-layouts/scoped.yaml +21 -0
  224. package/examples/room-layouts/shared.yaml +19 -0
  225. package/package.json +15 -18
  226. package/presets/README.md +78 -0
  227. package/presets/brain-catalog.json +8 -2
  228. package/presets/fleet/agent_templates/Critic.yaml +1 -1
  229. package/presets/fleet/agent_templates/Developer.yaml +1 -1
  230. package/presets/fleet/agent_templates/Engineer.yaml +5 -0
  231. package/presets/fleet/agent_templates/LocalCoordinator.yaml +1 -1
  232. package/presets/fleet/agents/FleetCoordinator.yaml +1 -1
  233. package/presets/fleet/brains/claude-fable-5-1-high.yaml +5 -0
  234. package/presets/fleet/brains/claude-fable-5-1-low.yaml +5 -0
  235. package/presets/fleet/brains/claude-fable-5-1-max.yaml +5 -0
  236. package/presets/fleet/brains/claude-fable-5-1-medium.yaml +5 -0
  237. package/presets/fleet/brains/claude-fable-5-1-xhigh.yaml +5 -0
  238. package/presets/fleet/brains/claude-fable-5-high.yaml +1 -1
  239. package/presets/fleet/brains/claude-fable-5-low.yaml +1 -1
  240. package/presets/fleet/brains/claude-fable-5-max.yaml +1 -1
  241. package/presets/fleet/brains/claude-fable-5-medium.yaml +1 -1
  242. package/presets/fleet/brains/claude-fable-5-xhigh.yaml +1 -1
  243. package/presets/fleet/brains/claude-haiku-4-5-20251001-default.yaml +4 -0
  244. package/presets/fleet/brains/claude-opus-5-5-high.yaml +5 -0
  245. package/presets/fleet/brains/claude-opus-5-5-low.yaml +5 -0
  246. package/presets/fleet/brains/claude-opus-5-5-max.yaml +5 -0
  247. package/presets/fleet/brains/claude-opus-5-5-medium.yaml +5 -0
  248. package/presets/fleet/brains/claude-opus-5-5-xhigh.yaml +5 -0
  249. package/presets/fleet/brains/claude-opus-5-high.yaml +1 -1
  250. package/presets/fleet/brains/claude-opus-5-low.yaml +1 -1
  251. package/presets/fleet/brains/claude-opus-5-max.yaml +1 -1
  252. package/presets/fleet/brains/claude-opus-5-medium.yaml +1 -1
  253. package/presets/fleet/brains/claude-opus-5-xhigh.yaml +1 -1
  254. package/presets/fleet/brains/claude-sonnet-5-high.yaml +1 -1
  255. package/presets/fleet/brains/claude-sonnet-5-low.yaml +1 -1
  256. package/presets/fleet/brains/claude-sonnet-5-max.yaml +1 -1
  257. package/presets/fleet/brains/claude-sonnet-5-medium.yaml +1 -1
  258. package/presets/fleet/brains/claude-sonnet-5-xhigh.yaml +1 -1
  259. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-high.yaml +1 -1
  260. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-low.yaml +1 -1
  261. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-medium.yaml +1 -1
  262. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-xhigh.yaml +1 -1
  263. package/presets/fleet/brains/codex-gpt-5-4-high.yaml +1 -1
  264. package/presets/fleet/brains/codex-gpt-5-4-low.yaml +1 -1
  265. package/presets/fleet/brains/codex-gpt-5-4-medium.yaml +1 -1
  266. package/presets/fleet/brains/codex-gpt-5-4-mini-high.yaml +1 -1
  267. package/presets/fleet/brains/codex-gpt-5-4-mini-low.yaml +1 -1
  268. package/presets/fleet/brains/codex-gpt-5-4-mini-medium.yaml +1 -1
  269. package/presets/fleet/brains/codex-gpt-5-4-mini-xhigh.yaml +1 -1
  270. package/presets/fleet/brains/codex-gpt-5-4-xhigh.yaml +1 -1
  271. package/presets/fleet/brains/codex-gpt-5-5-high.yaml +1 -1
  272. package/presets/fleet/brains/codex-gpt-5-5-low.yaml +1 -1
  273. package/presets/fleet/brains/codex-gpt-5-5-medium.yaml +1 -1
  274. package/presets/fleet/brains/codex-gpt-5-5-xhigh.yaml +1 -1
  275. package/presets/fleet/brains/codex-gpt-5-6-luna-high.yaml +1 -1
  276. package/presets/fleet/brains/codex-gpt-5-6-luna-low.yaml +1 -1
  277. package/presets/fleet/brains/codex-gpt-5-6-luna-max.yaml +1 -1
  278. package/presets/fleet/brains/codex-gpt-5-6-luna-medium.yaml +1 -1
  279. package/presets/fleet/brains/codex-gpt-5-6-luna-xhigh.yaml +1 -1
  280. package/presets/fleet/brains/codex-gpt-5-6-sol-high.yaml +1 -1
  281. package/presets/fleet/brains/codex-gpt-5-6-sol-low.yaml +1 -1
  282. package/presets/fleet/brains/codex-gpt-5-6-sol-max.yaml +1 -1
  283. package/presets/fleet/brains/codex-gpt-5-6-sol-medium.yaml +1 -1
  284. package/presets/fleet/brains/codex-gpt-5-6-sol-ultra.yaml +1 -1
  285. package/presets/fleet/brains/codex-gpt-5-6-sol-xhigh.yaml +1 -1
  286. package/presets/fleet/brains/codex-gpt-5-6-terra-high.yaml +1 -1
  287. package/presets/fleet/brains/codex-gpt-5-6-terra-low.yaml +1 -1
  288. package/presets/fleet/brains/codex-gpt-5-6-terra-max.yaml +1 -1
  289. package/presets/fleet/brains/codex-gpt-5-6-terra-medium.yaml +1 -1
  290. package/presets/fleet/brains/codex-gpt-5-6-terra-ultra.yaml +1 -1
  291. package/presets/fleet/brains/codex-gpt-5-6-terra-xhigh.yaml +1 -1
  292. package/presets/fleet/brains/codex-gpt-6-1-sol-high.yaml +5 -0
  293. package/presets/fleet/brains/codex-gpt-6-1-sol-low.yaml +5 -0
  294. package/presets/fleet/brains/codex-gpt-6-1-sol-max.yaml +5 -0
  295. package/presets/fleet/brains/codex-gpt-6-1-sol-medium.yaml +5 -0
  296. package/presets/fleet/brains/codex-gpt-6-1-sol-ultra.yaml +5 -0
  297. package/presets/fleet/brains/codex-gpt-6-1-sol-xhigh.yaml +5 -0
  298. package/presets/fleet/brains/codex-gpt-6-astra-high.yaml +1 -1
  299. package/presets/fleet/brains/codex-gpt-6-astra-low.yaml +1 -1
  300. package/presets/fleet/brains/codex-gpt-6-astra-max.yaml +1 -1
  301. package/presets/fleet/brains/codex-gpt-6-astra-medium.yaml +1 -1
  302. package/presets/fleet/brains/codex-gpt-6-astra-ultra.yaml +1 -1
  303. package/presets/fleet/brains/codex-gpt-6-astra-xhigh.yaml +1 -1
  304. package/presets/fleet/brains/codex-gpt-6-luna-high.yaml +5 -0
  305. package/presets/fleet/brains/codex-gpt-6-luna-low.yaml +5 -0
  306. package/presets/fleet/brains/codex-gpt-6-luna-max.yaml +5 -0
  307. package/presets/fleet/brains/codex-gpt-6-luna-medium.yaml +5 -0
  308. package/presets/fleet/brains/codex-gpt-6-luna-xhigh.yaml +5 -0
  309. package/presets/fleet/brains/codex-gpt-6-sol-high.yaml +5 -0
  310. package/presets/fleet/brains/codex-gpt-6-sol-low.yaml +5 -0
  311. package/presets/fleet/brains/codex-gpt-6-sol-max.yaml +5 -0
  312. package/presets/fleet/brains/codex-gpt-6-sol-medium.yaml +5 -0
  313. package/presets/fleet/brains/codex-gpt-6-sol-ultra.yaml +5 -0
  314. package/presets/fleet/brains/codex-gpt-6-sol-xhigh.yaml +5 -0
  315. package/presets/fleet/roles/Coordinator.yaml +34 -11
  316. package/presets/fleet/roles/Engineer.yaml +40 -0
  317. package/presets/fleet/room_layouts/engineering.yaml +25 -0
  318. package/presets/fleet/room_layouts/pair.yaml +24 -0
  319. package/presets/fleet/room_layouts/single.yaml +17 -0
  320. package/presets/fleet/room_layouts/team.yaml +30 -0
  321. package/presets/fleet/room_templates/engineering.yaml +19 -0
  322. package/presets/manifest.json +2 -2
  323. package/dist/web-app/assets/index-59GF-gsZ.css +0 -1
  324. package/dist/web-app/assets/index-C0h3ALvs.js +0 -10
  325. package/dist/web-app/icons/ours-fleet-maskable.svg +0 -4
  326. package/dist/web-app/icons/ours-fleet.svg +0 -4
  327. package/dist/web-app/index.html +0 -17
  328. package/dist/web-app/manifest.webmanifest +0 -15
  329. package/dist/web-app/offline.html +0 -18
  330. package/dist/web-app/sw.js +0 -51
@@ -1,130 +1,7 @@
1
- import { normalizeDaemonEndpoint } from '@ours.network/sdk/client';
2
- import { lstatSync, readFileSync, statSync } from 'node:fs';
3
- import { homedir } from 'node:os';
4
- import { isAbsolute, join } from 'node:path';
5
- const PROFILE_FIELDS = ['endpoint', 'expectedInstanceId', 'credentialPath'];
6
- const LOWERCASE_UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
7
- const LEGACY_CONFIG_FIELDS = ['apiToken', 'stateDir', 'port'];
8
- const LEGACY_ENV_FIELDS = ['OURS_API_TOKEN', 'OURS_STATE_DIR', 'OURS_PORT', 'OURS_DAEMON_ID'];
9
- export class ClientProfileError extends Error {
10
- constructor(message) {
11
- super(message);
12
- this.name = 'ClientProfileError';
13
- }
14
- }
15
- export function clientConfigPath(env) {
16
- if (env.OURS_CONFIG !== undefined)
17
- return env.OURS_CONFIG;
18
- const managedPath = join(env.HOME || homedir(), '.ours-client', 'profile.json');
19
- try {
20
- lstatSync(managedPath);
21
- return managedPath;
22
- }
23
- catch (error) {
24
- if (error.code !== 'ENOENT')
25
- throw invalid(managedPath, 'could not be read');
26
- return join(homedir(), '.ours', 'config.json');
27
- }
28
- }
29
- function invalid(path, detail) {
30
- return new ClientProfileError(`ours client profile ${JSON.stringify(path)} ${detail}`);
31
- }
32
- /**
33
- * Read Fleet's selected host-client profile without opening its credential.
34
- * A legacy daemon config remains legacy only when none of the profile fields is
35
- * present. Explicit and managed selections fail closed on invalid input.
36
- */
37
- export function readClientProfile(env) {
38
- const configPath = clientConfigPath(env);
39
- const managed = configPath === join(env.HOME || homedir(), '.ours-client', 'profile.json');
40
- const named = env.OURS_CONFIG !== undefined || managed;
41
- let text;
42
- try {
43
- text = readFileSync(configPath, 'utf8');
44
- }
45
- catch {
46
- if (named)
47
- throw invalid(configPath, 'could not be read');
48
- return undefined;
49
- }
50
- let parsed;
51
- try {
52
- parsed = JSON.parse(text);
53
- }
54
- catch {
55
- if (named)
56
- throw invalid(configPath, 'is not valid JSON');
57
- return undefined;
58
- }
59
- if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
60
- if (named)
61
- throw invalid(configPath, 'must contain a JSON object');
62
- return undefined;
63
- }
64
- const row = parsed;
65
- if (!PROFILE_FIELDS.some(field => Object.prototype.hasOwnProperty.call(row, field))) {
66
- if (managed)
67
- throw invalid(configPath, 'must contain a complete client profile');
68
- return undefined;
69
- }
70
- let metadata;
71
- try {
72
- metadata = statSync(configPath);
73
- }
74
- catch {
75
- throw invalid(configPath, 'could not be inspected');
76
- }
77
- if (!metadata.isFile())
78
- throw invalid(configPath, 'must be a regular file');
79
- const getuid = process.getuid;
80
- if (getuid && metadata.uid !== getuid())
81
- throw invalid(configPath, 'must be owned by this user');
82
- if ((metadata.mode & 0o077) !== 0)
83
- throw invalid(configPath, 'must not be accessible by group or other users');
84
- for (const field of PROFILE_FIELDS)
85
- if (typeof row[field] !== 'string' || !row[field].trim())
86
- throw invalid(configPath, `has an invalid or missing ${field}`);
87
- for (const field of LEGACY_CONFIG_FIELDS)
88
- if (Object.prototype.hasOwnProperty.call(row, field))
89
- throw invalid(configPath, `cannot combine ${field} with explicit profile selection`);
90
- for (const field of LEGACY_ENV_FIELDS)
91
- if (env[field]?.trim())
92
- throw invalid(configPath, `cannot combine ${field} with explicit profile selection`);
93
- const endpoint = row.endpoint.trim();
94
- let url;
95
- try {
96
- url = new URL(endpoint);
97
- }
98
- catch {
99
- throw invalid(configPath, 'has an invalid endpoint');
100
- }
101
- if ((url.protocol !== 'http:' && url.protocol !== 'https:') || url.username || url.password
102
- || url.search || url.hash)
103
- throw invalid(configPath, 'endpoint must be an HTTP or HTTPS base URL');
104
- const expectedInstanceId = row.expectedInstanceId.trim();
105
- if (!LOWERCASE_UUID.test(expectedInstanceId))
106
- throw invalid(configPath, 'expectedInstanceId must be a lowercase UUID');
107
- const credentialPath = row.credentialPath.trim();
108
- if (!isAbsolute(credentialPath))
109
- throw invalid(configPath, 'credentialPath must be absolute');
110
- let serverUrl;
111
- if (Object.hasOwn(row, 'serverUrl')) {
112
- if (typeof row.serverUrl !== 'string' || /[\s\\?#]/.test(row.serverUrl))
113
- throw invalid(configPath, 'serverUrl must be a safe HTTP or HTTPS base URL');
114
- try {
115
- serverUrl = normalizeDaemonEndpoint(row.serverUrl);
116
- }
117
- catch {
118
- throw invalid(configPath, 'serverUrl must be a safe HTTP or HTTPS base URL');
119
- }
120
- if (normalizeDaemonEndpoint(endpoint) !== serverUrl + '/daemon')
121
- throw invalid(configPath, 'endpoint must select serverUrl/daemon');
122
- }
123
- return Object.freeze({
124
- ...(serverUrl === undefined ? {} : { serverUrl }),
125
- endpoint: normalizeDaemonEndpoint(endpoint), expectedInstanceId, credentialPath, configPath,
126
- });
127
- }
1
+ import { clientProfilePath, readGatewayClientProfile } from '@ours.network/sdk/client';
2
+ export { ClientProfileError } from '@ours.network/sdk/client';
3
+ export const clientConfigPath = clientProfilePath;
4
+ export const readClientProfile = readGatewayClientProfile;
128
5
  export function clientProfileKey(profile) {
129
6
  return `${profile.endpoint}#${profile.expectedInstanceId}`;
130
7
  }
package/dist/config.d.ts CHANGED
@@ -2,6 +2,7 @@ import { type ConfigDiagnostic, type YamlMode } from './config-yaml.js';
2
2
  import type { IsolationConfig, WrapContext } from './isolation/types.js';
3
3
  import type { ResolvedWatchdog } from './watchdog/config.js';
4
4
  import type { AgentLoopsConfig, ResolvedLoop, ResolvedRoleLoop } from './loops/config.js';
5
+ import { validateRoomsConfig } from './rooms-tasks/config.js';
5
6
  export interface OverseeEntry {
6
7
  agent: string;
7
8
  interval: string;
@@ -35,6 +36,12 @@ export type FilesystemMode = 'read-only' | 'workspace' | 'unrestricted';
35
36
  export type UnattendedMode = 'deny' | 'wait';
36
37
  /** Monitor wake policy: preserve legacy booleans and add one explicit safe boundary. */
37
38
  export type MonitorInterrupt = boolean | 'after_tool';
39
+ /**
40
+ * Workflows Fleet prepares native harness execution policy for. Declared per
41
+ * Agent; never inferred from a role's name or persona, and never inherited.
42
+ */
43
+ export declare const MANAGED_CLI_WORKFLOW_IDS: readonly ["task-workflow"];
44
+ export type ManagedCliWorkflowId = (typeof MANAGED_CLI_WORKFLOW_IDS)[number];
38
45
  export interface CommonPermissions {
39
46
  approval: ApprovalMode;
40
47
  filesystem: FilesystemMode;
@@ -145,6 +152,8 @@ export interface RoleConfig {
145
152
  owner_channel?: OwnerChannelConfigInput;
146
153
  worklog?: WorklogPolicyInput;
147
154
  auth_proxy?: Partial<AuthProxyConfig>;
155
+ /** Fleet-prepared CLI workflows this role may run outside its command sandbox. */
156
+ managed_cli?: ManagedCliWorkflowId[];
148
157
  }
149
158
  export type AgentSelection<T extends Record<string, unknown> = Record<string, unknown>> = {
150
159
  ref: string;
@@ -166,6 +175,8 @@ export interface AgentDefinition {
166
175
  owner_channel?: OwnerChannelConfigInput;
167
176
  worklog?: WorklogPolicyInput;
168
177
  auth_proxy?: Partial<AuthProxyConfig>;
178
+ /** Fleet-prepared CLI workflows this Agent may run outside its command sandbox. */
179
+ managed_cli?: ManagedCliWorkflowId[];
169
180
  /** Scheduled turns scoped only to temporary launches; persistent Agents reject this field. */
170
181
  loops?: AgentLoopsConfig;
171
182
  }
@@ -178,6 +189,7 @@ export interface AgentTemplateInstance {
178
189
  }
179
190
  /** Internal first-boot payload for a Fleet-provisioned Cowork room member. */
180
191
  export interface RoomMemberStartup {
192
+ workspace?: import('./rooms-tasks/workspace.js').OwnedWorkspace;
181
193
  room_id: string;
182
194
  room_identity_cid: string;
183
195
  identity_name: string;
@@ -298,6 +310,8 @@ export declare function validateBrainValue(value: BarePreset, file: string, poin
298
310
  export declare function validateEffectiveAgentTemplate(definition: AgentTemplateDefinition, id?: string): AgentTemplateDefinition;
299
311
  export declare function assertBareKeys(value: unknown, allowed: string[], label: string): BarePreset;
300
312
  export declare function splitRootFor(base: string): string;
313
+ /** Read only room connection/owner settings for an existing layout. */
314
+ export declare function loadRoomsConfig(configPath?: string): ReturnType<typeof validateRoomsConfig>;
301
315
  /** Load a v2 manifest and bare Agent/Role/Brain documents from its stem directory. */
302
316
  export declare function loadConfig(configPath?: string, options?: {
303
317
  yamlMode?: YamlMode;
@@ -307,6 +321,8 @@ export declare function loadConfig(configPath?: string, options?: {
307
321
  temporary?: boolean;
308
322
  };
309
323
  skipWatchdogs?: boolean;
324
+ /** The one Owner invitation file that tunnel setup writes itself and that may therefore still be absent. */
325
+ deferredOwnerInviteFile?: string;
310
326
  }): FleetConfig;
311
327
  /** Full read-only Agent resolution used by launch dry-runs and room plan validation. */
312
328
  export declare function resolveAgentDefinitionDryRun(configPath: string, id: string, definition: AgentDefinition): ResolvedRole;
@@ -331,10 +347,22 @@ export declare function resolvePermissions(defaults: unknown, role: Partial<Comm
331
347
  * malformed block so a typo fails loudly rather than silently disarming a monitor.
332
348
  * Exported so temp-spawn (which builds a ResolvedRole by hand) resolves identically.
333
349
  */
350
+ /**
351
+ * A monitor without an explicit `interrupt` steers wakes at the next tool
352
+ * boundary. Hermes cannot, so its default stays non-interrupting; sessions
353
+ * without tool-boundary steering fall back to non-cancelling delivery.
354
+ */
355
+ export declare function defaultMonitorInterrupt(harness: string | undefined): MonitorInterrupt;
356
+ /**
357
+ * Resolve a persisted temporary-role monitor. A snapshot written before
358
+ * monitor.interrupt existed keeps the non-interrupting delivery it ran with.
359
+ */
360
+ export declare function resolveSnapshotMonitor(monitor: unknown): MonitorConfig;
334
361
  export declare function resolveMonitorConfig(defMonitor: unknown, roleMonitor?: Partial<MonitorConfig>, labels?: {
335
362
  base?: string;
336
363
  file?: string;
337
364
  name?: string;
365
+ harness?: string;
338
366
  }): MonitorConfig;
339
367
  export declare function findRole(cfg: FleetConfig, name: string): ResolvedRole;
340
368
  export {};
package/dist/config.js CHANGED
@@ -24,6 +24,11 @@ export const NOTIFY_EVENT_TYPES = [
24
24
  'message_received', 'file_received', 'sibling_contact_added', 'local_contact_request',
25
25
  'pending_message', 'contact_restored', 'inbound_error', 'state_import_failed',
26
26
  ];
27
+ /**
28
+ * Workflows Fleet prepares native harness execution policy for. Declared per
29
+ * Agent; never inferred from a role's name or persona, and never inherited.
30
+ */
31
+ export const MANAGED_CLI_WORKFLOW_IDS = ['task-workflow'];
27
32
  /** Default wake sources when a role does not list its own. */
28
33
  export const DEFAULT_WAKE_SOURCES = ['message_received', 'file_received', 'local_contact_request', 'pending_message'];
29
34
  const MONITOR_KEYS = [
@@ -139,7 +144,7 @@ export const ROLE_NAME_RE = /^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/;
139
144
  const ROLE_KEYS = [
140
145
  'harness', 'session', 'session_options', 'permissions', 'identity', 'cwd', 'coordinator', 'mission', 'persona', 'bio',
141
146
  'briefing_file', 'model', 'effort', 'model_chain', 'max_tokens', 'autocompact_pct', 'env', 'oversee', 'harness_options',
142
- 'isolation', 'monitor', 'owner_channel', 'worklog', 'auth_proxy',
147
+ 'isolation', 'monitor', 'owner_channel', 'worklog', 'auth_proxy', 'managed_cli',
143
148
  ];
144
149
  export const ROLE_PRESET_KEYS = ['mission', 'persona', 'bio', 'briefing_file'];
145
150
  export const BRAIN_PRESET_KEYS = [
@@ -148,7 +153,7 @@ export const BRAIN_PRESET_KEYS = [
148
153
  ];
149
154
  export const AGENT_KEYS = [
150
155
  'role', 'brain', 'permissions', 'identity', 'cwd', 'coordinator', 'env', 'oversee',
151
- 'isolation', 'monitor', 'owner_channel', 'worklog', 'auth_proxy', 'loops',
156
+ 'isolation', 'monitor', 'owner_channel', 'worklog', 'auth_proxy', 'loops', 'managed_cli',
152
157
  ];
153
158
  const AGENT_INSTANCE_KEYS = ['template', 'overrides'];
154
159
  const TEMPLATE_FORBIDDEN_KEYS = ['identity'];
@@ -236,6 +241,12 @@ function validateAgentScalars(value, file, id) {
236
241
  if (current !== undefined && (typeof current !== 'string' || !current.trim()))
237
242
  schemaError(file, `/agents/${id}/${key}`, 'must be a non-blank string');
238
243
  }
244
+ if (value.managed_cli !== undefined) {
245
+ const ids = value.managed_cli;
246
+ if (!Array.isArray(ids) || ids.some(entry => !MANAGED_CLI_WORKFLOW_IDS.includes(entry))
247
+ || new Set(ids).size !== ids.length)
248
+ schemaError(file, `/agents/${id}/managed_cli`, `must be a list of distinct workflow IDs; supported: ${MANAGED_CLI_WORKFLOW_IDS.join(', ')}`);
249
+ }
239
250
  if (value.coordinator !== undefined
240
251
  && (typeof value.coordinator !== 'string' || !ROLE_NAME_RE.test(value.coordinator)))
241
252
  schemaError(file, `/agents/${id}/coordinator`, 'must be a valid Agent ID');
@@ -575,6 +586,13 @@ function deepSub(v, vars) {
575
586
  return Object.fromEntries(Object.entries(v).map(([k, x]) => [k, deepSub(x, vars)]));
576
587
  return v;
577
588
  }
589
+ /** Read only room connection/owner settings for an existing layout. */
590
+ export function loadRoomsConfig(configPath = defaultConfigPath()) {
591
+ assertTrustedPath(configPath, 'file');
592
+ const doc = parseFleetDocument(configPath, readFileSync(configPath, 'utf8')).value;
593
+ const vars = (doc.vars ?? {});
594
+ return validateRoomsConfig(deepSub(doc.rooms, vars), vars, configPath);
595
+ }
578
596
  /** Load a v2 manifest and bare Agent/Role/Brain documents from its stem directory. */
579
597
  export function loadConfig(configPath, options = {}) {
580
598
  const base = configPath ?? defaultConfigPath();
@@ -649,11 +667,11 @@ export function loadConfig(configPath, options = {}) {
649
667
  if (problems.length)
650
668
  throw new ConfigError(`${file}: role '${name}' ${problems.join('; ')}`);
651
669
  }
652
- const monitor = resolveMonitorConfig(defaults.monitor, r.monitor, { base, file, name });
670
+ const harness = r.harness ?? defaults.harness ?? 'claude-code';
671
+ const monitor = resolveMonitorConfig(defaults.monitor, r.monitor, { base, file, name, harness });
653
672
  const ownerChannel = resolveOwnerChannelConfig(defaults.owner_channel, r.owner_channel, session, file, name);
654
673
  const worklog = resolveWorklogPolicy(defaults.worklog, r.worklog, file, name);
655
674
  const authProxy = resolveAuthProxy(defaults.auth_proxy, r.auth_proxy, file, name);
656
- const harness = r.harness ?? defaults.harness ?? 'claude-code';
657
675
  if (session === 'codex-app-server' && harness !== 'codex')
658
676
  throw new ConfigError(`${file}: role '${name}' session: codex-app-server requires harness: codex`);
659
677
  const defaultHarness = defaults.harness ?? 'claude-code';
@@ -757,7 +775,7 @@ export function loadConfig(configPath, options = {}) {
757
775
  if (doc.rooms !== undefined) {
758
776
  if (rooms)
759
777
  throw new ConfigError(`rooms: defined in multiple files; last: ${file}`);
760
- const validated = validateRoomsConfig(deepSub(doc.rooms, vars), vars, file);
778
+ const validated = validateRoomsConfig(deepSub(doc.rooms, vars), vars, file, options.deferredOwnerInviteFile);
761
779
  ownerInviteFingerprint = validated._invite?.fingerprint;
762
780
  ownerInvite = validated._invite?.value;
763
781
  const { _invite: _, ...clean } = validated;
@@ -1135,6 +1153,23 @@ function resolveStartStaggerMs(raw, base) {
1135
1153
  * malformed block so a typo fails loudly rather than silently disarming a monitor.
1136
1154
  * Exported so temp-spawn (which builds a ResolvedRole by hand) resolves identically.
1137
1155
  */
1156
+ /**
1157
+ * A monitor without an explicit `interrupt` steers wakes at the next tool
1158
+ * boundary. Hermes cannot, so its default stays non-interrupting; sessions
1159
+ * without tool-boundary steering fall back to non-cancelling delivery.
1160
+ */
1161
+ export function defaultMonitorInterrupt(harness) {
1162
+ return harness === 'hermes' ? false : 'after_tool';
1163
+ }
1164
+ /**
1165
+ * Resolve a persisted temporary-role monitor. A snapshot written before
1166
+ * monitor.interrupt existed keeps the non-interrupting delivery it ran with.
1167
+ */
1168
+ export function resolveSnapshotMonitor(monitor) {
1169
+ if (!isPlainObject(monitor))
1170
+ return resolveMonitorConfig(undefined, monitor);
1171
+ return resolveMonitorConfig(undefined, { interrupt: false, ...monitor });
1172
+ }
1138
1173
  export function resolveMonitorConfig(defMonitor, roleMonitor, labels = {}) {
1139
1174
  const where = labels.file && labels.name ? `${labels.file}: role '${labels.name}' ` : '';
1140
1175
  if (defMonitor !== undefined && !isPlainObject(defMonitor))
@@ -1169,7 +1204,7 @@ export function resolveMonitorConfig(defMonitor, roleMonitor, labels = {}) {
1169
1204
  wake_sources: merged.wake_sources ?? [...DEFAULT_WAKE_SOURCES],
1170
1205
  batch_ms: merged.batch_ms ?? MONITOR_DEFAULT_BATCH_MS,
1171
1206
  inject: merged.inject ?? 'notification',
1172
- interrupt: merged.interrupt ?? false,
1207
+ interrupt: merged.interrupt ?? defaultMonitorInterrupt(labels.harness),
1173
1208
  ...(merged.stall_recovery !== undefined ? { stall_recovery: merged.stall_recovery } : {}),
1174
1209
  ...(merged.stall_timeout_ms !== undefined ? { stall_timeout_ms: merged.stall_timeout_ms } : {}),
1175
1210
  turn_fail_threshold: merged.turn_fail_threshold ?? MONITOR_DEFAULT_TURN_FAIL_THRESHOLD,
package/dist/creation.js CHANGED
@@ -213,10 +213,10 @@ export async function ensureIdentity(name, profile, provisioner, log = () => { }
213
213
  export function daemonIdentityProvisioner(env = process.env, attachClient = attachOursClient, options = {}) {
214
214
  const connect = (leaseToken) => {
215
215
  const profile = readClientProfile(env);
216
- return attachClient(profile ? {
216
+ return attachClient({
217
217
  endpoint: profile.endpoint, expectedInstanceId: profile.expectedInstanceId,
218
218
  credentialPath: profile.credentialPath, sessionMode: 'external', leaseToken, env: {},
219
- } : { env, leaseToken, clientPid: process.pid });
219
+ });
220
220
  };
221
221
  const provisioner = {
222
222
  async exists(name) {
@@ -34,14 +34,7 @@ interface GenerationProbeDeps {
34
34
  canonicalize?(path: string): string;
35
35
  attachClient?(options: AttachOursClientOptions): Pick<OursClient, 'version' | 'identities' | 'close'> | Promise<Pick<OursClient, 'version' | 'identities' | 'close'>>;
36
36
  }
37
- /**
38
- * Corroborate the loopback daemon's unauthenticated `/info`, its credentialed
39
- * identity-index readiness route, and its local boot-generation record. The
40
- * index enforces auth when the daemon visibility requires it; open visibility
41
- * deliberately does not. No source is sufficient
42
- * alone: `/info` has no boot id, the identity index has no generation, and a
43
- * stale `ready` file can outlive the process that wrote it.
44
- */
37
+ /** Read generation and readiness through the selected gateway. Server paths are opaque. */
45
38
  export declare function probeDaemonGeneration(fetch: FetchLike, env: NodeJS.ProcessEnv, deps?: GenerationProbeDeps): Promise<DaemonGenerationProbe>;
46
39
  export declare class DaemonGenerationObserver {
47
40
  private current?;
@@ -1,8 +1,7 @@
1
- import { readFileSync, realpathSync } from 'node:fs';
2
- import { join, resolve } from 'node:path';
1
+ import { readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
3
  import { createHash, randomUUID } from 'node:crypto';
4
4
  import { attachOursClient, } from '@ours.network/sdk/client';
5
- import { readDaemonConfig, resolveEndpoint } from './monitor.js';
6
5
  import { readClientProfile } from './client-profile.js';
7
6
  import { replaceFileAtomically } from './atomic-file.js';
8
7
  export const DAEMON_RECOVERY_MAX_ATTEMPTS = 6;
@@ -39,129 +38,25 @@ function startupProgress(value) {
39
38
  return undefined;
40
39
  return row;
41
40
  }
42
- function canonical(path, deps) {
43
- const absolute = resolve(path);
44
- return deps.canonicalize?.(absolute) ?? realpathSync.native(absolute);
45
- }
46
- /**
47
- * Corroborate the loopback daemon's unauthenticated `/info`, its credentialed
48
- * identity-index readiness route, and its local boot-generation record. The
49
- * index enforces auth when the daemon visibility requires it; open visibility
50
- * deliberately does not. No source is sufficient
51
- * alone: `/info` has no boot id, the identity index has no generation, and a
52
- * stale `ready` file can outlive the process that wrote it.
53
- */
41
+ /** Read generation and readiness through the selected gateway. Server paths are opaque. */
54
42
  export async function probeDaemonGeneration(fetch, env, deps = {}) {
55
- const profile = readClientProfile(env);
56
- if (profile || env.OURS_DAEMON_ID)
57
- return probeSelectedDaemon(env, deps, profile);
58
- const endpoint = resolveEndpoint(env);
59
- let response;
60
- try {
61
- response = await fetch(`${endpoint.origin}/info`, { headers: endpoint.headers });
62
- }
63
- catch {
64
- return { state: 'unavailable', reason: 'DAEMON_INFO_UNREACHABLE' };
65
- }
66
- if (!response.ok)
67
- return {
68
- state: 'unavailable',
69
- reason: response.status === 401 ? 'DAEMON_INFO_UNAUTHORIZED' : 'DAEMON_INFO_HTTP_ERROR',
70
- };
71
- let info;
72
- try {
73
- const value = await response.json();
74
- if (!value || typeof value !== 'object' || Array.isArray(value))
75
- throw new Error('shape');
76
- info = value;
77
- }
78
- catch {
79
- return { state: 'unavailable', reason: 'DAEMON_INFO_INVALID' };
80
- }
81
- if (info.name !== 'ours' || !positiveInteger(info.pid)
82
- || typeof info.stateDir !== 'string' || info.stateDir.length === 0)
83
- return { state: 'unavailable', reason: 'DAEMON_INFO_INVALID' };
84
- let reportedStateDir;
85
- let expectedStateDir;
86
- try {
87
- reportedStateDir = canonical(info.stateDir, deps);
88
- expectedStateDir = canonical(endpoint.stateDir, deps);
89
- }
90
- catch {
91
- return { state: 'unavailable', reason: 'DAEMON_STATE_DIR_UNAVAILABLE' };
92
- }
93
- if (reportedStateDir !== expectedStateDir)
94
- return { state: 'unavailable', reason: 'DAEMON_STATE_DIR_MISMATCH' };
95
- // `/identities` is the daemon's credential-capable readiness route. A
96
- // valid-but-empty index is a documented intermediate startup state, never
97
- // proof of readiness.
98
- let identitiesResponse;
99
- try {
100
- identitiesResponse = await fetch(`${endpoint.origin}/identities`, { headers: endpoint.headers });
101
- }
102
- catch {
103
- return { state: 'unavailable', reason: 'DAEMON_IDENTITIES_UNREACHABLE' };
104
- }
105
- if (!identitiesResponse.ok)
106
- return {
107
- state: 'unavailable',
108
- reason: identitiesResponse.status === 401
109
- ? 'DAEMON_IDENTITIES_UNAUTHORIZED' : 'DAEMON_IDENTITIES_HTTP_ERROR',
110
- };
111
- try {
112
- const body = await identitiesResponse.json();
113
- if (!body || typeof body !== 'object' || Array.isArray(body)
114
- || !Array.isArray(body.identities))
115
- return { state: 'unavailable', reason: 'DAEMON_IDENTITIES_INVALID' };
116
- if (body.identities.length === 0)
117
- return { state: 'unavailable', reason: 'DAEMON_IDENTITIES_NOT_READY' };
118
- }
119
- catch {
120
- return { state: 'unavailable', reason: 'DAEMON_IDENTITIES_INVALID' };
121
- }
122
- let progress;
123
43
  try {
124
- const text = deps.readText?.(join(reportedStateDir, 'startup-progress.json'))
125
- ?? readFileSync(join(reportedStateDir, 'startup-progress.json'), 'utf8');
126
- progress = startupProgress(JSON.parse(text));
44
+ return await probeSelectedDaemon(deps, readClientProfile(env));
127
45
  }
128
46
  catch {
129
- return { state: 'unavailable', reason: 'DAEMON_PROGRESS_UNAVAILABLE' };
47
+ return { state: 'unavailable', reason: 'CLIENT_GATEWAY_PROFILE_INVALID' };
130
48
  }
131
- if (!progress)
132
- return { state: 'unavailable', reason: 'DAEMON_PROGRESS_INVALID' };
133
- if (progress.phase !== 'ready')
134
- return { state: 'unavailable', reason: 'DAEMON_PROGRESS_NOT_READY' };
135
- if (progress.pid !== info.pid)
136
- return { state: 'unavailable', reason: 'DAEMON_GENERATION_MISMATCH' };
137
- return {
138
- state: 'ready',
139
- generation: {
140
- // Keep bootId opaque. Its current writer uses `${pid}-${startedAt}`, but
141
- // equality—not its formatting—is the generation contract Fleet needs.
142
- bootId: progress.bootId,
143
- pid: progress.pid,
144
- startedAt: progress.startedAt,
145
- stateDir: reportedStateDir,
146
- },
147
- };
148
49
  }
149
50
  /** Container selection uses public SDK reads; daemon paths/PIDs remain opaque metadata. */
150
- async function probeSelectedDaemon(env, deps, profile) {
51
+ async function probeSelectedDaemon(deps, profile) {
151
52
  // Defer credential-file access to SDK selection, which checks the configured
152
53
  // daemon identity/capability before it reads or sends the credential.
153
- const endpoint = profile ? undefined : resolveEndpoint(env, false);
154
- const token = profile ? undefined : env.OURS_API_TOKEN?.trim() || readDaemonConfig(env).apiToken;
155
54
  let client;
156
55
  try {
157
- const options = profile ? {
56
+ const options = {
158
57
  endpoint: profile.endpoint, expectedInstanceId: profile.expectedInstanceId,
159
58
  credentialPath: profile.credentialPath,
160
59
  sessionMode: 'external', leaseToken: randomUUID(), env: {},
161
- } : {
162
- endpoint: endpoint.origin, expectedInstanceId: env.OURS_DAEMON_ID,
163
- sessionMode: 'external', leaseToken: randomUUID(),
164
- ...(token ? { token } : { credentialPath: join(endpoint.stateDir, 'daemon-token') }),
165
60
  };
166
61
  client = await (deps.attachClient?.(options) ?? attachOursClient(options));
167
62
  const info = await client.version({ startup: true });
@@ -189,7 +84,7 @@ async function probeSelectedDaemon(env, deps, profile) {
189
84
  return { state: 'unavailable', reason: 'DAEMON_SELECTED_PROBE_UNAVAILABLE' };
190
85
  }
191
86
  finally {
192
- await client?.close();
87
+ await client?.close().catch(() => undefined);
193
88
  }
194
89
  }
195
90
  export class DaemonGenerationObserver {