@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.
- package/README.md +122 -133
- package/dist/agent-recovery-gate.d.ts +17 -0
- package/dist/agent-recovery-gate.js +135 -0
- package/dist/application/capabilities.d.ts +0 -1
- package/dist/application/capabilities.js +3 -13
- package/dist/application/fleet-query-service.d.ts +0 -3
- package/dist/application/fleet-query-service.js +14 -25
- package/dist/application/role-creation-service.d.ts +5 -24
- package/dist/application/role-creation-service.js +27 -67
- package/dist/application/role-removal-service.js +6 -3
- package/dist/application/role-repository.d.ts +1 -2
- package/dist/application/role-repository.js +7 -7
- package/dist/application/session-control.d.ts +0 -12
- package/dist/application/session-control.js +0 -42
- package/dist/application/session-mutations.d.ts +5 -5
- package/dist/application/task-room-service.d.ts +1 -1
- package/dist/application/task-room-service.js +154 -16
- package/dist/application/types.d.ts +1 -8
- package/dist/briefing.js +12 -11
- package/dist/build-info.json +5 -5
- package/dist/cli.js +143 -63
- package/dist/config-yaml.d.ts +1 -1
- package/dist/config.d.ts +66 -6
- package/dist/config.js +398 -43
- package/dist/creation.d.ts +1 -1
- package/dist/creation.js +2 -2
- package/dist/daemon-recovery.d.ts +93 -0
- package/dist/daemon-recovery.js +328 -0
- package/dist/docs.d.ts +1 -1
- package/dist/docs.js +103 -127
- package/dist/doctor.js +18 -11
- package/dist/fleet-command-audit.d.ts +160 -0
- package/dist/fleet-command-audit.js +433 -0
- package/dist/fleet-proxy.d.ts +3 -1
- package/dist/fleet-proxy.js +30 -8
- package/dist/generated-agent-source.d.ts +9 -0
- package/dist/generated-agent-source.js +53 -0
- package/dist/harness/acp-session-transport.d.ts +8 -0
- package/dist/harness/acp-session-transport.js +3 -0
- package/dist/harness/agent-session.d.ts +51 -0
- package/dist/harness/agent-session.js +1 -0
- package/dist/harness/claude-code-session.d.ts +24 -0
- package/dist/harness/claude-code-session.js +36 -0
- package/dist/harness/claude-code.d.ts +2 -1
- package/dist/harness/claude-code.js +41 -81
- package/dist/harness/codex-session.d.ts +24 -0
- package/dist/harness/codex-session.js +36 -0
- package/dist/harness/codex.d.ts +2 -1
- package/dist/harness/codex.js +55 -45
- package/dist/harness/registry.js +4 -0
- package/dist/harness/types.d.ts +6 -38
- package/dist/index.d.ts +4 -6
- package/dist/index.js +2 -4
- package/dist/init-guidance.d.ts +1 -0
- package/dist/init-guidance.js +6 -0
- package/dist/isolation/resources.d.ts +3 -7
- package/dist/isolation/resources.js +3 -7
- package/dist/model-env.d.ts +4 -6
- package/dist/model-env.js +5 -8
- package/dist/monitor.d.ts +1 -24
- package/dist/monitor.js +5 -140
- package/dist/ops.js +4 -2
- package/dist/owner-channel/channel.d.ts +58 -2
- package/dist/owner-channel/channel.js +269 -29
- package/dist/owner-channel/commands.d.ts +2 -0
- package/dist/owner-channel/commands.js +31 -13
- package/dist/owner-channel/state.d.ts +2 -0
- package/dist/owner-channel/state.js +8 -2
- package/dist/preset-bootstrap.d.ts +10 -0
- package/dist/preset-bootstrap.js +85 -0
- package/dist/reports/artifact.d.ts +10 -0
- package/dist/reports/artifact.js +32 -0
- package/dist/reports/index.d.ts +5 -0
- package/dist/reports/index.js +5 -0
- package/dist/reports/render.d.ts +6 -0
- package/dist/reports/render.js +240 -0
- package/dist/reports/service.d.ts +42 -0
- package/dist/reports/service.js +57 -0
- package/dist/reports/tasks.d.ts +52 -0
- package/dist/reports/tasks.js +177 -0
- package/dist/reports/types.d.ts +315 -0
- package/dist/reports/types.js +6 -0
- package/dist/resolved-plan.d.ts +3 -2
- package/dist/resolved-plan.js +21 -5
- package/dist/rooms-tasks/cli.js +225 -40
- package/dist/rooms-tasks/config.js +24 -13
- package/dist/rooms-tasks/external-worker.d.ts +1 -0
- package/dist/rooms-tasks/external-worker.js +14 -9
- package/dist/rooms-tasks/provision.js +98 -36
- package/dist/rooms-tasks/templates.d.ts +0 -1
- package/dist/rooms-tasks/templates.js +9 -76
- package/dist/rooms-tasks/types.d.ts +10 -16
- package/dist/rooms-tasks/types.js +1 -4
- package/dist/runner.d.ts +13 -16
- package/dist/runner.js +110 -122
- package/dist/sensitive-config.d.ts +2 -0
- package/dist/sensitive-config.js +4 -0
- package/dist/session/acp.d.ts +7 -2
- package/dist/session/acp.js +29 -6
- package/dist/session/activity.d.ts +2 -3
- package/dist/session/activity.js +2 -3
- package/dist/session/arbiter.d.ts +4 -4
- package/dist/session/control.d.ts +17 -4
- package/dist/session/control.js +35 -0
- package/dist/session/types.d.ts +11 -5
- package/dist/spawn.d.ts +8 -33
- package/dist/spawn.js +53 -213
- package/dist/supervisor/none.d.ts +1 -1
- package/dist/supervisor/none.js +2 -2
- package/dist/temp-lifecycle.d.ts +1 -1
- package/dist/tmux.d.ts +1 -14
- package/dist/tmux.js +1 -51
- package/dist/watchdog/config.d.ts +10 -7
- package/dist/watchdog/config.js +30 -18
- package/dist/watchdog/run.js +12 -36
- package/dist/web/auth.d.ts +1 -1
- package/dist/web/fleet-config-service.d.ts +11 -8
- package/dist/web/fleet-config-service.js +384 -183
- package/dist/web/runtime.js +4 -15
- package/dist/web/server.d.ts +1 -3
- package/dist/web/server.js +3 -14
- package/dist/web/topology-promote.js +26 -19
- package/dist/web/topology.js +2 -2
- package/dist/web/yaml-document-edit.js +2 -0
- package/dist/web-app/assets/index-59GF-gsZ.css +1 -0
- package/dist/web-app/assets/index-BbTc5KtZ.js +10 -0
- package/dist/web-app/index.html +2 -2
- package/examples/fleet/agents/Alice.yaml +11 -0
- package/examples/fleet/agents/FleetCoordinator.yaml +7 -0
- package/examples/fleet/brains/claude-default.yaml +5 -0
- package/examples/fleet/roles/coordinator.yaml +5 -0
- package/examples/fleet/roles/developer.yaml +3 -0
- package/examples/fleet.yaml +74 -0
- package/package.json +5 -8
- package/presets/fleet/agents/Agent.yaml +3 -0
- package/presets/fleet/agents/Architect.yaml +3 -0
- package/presets/fleet/agents/Critic.yaml +3 -0
- package/presets/fleet/agents/Developer.yaml +3 -0
- package/presets/fleet/agents/Secretary.yaml +3 -0
- package/presets/fleet/agents/Tester.yaml +3 -0
- package/presets/fleet/brains/claude-default.yaml +5 -0
- package/presets/fleet/roles/Agent.yaml +6 -0
- package/presets/fleet/roles/Architect.yaml +6 -0
- package/presets/fleet/roles/Critic.yaml +6 -0
- package/presets/fleet/roles/Developer.yaml +6 -0
- package/presets/fleet/roles/Secretary.yaml +6 -0
- package/presets/fleet/roles/Tester.yaml +6 -0
- package/presets/fleet/room_templates/pair.yaml +11 -0
- package/presets/fleet/room_templates/single.yaml +8 -0
- package/presets/fleet/room_templates/team.yaml +12 -0
- package/presets/fleet.yaml +7 -0
- package/presets/manifest.json +5 -0
- package/dist/session/tmux.d.ts +0 -28
- package/dist/session/tmux.js +0 -80
- package/dist/web/terminal/bridge.d.ts +0 -27
- package/dist/web/terminal/bridge.js +0 -317
- package/dist/web-app/assets/TerminalView-C_G1ID2P.js +0 -9
- package/dist/web-app/assets/index-BCBK78hw.js +0 -10
- package/dist/web-app/assets/index-DuC-xnX4.css +0 -1
package/dist/cli.js
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { spawn as spawnChild } from 'node:child_process';
|
|
3
|
+
import { randomUUID } from 'node:crypto';
|
|
3
4
|
import { existsSync, mkdirSync, readFileSync, readdirSync, statSync } from 'node:fs';
|
|
4
5
|
import { realpathSync } from 'node:fs';
|
|
5
6
|
import { join as joinPath, resolve as resolvePath } from 'node:path';
|
|
6
7
|
import { createInterface } from 'node:readline';
|
|
7
8
|
import { Command } from 'commander';
|
|
8
9
|
import { VERSION } from './version.js';
|
|
10
|
+
import { INIT_COMPLETION_GUIDANCE } from './init-guidance.js';
|
|
11
|
+
import { bootstrapPresets } from './preset-bootstrap.js';
|
|
9
12
|
import { analyzeInstalls, buildInfo, buildLabel, discoverInstalls, runningLabel, } from './provenance.js';
|
|
10
13
|
import { agentDir, agentsRoot, tmpRoot, logsRoot, deriveXdgRuntimeDir } from './paths.js';
|
|
11
|
-
import { findRole, loadConfig } from './config.js';
|
|
14
|
+
import { findRole, loadConfig, ROLE_NAME_RE } from './config.js';
|
|
12
15
|
import { formatDuration } from './duration.js';
|
|
13
|
-
import { resolvedPlan } from './resolved-plan.js';
|
|
14
|
-
import { Tmux, tmuxArgs } from './tmux.js';
|
|
16
|
+
import { redactSensitive, resolvedPlan } from './resolved-plan.js';
|
|
15
17
|
import { pickBackend } from './supervisor/index.js';
|
|
16
18
|
import { up, down } from './ops.js';
|
|
17
19
|
import { readRestartLedger, runSupervised, runTemp } from './runner.js';
|
|
@@ -37,6 +39,7 @@ import { requestWebControl } from './web/control.js';
|
|
|
37
39
|
import { WebServiceManager } from './web/service.js';
|
|
38
40
|
import { WebAccessStore, passwordAccess, validatePublicOrigin } from './web/access.js';
|
|
39
41
|
import { FLEET_PROXY_CALLER_ENV, FLEET_PROXY_STATE_DIR_ENV, } from './fleet-proxy.js';
|
|
42
|
+
import { beginFleetAuditCollection, consumeFleetAuditCollection, FleetCliExit, } from './fleet-command-audit.js';
|
|
40
43
|
import './harness/claude-code.js'; // registers the claude-code adapter
|
|
41
44
|
import './harness/codex.js'; // registers the codex adapter
|
|
42
45
|
import { registerTemplateCommands, registerTaskCommands, registerRoomCommands } from './rooms-tasks/cli.js';
|
|
@@ -66,7 +69,10 @@ const roleLifecycle = (configPath, operationDeps) => {
|
|
|
66
69
|
return new RoleLifecycleService({ repository, ops: operationDeps, configPath,
|
|
67
70
|
status: async (roleId) => (await query.detail(roleId)).status });
|
|
68
71
|
};
|
|
69
|
-
const die = (e) => {
|
|
72
|
+
const die = (e) => {
|
|
73
|
+
console.error(String(e instanceof Error ? e.message : e));
|
|
74
|
+
throw new FleetCliExit(1);
|
|
75
|
+
};
|
|
70
76
|
/** Exec a child with our stdio (logs/attach). */
|
|
71
77
|
const passthrough = (cmd, args) => new Promise(resolve => {
|
|
72
78
|
const c = spawnChild(cmd, args, { stdio: 'inherit' });
|
|
@@ -74,10 +80,11 @@ const passthrough = (cmd, args) => new Promise(resolve => {
|
|
|
74
80
|
});
|
|
75
81
|
const program = new Command()
|
|
76
82
|
.name('ours-fleet')
|
|
77
|
-
.description('Fleet of persistent, identity-bound AI agents —
|
|
83
|
+
.description('Fleet of persistent, identity-bound AI agents — canonical Brain + Role definitions over ACP sessions.')
|
|
78
84
|
.enablePositionalOptions()
|
|
85
|
+
.exitOverride()
|
|
79
86
|
.version(VERSION);
|
|
80
|
-
const cOpt = (cmd) => cmd.option('-c, --configuration <file>', '
|
|
87
|
+
const cOpt = (cmd) => cmd.option('-c, --configuration <file>', 'manifest (default: ~/fleet.yaml; documents under ~/fleet/)');
|
|
81
88
|
const collect = (value, previous) => [...previous, value];
|
|
82
89
|
program.command('docs')
|
|
83
90
|
.alias('man')
|
|
@@ -218,6 +225,24 @@ function parseCodexConfig(values) {
|
|
|
218
225
|
}
|
|
219
226
|
return out;
|
|
220
227
|
}
|
|
228
|
+
function parseAgentSelection(raw, kind) {
|
|
229
|
+
if (raw === undefined)
|
|
230
|
+
return undefined;
|
|
231
|
+
if (ROLE_NAME_RE.test(raw))
|
|
232
|
+
return { ref: raw };
|
|
233
|
+
if (!raw.startsWith('inline:'))
|
|
234
|
+
throw new Error(`invalid --${kind}; use a declared ID or inline:{...}`);
|
|
235
|
+
let value;
|
|
236
|
+
try {
|
|
237
|
+
value = JSON.parse(raw.slice('inline:'.length));
|
|
238
|
+
}
|
|
239
|
+
catch {
|
|
240
|
+
throw new Error(`invalid --${kind} inline mapping`);
|
|
241
|
+
}
|
|
242
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
243
|
+
throw new Error(`invalid --${kind} inline mapping`);
|
|
244
|
+
return { inline: value };
|
|
245
|
+
}
|
|
221
246
|
cOpt(program.command('config').description('validate + print the merged plan (no side effects)'))
|
|
222
247
|
.option('--json', 'emit the stable, versioned, secret-safe resolved plan')
|
|
223
248
|
.option('--yaml-mode <mode>', 'non-plain YAML policy: compat|strict', 'compat')
|
|
@@ -261,14 +286,16 @@ cOpt(program.command('config').description('validate + print the merged plan (no
|
|
|
261
286
|
console.log(` cwd: ${r.cwd}`);
|
|
262
287
|
if (r.model)
|
|
263
288
|
console.log(` model: ${r.model}`);
|
|
289
|
+
if (r.effort)
|
|
290
|
+
console.log(` effort: ${r.effort}`);
|
|
264
291
|
if (r.harness_options && Object.keys(r.harness_options).length)
|
|
265
|
-
console.log(` options: ${JSON.stringify(r.harness_options)}`);
|
|
292
|
+
console.log(` options: ${JSON.stringify(redactSensitive(r.harness_options))}`);
|
|
266
293
|
if (r.coordinator)
|
|
267
294
|
console.log(` coordinator: ${r.coordinator}`);
|
|
268
295
|
if (r.mission)
|
|
269
296
|
console.log(` mission: ${r.mission.split('\n')[0]}`);
|
|
270
297
|
if (r.oversee?.length)
|
|
271
|
-
console.log(` oversees: ${r.oversee.map(o => `${o.
|
|
298
|
+
console.log(` oversees: ${r.oversee.map(o => `${o.agent}@${o.interval}`).join(', ')}`);
|
|
272
299
|
if (r.isolation) {
|
|
273
300
|
const iso = r.isolation;
|
|
274
301
|
const caps = [
|
|
@@ -369,15 +396,11 @@ cOpt(program.command('force-restart [names...]').description('re-sync + bounce F
|
|
|
369
396
|
});
|
|
370
397
|
program.command('ls').description('list running fleet sessions')
|
|
371
398
|
.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
399
|
const acp = [];
|
|
376
400
|
for (const root of [agentsRoot(), tmpRoot()]) {
|
|
377
401
|
if (!existsSync(root))
|
|
378
402
|
continue;
|
|
379
403
|
for (const name of readdirSync(root)) {
|
|
380
|
-
names.push(name);
|
|
381
404
|
const stateDir = joinPath(root, name);
|
|
382
405
|
if (!existsSync(controlSocketPath(stateDir)))
|
|
383
406
|
continue;
|
|
@@ -395,14 +418,13 @@ program.command('ls').description('list running fleet sessions')
|
|
|
395
418
|
catch { /* ignore stale sockets */ }
|
|
396
419
|
}
|
|
397
420
|
}
|
|
398
|
-
|
|
399
|
-
console.log([tmux, ...acp].filter(Boolean).join('\n') || '(none)');
|
|
421
|
+
console.log(acp.join('\n') || '(none)');
|
|
400
422
|
});
|
|
401
423
|
program.command('attach <name>').description('open the live console (Ctrl-b d to leave)')
|
|
402
424
|
.action(async (name) => {
|
|
403
425
|
const stateDir = acpStateDir(name);
|
|
404
426
|
if (!stateDir)
|
|
405
|
-
|
|
427
|
+
throw new SessionControlError('offline', 'agent session is offline');
|
|
406
428
|
try {
|
|
407
429
|
const { socket, send } = await followControl(stateDir, message => {
|
|
408
430
|
if ('event' in message)
|
|
@@ -439,7 +461,7 @@ program.command('attach <name>').description('open the live console (Ctrl-b d to
|
|
|
439
461
|
die(e);
|
|
440
462
|
}
|
|
441
463
|
});
|
|
442
|
-
/**
|
|
464
|
+
/** Preserve typed control failures and classify only definite missing-session errors as offline. */
|
|
443
465
|
const asControlError = (e) => {
|
|
444
466
|
if (e instanceof SessionControlError)
|
|
445
467
|
return e;
|
|
@@ -467,23 +489,17 @@ program.command('peek <name> [lines]').description('pane snapshot without attach
|
|
|
467
489
|
for (const event of events.slice(-(lines ? Number(lines) : 40)))
|
|
468
490
|
renderSessionEvent(event);
|
|
469
491
|
}
|
|
470
|
-
else
|
|
471
|
-
|
|
472
|
-
}
|
|
492
|
+
else
|
|
493
|
+
throw new SessionControlError('offline', 'agent session is offline');
|
|
473
494
|
}
|
|
474
495
|
catch (e) {
|
|
475
496
|
die(controlFailure(name, 'peek', e));
|
|
476
497
|
}
|
|
477
498
|
});
|
|
478
499
|
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
500
|
.action(async (name, text, opts) => {
|
|
481
501
|
const stateDir = acpStateDir(name);
|
|
482
|
-
if (
|
|
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)
|
|
502
|
+
if (!text?.length)
|
|
487
503
|
die('nothing to send: give text');
|
|
488
504
|
try {
|
|
489
505
|
if (stateDir) {
|
|
@@ -496,10 +512,8 @@ program.command('send <name> [text...]').description("type into the agent's cons
|
|
|
496
512
|
? `queued for ${name} behind ${queued.queuedBehind} running turn(s)`
|
|
497
513
|
: `queued for ${name}`);
|
|
498
514
|
}
|
|
499
|
-
else if (opts.key)
|
|
500
|
-
await new Tmux().sendKey(name, opts.key);
|
|
501
515
|
else
|
|
502
|
-
|
|
516
|
+
throw new SessionControlError('offline', 'agent session is offline');
|
|
503
517
|
}
|
|
504
518
|
catch (e) {
|
|
505
519
|
die(controlFailure(name, 'send', e, asControlError(e).kind === 'timeout'
|
|
@@ -1123,50 +1137,32 @@ cOpt(program.command('rm <name>').description('stop + remove a role (temporary e
|
|
|
1123
1137
|
}
|
|
1124
1138
|
});
|
|
1125
1139
|
cOpt(program.command('spawn [name]').description('spawn a new agent (permanent by default)'))
|
|
1126
|
-
.option('--
|
|
1140
|
+
.option('--name <name>', 'agent name (alternative to the positional name)')
|
|
1141
|
+
.option('--brain <selection>', 'Brain ID or inline:{...} definition')
|
|
1142
|
+
.option('--role <selection>', 'Role ID or inline:{...} definition')
|
|
1127
1143
|
.option('--temp', 'temporary: independent transient supervisor, archived on retirement, gone on reboot')
|
|
1128
|
-
.option('--
|
|
1129
|
-
.option('--session <backend>', 'session backend: tmux|acp (default: defaults.session or tmux)')
|
|
1130
|
-
.option('--mission <text>', 'one-line mission')
|
|
1131
|
-
.option('--mission-file <path>', 'UTF-8 mission text (mutually exclusive with --mission)')
|
|
1132
|
-
.option('--identity <name>', 'ours identity to bind (default: role name)')
|
|
1144
|
+
.option('--identity <name>', 'ours identity to bind (default: Agent name)')
|
|
1133
1145
|
.option('--cwd <dir>', 'working directory')
|
|
1134
1146
|
.option('--coordinator <name>', 'announce target')
|
|
1135
|
-
.option('--model <id>', 'model id to launch on (e.g. claude-fable-5); default: launcher default')
|
|
1136
|
-
.option('--permission-mode <mode>', 'harness permission mode (Codex: untrusted|on-request|never; Claude: native values)')
|
|
1137
1147
|
.option('--approval <mode>', 'fleet permission mode: ask|auto|allow (Codex ACP allow selects agent-full-access; deny is deprecated)')
|
|
1138
1148
|
.option('--filesystem <mode>', 'common filesystem intent: read-only|workspace|unrestricted')
|
|
1139
1149
|
.option('--unattended <mode>', 'permission behavior without a console: deny|wait')
|
|
1140
|
-
.option('--sandbox <mode>', 'Codex sandbox: read-only|workspace-write|danger-full-access')
|
|
1141
|
-
.option('--profile <name>', 'Codex profile file name ($CODEX_HOME/<name>.config.toml)')
|
|
1142
|
-
.option('--launcher <mode>', 'Codex launcher: auto|ours-codex|codex (default: auto)')
|
|
1143
|
-
.option('--search', 'enable Codex live web search')
|
|
1144
|
-
.option('--codex-config <key=value>', 'Codex config override (repeatable)', collect, [])
|
|
1145
|
-
.option('--add-dir <dir>', 'additional Codex writable directory (repeatable)', collect, [])
|
|
1146
|
-
.option('--monitor', 'legacy: consent to arm Codex\'s native monitor (wake owner is monitor.mode in YAML)')
|
|
1147
|
-
.option('--bio-file <file>', 'public bio (file)')
|
|
1148
|
-
.option('--persona-file <file>', 'persona / operating contract (file)')
|
|
1149
1150
|
.option('--isolation-file <path>', 'file holding an isolation: mapping (same schema as fleet.yaml)')
|
|
1150
1151
|
.option('--dry-run', 'validate and print without reserving or creating anything')
|
|
1151
1152
|
.option('--json', 'with --dry-run, emit a stable secret-safe JSON result')
|
|
1152
1153
|
.action(async (name, opts) => {
|
|
1153
1154
|
try {
|
|
1154
|
-
const roleName = String(name ?? opts.
|
|
1155
|
+
const roleName = String(name ?? opts.name ?? '');
|
|
1155
1156
|
if (!roleName)
|
|
1156
|
-
throw new Error('
|
|
1157
|
-
if (name && opts.
|
|
1158
|
-
throw new Error(`positional
|
|
1157
|
+
throw new Error('agent name is required (positional or --name)');
|
|
1158
|
+
if (name && opts.name && name !== opts.name)
|
|
1159
|
+
throw new Error(`positional agent name conflicts with --name`);
|
|
1159
1160
|
const o = {
|
|
1160
|
-
name: roleName, temp: opts.temp,
|
|
1161
|
-
|
|
1161
|
+
name: roleName, temp: opts.temp,
|
|
1162
|
+
brain: parseAgentSelection(opts.brain, 'brain'), role: parseAgentSelection(opts.role, 'role'),
|
|
1162
1163
|
identity: opts.identity, cwd: opts.cwd, coordinator: opts.coordinator,
|
|
1163
|
-
|
|
1164
|
-
permissionMode: opts.permissionMode, approval: opts.approval,
|
|
1164
|
+
approval: opts.approval,
|
|
1165
1165
|
filesystem: opts.filesystem, unattended: opts.unattended,
|
|
1166
|
-
sandbox: opts.sandbox, profile: opts.profile,
|
|
1167
|
-
launcher: opts.launcher, search: opts.search,
|
|
1168
|
-
codexConfig: parseCodexConfig(opts.codexConfig), addDirs: opts.addDir, monitor: opts.monitor,
|
|
1169
|
-
bioFile: opts.bioFile, personaFile: opts.personaFile,
|
|
1170
1166
|
isolationFile: opts.isolationFile, configPath: opts.configuration,
|
|
1171
1167
|
dryRun: opts.dryRun, json: opts.json,
|
|
1172
1168
|
};
|
|
@@ -1195,7 +1191,7 @@ cOpt(program.command('spawn [name]').description('spawn a new agent (permanent b
|
|
|
1195
1191
|
if (proxyStateDir) {
|
|
1196
1192
|
// Paths entered in the agent shell belong to that shell's cwd, not the
|
|
1197
1193
|
// supervisor process. Normalize before crossing the control boundary.
|
|
1198
|
-
for (const key of ['
|
|
1194
|
+
for (const key of ['isolationFile']) {
|
|
1199
1195
|
if (o[key])
|
|
1200
1196
|
o[key] = resolvePath(o[key]);
|
|
1201
1197
|
}
|
|
@@ -1259,15 +1255,19 @@ cOpt(program.command('doctor').description('prerequisite report'))
|
|
|
1259
1255
|
});
|
|
1260
1256
|
for (const c of rep.checks)
|
|
1261
1257
|
console.log(`${c.ok ? 'ok ' : 'MISS'} ${c.name.padEnd(22)} ${c.detail}`);
|
|
1262
|
-
|
|
1258
|
+
if (!rep.ok)
|
|
1259
|
+
throw new FleetCliExit(1);
|
|
1263
1260
|
});
|
|
1264
|
-
program.command('init').description('
|
|
1265
|
-
.action(async () => {
|
|
1261
|
+
cOpt(program.command('init').description('idempotent host setup plus missing packaged presets'))
|
|
1262
|
+
.action(async (opts) => {
|
|
1266
1263
|
for (const d of [agentsRoot(), tmpRoot(), logsRoot()])
|
|
1267
1264
|
mkdirSync(d, { recursive: true });
|
|
1268
1265
|
for (const m of await pickBackend().init(binPath))
|
|
1269
1266
|
console.log(m);
|
|
1270
|
-
|
|
1267
|
+
const seeded = bootstrapPresets(opts.configuration);
|
|
1268
|
+
console.log(`Packaged preset revision ${seeded.revision}: ${seeded.sourceRoot}`);
|
|
1269
|
+
console.log(`Created ${seeded.created.length}; preserved ${seeded.preserved.length} existing file(s).`);
|
|
1270
|
+
console.log(INIT_COMPLETION_GUIDANCE);
|
|
1271
1271
|
});
|
|
1272
1272
|
const webCommand = cOpt(program.command('web').description('start or open the secure localhost fleet web console'))
|
|
1273
1273
|
.enablePositionalOptions()
|
|
@@ -1508,4 +1508,84 @@ cOpt(program.command('_run-watchdogs', { hidden: true }))
|
|
|
1508
1508
|
die(e);
|
|
1509
1509
|
}
|
|
1510
1510
|
});
|
|
1511
|
-
|
|
1511
|
+
async function parseFleetCli() {
|
|
1512
|
+
const spawnIndex = process.argv.indexOf('spawn');
|
|
1513
|
+
if (spawnIndex >= 0 && process.argv.slice(spawnIndex + 1).some(arg => arg === '--harness' || arg.startsWith('--harness=') || arg === '--model' || arg.startsWith('--model='))) {
|
|
1514
|
+
console.error('error: --harness and --model were removed; select a Brain with --brain');
|
|
1515
|
+
throw new FleetCliExit(1);
|
|
1516
|
+
}
|
|
1517
|
+
try {
|
|
1518
|
+
await program.parseAsync(process.argv);
|
|
1519
|
+
}
|
|
1520
|
+
catch (error) {
|
|
1521
|
+
const commander = error;
|
|
1522
|
+
if (commander.exitCode === 0)
|
|
1523
|
+
return;
|
|
1524
|
+
if (typeof commander.exitCode === 'number')
|
|
1525
|
+
throw new FleetCliExit(commander.exitCode, 'validation', 'not_started');
|
|
1526
|
+
throw error;
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1529
|
+
async function runFleetCli() {
|
|
1530
|
+
const stateDir = process.env[FLEET_PROXY_STATE_DIR_ENV];
|
|
1531
|
+
const caller = process.env[FLEET_PROXY_CALLER_ENV];
|
|
1532
|
+
if (!stateDir || !caller) {
|
|
1533
|
+
await parseFleetCli();
|
|
1534
|
+
return;
|
|
1535
|
+
}
|
|
1536
|
+
const requestId = randomUUID();
|
|
1537
|
+
let attempt;
|
|
1538
|
+
const begun = await controlRequest(stateDir, {
|
|
1539
|
+
command: 'fleet_audit_begin', audit: { requestId, argv: process.argv.slice(2) },
|
|
1540
|
+
});
|
|
1541
|
+
if (!begun.ok)
|
|
1542
|
+
throw new SessionControlError(begun.kind ?? 'rejected', begun.error ?? 'fleet command audit refused');
|
|
1543
|
+
attempt = begun.result;
|
|
1544
|
+
if (attempt.caller !== caller)
|
|
1545
|
+
throw new SessionControlError('rejected', `fleet audit caller mismatch: expected '${caller}', got '${attempt.caller}'`);
|
|
1546
|
+
let exitCode = 0;
|
|
1547
|
+
let outcomeClass = 'success';
|
|
1548
|
+
let effect = 'completed';
|
|
1549
|
+
beginFleetAuditCollection();
|
|
1550
|
+
if (attempt.classification.decision !== 'allow') {
|
|
1551
|
+
exitCode = 1;
|
|
1552
|
+
outcomeClass = 'denied';
|
|
1553
|
+
effect = 'not_started';
|
|
1554
|
+
console.error(`fleet supervisor proxy ${attempt.classification.decision}: ${attempt.classification.command}`);
|
|
1555
|
+
}
|
|
1556
|
+
else {
|
|
1557
|
+
const originalExit = process.exit;
|
|
1558
|
+
process.exit = ((code) => { throw new FleetCliExit(code ?? 0); });
|
|
1559
|
+
try {
|
|
1560
|
+
await parseFleetCli();
|
|
1561
|
+
}
|
|
1562
|
+
catch (error) {
|
|
1563
|
+
exitCode = error instanceof FleetCliExit ? error.exitCode : 1;
|
|
1564
|
+
outcomeClass = error instanceof FleetCliExit ? error.outcomeClass : 'runtime';
|
|
1565
|
+
effect = error instanceof FleetCliExit ? error.effect : 'unknown';
|
|
1566
|
+
if (!(error instanceof FleetCliExit))
|
|
1567
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
1568
|
+
}
|
|
1569
|
+
finally {
|
|
1570
|
+
process.exit = originalExit;
|
|
1571
|
+
}
|
|
1572
|
+
}
|
|
1573
|
+
const metadata = consumeFleetAuditCollection();
|
|
1574
|
+
if (metadata.failure) {
|
|
1575
|
+
outcomeClass = metadata.failure.class;
|
|
1576
|
+
effect = metadata.failure.effect;
|
|
1577
|
+
}
|
|
1578
|
+
const finished = await controlRequest(stateDir, { command: 'fleet_audit_finish', audit: {
|
|
1579
|
+
correlationId: attempt.correlationId, class: outcomeClass, exitCode, effect,
|
|
1580
|
+
...(metadata.resourceIds ? { resourceIds: metadata.resourceIds } : {}),
|
|
1581
|
+
...(metadata.presentations ? { presentations: metadata.presentations } : {}),
|
|
1582
|
+
} });
|
|
1583
|
+
if (!finished.ok)
|
|
1584
|
+
throw new SessionControlError(finished.kind ?? 'backend', finished.error ?? `audit delivery failed after execution; effect status=${effect}`);
|
|
1585
|
+
process.exitCode = exitCode;
|
|
1586
|
+
}
|
|
1587
|
+
void runFleetCli().catch(error => {
|
|
1588
|
+
if (!(error instanceof FleetCliExit))
|
|
1589
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
1590
|
+
process.exitCode = error instanceof FleetCliExit ? error.exitCode : 1;
|
|
1591
|
+
});
|
package/dist/config-yaml.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export type YamlMode = 'compat' | 'strict';
|
|
2
|
-
export type ConfigDiagnosticKind = 'anchor' | 'alias' | 'explicit-tag' | 'non-scalar-key' | 'multiple-documents';
|
|
2
|
+
export type ConfigDiagnosticKind = 'anchor' | 'alias' | 'explicit-tag' | 'non-scalar-key' | 'multiple-documents' | 'deprecated-field';
|
|
3
3
|
export interface ConfigDiagnostic {
|
|
4
4
|
severity: 'warning';
|
|
5
5
|
kind: ConfigDiagnosticKind;
|
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
|
-
|
|
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 = '
|
|
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;
|
|
@@ -138,6 +137,27 @@ export interface RoleConfig {
|
|
|
138
137
|
worklog?: WorklogPolicyInput;
|
|
139
138
|
auth_proxy?: Partial<AuthProxyConfig>;
|
|
140
139
|
}
|
|
140
|
+
export type AgentSelection<T extends Record<string, unknown> = Record<string, unknown>> = {
|
|
141
|
+
ref: string;
|
|
142
|
+
} | {
|
|
143
|
+
inline: T;
|
|
144
|
+
};
|
|
145
|
+
/** Canonical authoring contract shared by configured, spawned, and room agents. */
|
|
146
|
+
export interface AgentDefinition {
|
|
147
|
+
role: AgentSelection;
|
|
148
|
+
brain: AgentSelection;
|
|
149
|
+
permissions?: Partial<CommonPermissions>;
|
|
150
|
+
identity?: string;
|
|
151
|
+
cwd?: string;
|
|
152
|
+
coordinator?: string;
|
|
153
|
+
env?: Record<string, string>;
|
|
154
|
+
oversee?: OverseeEntry[];
|
|
155
|
+
isolation?: IsolationConfig;
|
|
156
|
+
monitor?: Partial<MonitorConfig>;
|
|
157
|
+
owner_channel?: OwnerChannelConfigInput;
|
|
158
|
+
worklog?: WorklogPolicyInput;
|
|
159
|
+
auth_proxy?: Partial<AuthProxyConfig>;
|
|
160
|
+
}
|
|
141
161
|
/** Internal first-boot payload for a Fleet-provisioned Cowork room member. */
|
|
142
162
|
export interface RoomMemberStartup {
|
|
143
163
|
room_id: string;
|
|
@@ -169,14 +189,41 @@ export interface ResolvedRole extends Omit<RoleConfig, 'model' | 'owner_channel'
|
|
|
169
189
|
worklog?: WorklogPolicy;
|
|
170
190
|
auth_proxy?: AuthProxyConfig;
|
|
171
191
|
loops?: ResolvedRoleLoop[];
|
|
192
|
+
provenance?: Record<string, FieldProvenance>;
|
|
172
193
|
/** Internal/transient: never accepted as a user-authored RoleConfig key. */
|
|
173
194
|
roomMemberStartup?: RoomMemberStartup;
|
|
195
|
+
/** Original unresolved selections only; denied operational fields never enter inheritance state. */
|
|
196
|
+
agentSelections?: Pick<AgentDefinition, 'role' | 'brain'>;
|
|
197
|
+
}
|
|
198
|
+
export interface FieldProvenance {
|
|
199
|
+
sourceFile: string;
|
|
200
|
+
sourcePointer: string;
|
|
201
|
+
sourceKind: 'Agent' | 'Role' | 'Brain' | 'Manifest' | 'built-in';
|
|
202
|
+
sourceId?: string;
|
|
203
|
+
origin: 'explicit' | 'typed-default' | 'built-in';
|
|
204
|
+
viaReference?: {
|
|
205
|
+
sourcePointer: string;
|
|
206
|
+
kind: 'Role' | 'Brain';
|
|
207
|
+
id: string;
|
|
208
|
+
};
|
|
209
|
+
transforms: Array<{
|
|
210
|
+
kind: 'substitution' | 'adapter-normalization' | 'validation-default';
|
|
211
|
+
detail: string;
|
|
212
|
+
}>;
|
|
174
213
|
}
|
|
175
214
|
export interface FleetConfig {
|
|
176
215
|
roles: ResolvedRole[];
|
|
216
|
+
/** Canonical, variable-resolved Agent authoring documents keyed by stable Agent ID. */
|
|
217
|
+
agentDefinitions?: Record<string, AgentDefinition>;
|
|
177
218
|
vars: Record<string, string>;
|
|
178
219
|
defaults: Record<string, unknown>;
|
|
179
220
|
files: string[];
|
|
221
|
+
configMode?: 'split-v2';
|
|
222
|
+
sourceDocuments?: Array<{
|
|
223
|
+
kind: 'Manifest' | 'Agent' | 'Role' | 'Brain' | 'RoomTemplate';
|
|
224
|
+
id?: string;
|
|
225
|
+
path: string;
|
|
226
|
+
}>;
|
|
180
227
|
/** Fleet-wide delay (ms) enforced between agent launches to avoid boot bursts (0 = none). */
|
|
181
228
|
startStaggerMs: number;
|
|
182
229
|
/** Warning-first non-plain YAML migration diagnostics, in source order. */
|
|
@@ -207,9 +254,22 @@ export declare function resolveRoleModel(model: string | null | undefined, harne
|
|
|
207
254
|
*/
|
|
208
255
|
export declare function isolationContextFor(role: ResolvedRole): WrapContext;
|
|
209
256
|
export declare const ROLE_NAME_RE: RegExp;
|
|
210
|
-
|
|
257
|
+
export declare const ROLE_PRESET_KEYS: string[];
|
|
258
|
+
export declare const BRAIN_PRESET_KEYS: string[];
|
|
259
|
+
export declare const AGENT_KEYS: string[];
|
|
260
|
+
type BarePreset = Record<string, unknown>;
|
|
261
|
+
export declare function validateRoleValue(value: BarePreset, file: string, pointer: string): void;
|
|
262
|
+
export declare function validateBrainValue(value: BarePreset, file: string, pointer: string): void;
|
|
263
|
+
export declare function assertBareKeys(value: unknown, allowed: string[], label: string): BarePreset;
|
|
264
|
+
export declare function splitRootFor(base: string): string;
|
|
265
|
+
/** Load a v2 manifest and bare Agent/Role/Brain documents from its stem directory. */
|
|
211
266
|
export declare function loadConfig(configPath?: string, options?: {
|
|
212
267
|
yamlMode?: YamlMode;
|
|
268
|
+
additionalAgent?: {
|
|
269
|
+
id: string;
|
|
270
|
+
definition: AgentDefinition;
|
|
271
|
+
};
|
|
272
|
+
skipWatchdogs?: boolean;
|
|
213
273
|
}): FleetConfig;
|
|
214
274
|
/**
|
|
215
275
|
* Canonical form of a 64-hex container ID for authorization decisions. Hex
|