@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
@@ -1,11 +1,10 @@
1
1
  import { existsSync } from 'node:fs';
2
2
  import { isAbsolute } from 'node:path';
3
3
  import { parseDuration } from '../duration.js';
4
- import { ConfigError, ROLE_NAME_RE, resolveRoleModel } from '../config.js';
5
- import { validateIsolationConfig } from '../isolation/policy.js';
4
+ import { ConfigError, ROLE_NAME_RE } from '../config.js';
6
5
  const WATCHDOG_KEYS = [
7
- 'coordinator', 'enabled', 'interval', 'watch', 'harness', 'model', 'session',
8
- 'identity', 'timeout', 'keep_reports', 'alert_cooldown', 'prompt_file', 'isolation',
6
+ 'coordinator', 'enabled', 'interval', 'watch', 'agent',
7
+ 'identity', 'timeout', 'keep_reports', 'alert_cooldown', 'prompt_file',
9
8
  ];
10
9
  export const WATCHDOG_DEFAULT_INTERVAL_MS = 600_000;
11
10
  export const WATCHDOG_MIN_INTERVAL_MS = 60_000;
@@ -21,7 +20,7 @@ function deepSub(v, vars) {
21
20
  return Object.fromEntries(Object.entries(v).map(([k, x]) => [k, deepSub(x, vars)]));
22
21
  return v;
23
22
  }
24
- export function resolveWatchdogs(baseDoc, baseFile, roles, vars, defaults) {
23
+ export function resolveWatchdogs(baseDoc, baseFile, roles, vars, agentDefinitions, resolveInline) {
25
24
  const block = baseDoc.watchdogs;
26
25
  if (block === undefined || block === null)
27
26
  return [];
@@ -42,6 +41,9 @@ export function resolveWatchdogs(baseDoc, baseFile, roles, vars, defaults) {
42
41
  if (roleNames.has(name))
43
42
  throw new ConfigError(`${baseFile}: watchdog '${name}' collides with a role name`);
44
43
  const w = deepSub(rawEntry ?? {}, vars);
44
+ const legacy = ['harness', 'model', 'session', 'isolation'].filter(key => Object.hasOwn(w, key));
45
+ if (legacy.length)
46
+ throw new ConfigError(`${where}: E_LEGACY Brain/Agent-owned field(s) ${legacy.join(', ')} are unsupported; select a canonical Agent with agent`);
45
47
  const bad = Object.keys(w).filter(k => !WATCHDOG_KEYS.includes(k));
46
48
  if (bad.length)
47
49
  throw new ConfigError(`${where} has unknown key(s) ${bad.join(', ')}; allowed: ${WATCHDOG_KEYS.join(', ')}`);
@@ -60,23 +62,29 @@ export function resolveWatchdogs(baseDoc, baseFile, roles, vars, defaults) {
60
62
  if (collidingWatchdog !== undefined)
61
63
  throw new ConfigError(`${where}: identity '${identity}' collides with watchdog '${collidingWatchdog}'`);
62
64
  watchdogIdentities.set(identity, name);
63
- const harness = w.harness ?? defaults.harness ?? 'claude-code';
64
- const sessionRaw = w.session ?? defaults.session ?? 'acp';
65
- if (sessionRaw === 'tmux')
66
- throw new ConfigError(`${where}: session 'tmux' is no longer supported; use session: acp`);
67
- if (sessionRaw !== 'acp')
68
- throw new ConfigError(`${where}: session must be 'acp'`);
65
+ if (!w.agent || typeof w.agent !== 'object' || Array.isArray(w.agent))
66
+ throw new ConfigError(`${where}: agent is required (Agent ref or canonical Agent definition)`);
67
+ let agentDefinition;
68
+ let resolvedAgent;
69
+ if ('ref' in w.agent) {
70
+ const ref = w.agent.ref;
71
+ if (typeof ref !== 'string' || !ref)
72
+ throw new ConfigError(`${where}: agent.ref must be a declared Agent ID`);
73
+ agentDefinition = agentDefinitions[ref];
74
+ resolvedAgent = roles.find(role => role.name === ref);
75
+ if (!agentDefinition || !resolvedAgent)
76
+ throw new ConfigError(`${where}: Agent ref '${ref}' was not found`);
77
+ }
78
+ else {
79
+ agentDefinition = structuredClone(w.agent);
80
+ resolvedAgent = resolveInline(name, agentDefinition);
81
+ }
69
82
  if (w.prompt_file !== undefined) {
70
83
  if (typeof w.prompt_file !== 'string' || !isAbsolute(w.prompt_file))
71
84
  throw new ConfigError(`${where}: prompt_file must be an absolute path`);
72
85
  if (!existsSync(w.prompt_file))
73
86
  throw new ConfigError(`${where}: prompt_file not found: ${w.prompt_file}`);
74
87
  }
75
- if (w.isolation !== undefined) {
76
- const problems = validateIsolationConfig(w.isolation);
77
- if (problems.length)
78
- throw new ConfigError(`${where} ${problems.join('; ')}`);
79
- }
80
88
  const dur = (v, key, fallback, minMs) => {
81
89
  if (v === undefined)
82
90
  return fallback;
@@ -96,13 +104,15 @@ export function resolveWatchdogs(baseDoc, baseFile, roles, vars, defaults) {
96
104
  name, coordinator: w.coordinator.trim(),
97
105
  enabled: w.enabled ?? true,
98
106
  intervalMs: dur(w.interval, 'interval', WATCHDOG_DEFAULT_INTERVAL_MS, WATCHDOG_MIN_INTERVAL_MS),
99
- watch, watchExplicit: w.watch !== undefined, harness, session: sessionRaw,
100
- model: resolveRoleModel(w.model, w.harness, defaults),
107
+ watch, watchExplicit: w.watch !== undefined,
108
+ agentDefinition: structuredClone(agentDefinition), resolvedAgent: structuredClone(resolvedAgent),
109
+ harness: resolvedAgent.harness, session: resolvedAgent.session,
110
+ model: resolvedAgent.model, isolation: resolvedAgent.isolation,
101
111
  identity,
102
112
  timeoutMs: dur(w.timeout, 'timeout', WATCHDOG_DEFAULT_TIMEOUT_MS),
103
113
  keepReports,
104
114
  alertCooldownMs: dur(w.alert_cooldown, 'alert_cooldown', WATCHDOG_DEFAULT_COOLDOWN_MS),
105
- promptFile: w.prompt_file, isolation: w.isolation, sourceFile: baseFile,
115
+ promptFile: w.prompt_file, sourceFile: baseFile,
106
116
  });
107
117
  }
108
118
  return out;
@@ -10,7 +10,7 @@ import { applyRole } from '../ops.js';
10
10
  import { daemonIdentityInventoryProvisioner, ensureIdentity, } from '../creation.js';
11
11
  import { runOnce, START_STAGGER_FILE } from '../runner.js';
12
12
  import { agentDir, tmpRoot } from '../paths.js';
13
- import { loadConfig, resolveMonitorConfig, resolveWorklogPolicy, ROLE_NAME_RE, } from '../config.js';
13
+ import { loadConfig, ROLE_NAME_RE, } from '../config.js';
14
14
  import { getAdapter } from '../harness/registry.js';
15
15
  import { redactLogLine } from '../application/log-service.js';
16
16
  import { controlRequest, controlSocketPath } from '../session/control.js';
@@ -84,19 +84,10 @@ function readTail(runDir) {
84
84
  }
85
85
  }
86
86
  /** The temp role every watchdog-family run launches under. Isolation is opt-in. */
87
- function buildWatchdogRole(wd, cfg) {
87
+ function buildWatchdogRole(wd, _cfg) {
88
88
  return {
89
- name: wd.identity, sourceFile: '(watchdog)',
90
- harness: wd.harness, session: wd.session,
91
- identity: wd.identity, model: wd.model,
92
- // Watchdogs are observe-only by contract, but their sanctioned status commands
93
- // must reach host control sockets. Keep approvals/unattended escalation denied
94
- // while disabling the harness's native filesystem/network sandbox.
95
- permissions: { approval: 'deny', filesystem: 'unrestricted', unattended: 'deny' },
96
- permissionsDeclared: true,
97
- monitor: resolveMonitorConfig(cfg.defaults.monitor, undefined),
98
- worklog: resolveWorklogPolicy(cfg.defaults.worklog, undefined),
99
- isolation: wd.isolation,
89
+ ...structuredClone(wd.resolvedAgent),
90
+ name: wd.identity, sourceFile: '(watchdog)', identity: wd.identity,
100
91
  };
101
92
  }
102
93
  /** Discover temporary sessions that are live now, not merely stale dirs on disk. */
@@ -1,10 +1,13 @@
1
1
  import type { PrereqReport } from '../harness/types.js';
2
2
  export declare const REDACTED_ENV_VALUE = "__OURS_FLEET_SECRET_REDACTED__";
3
- export type EditableFleetModel = Record<string, unknown>;
3
+ export interface EditableFleetModel {
4
+ manifest: Record<string, unknown>;
5
+ agents: Record<string, Record<string, unknown>>;
6
+ }
4
7
  export interface ConfigReadResult {
5
8
  path: string;
6
- exists: boolean;
7
- firstRun: boolean;
9
+ exists: true;
10
+ firstRun: false;
8
11
  revision: string;
9
12
  model: EditableFleetModel;
10
13
  redactions: string[];
@@ -33,15 +36,15 @@ export interface ConfigWriteResult extends ConfigPreviewResult {
33
36
  export interface FleetConfigServiceOptions {
34
37
  configPath?: string;
35
38
  preflight?(configPath: string): Promise<PrereqReport>;
39
+ /** Fault-injection seam; called under lock immediately before each mutation. */
40
+ beforeMutation?(relativePath: string, index: number): void;
36
41
  }
37
42
  export declare class FleetConfigService {
38
43
  readonly path: string;
39
44
  private readonly preflight;
45
+ private readonly beforeMutation?;
40
46
  constructor(options?: FleetConfigServiceOptions);
41
47
  read(): ConfigReadResult;
42
- preview(baseRevision: string, model: unknown): Promise<ConfigPreviewResult>;
43
- write(baseRevision: string, model: unknown): Promise<ConfigWriteResult>;
44
- private readSource;
45
- private assertRevision;
46
- private validateCandidate;
48
+ preview(baseRevision: string, input: unknown): Promise<ConfigPreviewResult>;
49
+ write(baseRevision: string, input: unknown): Promise<ConfigWriteResult>;
47
50
  }