@ours.network/fleet 1.1.0-nightly.1 → 1.1.0-nightly.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +64 -73
- package/dist/application/role-creation-service.js +9 -2
- package/dist/application/role-removal-service.js +6 -3
- package/dist/application/types.d.ts +1 -1
- package/dist/briefing.js +2 -2
- package/dist/build-info.json +4 -4
- package/dist/cli.js +8 -5
- package/dist/config.d.ts +28 -2
- package/dist/config.js +307 -23
- package/dist/creation.d.ts +1 -1
- package/dist/creation.js +2 -2
- package/dist/docs.d.ts +1 -1
- package/dist/docs.js +31 -69
- package/dist/doctor.js +1 -1
- package/dist/generated-agent-source.d.ts +9 -0
- package/dist/generated-agent-source.js +53 -0
- package/dist/harness/agent-session.d.ts +13 -0
- package/dist/harness/claude-code-session.d.ts +4 -0
- package/dist/harness/claude-code-session.js +4 -0
- package/dist/harness/claude-code.js +10 -0
- package/dist/harness/codex-session.d.ts +4 -0
- package/dist/harness/codex-session.js +4 -0
- package/dist/harness/codex.js +14 -0
- package/dist/harness/registry.js +4 -0
- package/dist/init-guidance.d.ts +1 -0
- package/dist/init-guidance.js +5 -0
- package/dist/model-env.d.ts +0 -1
- package/dist/model-env.js +2 -4
- package/dist/ops.js +4 -2
- package/dist/resolved-plan.d.ts +3 -2
- package/dist/resolved-plan.js +21 -5
- package/dist/sensitive-config.d.ts +2 -0
- package/dist/sensitive-config.js +4 -0
- package/dist/spawn.d.ts +4 -4
- package/dist/spawn.js +52 -23
- package/dist/web/fleet-config-service.d.ts +11 -8
- package/dist/web/fleet-config-service.js +383 -183
- package/dist/web/topology-promote.js +17 -14
- package/dist/web/topology.js +2 -2
- package/dist/web/yaml-document-edit.js +2 -0
- package/dist/web-app/assets/index-BbE9EX6n.js +10 -0
- package/dist/web-app/index.html +1 -1
- 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 +2 -1
- package/dist/web-app/assets/index-DhMDVRU1.js +0 -10
package/dist/model-env.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { getAdapter } from './harness/registry.js';
|
|
1
2
|
/**
|
|
2
3
|
* Which environment variable a harness reads to pin the model it RUNS.
|
|
3
4
|
*
|
|
@@ -9,12 +10,9 @@
|
|
|
9
10
|
* ACP role, and a fleet-wide `defaults.env.ANTHROPIC_MODEL` silently outranked
|
|
10
11
|
* an explicitly requested one.
|
|
11
12
|
*/
|
|
12
|
-
export const MODEL_ENV_BY_HARNESS = {
|
|
13
|
-
'claude-code': 'ANTHROPIC_MODEL',
|
|
14
|
-
};
|
|
15
13
|
/** The model-pin variable for a harness, or undefined if it pins no model by env. */
|
|
16
14
|
export function modelEnvVar(harness) {
|
|
17
|
-
return harness === undefined ? undefined :
|
|
15
|
+
return harness === undefined ? undefined : getAdapter(harness).agentSession.modelEnvironmentVariable();
|
|
18
16
|
}
|
|
19
17
|
/**
|
|
20
18
|
* Resolve a role's environment and its runtime model TOGETHER, so the two can
|
package/dist/ops.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync, rmSync, unlinkSync, } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { randomUUID, createHash } from 'node:crypto';
|
|
4
|
-
import { agentDir,
|
|
4
|
+
import { agentDir, watchdogsRoot } from './paths.js';
|
|
5
5
|
import { findRole } from './config.js';
|
|
6
6
|
import { getAdapter } from './harness/registry.js';
|
|
7
7
|
import { generateBriefing } from './briefing.js';
|
|
@@ -9,6 +9,7 @@ import { resetRestartLedger } from './runner.js';
|
|
|
9
9
|
import { archiveTempState, stopTempSupervisor, tempSupervisorLiveness, } from './temp-lifecycle.js';
|
|
10
10
|
import { realExec } from './exec.js';
|
|
11
11
|
import { daemonIdentityProvisioner, reconcilePermanentRoleIdentities, } from './creation.js';
|
|
12
|
+
import { provesGeneratedAgentSource } from './generated-agent-source.js';
|
|
12
13
|
// Launch staggering now lives at the harness-launch point (the runner's start
|
|
13
14
|
// gate, driven by `start_stagger_ms`), so it covers systemd host-boot too — not
|
|
14
15
|
// just the `up`/`restart` command loop below. The old in-loop FLEET_START_STAGGER
|
|
@@ -264,9 +265,10 @@ export async function rmRole(cfg, name, deps) {
|
|
|
264
265
|
return;
|
|
265
266
|
}
|
|
266
267
|
const role = findRole(cfg, name);
|
|
268
|
+
const generatedSource = provesGeneratedAgentSource(agentDir(name), cfg.files[0], role.sourceFile);
|
|
267
269
|
await deps.backend.uninstall(name);
|
|
268
270
|
rmSync(agentDir(name), { recursive: true, force: true });
|
|
269
|
-
if (role.sourceFile
|
|
271
|
+
if (generatedSource && existsSync(role.sourceFile)) {
|
|
270
272
|
unlinkSync(role.sourceFile);
|
|
271
273
|
deps.log(`removed ${role.sourceFile}`);
|
|
272
274
|
}
|
package/dist/resolved-plan.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { FleetConfig, ResolvedRole } from './config.js';
|
|
2
|
-
export declare const RESOLVED_PLAN_SCHEMA_VERSION =
|
|
2
|
+
export declare const RESOLVED_PLAN_SCHEMA_VERSION = 2;
|
|
3
|
+
export declare const redactSensitive: (value: unknown, key?: string) => unknown;
|
|
3
4
|
/** Stable, secret-safe machine contract consumed by config JSON and spawn dry-run. */
|
|
4
5
|
export declare function resolvedPlan(cfg: FleetConfig): Record<string, unknown>;
|
|
5
|
-
export declare function resolvedRolePlan(role: ResolvedRole): Record<string, unknown>;
|
|
6
|
+
export declare function resolvedRolePlan(role: ResolvedRole, manifestDir?: string): Record<string, unknown>;
|
package/dist/resolved-plan.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { analyzeRolePermissions } from './permissions.js';
|
|
2
|
-
|
|
2
|
+
import { dirname, relative } from 'node:path';
|
|
3
|
+
import { isSensitiveConfigKey } from './sensitive-config.js';
|
|
4
|
+
export const RESOLVED_PLAN_SCHEMA_VERSION = 2;
|
|
3
5
|
const sortedObject = (value) => Object.fromEntries(Object.entries(value).sort(([a], [b]) => a.localeCompare(b)));
|
|
4
|
-
const redactSensitive = (value, key = '') => {
|
|
5
|
-
if (
|
|
6
|
+
export const redactSensitive = (value, key = '') => {
|
|
7
|
+
if (isSensitiveConfigKey(key))
|
|
6
8
|
return '<redacted>';
|
|
7
9
|
if (Array.isArray(value))
|
|
8
10
|
return value.map(item => redactSensitive(item));
|
|
@@ -14,12 +16,17 @@ const redactSensitive = (value, key = '') => {
|
|
|
14
16
|
};
|
|
15
17
|
/** Stable, secret-safe machine contract consumed by config JSON and spawn dry-run. */
|
|
16
18
|
export function resolvedPlan(cfg) {
|
|
19
|
+
const manifestDir = dirname(cfg.files[0] ?? '.');
|
|
17
20
|
return {
|
|
18
21
|
schemaVersion: RESOLVED_PLAN_SCHEMA_VERSION,
|
|
22
|
+
configMode: cfg.configMode ?? 'split-v2',
|
|
19
23
|
sourceFiles: [...cfg.files],
|
|
24
|
+
sourceDocuments: (cfg.sourceDocuments ?? []).map(document => ({
|
|
25
|
+
...document, path: relative(manifestDir, document.path) || document.path,
|
|
26
|
+
})),
|
|
20
27
|
startStaggerMs: cfg.startStaggerMs,
|
|
21
28
|
diagnostics: cfg.diagnostics.map(diagnostic => ({ ...diagnostic })),
|
|
22
|
-
roles: cfg.roles.map(resolvedRolePlan),
|
|
29
|
+
roles: cfg.roles.map(role => resolvedRolePlan(role, manifestDir)),
|
|
23
30
|
loops: cfg.loops.map(loop => sortedObject({
|
|
24
31
|
name: loop.name, selectors: [...loop.selectors], roles: [...loop.roleNames],
|
|
25
32
|
intervalMs: loop.intervalMs, initialDelayMs: loop.initialDelayMs, jitterMs: loop.jitterMs,
|
|
@@ -36,16 +43,25 @@ export function resolvedPlan(cfg) {
|
|
|
36
43
|
})),
|
|
37
44
|
};
|
|
38
45
|
}
|
|
39
|
-
export function resolvedRolePlan(role) {
|
|
46
|
+
export function resolvedRolePlan(role, manifestDir = dirname(role.sourceFile)) {
|
|
40
47
|
const analysis = analyzeRolePermissions(role);
|
|
41
48
|
return {
|
|
42
49
|
name: role.name,
|
|
43
50
|
sourceFile: role.sourceFile,
|
|
51
|
+
references: Object.values(role.provenance ?? {}).flatMap(item => item.viaReference ? [item.viaReference] : [])
|
|
52
|
+
.filter((item, index, all) => all.findIndex(other => JSON.stringify(other) === JSON.stringify(item)) === index)
|
|
53
|
+
.sort((a, b) => a.kind.localeCompare(b.kind) || a.id.localeCompare(b.id)
|
|
54
|
+
|| a.sourcePointer.localeCompare(b.sourcePointer)),
|
|
55
|
+
provenance: sortedObject(Object.fromEntries(Object.entries(role.provenance ?? {}).map(([field, item]) => [
|
|
56
|
+
field, { ...item, sourceFile: item.sourceKind === 'built-in'
|
|
57
|
+
? item.sourceFile : relative(manifestDir, item.sourceFile) || item.sourceFile },
|
|
58
|
+
]))),
|
|
44
59
|
identity: role.identity,
|
|
45
60
|
harness: role.harness,
|
|
46
61
|
session: role.session,
|
|
47
62
|
sessionOptions: role.session_options ?? null,
|
|
48
63
|
model: role.model ?? null,
|
|
64
|
+
effort: role.effort ?? null,
|
|
49
65
|
modelChain: role.model_chain ?? null,
|
|
50
66
|
cwd: role.cwd ?? null,
|
|
51
67
|
coordinator: role.coordinator ?? null,
|
package/dist/spawn.d.ts
CHANGED
|
@@ -85,17 +85,17 @@ export declare const effectiveIdentity: (o: SpawnOpts) => string;
|
|
|
85
85
|
export interface SpawnDryRun {
|
|
86
86
|
schemaVersion: 1;
|
|
87
87
|
warning: string;
|
|
88
|
-
roleDocument:
|
|
89
|
-
roles: Record<string, RoleConfig>;
|
|
90
|
-
};
|
|
88
|
+
roleDocument: Record<string, unknown>;
|
|
91
89
|
resolvedRole: ResolvedRole;
|
|
92
90
|
}
|
|
91
|
+
/** Bare Agent document written by v2 spawn: inline Role × inline Brain + operations. */
|
|
92
|
+
export declare function buildAgentDocument(raw: RoleConfig): Record<string, unknown>;
|
|
93
93
|
/**
|
|
94
94
|
* Validate and resolve a spawn without reserving names, contacting the daemon,
|
|
95
95
|
* or writing state. Collision checks are necessarily a point-in-time snapshot.
|
|
96
96
|
*/
|
|
97
97
|
export declare function spawnDryRun(o: SpawnOpts): SpawnDryRun;
|
|
98
|
-
/** Permanent spawn: persist
|
|
98
|
+
/** Permanent spawn: persist one bare Agent document under the selected v2 root. */
|
|
99
99
|
export declare function spawnPermanent(o: SpawnOpts, deps: OpsDeps, creation?: CreationDeps): Promise<string>;
|
|
100
100
|
export type { SupervisorLauncher } from './temp-lifecycle.js';
|
|
101
101
|
/** Temp spawn: live state under ~/.ours-fleet/tmp, independent transient supervision. */
|
package/dist/spawn.js
CHANGED
|
@@ -2,14 +2,15 @@ import { spawn as spawnChild } from 'node:child_process';
|
|
|
2
2
|
import { closeSync, existsSync, mkdirSync, openSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
4
|
import { parse, stringify } from 'yaml';
|
|
5
|
-
import { agentDir,
|
|
5
|
+
import { agentDir, defaultConfigPath } from './paths.js';
|
|
6
6
|
import { validateIsolationConfig } from './isolation/policy.js';
|
|
7
|
-
import { loadConfig, resolveAuthProxy, resolveModelChain, resolveMonitorConfig, resolveOwnerChannelConfig, resolvePermissions, resolveRoleModel, resolveWorklogPolicy, validateMonitorConfig, } from './config.js';
|
|
7
|
+
import { loadConfig, resolveAuthProxy, resolveModelChain, resolveMonitorConfig, resolveOwnerChannelConfig, resolvePermissions, resolveRoleModel, resolveWorklogPolicy, splitRootFor, validateMonitorConfig, } from './config.js';
|
|
8
8
|
import { resolveRoleModelEnv } from './model-env.js';
|
|
9
9
|
import { applyRole, up } from './ops.js';
|
|
10
10
|
import { START_STAGGER_FILE } from './runner.js';
|
|
11
11
|
import { buildProvenance, daemonIdentityProvisioner, ensureIdentity, provenanceOf, withCreationTransaction, writeProvenance, writeRoleFile, } from './creation.js';
|
|
12
12
|
import { VERSION } from './version.js';
|
|
13
|
+
import { recordGeneratedAgentSource } from './generated-agent-source.js';
|
|
13
14
|
import './harness/claude-code.js';
|
|
14
15
|
import './harness/codex.js';
|
|
15
16
|
import { getAdapter } from './harness/registry.js';
|
|
@@ -34,8 +35,9 @@ export function profileValues(o) {
|
|
|
34
35
|
/** Pure option-to-role mapping shared by CLI and application services. */
|
|
35
36
|
export function buildRoleConfig(o, defaultHarness) {
|
|
36
37
|
const r = {};
|
|
37
|
-
|
|
38
|
-
|
|
38
|
+
const harness = o.harness ?? defaultHarness;
|
|
39
|
+
if (harness)
|
|
40
|
+
r.harness = harness;
|
|
39
41
|
if (o.session)
|
|
40
42
|
r.session = o.session;
|
|
41
43
|
if (o.identity)
|
|
@@ -52,7 +54,8 @@ export function buildRoleConfig(o, defaultHarness) {
|
|
|
52
54
|
r.model = null;
|
|
53
55
|
else if (o.model?.trim())
|
|
54
56
|
r.model = o.model.trim();
|
|
55
|
-
|
|
57
|
+
if (o.reasoningEffort?.trim())
|
|
58
|
+
r.effort = o.reasoningEffort.trim();
|
|
56
59
|
const harnessOptions = {};
|
|
57
60
|
if (o.permissionMode)
|
|
58
61
|
harnessOptions[harness === 'claude-code' ? 'permission_mode' : 'approval'] = o.permissionMode;
|
|
@@ -65,12 +68,8 @@ export function buildRoleConfig(o, defaultHarness) {
|
|
|
65
68
|
if (o.search === true)
|
|
66
69
|
harnessOptions.search = true;
|
|
67
70
|
const codexConfig = { ...(o.codexConfig ?? {}) };
|
|
68
|
-
if (o.reasoningEffort && harness === 'codex')
|
|
69
|
-
codexConfig.model_reasoning_effort = o.reasoningEffort;
|
|
70
71
|
if (Object.keys(codexConfig).length)
|
|
71
72
|
harnessOptions.config = codexConfig;
|
|
72
|
-
if (o.reasoningEffort && harness === 'claude-code')
|
|
73
|
-
harnessOptions.effort = o.reasoningEffort;
|
|
74
73
|
if (o.addDirs?.length)
|
|
75
74
|
harnessOptions.add_dirs = o.addDirs;
|
|
76
75
|
if (o.monitor === true)
|
|
@@ -174,6 +173,23 @@ function assertNameFree(o) {
|
|
|
174
173
|
}
|
|
175
174
|
/** The ours identity a spawn will bind: explicit, else the role name. */
|
|
176
175
|
export const effectiveIdentity = (o) => o.identity ?? o.name;
|
|
176
|
+
/** Bare Agent document written by v2 spawn: inline Role × inline Brain + operations. */
|
|
177
|
+
export function buildAgentDocument(raw) {
|
|
178
|
+
const role = Object.fromEntries(['mission', 'persona', 'bio', 'briefing_file']
|
|
179
|
+
.filter(key => raw[key] !== undefined)
|
|
180
|
+
.map(key => [key, raw[key]]));
|
|
181
|
+
const brain = Object.fromEntries([
|
|
182
|
+
'harness', 'session', 'session_options', 'model', 'model_chain', 'max_tokens',
|
|
183
|
+
'autocompact_pct', 'harness_options', 'effort',
|
|
184
|
+
].filter(key => raw[key] !== undefined)
|
|
185
|
+
.map(key => [key, raw[key]]));
|
|
186
|
+
const operational = Object.fromEntries([
|
|
187
|
+
'permissions', 'identity', 'cwd', 'coordinator', 'env', 'oversee', 'isolation',
|
|
188
|
+
'monitor', 'owner_channel', 'worklog', 'auth_proxy',
|
|
189
|
+
].filter(key => raw[key] !== undefined)
|
|
190
|
+
.map(key => [key, raw[key]]));
|
|
191
|
+
return { role: { inline: role }, brain: { inline: brain }, ...operational };
|
|
192
|
+
}
|
|
177
193
|
/**
|
|
178
194
|
* Validate and resolve a spawn without reserving names, contacting the daemon,
|
|
179
195
|
* or writing state. Collision checks are necessarily a point-in-time snapshot.
|
|
@@ -186,7 +202,7 @@ export function spawnDryRun(o) {
|
|
|
186
202
|
readMissionFile(o.missionFile);
|
|
187
203
|
assertNameFree(o);
|
|
188
204
|
const cfg = loadConfig(o.configPath);
|
|
189
|
-
const raw = buildRoleConfig(o, cfg.defaults.harness);
|
|
205
|
+
const raw = buildRoleConfig(o, cfg.defaults.harness ?? 'claude-code');
|
|
190
206
|
const harnessOptions = {
|
|
191
207
|
...(cfg.defaults.harness_options ?? {}),
|
|
192
208
|
...(raw.harness_options ?? {}),
|
|
@@ -204,23 +220,30 @@ export function spawnDryRun(o) {
|
|
|
204
220
|
roleEnv: raw.env,
|
|
205
221
|
...(authProxy ? { authProxyBaseUrl: authProxy.base_url } : {}),
|
|
206
222
|
});
|
|
207
|
-
const
|
|
223
|
+
const adapter = getAdapter(harness);
|
|
224
|
+
const brain = adapter.agentSession.resolveBrain({
|
|
225
|
+
model: modelEnv.model,
|
|
226
|
+
effort: raw.effort,
|
|
227
|
+
harnessOptions: Object.keys(harnessOptions).length ? harnessOptions : undefined,
|
|
228
|
+
});
|
|
229
|
+
const model = brain.model ?? undefined;
|
|
208
230
|
const session = raw.session ?? cfg.defaults.session ?? 'acp';
|
|
209
231
|
const resolvedRole = {
|
|
210
232
|
...raw,
|
|
211
233
|
name: o.name,
|
|
212
|
-
sourceFile: o.temp ? '(temp dry-run)' : join(
|
|
234
|
+
sourceFile: o.temp ? '(temp dry-run)' : join(splitRootFor(o.configPath ?? defaultConfigPath()), 'agents', `${o.name}.yaml`),
|
|
213
235
|
harness,
|
|
214
236
|
session,
|
|
215
237
|
session_options: raw.session_options,
|
|
216
238
|
permissions: resolvePermissions(cfg.defaults.permissions, raw.permissions),
|
|
217
239
|
permissionsDeclared: raw.permissions !== undefined || cfg.defaults.permissions !== undefined,
|
|
218
240
|
identity: effectiveIdentity(o),
|
|
241
|
+
effort: raw.effort,
|
|
219
242
|
model,
|
|
220
243
|
model_chain: resolveModelChain(model, raw.model_chain ?? (inheritsModelDefaults
|
|
221
244
|
? cfg.defaults.model_chain
|
|
222
245
|
: undefined)),
|
|
223
|
-
harness_options:
|
|
246
|
+
harness_options: brain.harnessOptions,
|
|
224
247
|
isolation: raw.isolation ?? cfg.defaults.isolation,
|
|
225
248
|
monitor: resolveMonitorConfig(cfg.defaults.monitor, raw.monitor),
|
|
226
249
|
owner_channel: resolveOwnerChannelConfig(cfg.defaults.owner_channel, raw.owner_channel, session),
|
|
@@ -228,7 +251,6 @@ export function spawnDryRun(o) {
|
|
|
228
251
|
auth_proxy: authProxy,
|
|
229
252
|
};
|
|
230
253
|
resolvedRole.env = modelEnv.env;
|
|
231
|
-
const adapter = getAdapter(resolvedRole.harness);
|
|
232
254
|
if (resolvedRole.auth_proxy && resolvedRole.harness !== 'claude-code')
|
|
233
255
|
throw new Error('auth_proxy is supported only by claude-code');
|
|
234
256
|
const optionProblems = adapter.validateOptions(resolvedRole.harness_options, resolvedRole);
|
|
@@ -237,7 +259,7 @@ export function spawnDryRun(o) {
|
|
|
237
259
|
return {
|
|
238
260
|
schemaVersion: 1,
|
|
239
261
|
warning: 'collision checks are a snapshot; a real spawn reserves names atomically',
|
|
240
|
-
roleDocument:
|
|
262
|
+
roleDocument: buildAgentDocument(raw),
|
|
241
263
|
resolvedRole,
|
|
242
264
|
};
|
|
243
265
|
}
|
|
@@ -278,7 +300,7 @@ function provenanceSettings(o, defaults) {
|
|
|
278
300
|
monitor: tagged('monitorConfig', provenanceOf(o.monitorConfig, defaults.monitor, { mode: 'fleet' })),
|
|
279
301
|
};
|
|
280
302
|
}
|
|
281
|
-
/** Permanent spawn: persist
|
|
303
|
+
/** Permanent spawn: persist one bare Agent document under the selected v2 root. */
|
|
282
304
|
export async function spawnPermanent(o, deps, creation = {}) {
|
|
283
305
|
validateSpawnOpts(o);
|
|
284
306
|
if (o.isolationFile)
|
|
@@ -307,12 +329,11 @@ export async function spawnPermanent(o, deps, creation = {}) {
|
|
|
307
329
|
await creation.identityProvisioner?.remove?.(effectiveIdentity(o));
|
|
308
330
|
},
|
|
309
331
|
});
|
|
310
|
-
|
|
332
|
+
const agentRoot = join(splitRootFor(o.configPath ?? defaultConfigPath()), 'agents');
|
|
333
|
+
mkdirSync(agentRoot, { recursive: true });
|
|
311
334
|
creation.onStage?.('writing_role');
|
|
312
|
-
const file = join(
|
|
313
|
-
writeRoleFile(tx, file, stringify(
|
|
314
|
-
roles: { [o.name]: buildRoleConfig(o, cfg.defaults.harness) },
|
|
315
|
-
}));
|
|
335
|
+
const file = join(agentRoot, `${o.name}.yaml`);
|
|
336
|
+
writeRoleFile(tx, file, stringify(buildAgentDocument(buildRoleConfig(o, cfg.defaults.harness ?? 'claude-code'))));
|
|
316
337
|
// `up` materialises the state dir and registers the service. Journal the
|
|
317
338
|
// dir before it exists so a failure leaves the name genuinely reusable
|
|
318
339
|
// rather than blocked by a half-built directory.
|
|
@@ -343,6 +364,7 @@ export async function spawnPermanent(o, deps, creation = {}) {
|
|
|
343
364
|
});
|
|
344
365
|
mkdirSync(agentDir(o.name), { recursive: true });
|
|
345
366
|
writeProvenance(agentDir(o.name), provenance);
|
|
367
|
+
recordGeneratedAgentSource(agentDir(o.name), o.configPath ?? defaultConfigPath(), file);
|
|
346
368
|
creation.onStage?.('registering_supervisor');
|
|
347
369
|
await up(loadConfig(o.configPath), [o.name], {
|
|
348
370
|
...deps,
|
|
@@ -418,7 +440,13 @@ async function spawnTempInner(o, binPath, launch, tx, onStage) {
|
|
|
418
440
|
roleEnv: fromOpts.env,
|
|
419
441
|
...(tempAuthProxy ? { authProxyBaseUrl: tempAuthProxy.base_url } : {}),
|
|
420
442
|
});
|
|
421
|
-
const
|
|
443
|
+
const adapter = getAdapter(harness);
|
|
444
|
+
const brain = adapter.agentSession.resolveBrain({
|
|
445
|
+
model: modelEnv.model,
|
|
446
|
+
effort: fromOpts.effort,
|
|
447
|
+
harnessOptions: Object.keys(mergedHarnessOptions).length ? mergedHarnessOptions : undefined,
|
|
448
|
+
});
|
|
449
|
+
const model = brain.model ?? undefined;
|
|
422
450
|
const session = o.session ?? cfg.defaults.session ?? 'acp';
|
|
423
451
|
const role = {
|
|
424
452
|
...fromOpts, // includes `isolation` when --isolation-file was given
|
|
@@ -426,11 +454,12 @@ async function spawnTempInner(o, binPath, launch, tx, onStage) {
|
|
|
426
454
|
harness,
|
|
427
455
|
session,
|
|
428
456
|
identity: o.identity ?? o.name,
|
|
457
|
+
effort: fromOpts.effort,
|
|
429
458
|
model,
|
|
430
459
|
model_chain: resolveModelChain(model, fromOpts.model_chain ?? (inheritsModelDefaults
|
|
431
460
|
? cfg.defaults.model_chain
|
|
432
461
|
: undefined)),
|
|
433
|
-
harness_options:
|
|
462
|
+
harness_options: brain.harnessOptions,
|
|
434
463
|
permissions: resolvePermissions(cfg.defaults.permissions, fromOpts.permissions),
|
|
435
464
|
permissionsDeclared: fromOpts.permissions !== undefined || cfg.defaults.permissions !== undefined,
|
|
436
465
|
// Temp agents inherit the fleet-wide monitor defaults via the snapshot.
|
|
@@ -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
|
|
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:
|
|
7
|
-
firstRun:
|
|
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,
|
|
43
|
-
write(baseRevision: string,
|
|
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
|
}
|