@ours.network/fleet 1.1.0-nightly.1 → 1.1.0-nightly.2

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 (50) hide show
  1. package/README.md +64 -73
  2. package/dist/application/role-creation-service.js +9 -2
  3. package/dist/application/role-removal-service.js +6 -3
  4. package/dist/application/types.d.ts +1 -1
  5. package/dist/briefing.js +2 -2
  6. package/dist/build-info.json +4 -4
  7. package/dist/cli.js +8 -5
  8. package/dist/config.d.ts +28 -2
  9. package/dist/config.js +307 -23
  10. package/dist/creation.d.ts +1 -1
  11. package/dist/creation.js +2 -2
  12. package/dist/docs.d.ts +1 -1
  13. package/dist/docs.js +31 -69
  14. package/dist/doctor.js +1 -1
  15. package/dist/generated-agent-source.d.ts +9 -0
  16. package/dist/generated-agent-source.js +53 -0
  17. package/dist/harness/agent-session.d.ts +13 -0
  18. package/dist/harness/claude-code-session.d.ts +4 -0
  19. package/dist/harness/claude-code-session.js +4 -0
  20. package/dist/harness/claude-code.js +10 -0
  21. package/dist/harness/codex-session.d.ts +4 -0
  22. package/dist/harness/codex-session.js +4 -0
  23. package/dist/harness/codex.js +14 -0
  24. package/dist/harness/registry.js +4 -0
  25. package/dist/init-guidance.d.ts +1 -0
  26. package/dist/init-guidance.js +5 -0
  27. package/dist/model-env.d.ts +0 -1
  28. package/dist/model-env.js +2 -4
  29. package/dist/ops.js +4 -2
  30. package/dist/resolved-plan.d.ts +3 -2
  31. package/dist/resolved-plan.js +21 -5
  32. package/dist/sensitive-config.d.ts +2 -0
  33. package/dist/sensitive-config.js +4 -0
  34. package/dist/spawn.d.ts +4 -4
  35. package/dist/spawn.js +52 -23
  36. package/dist/web/fleet-config-service.d.ts +11 -8
  37. package/dist/web/fleet-config-service.js +383 -183
  38. package/dist/web/topology-promote.js +17 -14
  39. package/dist/web/topology.js +2 -2
  40. package/dist/web/yaml-document-edit.js +2 -0
  41. package/dist/web-app/assets/index-BbE9EX6n.js +10 -0
  42. package/dist/web-app/index.html +1 -1
  43. package/examples/fleet/agents/Alice.yaml +11 -0
  44. package/examples/fleet/agents/FleetCoordinator.yaml +7 -0
  45. package/examples/fleet/brains/claude-default.yaml +5 -0
  46. package/examples/fleet/roles/coordinator.yaml +5 -0
  47. package/examples/fleet/roles/developer.yaml +3 -0
  48. package/examples/fleet.yaml +74 -0
  49. package/package.json +2 -1
  50. package/dist/web-app/assets/index-DhMDVRU1.js +0 -10
package/README.md CHANGED
@@ -19,10 +19,10 @@ turns such sessions into **roles**: long-lived agents that
19
19
  - can **spawn subagents** (permanent or temporary) and **oversee** them: peek into
20
20
  a ward's console, answer a stuck prompt, nudge it back to work.
21
21
 
22
- The whole fleet is described in one hand-written `~/fleet.yaml`
23
- ("docker-compose for agents"): who exists, what harness they run in, their mission,
24
- persona, bio, working directory, and who oversees whom. `ours-fleet up` makes
25
- reality match the file.
22
+ The fleet uses a v2 manifest plus typed documents: `~/fleet.yaml` holds
23
+ fleet-wide operational policy and automation, while `~/fleet/agents`,
24
+ `~/fleet/roles`, and `~/fleet/brains` hold bare Agent, Role, and Brain documents.
25
+ `ours-fleet up` makes reality match that trusted split source set.
26
26
 
27
27
  **Harness-agnostic by design.** The core never assumes a specific agent CLI; each
28
28
  harness is a small adapter (how to launch, how to resume, how to wire config).
@@ -31,17 +31,19 @@ public — each additional harness (Gemini CLI, OpenCode, …) is a small adapte
31
31
  A single fleet can mix harnesses per role:
32
32
 
33
33
  ```yaml
34
- roles:
35
- Reviewer: # runs in Claude Code
36
- harness: claude-code
37
- Prototyper: # runs in Codex CLI
38
- harness: codex
34
+ # ~/fleet/agents/Reviewer.yaml
35
+ role: { ref: reviewer }
36
+ brain: { inline: { harness: claude-code, session: acp } }
37
+
38
+ # ~/fleet/agents/Prototyper.yaml
39
+ role: { inline: { mission: Build prototypes } }
40
+ brain: { inline: { harness: codex, session: acp } }
39
41
  ```
40
42
 
41
43
  ## How it works
42
44
 
43
45
  ```
44
- ~/fleet.yaml + ~/fleet.d/*.yaml your declaration
46
+ ~/fleet.yaml + ~/fleet/{agents,roles,brains}/*.yaml your declaration
45
47
  │ ours-fleet up
46
48
  ▼
47
49
  briefing.md per role ──► agent session adapter ──► ACP client/session ──► ACP agent
@@ -105,8 +107,9 @@ become that account and repeat.
105
107
  ## Quickstart
106
108
 
107
109
  ```sh
110
+ cp -R "$(npm root -g)/@ours.network/fleet/examples/fleet" ~/fleet
108
111
  cp "$(npm root -g)/@ours.network/fleet/examples/fleet.yaml" ~/fleet.yaml
109
- $EDITOR ~/fleet.yaml # name your roles, missions, personas
112
+ $EDITOR ~/fleet/agents/*.yaml # compose Role + Brain and operational settings
110
113
  ours-fleet up # boot the fleet (staggered)
111
114
  ours-fleet ls # running consoles
112
115
  ours-fleet attach Alice # watch one live (Ctrl-b d to leave)
@@ -252,27 +255,19 @@ Security boundaries:
252
255
  - tests use temporary fleet homes and fake supervisors/identity providers—never
253
256
  active role state.
254
257
 
255
- Permanent spawns are written to `~/fleet.d/<Name>.yaml`; the CLI never edits your
256
- hand-written `~/fleet.yaml`. `ours-fleet rm <Name>` unspawns.
257
-
258
- The web console is the one writer that can touch the base file, and it saves the
259
- file as a whole document: its setup wizard and configuration editor may create,
260
- change or remove any top-level block, including `vars:`, `defaults:`, `roles:`,
261
- `watchdogs:` and `loops:`. (`defaults:`, `watchdogs:` and `loops:` can only live
262
- in the base file — a `~/fleet.d/*.yaml` drop-in may declare `roles:` and nothing
263
- else.) Top-level keys the console does not recognise are round-tripped untouched.
264
-
265
- Console edits are applied as surgical splices against the file's exact bytes, so
266
- an unchanged save is a byte-for-byte no-op and lines outside the edit keep their
267
- comments, spacing and quoting. One bounded exception: changing the *length* of a
268
- block sequence — adding or removing an entry under `watch:`, `oversee:`, `roles:`
269
- or `wake_sources:` — rewrites that one collection as a whole, which drops inline
270
- comments written on its individual items. The loss is confined to the collection
271
- you edited, is shown in the diff before anything is written, and can be declined
272
- by not saving. Each write is revision-guarded, reviewed as a diff of the real
273
- file, validated with the real loader, and preceded by a timestamped backup.
274
- Values under `env:` — and the `vars:` entries they interpolate — are masked in
275
- the diff and never leave the host.
258
+ Permanent spawns write a bare Agent document to
259
+ `~/fleet/agents/<Name>.yaml`; generated-source ownership is recorded so removal
260
+ never deletes an unproven hand-written neighbor. `ours-fleet rm <Name>` unspawns.
261
+
262
+ The web editor reads and writes the split document model explicitly as
263
+ `{manifest, agents}`. Role and Brain presets are visible through Agent refs but
264
+ remain read-only. Saves use one aggregate revision over the manifest and all
265
+ Agent/Role/Brain sources, validate an exact-stem private staging tree, show a
266
+ deterministic redacted diff per document, and hold one manifest-root lock.
267
+ Changed/deleted files are backed up together; any partial failure restores every
268
+ document or reports the private recovery directory. An unchanged save is a
269
+ byte-for-byte no-op and creates no backup. Nested environment, authentication,
270
+ invite, and isolation secrets never leave the host.
276
271
 
277
272
  From inside Claude Code, Codex, or Hermes with the core `ours` plugin installed,
278
273
  say **"spawn an ours agent …"**. The core skill checks for `ours-fleet`, installs
@@ -290,16 +285,16 @@ ours-fleet peek Worker # what is it doing?
290
285
  ours-fleet send Worker "continue with the tests, then report"
291
286
  ```
292
287
 
293
- Declare standing assignments in `fleet.yaml` (rendered into the overseer's
288
+ Declare standing assignments in the overseer's bare Agent document (rendered into its
294
289
  briefing) — or just write "keep an eye on Alice and Bob every 5 minutes" in a
295
290
  persona; the bundled `oversee-agents` skill defines what that means operationally:
296
291
 
297
292
  ```yaml
298
- roles:
299
- FleetCoordinator:
300
- oversee:
301
- - { role: Alice, interval: 5m }
302
- - { role: Bob, interval: 5m }
293
+ role: { ref: coordinator }
294
+ brain: { ref: claude-default }
295
+ oversee:
296
+ - { agent: Alice, interval: 5m }
297
+ - { agent: Bob, interval: 5m }
303
298
  ```
304
299
 
305
300
  ## Command reference
@@ -342,28 +337,30 @@ in its state dir) across supervisor-triggered restarts — systemd/launchd re-in
342
337
  agent process with no arguments, so without this the role would silently fall back to
343
338
  the default `~/fleet.yaml` on its very first crash-restart and fail to resolve.
344
339
 
345
- ## fleet.yaml reference
340
+ ## split configuration field reference
346
341
 
347
- ```yaml
342
+ The following is a schematic field inventory, not a single YAML document. Host
343
+ settings belong in `~/fleet.yaml`; reusable behavior and harness settings belong
344
+ in `~/fleet/roles/<id>.yaml` and `~/fleet/brains/<id>.yaml`; operational fields
345
+ belong in `~/fleet/agents/<id>.yaml`.
346
+
347
+ ```text
348
348
  vars: { work_root: /home/me/work } # ${var} substitution anywhere below
349
+ api_version: ours.network/fleet/v2
349
350
  start_stagger_ms: 0 # delay between agent LAUNCHES (host-wide, ms); 0 = no stagger
350
351
  defaults:
351
- harness: claude-code # for roles that don't set one
352
- session: acp # supported Fleet↔agent session path
353
352
  permissions: # common intent, translated by each harness/backend
354
353
  approval: ask # ask | auto | allow (`deny` is a deprecated alias)
355
354
  filesystem: workspace # read-only | workspace | unrestricted
356
355
  unattended: deny # deny | wait
357
- model: claude-fable-5 # default model for roles that don't set one (per-role model / --model wins)
358
- max_tokens: 500000 # session cap (harness-interpreted)
359
356
  monitor:
360
357
  mode: fleet # fleet (default) | native
361
358
  worklog: # built-ins shown; set false to opt out
362
359
  max_kb: 1024 # rotate only above this active-log size
363
360
  keep_tail_kb: 256 # UTF-8 tail; line-aligned when one fits
364
361
  max_archives: 12 # recent beside log; older preserved cold
365
- roles:
366
- Name: # [A-Za-z0-9_-]+
362
+ Agent document:
363
+ Name: # filename stem; [A-Za-z0-9_-]+
367
364
  harness: claude-code
368
365
  session: acp # optional; ACP is the only supported session
369
366
  session_options:
@@ -400,7 +397,7 @@ roles:
400
397
  bio: | # public card (published as bio)
401
398
  briefing_file: curated.md # replaces the generated narrative
402
399
  env: { KEY: value } # extra session env
403
- oversee: [{ role: X, interval: 5m }]
400
+ oversee: [{ agent: X, interval: 5m }]
404
401
  harness_options: # adapter-owned, adapter-validated
405
402
  plugins: { "name@marketplace": false } # claude-code: plugin overrides
406
403
  # mem_palace: false # claude-code: disable memory plugin
@@ -438,13 +435,12 @@ loops: # trusted local scheduled ACP turns
438
435
  If nothing material changed, complete silently without an owner report.
439
436
  ```
440
437
 
441
- Merge order: `fleet.yaml` ← `fleet.d/*.yaml`; a duplicate role name is a hard
442
- error naming both files. Identities and roles are decoupled — removing a role
443
- never deletes an identity. `session` is ACP-backed for both supported harnesses.
444
- `defaults.harness_options` is shallow-merged with each
445
- role's `harness_options`, so a fleet can set common Codex permission/profile defaults
446
- and override individual keys per role. `monitor` merges the same way — a role block
447
- overrides `defaults.monitor` key-by-key.
438
+ The v2 loader reads the manifest, then sorted bare Role, Brain, and Agent
439
+ documents from its exact stem directory. Duplicate filename-derived IDs are a
440
+ hard error. Identities and Agents are decoupled—removing an Agent never deletes
441
+ an identity. Role, Brain, and operational ownership are composed explicitly;
442
+ there is no generic cross-kind merge. Manifest operational defaults such as
443
+ `monitor` and `permissions` merge only within their owning Agent fields.
448
444
 
449
445
  Every supervised role is a client of the operator-configured ours daemon. Fleet strips the
450
446
  obsolete, presence-sensitive `OURS_AUTOSTART` variable from ACP harness
@@ -1037,27 +1033,22 @@ the structured CLI watcher, because a detached process cannot wake a Codex turn.
1037
1033
  wait is re-entered after each handled message; `ours-codex` instead wakes the idle
1038
1034
  session through its App Server integration.
1039
1035
 
1040
- The main Codex controls needed by fleet roles are available declaratively and when spawning:
1036
+ The main Codex controls are Brain-owned and also available when spawning:
1041
1037
 
1042
1038
  ```yaml
1043
- defaults:
1044
- harness: codex
1045
- model: gpt-5.4
1046
- harness_options:
1047
- launcher: auto
1048
- profile: fleet # $CODEX_HOME/fleet.config.toml
1049
- sandbox: workspace-write
1050
- approval: on-request
1051
- monitor: true # consent for Codex's native monitor; not the wake-owner selector
1052
- search: true
1053
- add_dirs: [/data/shared]
1054
- config:
1055
- model_reasoning_effort: high
1056
-
1057
- roles:
1058
- Reviewer:
1059
- harness_options:
1060
- sandbox: read-only # overrides just this default key
1039
+ # ~/fleet/brains/codex-review.yaml
1040
+ harness: codex
1041
+ session: acp
1042
+ model: gpt-5.4
1043
+ harness_options:
1044
+ launcher: auto
1045
+ profile: fleet
1046
+ sandbox: read-only
1047
+ approval: on-request
1048
+ monitor: true
1049
+ search: true
1050
+ add_dirs: [/data/shared]
1051
+ config: { model_reasoning_effort: high }
1061
1052
  ```
1062
1053
 
1063
1054
  Equivalent one-off/permanent spawn controls include `--model`, `--permission-mode`,
@@ -10,6 +10,7 @@ import { FleetError, normalizeError } from './errors.js';
10
10
  import { inheritCallerSpawnDefaults } from '../fleet-proxy.js';
11
11
  import { effectivePermissionMode } from '../permissions.js';
12
12
  import { effectiveRoleModel } from '../model-env.js';
13
+ import { getAdapter } from '../harness/registry.js';
13
14
  import { claudeModelCatalog, codexModelCatalog, } from './model-catalog.js';
14
15
  const canonical = (value) => {
15
16
  if (Array.isArray(value))
@@ -315,8 +316,14 @@ export class RoleCreationService {
315
316
  throw new FleetError('invalid_request', 'unsupported lifetime');
316
317
  bounded(input.model ?? undefined, 'model', 128);
317
318
  bounded(input.reasoningEffort ?? undefined, 'reasoning effort', 16);
318
- if (input.reasoningEffort != null && !['low', 'medium', 'high', 'xhigh', 'max', 'ultra'].includes(input.reasoningEffort))
319
- throw new FleetError('invalid_request', 'unsupported reasoning effort');
319
+ if (input.reasoningEffort != null) {
320
+ try {
321
+ getAdapter(input.harness).agentSession.resolveBrain({ effort: input.reasoningEffort });
322
+ }
323
+ catch (error) {
324
+ throw new FleetError('invalid_request', error.message);
325
+ }
326
+ }
320
327
  bounded(input.mission, 'mission', 4_096);
321
328
  bounded(input.coordinator, 'coordinator', 128);
322
329
  bounded(input.bio, 'bio', 8_192);
@@ -4,6 +4,7 @@ import { loadConfig, ROLE_NAME_RE } from '../config.js';
4
4
  import { agentDir, stateRoot } from '../paths.js';
5
5
  import { rmRole } from '../ops.js';
6
6
  import { FleetError } from './errors.js';
7
+ import { provesGeneratedAgentSource } from '../generated-agent-source.js';
7
8
  export class RoleRemovalService {
8
9
  options;
9
10
  constructor(options) {
@@ -45,8 +46,9 @@ export class RoleRemovalService {
45
46
  `Stop and uninstall the exact backend registration for '${requested}'.`,
46
47
  `Archive then remove ${lifetime === 'temporary' ? temporaryState : permanentState}.`,
47
48
  ];
48
- if (role?.sourceFile.includes('/fleet.d/'))
49
- effects.push(`Remove spawned configuration ${role.sourceFile}.`);
49
+ const generatedSource = role && provesGeneratedAgentSource(agentDir(requested), cfg.files[0], role.sourceFile);
50
+ if (generatedSource)
51
+ effects.push(`Remove generated Agent configuration ${role.sourceFile}.`);
50
52
  else if (role)
51
53
  effects.push(`Keep hand-written configuration ${role.sourceFile}; the role can be recreated from it.`);
52
54
  else
@@ -76,7 +78,8 @@ export class RoleRemovalService {
76
78
  mkdirSync(recoveryPath, { recursive: true, mode: 0o700 });
77
79
  if (existsSync(sourceState))
78
80
  cpSync(sourceState, join(recoveryPath, 'state'), { recursive: true });
79
- if (role?.sourceFile.includes('/fleet.d/') && existsSync(role.sourceFile))
81
+ if (role && provesGeneratedAgentSource(agentDir(preview.role), cfg.files[0], role.sourceFile)
82
+ && existsSync(role.sourceFile))
80
83
  cpSync(role.sourceFile, join(recoveryPath, basename(role.sourceFile)));
81
84
  if (role || preview.lifetime === 'temporary')
82
85
  await rmRole(cfg, preview.role, this.options.ops);
@@ -18,7 +18,7 @@ export interface ResolvedRoleView {
18
18
  sandbox?: string;
19
19
  };
20
20
  oversee?: Array<{
21
- role: string;
21
+ agent: string;
22
22
  interval: string;
23
23
  }>;
24
24
  }
package/dist/briefing.js CHANGED
@@ -196,12 +196,12 @@ export function generateBriefing(role, v, opts) {
196
196
  L.push('', '## Oversight assignments');
197
197
  L.push('These agents are your wards — you keep them unstuck:');
198
198
  for (const o of role.oversee)
199
- L.push(`- **${o.role}** — check every ${o.interval}`);
199
+ L.push(`- **${o.agent}** — check every ${o.interval}`);
200
200
  L.push('');
201
201
  L.push('Procedure (see also the oversee-agents skill if available). On each tick, for each ward');
202
202
  L.push('run BOTH — they answer different questions:');
203
203
  for (const o of role.oversee)
204
- L.push(`\`ours-fleet status ${o.role}\` then \`ours-fleet peek ${o.role}\``);
204
+ L.push(`\`ours-fleet status ${o.agent}\` then \`ours-fleet peek ${o.agent}\``);
205
205
  L.push('');
206
206
  L.push('**One console command is not a liveness verdict.** A `peek` or `send` that fails tells');
207
207
  L.push('you what happened to YOUR REQUEST, and only one of its outcomes says the agent is gone.');
@@ -1,9 +1,9 @@
1
1
  {
2
- "version": "1.1.0-nightly.1",
3
- "buildId": "bf3c406ba57c",
4
- "commit": "10d6da8bfd9903d216bce80a9ef89a42c282f8d9",
2
+ "version": "1.1.0-nightly.2",
3
+ "buildId": "da774e6be6b1",
4
+ "commit": "f8890b0449f4142a0c27a195e1351555fe599186",
5
5
  "dirty": true,
6
- "builtAt": "2026-08-30T07:44:27.551Z",
6
+ "builtAt": "2026-08-30T15:08:00.155Z",
7
7
  "capabilities": [
8
8
  "monitor.interrupt.after_tool"
9
9
  ]
package/dist/cli.js CHANGED
@@ -6,11 +6,12 @@ import { join as joinPath, resolve as resolvePath } from 'node:path';
6
6
  import { createInterface } from 'node:readline';
7
7
  import { Command } from 'commander';
8
8
  import { VERSION } from './version.js';
9
+ import { INIT_COMPLETION_GUIDANCE } from './init-guidance.js';
9
10
  import { analyzeInstalls, buildInfo, buildLabel, discoverInstalls, runningLabel, } from './provenance.js';
10
11
  import { agentDir, agentsRoot, tmpRoot, logsRoot, deriveXdgRuntimeDir } from './paths.js';
11
12
  import { findRole, loadConfig } from './config.js';
12
13
  import { formatDuration } from './duration.js';
13
- import { resolvedPlan } from './resolved-plan.js';
14
+ import { redactSensitive, resolvedPlan } from './resolved-plan.js';
14
15
  import { pickBackend } from './supervisor/index.js';
15
16
  import { up, down } from './ops.js';
16
17
  import { readRestartLedger, runSupervised, runTemp } from './runner.js';
@@ -76,7 +77,7 @@ const program = new Command()
76
77
  .description('Fleet of persistent, identity-bound AI agents — selectable harnesses over ACP sessions.')
77
78
  .enablePositionalOptions()
78
79
  .version(VERSION);
79
- const cOpt = (cmd) => cmd.option('-c, --configuration <file>', 'config file (default: ~/fleet.yaml + ~/fleet.d/)');
80
+ const cOpt = (cmd) => cmd.option('-c, --configuration <file>', 'manifest (default: ~/fleet.yaml; documents under ~/fleet/)');
80
81
  const collect = (value, previous) => [...previous, value];
81
82
  program.command('docs')
82
83
  .alias('man')
@@ -260,14 +261,16 @@ cOpt(program.command('config').description('validate + print the merged plan (no
260
261
  console.log(` cwd: ${r.cwd}`);
261
262
  if (r.model)
262
263
  console.log(` model: ${r.model}`);
264
+ if (r.effort)
265
+ console.log(` effort: ${r.effort}`);
263
266
  if (r.harness_options && Object.keys(r.harness_options).length)
264
- console.log(` options: ${JSON.stringify(r.harness_options)}`);
267
+ console.log(` options: ${JSON.stringify(redactSensitive(r.harness_options))}`);
265
268
  if (r.coordinator)
266
269
  console.log(` coordinator: ${r.coordinator}`);
267
270
  if (r.mission)
268
271
  console.log(` mission: ${r.mission.split('\n')[0]}`);
269
272
  if (r.oversee?.length)
270
- console.log(` oversees: ${r.oversee.map(o => `${o.role}@${o.interval}`).join(', ')}`);
273
+ console.log(` oversees: ${r.oversee.map(o => `${o.agent}@${o.interval}`).join(', ')}`);
271
274
  if (r.isolation) {
272
275
  const iso = r.isolation;
273
276
  const caps = [
@@ -1253,7 +1256,7 @@ program.command('init').description('one-time host setup (units, dirs, linger)')
1253
1256
  mkdirSync(d, { recursive: true });
1254
1257
  for (const m of await pickBackend().init(binPath))
1255
1258
  console.log(m);
1256
- console.log('\nNext: copy examples/fleet.yaml to ~/fleet.yaml, edit, then: ours-fleet up');
1259
+ console.log(INIT_COMPLETION_GUIDANCE);
1257
1260
  });
1258
1261
  const webCommand = cOpt(program.command('web').description('start or open the secure localhost fleet web console'))
1259
1262
  .enablePositionalOptions()
package/dist/config.d.ts CHANGED
@@ -3,7 +3,7 @@ import type { IsolationConfig, WrapContext } from './isolation/types.js';
3
3
  import type { ResolvedWatchdog } from './watchdog/config.js';
4
4
  import type { ResolvedLoop, ResolvedRoleLoop } from './loops/config.js';
5
5
  export interface OverseeEntry {
6
- role: string;
6
+ agent: string;
7
7
  interval: string;
8
8
  }
9
9
  export interface WorklogPolicy {
@@ -123,6 +123,8 @@ export interface RoleConfig {
123
123
  briefing_file?: string;
124
124
  /** Explicit null means use the selected harness's own default, bypassing fleet defaults. */
125
125
  model?: string | null;
126
+ /** Neutral Brain reasoning effort, retained for inspection after adapter translation. */
127
+ effort?: string;
126
128
  model_chain?: string[];
127
129
  max_tokens?: number;
128
130
  autocompact_pct?: number;
@@ -166,14 +168,37 @@ export interface ResolvedRole extends Omit<RoleConfig, 'model' | 'owner_channel'
166
168
  worklog?: WorklogPolicy;
167
169
  auth_proxy?: AuthProxyConfig;
168
170
  loops?: ResolvedRoleLoop[];
171
+ provenance?: Record<string, FieldProvenance>;
169
172
  /** Internal/transient: never accepted as a user-authored RoleConfig key. */
170
173
  roomMemberStartup?: RoomMemberStartup;
171
174
  }
175
+ export interface FieldProvenance {
176
+ sourceFile: string;
177
+ sourcePointer: string;
178
+ sourceKind: 'Agent' | 'Role' | 'Brain' | 'Manifest' | 'built-in';
179
+ sourceId?: string;
180
+ origin: 'explicit' | 'typed-default' | 'built-in';
181
+ viaReference?: {
182
+ sourcePointer: string;
183
+ kind: 'Role' | 'Brain';
184
+ id: string;
185
+ };
186
+ transforms: Array<{
187
+ kind: 'substitution' | 'adapter-normalization' | 'validation-default';
188
+ detail: string;
189
+ }>;
190
+ }
172
191
  export interface FleetConfig {
173
192
  roles: ResolvedRole[];
174
193
  vars: Record<string, string>;
175
194
  defaults: Record<string, unknown>;
176
195
  files: string[];
196
+ configMode?: 'split-v2';
197
+ sourceDocuments?: Array<{
198
+ kind: 'Manifest' | 'Agent' | 'Role' | 'Brain';
199
+ id?: string;
200
+ path: string;
201
+ }>;
177
202
  /** Fleet-wide delay (ms) enforced between agent launches to avoid boot bursts (0 = none). */
178
203
  startStaggerMs: number;
179
204
  /** Warning-first non-plain YAML migration diagnostics, in source order. */
@@ -204,7 +229,8 @@ export declare function resolveRoleModel(model: string | null | undefined, harne
204
229
  */
205
230
  export declare function isolationContextFor(role: ResolvedRole): WrapContext;
206
231
  export declare const ROLE_NAME_RE: RegExp;
207
- /** Load ~/fleet.yaml (or an explicit path) merged with ~/fleet.d/*.yaml drop-ins. */
232
+ export declare function splitRootFor(base: string): string;
233
+ /** Load a v2 manifest and bare Agent/Role/Brain documents from its stem directory. */
208
234
  export declare function loadConfig(configPath?: string, options?: {
209
235
  yamlMode?: YamlMode;
210
236
  }): FleetConfig;