@ours.network/fleet 1.0.5 → 1.1.0-nightly.10

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 (159) hide show
  1. package/README.md +122 -133
  2. package/dist/agent-recovery-gate.d.ts +17 -0
  3. package/dist/agent-recovery-gate.js +135 -0
  4. package/dist/application/capabilities.d.ts +0 -1
  5. package/dist/application/capabilities.js +3 -13
  6. package/dist/application/fleet-query-service.d.ts +0 -3
  7. package/dist/application/fleet-query-service.js +14 -25
  8. package/dist/application/role-creation-service.d.ts +5 -24
  9. package/dist/application/role-creation-service.js +27 -67
  10. package/dist/application/role-removal-service.js +6 -3
  11. package/dist/application/role-repository.d.ts +1 -2
  12. package/dist/application/role-repository.js +7 -7
  13. package/dist/application/session-control.d.ts +0 -12
  14. package/dist/application/session-control.js +0 -42
  15. package/dist/application/session-mutations.d.ts +5 -5
  16. package/dist/application/task-room-service.d.ts +1 -1
  17. package/dist/application/task-room-service.js +154 -16
  18. package/dist/application/types.d.ts +1 -8
  19. package/dist/briefing.js +12 -11
  20. package/dist/build-info.json +5 -5
  21. package/dist/cli.js +143 -63
  22. package/dist/config-yaml.d.ts +1 -1
  23. package/dist/config.d.ts +66 -6
  24. package/dist/config.js +398 -43
  25. package/dist/creation.d.ts +1 -1
  26. package/dist/creation.js +2 -2
  27. package/dist/daemon-recovery.d.ts +93 -0
  28. package/dist/daemon-recovery.js +328 -0
  29. package/dist/docs.d.ts +1 -1
  30. package/dist/docs.js +103 -127
  31. package/dist/doctor.js +18 -11
  32. package/dist/fleet-command-audit.d.ts +160 -0
  33. package/dist/fleet-command-audit.js +433 -0
  34. package/dist/fleet-proxy.d.ts +3 -1
  35. package/dist/fleet-proxy.js +30 -8
  36. package/dist/generated-agent-source.d.ts +9 -0
  37. package/dist/generated-agent-source.js +53 -0
  38. package/dist/harness/acp-session-transport.d.ts +8 -0
  39. package/dist/harness/acp-session-transport.js +3 -0
  40. package/dist/harness/agent-session.d.ts +51 -0
  41. package/dist/harness/agent-session.js +1 -0
  42. package/dist/harness/claude-code-session.d.ts +24 -0
  43. package/dist/harness/claude-code-session.js +36 -0
  44. package/dist/harness/claude-code.d.ts +2 -1
  45. package/dist/harness/claude-code.js +41 -81
  46. package/dist/harness/codex-session.d.ts +24 -0
  47. package/dist/harness/codex-session.js +36 -0
  48. package/dist/harness/codex.d.ts +2 -1
  49. package/dist/harness/codex.js +55 -45
  50. package/dist/harness/registry.js +4 -0
  51. package/dist/harness/types.d.ts +6 -38
  52. package/dist/index.d.ts +4 -6
  53. package/dist/index.js +2 -4
  54. package/dist/init-guidance.d.ts +1 -0
  55. package/dist/init-guidance.js +6 -0
  56. package/dist/isolation/resources.d.ts +3 -7
  57. package/dist/isolation/resources.js +3 -7
  58. package/dist/model-env.d.ts +4 -6
  59. package/dist/model-env.js +5 -8
  60. package/dist/monitor.d.ts +1 -24
  61. package/dist/monitor.js +5 -140
  62. package/dist/ops.js +4 -2
  63. package/dist/owner-channel/channel.d.ts +58 -2
  64. package/dist/owner-channel/channel.js +269 -29
  65. package/dist/owner-channel/commands.d.ts +2 -0
  66. package/dist/owner-channel/commands.js +31 -13
  67. package/dist/owner-channel/state.d.ts +2 -0
  68. package/dist/owner-channel/state.js +8 -2
  69. package/dist/preset-bootstrap.d.ts +10 -0
  70. package/dist/preset-bootstrap.js +85 -0
  71. package/dist/reports/artifact.d.ts +10 -0
  72. package/dist/reports/artifact.js +32 -0
  73. package/dist/reports/index.d.ts +5 -0
  74. package/dist/reports/index.js +5 -0
  75. package/dist/reports/render.d.ts +6 -0
  76. package/dist/reports/render.js +240 -0
  77. package/dist/reports/service.d.ts +42 -0
  78. package/dist/reports/service.js +57 -0
  79. package/dist/reports/tasks.d.ts +52 -0
  80. package/dist/reports/tasks.js +177 -0
  81. package/dist/reports/types.d.ts +315 -0
  82. package/dist/reports/types.js +6 -0
  83. package/dist/resolved-plan.d.ts +3 -2
  84. package/dist/resolved-plan.js +21 -5
  85. package/dist/rooms-tasks/cli.js +225 -40
  86. package/dist/rooms-tasks/config.js +24 -13
  87. package/dist/rooms-tasks/external-worker.d.ts +1 -0
  88. package/dist/rooms-tasks/external-worker.js +14 -9
  89. package/dist/rooms-tasks/provision.js +98 -36
  90. package/dist/rooms-tasks/templates.d.ts +0 -1
  91. package/dist/rooms-tasks/templates.js +9 -76
  92. package/dist/rooms-tasks/types.d.ts +10 -16
  93. package/dist/rooms-tasks/types.js +1 -4
  94. package/dist/runner.d.ts +13 -16
  95. package/dist/runner.js +110 -122
  96. package/dist/sensitive-config.d.ts +2 -0
  97. package/dist/sensitive-config.js +4 -0
  98. package/dist/session/acp.d.ts +7 -2
  99. package/dist/session/acp.js +29 -6
  100. package/dist/session/activity.d.ts +2 -3
  101. package/dist/session/activity.js +2 -3
  102. package/dist/session/arbiter.d.ts +4 -4
  103. package/dist/session/control.d.ts +17 -4
  104. package/dist/session/control.js +35 -0
  105. package/dist/session/types.d.ts +11 -5
  106. package/dist/spawn.d.ts +8 -33
  107. package/dist/spawn.js +53 -213
  108. package/dist/supervisor/none.d.ts +1 -1
  109. package/dist/supervisor/none.js +2 -2
  110. package/dist/temp-lifecycle.d.ts +1 -1
  111. package/dist/tmux.d.ts +1 -14
  112. package/dist/tmux.js +1 -51
  113. package/dist/watchdog/config.d.ts +10 -7
  114. package/dist/watchdog/config.js +30 -18
  115. package/dist/watchdog/run.js +12 -36
  116. package/dist/web/auth.d.ts +1 -1
  117. package/dist/web/fleet-config-service.d.ts +11 -8
  118. package/dist/web/fleet-config-service.js +384 -183
  119. package/dist/web/runtime.js +4 -15
  120. package/dist/web/server.d.ts +1 -3
  121. package/dist/web/server.js +3 -14
  122. package/dist/web/topology-promote.js +26 -19
  123. package/dist/web/topology.js +2 -2
  124. package/dist/web/yaml-document-edit.js +2 -0
  125. package/dist/web-app/assets/index-59GF-gsZ.css +1 -0
  126. package/dist/web-app/assets/index-BbTc5KtZ.js +10 -0
  127. package/dist/web-app/index.html +2 -2
  128. package/examples/fleet/agents/Alice.yaml +11 -0
  129. package/examples/fleet/agents/FleetCoordinator.yaml +7 -0
  130. package/examples/fleet/brains/claude-default.yaml +5 -0
  131. package/examples/fleet/roles/coordinator.yaml +5 -0
  132. package/examples/fleet/roles/developer.yaml +3 -0
  133. package/examples/fleet.yaml +74 -0
  134. package/package.json +5 -8
  135. package/presets/fleet/agents/Agent.yaml +3 -0
  136. package/presets/fleet/agents/Architect.yaml +3 -0
  137. package/presets/fleet/agents/Critic.yaml +3 -0
  138. package/presets/fleet/agents/Developer.yaml +3 -0
  139. package/presets/fleet/agents/Secretary.yaml +3 -0
  140. package/presets/fleet/agents/Tester.yaml +3 -0
  141. package/presets/fleet/brains/claude-default.yaml +5 -0
  142. package/presets/fleet/roles/Agent.yaml +6 -0
  143. package/presets/fleet/roles/Architect.yaml +6 -0
  144. package/presets/fleet/roles/Critic.yaml +6 -0
  145. package/presets/fleet/roles/Developer.yaml +6 -0
  146. package/presets/fleet/roles/Secretary.yaml +6 -0
  147. package/presets/fleet/roles/Tester.yaml +6 -0
  148. package/presets/fleet/room_templates/pair.yaml +11 -0
  149. package/presets/fleet/room_templates/single.yaml +8 -0
  150. package/presets/fleet/room_templates/team.yaml +12 -0
  151. package/presets/fleet.yaml +7 -0
  152. package/presets/manifest.json +5 -0
  153. package/dist/session/tmux.d.ts +0 -28
  154. package/dist/session/tmux.js +0 -80
  155. package/dist/web/terminal/bridge.d.ts +0 -27
  156. package/dist/web/terminal/bridge.js +0 -317
  157. package/dist/web-app/assets/TerminalView-C_G1ID2P.js +0 -9
  158. package/dist/web-app/assets/index-BCBK78hw.js +0 -10
  159. package/dist/web-app/assets/index-DuC-xnX4.css +0 -1
package/dist/docs.js CHANGED
@@ -6,11 +6,11 @@
6
6
  */
7
7
  export const AI_DOCS = `# ours-fleet reference
8
8
 
9
- ours-fleet runs persistent or temporary, identity-bound AI roles. A role selects
10
- a harness independently from its session backend:
9
+ ours-fleet runs persistent or temporary, identity-bound AI roles through the
10
+ structured ACP session path:
11
11
 
12
12
  - harness: \`claude-code\` or \`codex\`
13
- - session: \`tmux\` (default) or \`acp\`
13
+ - session: \`acp\` (default and only supported value)
14
14
  - lifetime: permanent (supervised, restartable) or \`spawn --temp\`
15
15
 
16
16
  ## Discover and validate
@@ -23,24 +23,20 @@ ours-fleet doctor [-c FILE] [--harness codex|claude-code]
23
23
  ours-fleet version [--json] # build identity, capabilities, every install on PATH
24
24
  \`\`\`
25
25
 
26
- Default configuration is \`~/fleet.yaml\` plus sorted \`~/fleet.d/*.yaml\` role
27
- drop-ins. An explicit \`-c FILE\` replaces \`~/fleet.yaml\`; fleet.d still adds
28
- roles. Validate with \`config\` and \`doctor\` before starting or restarting.
29
-
30
- The CLI never writes the base file: \`spawn\` writes \`~/fleet.d/Name.yaml\`. The
31
- web console does write it, as a whole document — its setup wizard and
32
- configuration editor may create, change or remove any top-level block, including
33
- \`vars:\`, \`defaults:\`, \`roles:\`, \`watchdogs:\` and \`loops:\`. Only the base
34
- file may hold \`defaults:\`, \`watchdogs:\` and \`loops:\`; a fleet.d drop-in may
35
- declare \`roles:\` and nothing else. Unrecognised top-level keys are round-tripped
36
- untouched. Console edits are applied as surgical splices against the file's exact
37
- bytes, so an unchanged save is byte-identical and lines outside the edit keep their
38
- comments and spacing. One exception: changing the length of a block sequence
39
- (\`watch:\`, \`oversee:\`, \`roles:\`, \`wake_sources:\`) may replace that collection
40
- wholesale and drop inline comments written on its items; lines outside that
41
- collection remain byte-preserved. Each save is revision-guarded, reviewed as a diff
42
- of the real file before anything is written, validated by the real loader, and
43
- backed up next to the file first.
26
+ Configuration v2 is \`~/fleet.yaml\` plus typed bare documents under the exact
27
+ stem directories \`~/fleet/agents\`, \`~/fleet/roles\`, \`~/fleet/brains\`, and
28
+ \`~/fleet/room_templates\`.
29
+ The manifest owns fleet-wide operational defaults and automation; each Agent
30
+ selects one inline/ref Role and Brain and carries its operational fields.
31
+ Legacy top-level \`roles:\` and \`fleet.d\` are rejected. Validate the complete
32
+ trusted source set with \`config\` and \`doctor\` before starting or restarting.
33
+
34
+ Permanent \`spawn\` writes \`~/fleet/agents/Name.yaml\`. The web console edits an
35
+ explicit \`{manifest, agents}\` model while Role/Brain presets remain read-only.
36
+ Its aggregate revision includes every Agent/Role/Brain/Room-template source, previews a
37
+ redacted per-document diff in an exact-stem private staging tree, and saves under
38
+ one root lock with a private multi-file backup and full rollback. A no-op is
39
+ byte-identical and creates no backup.
44
40
 
45
41
  ## Build identity and install provenance
46
42
 
@@ -83,7 +79,7 @@ PATH order.
83
79
  ## Lifecycle and console commands
84
80
 
85
81
  \`\`\`sh
86
- ours-fleet init
82
+ ours-fleet init [-c FILE] # seed missing presets; never replace an existing file
87
83
  ours-fleet up|down [Name...]
88
84
  ours-fleet restart [Name...] # preserve/resume harness context
89
85
  ours-fleet force-restart [Name...] # fresh context; briefing is reloaded
@@ -91,15 +87,14 @@ ours-fleet ls
91
87
  ours-fleet status|peek|attach|logs Name
92
88
  ours-fleet logs -f Name
93
89
  ours-fleet send Name "prompt"
94
- ours-fleet send Name --key Enter # tmux only
95
90
  ours-fleet rm Name
96
91
  ours-fleet watchdog-report <name> [run-id] [--list] [--json]
97
92
  ours-fleet watchdog-run <name>
98
93
  \`\`\`
99
94
 
100
- \`peek\`, \`attach\`, and text \`send\` work with tmux and ACP. ACP attachment
101
- also accepts \`/permit <permission-id> <option-id>\`, \`/interrupt\`, and
102
- \`/detach\`. Raw \`--key\` input is tmux-only.
95
+ \`peek\`, \`attach\`, and text \`send\` use the structured agent session.
96
+ Attachment also accepts \`/permit <permission-id> <option-id>\`, \`/interrupt\`,
97
+ and \`/detach\`.
103
98
 
104
99
  ## Local web console
105
100
 
@@ -155,21 +150,19 @@ harness's own default.
155
150
  ## Spawn
156
151
 
157
152
  \`\`\`sh
158
- ours-fleet spawn [--temp] [Name | --role Name] \\
159
- --harness codex|claude-code --session tmux|acp \\
160
- --mission "one line" --cwd /absolute/path --identity Identity \\
161
- --coordinator Coordinator --model MODEL \\
153
+ ours-fleet spawn [--temp] [Name | --name Name] \\
154
+ --brain BRAIN_ID --role ROLE_ID \\
155
+ --cwd /absolute/path --identity Identity --coordinator Coordinator \\
162
156
  --approval ask|auto|allow \\
163
157
  --filesystem read-only|workspace|unrestricted \\
164
- --unattended deny|wait \\
165
- --bio-file /path/bio.md --persona-file /path/persona.md
158
+ --unattended deny|wait --isolation-file /path/isolation.yaml
166
159
  \`\`\`
167
160
 
168
- Permanent spawn writes \`~/fleet.d/Name.yaml\` and starts a supervised role.
161
+ Permanent spawn writes \`~/fleet/agents/Name.yaml\` and starts a supervised role.
169
162
  \`--temp\` writes active state under \`~/.ours-fleet/tmp\` and starts an independent
170
163
  transient supervisor (a collected systemd unit or submitted launchd job). It is
171
164
  not enabled across reboot and does not die when the role that spawned it restarts.
172
- Both lifetimes support \`--session acp\`. When a temporary role's bound identity
165
+ Brain definitions own the ACP session backend. When a temporary role's bound identity
173
166
  closes or its session ends, the supervisor, monitor and live roster entry retire
174
167
  together; state moves intact to \`~/.ours-fleet/recovery/temporary\` with a
175
168
  termination record. Failed launches use the same archive rather than deleting
@@ -201,88 +194,47 @@ only sustained authoritative absence closes the role. Unreachable, malformed, or
201
194
  valid-but-empty daemon indexes are ambiguous and reset closure debounce rather
202
195
  than becoming cleanup authority.
203
196
 
204
- Inside a managed ACP role, the same CLI automatically routes a real \`spawn\`
205
- through that role's authenticated supervisor control socket. \`--role Name\` is
206
- accepted as an alternative to the positional name, so a minimal delegated call
207
- is \`ours-fleet spawn --role DeveloperX --temp\`. The supervisor records the
208
- calling role, performs creation, and only after success sends a structured
209
- spawn notice through the caller's owner channel when one is configured.
210
-
211
- Omitted harness, session, working directory, coordinator, neutral permissions,
212
- fleet monitor policy, and (when the harness is unchanged) model inherit from the
213
- calling role. Explicit options always win. Selecting a different harness without
214
- \`--model\` leaves model selection to that harness/fleet defaults rather than
215
- copying an incompatible caller model. This automatic proxy is a convenience and
197
+ Inside a managed ACP role, public \`ours-fleet\` commands cross an authenticated
198
+ supervisor attribution boundary before Commander parsing. The original CLI remains
199
+ the executor inside the role's existing OS sandbox, and ordinary CLI validation is
200
+ the source of truth. Hidden worker entry points remain internal; public lifecycle and
201
+ operator commands are not restricted by the proxy.
202
+
203
+ Command invocation, raw argv, read-only work, validation failures, and generic
204
+ outcomes are never forwarded to the Owner-visible channel. Fleet announces only
205
+ confirmed Agent, Task, and Room lifecycle changes. Local diagnostics retain
206
+ structurally redacted command metadata. Lifecycle delivery uncertainty is logged,
207
+ never recursively announced, and never reruns or blindly retries an effect.
208
+ Room participant summaries describe creation and activation. Fleet has no public
209
+ post-create Room membership mutation, so it does not claim a separate membership event.
210
+
211
+ Omitted Brain and Role selections, working directory, coordinator, neutral permissions,
212
+ and fleet monitor policy inherit from the calling Agent. Explicit options always win.
213
+ Identity, mission/profile text, environment, owner routing, auth proxy, room startup,
214
+ isolation, worklog, and sensitive inline Brain values never inherit implicitly.
215
+ This automatic proxy is a convenience and
216
216
  attribution mechanism, not an isolation boundary: an unrestricted role can still
217
- invoke another binary path directly. Tmux roles and host/operator shells keep the
218
- ordinary direct CLI behavior.
217
+ invoke another binary path directly. Host/operator shells keep the ordinary direct
218
+ CLI behavior.
219
219
 
220
- Codex-specific spawn flags: \`--sandbox\`, \`--permission-mode\`, \`--launcher\`,
221
- \`--profile\`, \`--search\`, repeatable \`--codex-config key=value\`, repeatable
222
- \`--add-dir\`, and legacy \`--monitor\` (consent for the native Codex monitor,
223
- not the \`monitor.mode\` wake-owner selector). Run \`ours-fleet help spawn\` for
224
- exact values.
220
+ Brain owns harness, session, model, reasoning effort, token limits, and native harness
221
+ options. Removed runtime flags are rejected with migration guidance rather than silently
222
+ reinterpreted. A selection is a stable ID or an explicit \`inline:{...}\` mapping.
225
223
 
226
224
  ## fleet.yaml
227
225
 
228
226
  \`\`\`yaml
227
+ api_version: ours.network/fleet/v2
229
228
  vars:
230
229
  work_root: /home/me/work
231
230
  start_stagger_ms: 0
232
231
  defaults:
233
- harness: codex
234
- session: acp
235
- model: gpt-model-id
236
232
  permissions:
237
233
  approval: ask
238
234
  filesystem: workspace
239
235
  unattended: deny
240
236
  monitor:
241
237
  mode: fleet # fleet (default) | native
242
- roles:
243
- Coordinator:
244
- harness: codex
245
- session: acp
246
- identity: Coordinator
247
- cwd: \${work_root}/project
248
- mission: Coordinate work and delegate implementation.
249
- model: gpt-model-id
250
- permissions:
251
- approval: ask
252
- filesystem: workspace
253
- unattended: deny
254
- session_options: # advanced overrides; normally omit
255
- # acp:
256
- # command: [/custom/codex-acp, --flag]
257
- tmux:
258
- boot_grace_ms: 10000
259
- monitor:
260
- mode: fleet # fleet supervisor | native harness monitor
261
- interrupt: false # false queues; true cancels; after_tool steers at an ACP tool boundary
262
- wake_sources: [message_received, file_received, local_contact_request, pending_message]
263
- batch_ms: 2000
264
- inject: notification
265
- turn_fail_threshold: 3
266
- harness_options:
267
- launcher: auto
268
- sandbox: workspace-write
269
- approval: on-request
270
- search: false
271
- profile: fleet
272
- add_dirs: [/data/shared]
273
- config:
274
- model_reasoning_effort: high
275
- mcp_servers: # claude-code: per-role MCP servers, additive by default
276
- ours: { command: ours-mcp, args: [proxy] }
277
- mcp_servers_only: false # true = ONLY these; drops user/project/plugin servers
278
- bio: Public role card and when peers should engage it.
279
- persona: Local operating contract, boundaries, and escalation policy.
280
- briefing_file: /absolute/custom-briefing.md
281
- coordinator: AnotherCoordinator
282
- env:
283
- KEY: value
284
- oversee:
285
- - { role: Worker, interval: 5m }
286
238
  watchdogs:
287
239
  nightwatch: # [A-Za-z0-9_-], must not collide with a role name
288
240
  coordinator: FleetCoordinator # required — where alerts go
@@ -290,42 +242,69 @@ watchdogs:
290
242
  enabled: true # default true; false = configured but never scheduled
291
243
  interval: 10m # default 10m; 30s | 10m | 2h, minimum 1m
292
244
  watch: [Alice, CodexReviewer] # explicit lists are exact; omit for configured + live temp roles
293
- harness: claude-code # default: defaults.harness
294
- model: claude-fable-5 # default: same resolution rule roles use (resolveRoleModel)
295
- session: acp # default: defaults.session
245
+ agent: { ref: WatchdogAgent } # required: declared Agent ID, or canonical inline Agent definition
296
246
  identity: Watchdog-nightwatch # default: Watchdog-<name>
297
247
  timeout: 5m # default 5m; a run past this is killed and recorded as error
298
248
  keep_reports: 50 # default 50 reports retained per watchdog
299
249
  alert_cooldown: 60m # default 60m before the same finding alerts again
300
250
  prompt_file: /abs/extra.md # optional extra focus, APPENDED to the fixed contract
301
- isolation: # optional; omitted means no OS sandbox, like an ordinary role
302
- backend: bubblewrap # when present, the ordinary role isolation schema applies
303
- network: broker
304
- fs: { read: [/opt/watch-data] }
251
+ \`\`\`
252
+
253
+ An Agent is a separate bare document under \`~/fleet/agents/<ID>.yaml\`:
254
+
255
+ \`\`\`yaml
256
+ role: { inline: { mission: Coordinate work and delegate implementation. } }
257
+ brain: { inline: { harness: codex, session: acp, model: gpt-model-id } }
258
+ identity: Coordinator
259
+ cwd: \${work_root}/project
260
+ oversee: [{ agent: Worker, interval: 5m }]
305
261
  \`\`\`
306
262
 
307
263
  A watchdog observes and reports; it never restarts, stops, spawns, or removes a
308
264
  role, answers a pending permission, edits a workspace, or approves anything on
309
265
  the owner's behalf. \`watchdogs:\` may appear only in the base config
310
- (\`~/fleet.yaml\` or \`-c FILE\`), not in \`~/fleet.d/*.yaml\` drop-ins.
311
- Watchdogs are not isolated by default. An explicit watchdog \`isolation:\` block
312
- uses the same policy schema as a role and is applied unchanged; declare every
313
- extra filesystem access required by a custom prompt there.
266
+ (\`~/fleet.yaml\` or \`-c FILE\`); Agent/Role/Brain documents never own it.
267
+ The selected Agent owns Brain, Role, permissions, isolation, and every other
268
+ agent setting. Legacy watchdog \`harness\`, \`model\`, \`session\`, and
269
+ \`isolation\` fields fail with migration guidance.
314
270
  When \`watch:\` is omitted, each run watches the configured roles plus temporary
315
271
  fleet roles that are live when the run starts. An explicit \`watch:\` list is
316
272
  never augmented.
317
273
 
318
- Role values override defaults. \`\${name}\` substitutes entries from \`vars\`.
319
- Other role fields include \`max_tokens\`, \`autocompact_pct\`, and \`isolation\`.
274
+ Agent operational values override manifest operational defaults. Role and Brain
275
+ ownership never cross-merges. \`\${name}\` substitutes entries from \`vars\`.
276
+ Brain fields include \`max_tokens\` and \`autocompact_pct\`; isolation is Agent-owned.
320
277
  Use README.md for the complete isolation policy and resource-cap schema.
321
278
 
322
279
  Supervised roles connect to the operator-configured ours daemon; they do not own its
323
280
  lifecycle. Fleet strips the obsolete, presence-sensitive \`OURS_AUTOSTART\` variable from
324
- tmux and ACP children; \`ours-mcp proxy\` is client-only and never starts a daemon. Start
281
+ agent-session children; \`ours-mcp proxy\` is client-only and never starts a daemon. Start
325
282
  the shared daemon only through an explicit operator or installer/setup flow.
326
283
 
327
284
  ## Rooms and tasks
328
285
 
286
+ \`init\` materializes editable \`single\`, \`pair\`, and \`team\` Room templates plus
287
+ their exact-cased Agent, Role, and Brain presets. The command prints the packaged
288
+ preset revision and source directory. Inspect provenance and content before use:
289
+
290
+ \`\`\`sh
291
+ ours-fleet config [-c FILE]
292
+ ours-fleet template list [-c FILE]
293
+ ours-fleet template show team [-c FILE]
294
+ ours-fleet task create --title "Solo task" --template single [-c FILE]
295
+ ours-fleet task create --title "Reviewed change" --template pair [-c FILE]
296
+ ours-fleet task create --title "Phased delivery" --template team [-c FILE]
297
+ \`\`\`
298
+
299
+ An alternate manifest \`-c /path/custom.yaml\` uses \`/path/custom/\` as its split
300
+ root. Repeated init only fills missing files and never adopts a newer default.
301
+ For explicit adoption, copy one file from init's reported packaged source beside
302
+ the target as \`.new-default\`, inspect \`diff -u TARGET TARGET.new-default\`, then
303
+ replace TARGET yourself. Upgrades from hardcoded templates require one
304
+ \`ours-fleet init\` (or \`init -c FILE\`) migration pass. A manifest-level template
305
+ may shadow a same-named file only with \`override_builtin: true\` and a higher
306
+ version; this compatibility marker is deprecated and reported as a diagnostic.
307
+
329
308
  Rooms always use \`ours-cowork\`; there is no room-provider selector. Configure
330
309
  the cowork daemon connection and room owner directly:
331
310
 
@@ -496,9 +475,7 @@ Security meaning: \`ask\` maps to Codex \`untrusted\` and Claude \`default\`.
496
475
  \`auto\` selects Codex ACP \`agent\` (\`on-request\` + \`workspace-write\`) and
497
476
  Claude \`acceptEdits\`. \`approval: allow\` selects Codex ACP's fully
498
477
  non-interactive yolo mode, reported as \`agent-full-access\` (\`never\` +
499
- \`danger-full-access\`), and Claude \`bypassPermissions\`. Codex tmux retains
500
- independent approval and sandbox flags: \`auto\` is \`on-request\`, \`allow\`
501
- is \`never\`, and \`filesystem\` still selects the sandbox. These modes genuinely
478
+ \`danger-full-access\`), and Claude \`bypassPermissions\`. These modes genuinely
502
479
  permit the actions the role was authorized to take —
503
480
  \`dontAsk\` only suppresses the prompt while still refusing the action. Nothing
504
481
  other than an explicit \`allow\` becomes non-interactive. Legacy \`deny\` keeps
@@ -531,16 +508,15 @@ dontAsk, bypassPermissions), \`plugins\`, \`mem_palace\`,
531
508
  \`mcp_servers\` declares MCP servers for the role, in \`.mcp.json\`'s own shape
532
509
  (a map of name to \`{ command, args, env }\`, or \`{ type: http|sse, url,
533
510
  headers }\`). By default they are ADDED to whatever the OS user running the role
534
- already has configured, on both session types: tmux passes \`--mcp-config\`, and
535
- ACP sends them in \`session/new\`.
511
+ already has configured. The Claude Code adapter sends them in \`session/new\`.
536
512
 
537
513
  When \`mcp_servers\` is absent, Fleet sends ACP's protocol-required empty
538
514
  \`mcpServers\` array without an exclusive override, so the agent keeps its inherited
539
515
  servers. An explicitly empty configured set is different: Fleet preserves that intent
540
516
  through the bundled adapter's compatibility path and disables every inherited server.
541
517
 
542
- \`mcp_servers_only: true\` makes the declared set EXCLUSIVE — \`--strict-mcp-config\`
543
- on tmux, \`strictMcpConfig\` on ACP. It is all-or-nothing and it ignores every
518
+ \`mcp_servers_only: true\` makes the declared set EXCLUSIVE through
519
+ \`strictMcpConfig\`. It is all-or-nothing and it ignores every
544
520
  other MCP configuration: project \`.mcp.json\`, user settings, and **plugins**.
545
521
  The ours connector is normally installed as a plugin, so a strict role that does
546
522
  not re-declare it has no \`send_message\` and no \`get_messages\` — it cannot even
@@ -566,8 +542,8 @@ The maintained \`@agentclientprotocol/codex-acp\` and
566
542
  \`@agentclientprotocol/claude-agent-acp\` runtimes are bundled automatically as
567
543
  optional ours-fleet dependencies. The supervisor resolves their executable
568
544
  entrypoints internally, so default ACP roles do not depend on global PATH.
569
- The maintained Claude adapter requires Node 22; tmux and Codex ACP continue to
570
- work on the ours-fleet core minimum of Node 20.
545
+ The maintained Claude adapter requires Node 22; Codex ACP continues to work on
546
+ the ours-fleet core minimum of Node 20.
571
547
 
572
548
  Override an adapter only when necessary with \`session_options.acp.command\`
573
549
  (string or argv list). If optional dependencies were deliberately omitted,
@@ -581,7 +557,7 @@ ours-fleet falls back to a compatible globally installed \`codex-acp\` or
581
557
  - \`fleet\` (default): the ours-fleet supervisor consumes body-free daemon
582
558
  events and advances its durable cursor only after delivery is accepted. ACP
583
559
  uses live steering when supported and falls back to structured
584
- \`session/prompt\`; tmux uses verified console injection.
560
+ \`session/prompt\`.
585
561
  - \`native\`: ours-fleet starts no supervisor monitor; the generated briefing
586
562
  instructs Claude Code or Codex to arm its harness-native wake mechanism.
587
563
 
@@ -712,8 +688,8 @@ provenance checks.
712
688
  The channel identity must be unique and must not be a role identity. The bridge
713
689
  persists bounded wire IDs only, never message/reply plaintext, and requeues input
714
690
  before starting its turn for at-least-once crash recovery. It currently requires
715
- \`session: acp\`: tmux has no structured, turn-correlated final answer, and pane
716
- scraping cannot provide the same reliable reply guarantee.
691
+ the structured agent-session interface backed by ACP so correlated final replies
692
+ retain their delivery guarantee.
717
693
 
718
694
  ### Live contact and owner administration
719
695
 
@@ -761,7 +737,7 @@ contains bounded CIDs and audit actions only. Corruption disables all effective
761
737
  owners and refuses mutation rather than resurrecting authority; revoking the
762
738
  last effective owner is always refused.
763
739
 
764
- A missing/stopped role, tmux session, role without \`owner_channel\`, unavailable
740
+ A missing/stopped role, role without \`owner_channel\`, unavailable
765
741
  MCP client, or a role entering shutdown returns an actionable error with no
766
742
  side effects. Management uses no network listener and never logs or persists
767
743
  invite material.
package/dist/doctor.js CHANGED
@@ -13,6 +13,7 @@ import { authResolutionHint, resolveEndpoint, } from './monitor.js';
13
13
  import { readScheduledLoops, storedLoopHealth } from './loops/state.js';
14
14
  import { controlSocketPath } from './session/control.js';
15
15
  import { analyzeInstalls, buildInfo, buildLabel, discoverInstalls, BIN_NAME, } from './provenance.js';
16
+ import { readDaemonRecoveryStatus } from './daemon-recovery.js';
16
17
  /** Which cgroup-v2 controllers are delegated to this user manager (advisory). */
17
18
  function cgroupDelegationDetail() {
18
19
  try {
@@ -94,7 +95,7 @@ export async function doctor(opts = {}, exec = realExec, platform = process.plat
94
95
  const loaded = loadConfigResult(opts.configPath, opts.yamlMode);
95
96
  const roles = loaded.ok ? loaded.roles : [];
96
97
  checks.push(loaded.ok
97
- ? { name: 'config', ok: true, detail: loaded.files.join(' + ') || '(none — no fleet.yaml or fleet.d)' }
98
+ ? { name: 'config', ok: true, detail: loaded.files.join(' + ') }
98
99
  : { name: 'config', ok: false, detail: loaded.error });
99
100
  checks.push(loaded.ok
100
101
  ? { name: 'roles', ok: true, detail: `${roles.length} configured` }
@@ -106,6 +107,19 @@ export async function doctor(opts = {}, exec = realExec, platform = process.plat
106
107
  ok: true,
107
108
  detail: `warning: ${diagnostic.message}`,
108
109
  });
110
+ for (const role of roles) {
111
+ const recovery = readDaemonRecoveryStatus(agentDir(role.name));
112
+ if (!recovery)
113
+ continue;
114
+ const paths = ['agent', 'owner']
115
+ .map(name => `${name}=${recovery.paths[name].state}`)
116
+ .join(', ');
117
+ checks.push({
118
+ name: `daemon recovery: ${role.name}`,
119
+ ok: recovery.state === 'recovered',
120
+ detail: `${recovery.state}; ${paths}; epoch ${recovery.epoch || 'unavailable'}`,
121
+ });
122
+ }
109
123
  // ── Rooms/tasks checks ────────────────────────────────────────
110
124
  if (loaded.ok && loaded.rooms) {
111
125
  const rooms = loaded.rooms;
@@ -149,13 +163,13 @@ export async function doctor(opts = {}, exec = realExec, platform = process.plat
149
163
  const roleNames = new Set(roles.map(r => r.name));
150
164
  for (const t of templates) {
151
165
  const badRefs = t.members
152
- .filter(m => m.role_ref && !roleNames.has(m.role_ref))
153
- .map(m => m.role_ref);
166
+ .filter(m => 'ref' in m.agent && !roleNames.has(m.agent.ref))
167
+ .map(m => 'ref' in m.agent ? m.agent.ref : '');
154
168
  if (badRefs.length) {
155
169
  checks.push({
156
170
  name: `template: ${t.name}@${t.version}`,
157
171
  ok: true,
158
- detail: `warning: role_ref(s) ${badRefs.join(', ')} not found in configured roles`,
172
+ detail: `warning: Agent ref(s) ${badRefs.join(', ')} not found in configured Agents`,
159
173
  });
160
174
  }
161
175
  }
@@ -226,13 +240,6 @@ export async function doctor(opts = {}, exec = realExec, platform = process.plat
226
240
  }
227
241
  catch { /* state dir may not exist yet */ }
228
242
  }
229
- if (roles.length === 0 || roles.some(role => (role.session ?? 'tmux') === 'tmux')) {
230
- const tmux = await exec('tmux', ['-V']);
231
- checks.push({
232
- name: 'tmux', ok: tmux.code === 0,
233
- detail: tmux.code === 0 ? tmux.stdout.trim() : 'not found — apt install tmux / brew install tmux',
234
- });
235
- }
236
243
  try {
237
244
  const client = await attachDaemon({
238
245
  env: process.env,
@@ -0,0 +1,160 @@
1
+ export type FleetCommandDecision = 'allow' | 'deny' | 'unsupported';
2
+ export type FleetCommandOutcomeClass = 'success' | 'validation' | 'denied' | 'runtime' | 'timeout' | 'proxy' | 'delivery';
3
+ export declare class FleetCliExit extends Error {
4
+ readonly exitCode: number;
5
+ readonly outcomeClass: FleetCommandOutcomeClass;
6
+ readonly effect: 'not_started' | 'completed' | 'unknown';
7
+ constructor(exitCode: number, outcomeClass?: FleetCommandOutcomeClass, effect?: 'not_started' | 'completed' | 'unknown');
8
+ }
9
+ export type FleetAuditPresentation = {
10
+ kind: 'agent_started';
11
+ eventId: string;
12
+ id: string;
13
+ name: string;
14
+ lifetime: 'permanent' | 'temporary';
15
+ brain: string;
16
+ role: string;
17
+ harness: string;
18
+ session: 'acp';
19
+ model?: string;
20
+ permissions?: string;
21
+ parent: string;
22
+ actionId: string;
23
+ inherited: string[];
24
+ } | {
25
+ kind: 'task';
26
+ operation: 'create' | 'start' | 'work' | 'block' | 'unblock' | 'review' | 'done' | 'cancel' | 'finish' | 'delete' | 'settling';
27
+ eventId: string;
28
+ id: string;
29
+ title?: string;
30
+ previousState?: string;
31
+ newState: string;
32
+ template?: string;
33
+ roomId?: string;
34
+ revision?: string;
35
+ list?: string;
36
+ agents: Array<{
37
+ name: string;
38
+ brain?: string;
39
+ role: string;
40
+ permissions?: string;
41
+ }>;
42
+ } | {
43
+ kind: 'room';
44
+ operation: 'create' | 'activate' | 'close' | 'delete';
45
+ eventId: string;
46
+ id: string;
47
+ previousState?: string;
48
+ newState: string;
49
+ revision?: string;
50
+ name?: string;
51
+ template?: string;
52
+ taskId?: string;
53
+ participants: Array<{
54
+ name: string;
55
+ id?: string;
56
+ brain?: string;
57
+ role: string;
58
+ permissions?: string;
59
+ }>;
60
+ } | {
61
+ kind: 'lifecycle_failure';
62
+ eventId: string;
63
+ resource: 'Agent' | 'Task' | 'Room';
64
+ id: string;
65
+ state: string;
66
+ category: 'provision_failed' | 'provision_pending' | 'readiness_failed' | 'settlement_failed' | 'settlement_pending' | 'cleanup_failed' | 'cleanup_pending';
67
+ };
68
+ /** Commands are Owner-silent unless they record one of these semantic lifecycle kinds. */
69
+ export declare const FLEET_LIFECYCLE_EVENT_KINDS: readonly ["agent_started", "task", "room", "lifecycle_failure"];
70
+ /** Stable semantic dedupe basis: resource kind + stable resource ID + transition/action ID. */
71
+ export declare function lifecycleEventDigestBasis(value: FleetAuditPresentation): string;
72
+ export declare function beginFleetAuditCollection(): void;
73
+ export declare function recordFleetAuditResource(kind: 'agent' | 'task' | 'room', id: string | undefined): void;
74
+ type FleetAuditPresentationInput = FleetAuditPresentation extends infer T ? T extends FleetAuditPresentation ? Omit<T, 'eventId'> & {
75
+ eventId?: string;
76
+ } : never : never;
77
+ export declare function recordFleetAuditPresentation(value: FleetAuditPresentationInput): void;
78
+ export declare function recordFleetAuditFailure(failure: {
79
+ class: FleetCommandOutcomeClass;
80
+ effect: 'not_started' | 'completed' | 'unknown';
81
+ }): void;
82
+ export declare function consumeFleetAuditCollection(): {
83
+ resourceIds?: Record<string, string>;
84
+ presentations?: FleetAuditPresentation[];
85
+ failure?: {
86
+ class: FleetCommandOutcomeClass;
87
+ effect: 'not_started' | 'completed' | 'unknown';
88
+ };
89
+ };
90
+ export interface FleetCommandClassification {
91
+ command: string;
92
+ route: string;
93
+ decision: FleetCommandDecision;
94
+ }
95
+ export interface FleetAuditAttempt {
96
+ version: 1;
97
+ correlationId: string;
98
+ requestId: string;
99
+ caller: string;
100
+ invokedAt: string;
101
+ classification: FleetCommandClassification;
102
+ argv: string[];
103
+ invocation: 'sending' | 'delivered' | 'uncertain';
104
+ outcome?: {
105
+ completedAt: string;
106
+ class: FleetCommandOutcomeClass;
107
+ exitCode?: number;
108
+ effect: 'not_started' | 'completed' | 'unknown';
109
+ delivery: 'sending' | 'delivered' | 'uncertain';
110
+ resourceIds?: Record<string, string>;
111
+ presentations?: FleetAuditPresentation[];
112
+ };
113
+ }
114
+ export declare const fleetProxyCommandInventory: Readonly<{
115
+ [k: string]: string[];
116
+ }>;
117
+ export declare const fleetProxyTopLevelInventory: Readonly<{
118
+ safeRead: string[];
119
+ agent: string[];
120
+ public: string[];
121
+ denied: string[];
122
+ hidden: string[];
123
+ aliases: string[];
124
+ }>;
125
+ /** Classify before Commander parsing. Unknown and internal paths fail closed. */
126
+ export declare function classifyFleetArgv(argv: readonly string[]): FleetCommandClassification;
127
+ /** Faithful ordered argv with deterministic structural redaction. */
128
+ export declare function redactFleetArgv(argv: readonly string[]): string[];
129
+ /** Durable write-once attempt ledger; contains redacted argv only. */
130
+ export declare class FleetCommandAuditStore {
131
+ private readonly path;
132
+ private readonly deps;
133
+ private attempts;
134
+ constructor(path: string, deps?: {
135
+ now(): Date;
136
+ uuid(): string;
137
+ });
138
+ list(): readonly FleetAuditAttempt[];
139
+ begin(requestId: string, caller: string, argv: readonly string[]): FleetAuditAttempt;
140
+ invocation(correlationId: string, caller: string, delivery: 'delivered' | 'uncertain'): FleetAuditAttempt;
141
+ finish(correlationId: string, caller: string, outcome: Omit<NonNullable<FleetAuditAttempt['outcome']>, 'completedAt' | 'delivery'>): FleetAuditAttempt;
142
+ outcome(correlationId: string, caller: string, delivery: 'delivered' | 'uncertain'): FleetAuditAttempt;
143
+ private owned;
144
+ private persist;
145
+ }
146
+ export declare function validateFleetAuditBegin(value: unknown): asserts value is {
147
+ requestId: string;
148
+ argv: string[];
149
+ };
150
+ export declare function validateFleetAuditFinish(value: unknown): asserts value is {
151
+ correlationId: string;
152
+ class: FleetCommandOutcomeClass;
153
+ exitCode?: number;
154
+ effect: 'not_started' | 'completed' | 'unknown';
155
+ resourceIds?: Record<string, string>;
156
+ presentations?: FleetAuditPresentation[];
157
+ };
158
+ /** Compact Owner presentation. It intentionally has no command, argv, environment, or correlation data. */
159
+ export declare function renderFleetLifecycleEvent(value: FleetAuditPresentation): string;
160
+ export {};