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

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 (65) hide show
  1. package/README.md +76 -93
  2. package/dist/application/role-creation-service.d.ts +2 -21
  3. package/dist/application/role-creation-service.js +15 -64
  4. package/dist/application/role-removal-service.js +6 -3
  5. package/dist/application/task-room-service.js +7 -2
  6. package/dist/application/types.d.ts +1 -1
  7. package/dist/briefing.js +5 -5
  8. package/dist/build-info.json +4 -4
  9. package/dist/cli.js +48 -38
  10. package/dist/config.d.ts +65 -2
  11. package/dist/config.js +337 -25
  12. package/dist/creation.d.ts +1 -1
  13. package/dist/creation.js +2 -2
  14. package/dist/docs.d.ts +1 -1
  15. package/dist/docs.js +48 -97
  16. package/dist/doctor.js +4 -4
  17. package/dist/fleet-proxy.js +30 -8
  18. package/dist/generated-agent-source.d.ts +9 -0
  19. package/dist/generated-agent-source.js +53 -0
  20. package/dist/harness/agent-session.d.ts +13 -0
  21. package/dist/harness/claude-code-session.d.ts +4 -0
  22. package/dist/harness/claude-code-session.js +4 -0
  23. package/dist/harness/claude-code.js +10 -0
  24. package/dist/harness/codex-session.d.ts +4 -0
  25. package/dist/harness/codex-session.js +4 -0
  26. package/dist/harness/codex.js +14 -0
  27. package/dist/harness/registry.js +4 -0
  28. package/dist/index.d.ts +1 -1
  29. package/dist/index.js +1 -1
  30. package/dist/init-guidance.d.ts +1 -0
  31. package/dist/init-guidance.js +5 -0
  32. package/dist/model-env.d.ts +2 -3
  33. package/dist/model-env.js +3 -5
  34. package/dist/ops.js +4 -2
  35. package/dist/owner-channel/commands.js +2 -1
  36. package/dist/resolved-plan.d.ts +3 -2
  37. package/dist/resolved-plan.js +21 -5
  38. package/dist/rooms-tasks/cli.js +2 -1
  39. package/dist/rooms-tasks/config.js +24 -9
  40. package/dist/rooms-tasks/provision.js +71 -36
  41. package/dist/rooms-tasks/templates.js +6 -6
  42. package/dist/rooms-tasks/types.d.ts +8 -15
  43. package/dist/rooms-tasks/types.js +1 -4
  44. package/dist/sensitive-config.d.ts +2 -0
  45. package/dist/sensitive-config.js +4 -0
  46. package/dist/spawn.d.ts +8 -33
  47. package/dist/spawn.js +52 -211
  48. package/dist/watchdog/config.d.ts +10 -7
  49. package/dist/watchdog/config.js +29 -19
  50. package/dist/watchdog/run.js +4 -13
  51. package/dist/web/fleet-config-service.d.ts +11 -8
  52. package/dist/web/fleet-config-service.js +383 -183
  53. package/dist/web/topology-promote.js +25 -15
  54. package/dist/web/topology.js +2 -2
  55. package/dist/web/yaml-document-edit.js +2 -0
  56. package/dist/web-app/assets/index-BbTc5KtZ.js +10 -0
  57. package/dist/web-app/index.html +1 -1
  58. package/examples/fleet/agents/Alice.yaml +11 -0
  59. package/examples/fleet/agents/FleetCoordinator.yaml +7 -0
  60. package/examples/fleet/brains/claude-default.yaml +5 -0
  61. package/examples/fleet/roles/coordinator.yaml +5 -0
  62. package/examples/fleet/roles/developer.yaml +3 -0
  63. package/examples/fleet.yaml +74 -0
  64. package/package.json +2 -1
  65. package/dist/web-app/assets/index-DhMDVRU1.js +0 -10
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
- import { findRole, loadConfig } from './config.js';
12
+ import { findRole, loadConfig, ROLE_NAME_RE } 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';
@@ -73,10 +74,10 @@ const passthrough = (cmd, args) => new Promise(resolve => {
73
74
  });
74
75
  const program = new Command()
75
76
  .name('ours-fleet')
76
- .description('Fleet of persistent, identity-bound AI agents — selectable harnesses over ACP sessions.')
77
+ .description('Fleet of persistent, identity-bound AI agents — canonical Brain + Role definitions 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')
@@ -217,6 +218,24 @@ function parseCodexConfig(values) {
217
218
  }
218
219
  return out;
219
220
  }
221
+ function parseAgentSelection(raw, kind) {
222
+ if (raw === undefined)
223
+ return undefined;
224
+ if (ROLE_NAME_RE.test(raw))
225
+ return { ref: raw };
226
+ if (!raw.startsWith('inline:'))
227
+ throw new Error(`invalid --${kind}; use a declared ID or inline:{...}`);
228
+ let value;
229
+ try {
230
+ value = JSON.parse(raw.slice('inline:'.length));
231
+ }
232
+ catch {
233
+ throw new Error(`invalid --${kind} inline mapping`);
234
+ }
235
+ if (!value || typeof value !== 'object' || Array.isArray(value))
236
+ throw new Error(`invalid --${kind} inline mapping`);
237
+ return { inline: value };
238
+ }
220
239
  cOpt(program.command('config').description('validate + print the merged plan (no side effects)'))
221
240
  .option('--json', 'emit the stable, versioned, secret-safe resolved plan')
222
241
  .option('--yaml-mode <mode>', 'non-plain YAML policy: compat|strict', 'compat')
@@ -260,14 +279,16 @@ cOpt(program.command('config').description('validate + print the merged plan (no
260
279
  console.log(` cwd: ${r.cwd}`);
261
280
  if (r.model)
262
281
  console.log(` model: ${r.model}`);
282
+ if (r.effort)
283
+ console.log(` effort: ${r.effort}`);
263
284
  if (r.harness_options && Object.keys(r.harness_options).length)
264
- console.log(` options: ${JSON.stringify(r.harness_options)}`);
285
+ console.log(` options: ${JSON.stringify(redactSensitive(r.harness_options))}`);
265
286
  if (r.coordinator)
266
287
  console.log(` coordinator: ${r.coordinator}`);
267
288
  if (r.mission)
268
289
  console.log(` mission: ${r.mission.split('\n')[0]}`);
269
290
  if (r.oversee?.length)
270
- console.log(` oversees: ${r.oversee.map(o => `${o.role}@${o.interval}`).join(', ')}`);
291
+ console.log(` oversees: ${r.oversee.map(o => `${o.agent}@${o.interval}`).join(', ')}`);
271
292
  if (r.isolation) {
272
293
  const iso = r.isolation;
273
294
  const caps = [
@@ -1109,50 +1130,32 @@ cOpt(program.command('rm <name>').description('stop + remove a role (temporary e
1109
1130
  }
1110
1131
  });
1111
1132
  cOpt(program.command('spawn [name]').description('spawn a new agent (permanent by default)'))
1112
- .option('--role <name>', 'role name (alternative to the positional name)')
1133
+ .option('--name <name>', 'agent name (alternative to the positional name)')
1134
+ .option('--brain <selection>', 'Brain ID or inline:{...} definition')
1135
+ .option('--role <selection>', 'Role ID or inline:{...} definition')
1113
1136
  .option('--temp', 'temporary: independent transient supervisor, archived on retirement, gone on reboot')
1114
- .option('--harness <id>', 'harness adapter (default: defaults.harness)')
1115
- .option('--session <backend>', 'session backend: acp (default: acp)')
1116
- .option('--mission <text>', 'one-line mission')
1117
- .option('--mission-file <path>', 'UTF-8 mission text (mutually exclusive with --mission)')
1118
- .option('--identity <name>', 'ours identity to bind (default: role name)')
1137
+ .option('--identity <name>', 'ours identity to bind (default: Agent name)')
1119
1138
  .option('--cwd <dir>', 'working directory')
1120
1139
  .option('--coordinator <name>', 'announce target')
1121
- .option('--model <id>', 'model id to launch on (e.g. claude-fable-5); default: launcher default')
1122
- .option('--permission-mode <mode>', 'harness permission mode (Codex: untrusted|on-request|never; Claude: native values)')
1123
1140
  .option('--approval <mode>', 'fleet permission mode: ask|auto|allow (Codex ACP allow selects agent-full-access; deny is deprecated)')
1124
1141
  .option('--filesystem <mode>', 'common filesystem intent: read-only|workspace|unrestricted')
1125
1142
  .option('--unattended <mode>', 'permission behavior without a console: deny|wait')
1126
- .option('--sandbox <mode>', 'Codex sandbox: read-only|workspace-write|danger-full-access')
1127
- .option('--profile <name>', 'Codex profile file name ($CODEX_HOME/<name>.config.toml)')
1128
- .option('--launcher <mode>', 'Codex launcher: auto|ours-codex|codex (default: auto)')
1129
- .option('--search', 'enable Codex live web search')
1130
- .option('--codex-config <key=value>', 'Codex config override (repeatable)', collect, [])
1131
- .option('--add-dir <dir>', 'additional Codex writable directory (repeatable)', collect, [])
1132
- .option('--monitor', 'legacy: consent to arm Codex\'s native monitor (wake owner is monitor.mode in YAML)')
1133
- .option('--bio-file <file>', 'public bio (file)')
1134
- .option('--persona-file <file>', 'persona / operating contract (file)')
1135
1143
  .option('--isolation-file <path>', 'file holding an isolation: mapping (same schema as fleet.yaml)')
1136
1144
  .option('--dry-run', 'validate and print without reserving or creating anything')
1137
1145
  .option('--json', 'with --dry-run, emit a stable secret-safe JSON result')
1138
1146
  .action(async (name, opts) => {
1139
1147
  try {
1140
- const roleName = String(name ?? opts.role ?? '');
1148
+ const roleName = String(name ?? opts.name ?? '');
1141
1149
  if (!roleName)
1142
- throw new Error('role name is required (positional or --role)');
1143
- if (name && opts.role && name !== opts.role)
1144
- throw new Error(`positional role '${name}' conflicts with --role '${opts.role}'`);
1150
+ throw new Error('agent name is required (positional or --name)');
1151
+ if (name && opts.name && name !== opts.name)
1152
+ throw new Error(`positional agent name conflicts with --name`);
1145
1153
  const o = {
1146
- name: roleName, temp: opts.temp, harness: opts.harness, session: opts.session, mission: opts.mission,
1147
- missionFile: opts.missionFile,
1154
+ name: roleName, temp: opts.temp,
1155
+ brain: parseAgentSelection(opts.brain, 'brain'), role: parseAgentSelection(opts.role, 'role'),
1148
1156
  identity: opts.identity, cwd: opts.cwd, coordinator: opts.coordinator,
1149
- model: opts.model,
1150
- permissionMode: opts.permissionMode, approval: opts.approval,
1157
+ approval: opts.approval,
1151
1158
  filesystem: opts.filesystem, unattended: opts.unattended,
1152
- sandbox: opts.sandbox, profile: opts.profile,
1153
- launcher: opts.launcher, search: opts.search,
1154
- codexConfig: parseCodexConfig(opts.codexConfig), addDirs: opts.addDir, monitor: opts.monitor,
1155
- bioFile: opts.bioFile, personaFile: opts.personaFile,
1156
1159
  isolationFile: opts.isolationFile, configPath: opts.configuration,
1157
1160
  dryRun: opts.dryRun, json: opts.json,
1158
1161
  };
@@ -1181,7 +1184,7 @@ cOpt(program.command('spawn [name]').description('spawn a new agent (permanent b
1181
1184
  if (proxyStateDir) {
1182
1185
  // Paths entered in the agent shell belong to that shell's cwd, not the
1183
1186
  // supervisor process. Normalize before crossing the control boundary.
1184
- for (const key of ['missionFile', 'bioFile', 'personaFile', 'isolationFile']) {
1187
+ for (const key of ['isolationFile']) {
1185
1188
  if (o[key])
1186
1189
  o[key] = resolvePath(o[key]);
1187
1190
  }
@@ -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()
@@ -1494,4 +1497,11 @@ cOpt(program.command('_run-watchdogs', { hidden: true }))
1494
1497
  die(e);
1495
1498
  }
1496
1499
  });
1497
- program.parseAsync(process.argv);
1500
+ const spawnIndex = process.argv.indexOf('spawn');
1501
+ if (spawnIndex >= 0 && process.argv.slice(spawnIndex + 1).some(arg => arg === '--harness' || arg.startsWith('--harness=') || arg === '--model' || arg.startsWith('--model='))) {
1502
+ console.error('error: --harness and --model were removed; select a Brain with --brain');
1503
+ process.exitCode = 1;
1504
+ }
1505
+ else {
1506
+ program.parseAsync(process.argv);
1507
+ }
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;
@@ -135,6 +137,27 @@ export interface RoleConfig {
135
137
  worklog?: WorklogPolicyInput;
136
138
  auth_proxy?: Partial<AuthProxyConfig>;
137
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
+ }
138
161
  /** Internal first-boot payload for a Fleet-provisioned Cowork room member. */
139
162
  export interface RoomMemberStartup {
140
163
  room_id: string;
@@ -166,14 +189,41 @@ export interface ResolvedRole extends Omit<RoleConfig, 'model' | 'owner_channel'
166
189
  worklog?: WorklogPolicy;
167
190
  auth_proxy?: AuthProxyConfig;
168
191
  loops?: ResolvedRoleLoop[];
192
+ provenance?: Record<string, FieldProvenance>;
169
193
  /** Internal/transient: never accepted as a user-authored RoleConfig key. */
170
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
+ }>;
171
213
  }
172
214
  export interface FleetConfig {
173
215
  roles: ResolvedRole[];
216
+ /** Canonical, variable-resolved Agent authoring documents keyed by stable Agent ID. */
217
+ agentDefinitions?: Record<string, AgentDefinition>;
174
218
  vars: Record<string, string>;
175
219
  defaults: Record<string, unknown>;
176
220
  files: string[];
221
+ configMode?: 'split-v2';
222
+ sourceDocuments?: Array<{
223
+ kind: 'Manifest' | 'Agent' | 'Role' | 'Brain';
224
+ id?: string;
225
+ path: string;
226
+ }>;
177
227
  /** Fleet-wide delay (ms) enforced between agent launches to avoid boot bursts (0 = none). */
178
228
  startStaggerMs: number;
179
229
  /** Warning-first non-plain YAML migration diagnostics, in source order. */
@@ -204,9 +254,22 @@ export declare function resolveRoleModel(model: string | null | undefined, harne
204
254
  */
205
255
  export declare function isolationContextFor(role: ResolvedRole): WrapContext;
206
256
  export declare const ROLE_NAME_RE: RegExp;
207
- /** Load ~/fleet.yaml (or an explicit path) merged with ~/fleet.d/*.yaml drop-ins. */
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. */
208
266
  export declare function loadConfig(configPath?: string, options?: {
209
267
  yamlMode?: YamlMode;
268
+ additionalAgent?: {
269
+ id: string;
270
+ definition: AgentDefinition;
271
+ };
272
+ skipWatchdogs?: boolean;
210
273
  }): FleetConfig;
211
274
  /**
212
275
  * Canonical form of a 64-hex container ID for authorization decisions. Hex