@ours.network/fleet 1.0.5 → 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 (100) hide show
  1. package/README.md +90 -112
  2. package/dist/application/capabilities.d.ts +0 -1
  3. package/dist/application/capabilities.js +3 -13
  4. package/dist/application/fleet-query-service.d.ts +0 -3
  5. package/dist/application/fleet-query-service.js +1 -24
  6. package/dist/application/role-creation-service.d.ts +5 -5
  7. package/dist/application/role-creation-service.js +14 -9
  8. package/dist/application/role-removal-service.js +6 -3
  9. package/dist/application/role-repository.d.ts +1 -2
  10. package/dist/application/role-repository.js +7 -7
  11. package/dist/application/session-control.d.ts +0 -12
  12. package/dist/application/session-control.js +0 -42
  13. package/dist/application/session-mutations.d.ts +5 -5
  14. package/dist/application/types.d.ts +1 -8
  15. package/dist/briefing.js +3 -6
  16. package/dist/build-info.json +5 -5
  17. package/dist/cli.js +17 -28
  18. package/dist/config.d.ts +29 -6
  19. package/dist/config.js +320 -40
  20. package/dist/creation.d.ts +1 -1
  21. package/dist/creation.js +2 -2
  22. package/dist/docs.d.ts +1 -1
  23. package/dist/docs.js +51 -95
  24. package/dist/doctor.js +1 -8
  25. package/dist/fleet-proxy.d.ts +1 -1
  26. package/dist/generated-agent-source.d.ts +9 -0
  27. package/dist/generated-agent-source.js +53 -0
  28. package/dist/harness/acp-session-transport.d.ts +8 -0
  29. package/dist/harness/acp-session-transport.js +3 -0
  30. package/dist/harness/agent-session.d.ts +46 -0
  31. package/dist/harness/agent-session.js +1 -0
  32. package/dist/harness/claude-code-session.d.ts +22 -0
  33. package/dist/harness/claude-code-session.js +32 -0
  34. package/dist/harness/claude-code.d.ts +2 -1
  35. package/dist/harness/claude-code.js +39 -81
  36. package/dist/harness/codex-session.d.ts +22 -0
  37. package/dist/harness/codex-session.js +32 -0
  38. package/dist/harness/codex.d.ts +2 -1
  39. package/dist/harness/codex.js +49 -45
  40. package/dist/harness/registry.js +4 -0
  41. package/dist/harness/types.d.ts +6 -38
  42. package/dist/index.d.ts +3 -5
  43. package/dist/index.js +1 -3
  44. package/dist/init-guidance.d.ts +1 -0
  45. package/dist/init-guidance.js +5 -0
  46. package/dist/isolation/resources.d.ts +3 -7
  47. package/dist/isolation/resources.js +3 -7
  48. package/dist/model-env.d.ts +2 -4
  49. package/dist/model-env.js +4 -7
  50. package/dist/monitor.d.ts +1 -24
  51. package/dist/monitor.js +5 -140
  52. package/dist/ops.js +4 -2
  53. package/dist/owner-channel/channel.d.ts +2 -2
  54. package/dist/resolved-plan.d.ts +3 -2
  55. package/dist/resolved-plan.js +21 -5
  56. package/dist/runner.d.ts +5 -15
  57. package/dist/runner.js +26 -102
  58. package/dist/sensitive-config.d.ts +2 -0
  59. package/dist/sensitive-config.js +4 -0
  60. package/dist/session/acp.d.ts +2 -2
  61. package/dist/session/acp.js +2 -3
  62. package/dist/session/activity.d.ts +2 -3
  63. package/dist/session/activity.js +2 -3
  64. package/dist/session/arbiter.d.ts +4 -4
  65. package/dist/session/control.d.ts +3 -3
  66. package/dist/session/types.d.ts +11 -5
  67. package/dist/spawn.d.ts +4 -4
  68. package/dist/spawn.js +60 -32
  69. package/dist/supervisor/none.d.ts +1 -1
  70. package/dist/supervisor/none.js +2 -2
  71. package/dist/tmux.d.ts +1 -14
  72. package/dist/tmux.js +1 -51
  73. package/dist/watchdog/config.js +6 -4
  74. package/dist/watchdog/run.js +8 -23
  75. package/dist/web/auth.d.ts +1 -1
  76. package/dist/web/fleet-config-service.d.ts +11 -8
  77. package/dist/web/fleet-config-service.js +383 -183
  78. package/dist/web/runtime.js +4 -15
  79. package/dist/web/server.d.ts +1 -3
  80. package/dist/web/server.js +3 -14
  81. package/dist/web/topology-promote.js +18 -18
  82. package/dist/web/topology.js +2 -2
  83. package/dist/web/yaml-document-edit.js +2 -0
  84. package/dist/web-app/assets/index-59GF-gsZ.css +1 -0
  85. package/dist/web-app/assets/index-BbE9EX6n.js +10 -0
  86. package/dist/web-app/index.html +2 -2
  87. package/examples/fleet/agents/Alice.yaml +11 -0
  88. package/examples/fleet/agents/FleetCoordinator.yaml +7 -0
  89. package/examples/fleet/brains/claude-default.yaml +5 -0
  90. package/examples/fleet/roles/coordinator.yaml +5 -0
  91. package/examples/fleet/roles/developer.yaml +3 -0
  92. package/examples/fleet.yaml +74 -0
  93. package/package.json +4 -8
  94. package/dist/session/tmux.d.ts +0 -28
  95. package/dist/session/tmux.js +0 -80
  96. package/dist/web/terminal/bridge.d.ts +0 -27
  97. package/dist/web/terminal/bridge.js +0 -317
  98. package/dist/web-app/assets/TerminalView-C_G1ID2P.js +0 -9
  99. package/dist/web-app/assets/index-BCBK78hw.js +0 -10
  100. package/dist/web-app/assets/index-DuC-xnX4.css +0 -1
@@ -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);
@@ -3,9 +3,8 @@ export interface RoleRepositoryOptions {
3
3
  configPath?: string;
4
4
  permanentRoot?: string;
5
5
  temporaryRoot?: string;
6
- probeBackend?: (name: string, intended?: 'acp' | 'tmux') => Promise<{
6
+ probeBackend?: (name: string, intended?: 'acp') => Promise<{
7
7
  acp: boolean;
8
- tmux: boolean;
9
8
  }>;
10
9
  concurrency?: number;
11
10
  timeoutMs?: number;
@@ -42,7 +42,11 @@ function snapshot(path, name) {
42
42
  const raw = parse(readFileSync(path, 'utf8'));
43
43
  if (!raw || typeof raw !== 'object')
44
44
  throw new Error('snapshot must be a mapping');
45
- const session = raw.session === 'acp' || raw.session === 'tmux' ? raw.session : 'tmux';
45
+ if (raw.session === 'tmux')
46
+ throw new Error('session: tmux is no longer supported; use session: acp with the Codex or Claude Code adapter');
47
+ if (raw.session !== undefined && raw.session !== 'acp')
48
+ throw new Error(`unsupported session '${String(raw.session)}'; expected session: acp`);
49
+ const session = 'acp';
46
50
  return {
47
51
  role: {
48
52
  ...raw, name, harness: typeof raw.harness === 'string' ? raw.harness : 'claude-code',
@@ -167,14 +171,10 @@ export class RoleRepository {
167
171
  const timeoutMs = this.options.timeoutMs ?? 2_000;
168
172
  const result = await Promise.race([
169
173
  this.options.probeBackend(name, intended),
170
- new Promise(resolve => setTimeout(() => resolve({ acp: false, tmux: false }), timeoutMs)),
171
- ]).catch(() => ({ acp: false, tmux: false }));
172
- if (result.acp && result.tmux)
173
- return 'ambiguous';
174
+ new Promise(resolve => setTimeout(() => resolve({ acp: false }), timeoutMs)),
175
+ ]).catch(() => ({ acp: false }));
174
176
  if (result.acp)
175
177
  return 'acp';
176
- if (result.tmux)
177
- return 'tmux';
178
178
  return 'none';
179
179
  }
180
180
  }
@@ -1,7 +1,6 @@
1
1
  import { controlRequest } from '../session/control.js';
2
2
  import type { ConversationEventV1, ConversationSnapshot, PromptReceipt } from '../session/conversation-types.js';
3
3
  import type { SessionSnapshot } from '../session/types.js';
4
- import { Tmux } from '../tmux.js';
5
4
  import type { OutputPage, SendReceipt, SessionDescriptor } from './types.js';
6
5
  export interface ConversationPageView {
7
6
  events: ConversationEventV1[];
@@ -120,14 +119,3 @@ export declare class AcpRoleSessionAdapter implements RoleSessionControl {
120
119
  }>;
121
120
  private call;
122
121
  }
123
- export declare class TmuxRoleSessionAdapter implements RoleSessionControl {
124
- private readonly roleId;
125
- private readonly tmux;
126
- constructor(roleId: string, tmux?: Tmux);
127
- describe(): Promise<SessionDescriptor>;
128
- snapshot(): Promise<SessionSnapshot>;
129
- recentOutput(request?: {
130
- limit?: number;
131
- }): Promise<OutputPage>;
132
- sendText(text: string): Promise<SendReceipt>;
133
- }
@@ -1,6 +1,4 @@
1
- import { randomUUID } from 'node:crypto';
2
1
  import { controlRequest, followConversation } from '../session/control.js';
3
- import { Tmux } from '../tmux.js';
4
2
  import { FleetError, normalizeError } from './errors.js';
5
3
  const visibleEvents = (events) => events.filter(event => event.kind !== 'thought');
6
4
  export class AcpRoleSessionAdapter {
@@ -149,43 +147,3 @@ export class AcpRoleSessionAdapter {
149
147
  }
150
148
  }
151
149
  }
152
- export class TmuxRoleSessionAdapter {
153
- roleId;
154
- tmux;
155
- constructor(roleId, tmux = new Tmux()) {
156
- this.roleId = roleId;
157
- this.tmux = tmux;
158
- }
159
- async describe() {
160
- return { backend: 'tmux', protocolVersion: 1, features: ['text', 'capture'] };
161
- }
162
- async snapshot() {
163
- const alive = await this.tmux.has(this.roleId);
164
- return { backend: 'tmux', alive, readiness: alive ? 'idle' : 'failed' };
165
- }
166
- async recentOutput(request = {}) {
167
- try {
168
- const text = await this.tmux.capture(this.roleId, Math.min(request.limit ?? 100, 500));
169
- return { events: [], text, truncated: false };
170
- }
171
- catch (error) {
172
- throw normalizeError(error);
173
- }
174
- }
175
- async sendText(text) {
176
- if (!text.trim())
177
- throw new FleetError('invalid_request', 'text is required');
178
- if (Buffer.byteLength(text) > 32 * 1024)
179
- throw new FleetError('invalid_request', 'text exceeds 32 KiB');
180
- try {
181
- await this.tmux.sendText(this.roleId, text);
182
- }
183
- catch (error) {
184
- throw normalizeError(error);
185
- }
186
- return {
187
- accepted: true, promptId: randomUUID(), queuedBehind: 0, terminalOutcomeKnown: false,
188
- detail: 'literal text and Enter sent; terminal outcome is unknown',
189
- };
190
- }
191
- }
@@ -1,7 +1,7 @@
1
- import type { InterruptOutcome, QueuedPrompt, SessionHandle, SubmitPromptOptions, TurnCancellationSource } from '../session/types.js';
1
+ import type { AgentSession, InterruptOutcome, QueuedPrompt, SubmitPromptOptions, TurnCancellationSource } from '../session/types.js';
2
2
  /** Thin in-process session mutation kernel. Callers retain every policy decision. */
3
- export declare const queueSessionPrompt: (session: SessionHandle, text: string, options?: SubmitPromptOptions) => Promise<QueuedPrompt>;
4
- export declare const interruptSession: (session: SessionHandle, source: TurnCancellationSource) => Promise<InterruptOutcome>;
5
- export declare const respondSessionPermission: (session: SessionHandle, permissionId: string, optionId: string) => boolean;
3
+ export declare const queueSessionPrompt: (session: AgentSession, text: string, options?: SubmitPromptOptions) => Promise<QueuedPrompt>;
4
+ export declare const interruptSession: (session: AgentSession, source: TurnCancellationSource) => Promise<InterruptOutcome>;
5
+ export declare const respondSessionPermission: (session: AgentSession, permissionId: string, optionId: string) => boolean;
6
6
  export type GenerationPermissionResult = 'accepted' | 'stale' | 'unavailable';
7
- export declare const respondSessionPermissionV2: (session: SessionHandle, permissionId: string, optionId: string, sessionGeneration: string) => GenerationPermissionResult;
7
+ export declare const respondSessionPermissionV2: (session: AgentSession, permissionId: string, optionId: string, sessionGeneration: string) => GenerationPermissionResult;
@@ -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
  }
@@ -117,7 +117,6 @@ export interface RoleCapabilities {
117
117
  };
118
118
  input: {
119
119
  text: boolean;
120
- rawKeys: boolean;
121
120
  interrupt: boolean;
122
121
  steering: boolean;
123
122
  };
@@ -125,12 +124,6 @@ export interface RoleCapabilities {
125
124
  observe: boolean;
126
125
  respond: boolean;
127
126
  };
128
- terminal: {
129
- available: boolean;
130
- reason?: 'wrong_backend' | 'pty_unavailable' | 'offline' | 'incompatible_runner';
131
- multiViewer: boolean;
132
- writerLease: boolean;
133
- };
134
127
  lifecycle: {
135
128
  start: boolean;
136
129
  stop: boolean;
package/dist/briefing.js CHANGED
@@ -190,21 +190,18 @@ export function generateBriefing(role, v, opts) {
190
190
  L.push(`7. Await messages. When the monitor wakes you (or the owner requests a manual check),`);
191
191
  L.push(` call **${v.getMessagesTool}**, act on them,`);
192
192
  L.push(` and reply with ${v.sendTool}. No coordinator is configured — the owner drives you`);
193
- // NOT `tmux attach -t <name>`: each role's pane lives on its own tmux
194
- // socket (#32), so a bare attach finds no server. `ours-fleet attach`
195
- // addresses the right one.
196
193
  L.push(` via \`ours-fleet attach ${role.name}\` or by messaging "${id}".`);
197
194
  }
198
195
  if (role.oversee?.length) {
199
196
  L.push('', '## Oversight assignments');
200
197
  L.push('These agents are your wards — you keep them unstuck:');
201
198
  for (const o of role.oversee)
202
- L.push(`- **${o.role}** — check every ${o.interval}`);
199
+ L.push(`- **${o.agent}** — check every ${o.interval}`);
203
200
  L.push('');
204
201
  L.push('Procedure (see also the oversee-agents skill if available). On each tick, for each ward');
205
202
  L.push('run BOTH — they answer different questions:');
206
203
  for (const o of role.oversee)
207
- 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}\``);
208
205
  L.push('');
209
206
  L.push('**One console command is not a liveness verdict.** A `peek` or `send` that fails tells');
210
207
  L.push('you what happened to YOUR REQUEST, and only one of its outcomes says the agent is gone.');
@@ -221,7 +218,7 @@ export function generateBriefing(role, v, opts) {
221
218
  L.push('idle or stalled from `readiness=idle` alone.');
222
219
  L.push('');
223
220
  L.push('Then judge the console content: stuck on a prompt/menu/trust dialog → answer it directly');
224
- L.push('with `ours-fleet send <Name> "<text>"` (or `--key <K>` for raw keys); idle with work');
221
+ L.push('with `ours-fleet send <Name> "<text>"`; idle with work');
225
222
  L.push('assigned → nudge; actively working → do nothing, and do not mistake a long turn for a');
226
223
  L.push('stall. Escalate over ours messaging only when you cannot resolve it yourself.');
227
224
  }
@@ -1,9 +1,9 @@
1
1
  {
2
- "version": "1.0.5",
3
- "buildId": "233dcfbef2e0",
4
- "commit": "5846aa20eb58d5c4ecf8a98bb6a7d3f578afdc2b",
5
- "dirty": false,
6
- "builtAt": "2026-08-26T08:39:49.505Z",
2
+ "version": "1.1.0-nightly.2",
3
+ "buildId": "da774e6be6b1",
4
+ "commit": "f8890b0449f4142a0c27a195e1351555fe599186",
5
+ "dirty": true,
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,12 +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 { Tmux, tmuxArgs } from './tmux.js';
14
+ import { redactSensitive, resolvedPlan } from './resolved-plan.js';
15
15
  import { pickBackend } from './supervisor/index.js';
16
16
  import { up, down } from './ops.js';
17
17
  import { readRestartLedger, runSupervised, runTemp } from './runner.js';
@@ -74,10 +74,10 @@ const passthrough = (cmd, args) => new Promise(resolve => {
74
74
  });
75
75
  const program = new Command()
76
76
  .name('ours-fleet')
77
- .description('Fleet of persistent, identity-bound AI agents — selectable harness and tmux/ACP sessions.')
77
+ .description('Fleet of persistent, identity-bound AI agents — selectable harnesses over ACP sessions.')
78
78
  .enablePositionalOptions()
79
79
  .version(VERSION);
80
- 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/)');
81
81
  const collect = (value, previous) => [...previous, value];
82
82
  program.command('docs')
83
83
  .alias('man')
@@ -261,14 +261,16 @@ cOpt(program.command('config').description('validate + print the merged plan (no
261
261
  console.log(` cwd: ${r.cwd}`);
262
262
  if (r.model)
263
263
  console.log(` model: ${r.model}`);
264
+ if (r.effort)
265
+ console.log(` effort: ${r.effort}`);
264
266
  if (r.harness_options && Object.keys(r.harness_options).length)
265
- console.log(` options: ${JSON.stringify(r.harness_options)}`);
267
+ console.log(` options: ${JSON.stringify(redactSensitive(r.harness_options))}`);
266
268
  if (r.coordinator)
267
269
  console.log(` coordinator: ${r.coordinator}`);
268
270
  if (r.mission)
269
271
  console.log(` mission: ${r.mission.split('\n')[0]}`);
270
272
  if (r.oversee?.length)
271
- 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(', ')}`);
272
274
  if (r.isolation) {
273
275
  const iso = r.isolation;
274
276
  const caps = [
@@ -369,15 +371,11 @@ cOpt(program.command('force-restart [names...]').description('re-sync + bounce F
369
371
  });
370
372
  program.command('ls').description('list running fleet sessions')
371
373
  .action(async () => {
372
- // Each session has its own tmux server (#32), so there is no single server
373
- // to ask: the known role names ARE the list of servers to poll.
374
- const names = [];
375
374
  const acp = [];
376
375
  for (const root of [agentsRoot(), tmpRoot()]) {
377
376
  if (!existsSync(root))
378
377
  continue;
379
378
  for (const name of readdirSync(root)) {
380
- names.push(name);
381
379
  const stateDir = joinPath(root, name);
382
380
  if (!existsSync(controlSocketPath(stateDir)))
383
381
  continue;
@@ -395,14 +393,13 @@ program.command('ls').description('list running fleet sessions')
395
393
  catch { /* ignore stale sockets */ }
396
394
  }
397
395
  }
398
- const tmux = await new Tmux().list(names);
399
- console.log([tmux, ...acp].filter(Boolean).join('\n') || '(none)');
396
+ console.log(acp.join('\n') || '(none)');
400
397
  });
401
398
  program.command('attach <name>').description('open the live console (Ctrl-b d to leave)')
402
399
  .action(async (name) => {
403
400
  const stateDir = acpStateDir(name);
404
401
  if (!stateDir)
405
- process.exit(await passthrough('tmux', tmuxArgs(name, ['attach', '-t', name])));
402
+ throw new SessionControlError('offline', 'agent session is offline');
406
403
  try {
407
404
  const { socket, send } = await followControl(stateDir, message => {
408
405
  if ('event' in message)
@@ -439,7 +436,7 @@ program.command('attach <name>').description('open the live console (Ctrl-b d to
439
436
  die(e);
440
437
  }
441
438
  });
442
- /** Classify a raw tmux failure: only "no such session" proves the pane is gone. */
439
+ /** Preserve typed control failures and classify only definite missing-session errors as offline. */
443
440
  const asControlError = (e) => {
444
441
  if (e instanceof SessionControlError)
445
442
  return e;
@@ -467,23 +464,17 @@ program.command('peek <name> [lines]').description('pane snapshot without attach
467
464
  for (const event of events.slice(-(lines ? Number(lines) : 40)))
468
465
  renderSessionEvent(event);
469
466
  }
470
- else {
471
- console.log(await new Tmux().capture(name, lines ? Number(lines) : 40));
472
- }
467
+ else
468
+ throw new SessionControlError('offline', 'agent session is offline');
473
469
  }
474
470
  catch (e) {
475
471
  die(controlFailure(name, 'peek', e));
476
472
  }
477
473
  });
478
474
  program.command('send <name> [text...]').description("type into the agent's console")
479
- .option('--key <key>', 'send a raw key instead (Escape, Up, C-c, ...)')
480
475
  .action(async (name, text, opts) => {
481
476
  const stateDir = acpStateDir(name);
482
- if (stateDir && opts.key)
483
- die('--key is available only for tmux sessions');
484
- if (!stateDir && !opts.key && !text?.length)
485
- die('nothing to send: give text or --key');
486
- if (stateDir && !text?.length)
477
+ if (!text?.length)
487
478
  die('nothing to send: give text');
488
479
  try {
489
480
  if (stateDir) {
@@ -496,10 +487,8 @@ program.command('send <name> [text...]').description("type into the agent's cons
496
487
  ? `queued for ${name} behind ${queued.queuedBehind} running turn(s)`
497
488
  : `queued for ${name}`);
498
489
  }
499
- else if (opts.key)
500
- await new Tmux().sendKey(name, opts.key);
501
490
  else
502
- await new Tmux().sendText(name, text.join(' '));
491
+ throw new SessionControlError('offline', 'agent session is offline');
503
492
  }
504
493
  catch (e) {
505
494
  die(controlFailure(name, 'send', e, asControlError(e).kind === 'timeout'
@@ -1126,7 +1115,7 @@ cOpt(program.command('spawn [name]').description('spawn a new agent (permanent b
1126
1115
  .option('--role <name>', 'role name (alternative to the positional name)')
1127
1116
  .option('--temp', 'temporary: independent transient supervisor, archived on retirement, gone on reboot')
1128
1117
  .option('--harness <id>', 'harness adapter (default: defaults.harness)')
1129
- .option('--session <backend>', 'session backend: tmux|acp (default: defaults.session or tmux)')
1118
+ .option('--session <backend>', 'session backend: acp (default: acp)')
1130
1119
  .option('--mission <text>', 'one-line mission')
1131
1120
  .option('--mission-file <path>', 'UTF-8 mission text (mutually exclusive with --mission)')
1132
1121
  .option('--identity <name>', 'ours identity to bind (default: role name)')
@@ -1267,7 +1256,7 @@ program.command('init').description('one-time host setup (units, dirs, linger)')
1267
1256
  mkdirSync(d, { recursive: true });
1268
1257
  for (const m of await pickBackend().init(binPath))
1269
1258
  console.log(m);
1270
- console.log('\nNext: copy examples/fleet.yaml to ~/fleet.yaml, edit, then: ours-fleet up');
1259
+ console.log(INIT_COMPLETION_GUIDANCE);
1271
1260
  });
1272
1261
  const webCommand = cOpt(program.command('web').description('start or open the secure localhost fleet web console'))
1273
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 {
@@ -25,7 +25,7 @@ export declare const NOTIFY_EVENT_TYPES: readonly ["message_received", "file_rec
25
25
  export type NotifyEventType = (typeof NOTIFY_EVENT_TYPES)[number];
26
26
  export type InjectMode = 'notification' | 'full';
27
27
  export type MonitorMode = 'fleet' | 'native';
28
- export type SessionBackendId = 'tmux' | 'acp';
28
+ export type SessionBackendId = 'acp';
29
29
  /** Public, harness-neutral permission policy. */
30
30
  export type FleetPermissionMode = 'ask' | 'auto' | 'allow';
31
31
  /** `deny` is a deprecated, fail-closed compatibility alias retained for old fleet files. */
@@ -44,9 +44,6 @@ export interface SessionOptions {
44
44
  /** ACP agent command and arguments. Defaults are supplied by the harness adapter. */
45
45
  command?: string | string[];
46
46
  };
47
- tmux?: {
48
- boot_grace_ms?: number;
49
- };
50
47
  }
51
48
  /** Resolved per-role supervisor-monitor config. */
52
49
  export interface MonitorConfig {
@@ -126,6 +123,8 @@ export interface RoleConfig {
126
123
  briefing_file?: string;
127
124
  /** Explicit null means use the selected harness's own default, bypassing fleet defaults. */
128
125
  model?: string | null;
126
+ /** Neutral Brain reasoning effort, retained for inspection after adapter translation. */
127
+ effort?: string;
129
128
  model_chain?: string[];
130
129
  max_tokens?: number;
131
130
  autocompact_pct?: number;
@@ -169,14 +168,37 @@ export interface ResolvedRole extends Omit<RoleConfig, 'model' | 'owner_channel'
169
168
  worklog?: WorklogPolicy;
170
169
  auth_proxy?: AuthProxyConfig;
171
170
  loops?: ResolvedRoleLoop[];
171
+ provenance?: Record<string, FieldProvenance>;
172
172
  /** Internal/transient: never accepted as a user-authored RoleConfig key. */
173
173
  roomMemberStartup?: RoomMemberStartup;
174
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
+ }
175
191
  export interface FleetConfig {
176
192
  roles: ResolvedRole[];
177
193
  vars: Record<string, string>;
178
194
  defaults: Record<string, unknown>;
179
195
  files: string[];
196
+ configMode?: 'split-v2';
197
+ sourceDocuments?: Array<{
198
+ kind: 'Manifest' | 'Agent' | 'Role' | 'Brain';
199
+ id?: string;
200
+ path: string;
201
+ }>;
180
202
  /** Fleet-wide delay (ms) enforced between agent launches to avoid boot bursts (0 = none). */
181
203
  startStaggerMs: number;
182
204
  /** Warning-first non-plain YAML migration diagnostics, in source order. */
@@ -207,7 +229,8 @@ export declare function resolveRoleModel(model: string | null | undefined, harne
207
229
  */
208
230
  export declare function isolationContextFor(role: ResolvedRole): WrapContext;
209
231
  export declare const ROLE_NAME_RE: RegExp;
210
- /** 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. */
211
234
  export declare function loadConfig(configPath?: string, options?: {
212
235
  yamlMode?: YamlMode;
213
236
  }): FleetConfig;