@ours.network/fleet 1.1.0-nightly.20 → 1.1.0-nightly.21
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 +36 -2
- package/dist/application/task-room-service.js +16 -8
- package/dist/build-info.json +4 -4
- package/dist/cli.js +15 -4
- package/dist/config.d.ts +8 -1
- package/dist/config.js +17 -7
- package/dist/creation.d.ts +1 -1
- package/dist/creation.js +3 -2
- package/dist/docs.d.ts +1 -1
- package/dist/docs.js +21 -0
- package/dist/fleet-command-audit.js +14 -2
- package/dist/lifecycle-summary.d.ts +16 -0
- package/dist/lifecycle-summary.js +18 -0
- package/dist/loops/config.d.ts +10 -1
- package/dist/loops/config.js +35 -3
- package/dist/resolved-plan.js +9 -1
- package/dist/rooms-tasks/cli.js +14 -3
- package/dist/rooms-tasks/launch-snapshot.js +5 -0
- package/dist/rooms-tasks/member-overrides.d.ts +3 -0
- package/dist/rooms-tasks/member-overrides.js +39 -5
- package/dist/rooms-tasks/provision.js +5 -0
- package/dist/rooms-tasks/types.d.ts +2 -0
- package/dist/runner.d.ts +3 -0
- package/dist/runner.js +17 -3
- package/dist/spawn.d.ts +9 -0
- package/dist/spawn.js +65 -8
- package/package.json +1 -1
package/dist/spawn.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { spawn as spawnChild } from 'node:child_process';
|
|
2
|
-
import { closeSync, existsSync, mkdirSync, openSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { closeSync, existsSync, lstatSync, mkdirSync, openSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
4
|
import { parse, stringify } from 'yaml';
|
|
5
5
|
import { agentDir, defaultConfigPath } from './paths.js';
|
|
@@ -22,9 +22,12 @@ export function agentDefinitionFromSpawn(o) {
|
|
|
22
22
|
if (o.agentDefinition) {
|
|
23
23
|
if (o.brain !== undefined || o.role !== undefined || o.cwd !== undefined
|
|
24
24
|
|| o.coordinator !== undefined || o.approval !== undefined || o.filesystem !== undefined
|
|
25
|
-
|| o.unattended !== undefined || o.isolationFile !== undefined || o.monitorConfig !== undefined
|
|
25
|
+
|| o.unattended !== undefined || o.isolationFile !== undefined || o.monitorConfig !== undefined
|
|
26
|
+
|| o.loopsFile !== undefined)
|
|
26
27
|
throw new Error('canonical agentDefinition conflicts with separate Agent fields');
|
|
27
28
|
const definition = structuredClone(o.agentDefinition);
|
|
29
|
+
if (o.noLoops === true && definition.loops !== undefined)
|
|
30
|
+
throw new Error('canonical agentDefinition loops conflict with explicit no-loops policy');
|
|
28
31
|
if (o.identity)
|
|
29
32
|
definition.identity = o.identity;
|
|
30
33
|
return definition;
|
|
@@ -46,12 +49,21 @@ export function agentDefinitionFromSpawn(o) {
|
|
|
46
49
|
...(permissions ? { permissions } : {}),
|
|
47
50
|
...(o.isolationFile ? { isolation: readIsolationFile(o.isolationFile) } : {}),
|
|
48
51
|
...(o.monitorConfig ? { monitor: structuredClone(o.monitorConfig) } : {}),
|
|
52
|
+
...(o.loopsFile ? { loops: readLoopsFile(o.loopsFile) } : {}),
|
|
49
53
|
};
|
|
50
54
|
}
|
|
51
55
|
function resolvedSpawn(o) {
|
|
52
56
|
const definition = agentDefinitionFromSpawn(o);
|
|
53
|
-
const cfg = loadConfig(o.configPath, {
|
|
54
|
-
|
|
57
|
+
const cfg = loadConfig(o.configPath, {
|
|
58
|
+
additionalAgent: { id: o.name, definition, temporary: o.temp === true },
|
|
59
|
+
});
|
|
60
|
+
const role = findRole(cfg, o.name);
|
|
61
|
+
if (o.temp)
|
|
62
|
+
role.temporaryLoopSource = o.loopSource
|
|
63
|
+
?? (role.temporaryLoops?.length ? 'agent-template' : 'omitted');
|
|
64
|
+
if (o.noLoops === true)
|
|
65
|
+
role.temporaryLoops = [];
|
|
66
|
+
return { definition, role };
|
|
55
67
|
}
|
|
56
68
|
/**
|
|
57
69
|
* Read and validate an `--isolation-file`. The file is the existing
|
|
@@ -78,7 +90,37 @@ export function readIsolationFile(path) {
|
|
|
78
90
|
throw new Error(`--isolation-file ${path}: ${problems.join('; ')}`);
|
|
79
91
|
return cfg;
|
|
80
92
|
}
|
|
93
|
+
/** Read a private canonical temporary-loop override before any creation side effect. */
|
|
94
|
+
export function readLoopsFile(path) {
|
|
95
|
+
const stat = lstatSync(path);
|
|
96
|
+
const uid = process.getuid?.();
|
|
97
|
+
if (!stat.isFile() || stat.isSymbolicLink() || stat.size > 1_000_000
|
|
98
|
+
|| (uid !== undefined && stat.uid !== uid) || (stat.mode & 0o777) !== 0o600)
|
|
99
|
+
throw new Error(`${path}: loops file must be an owner-only regular file no larger than 1 MB`);
|
|
100
|
+
let raw;
|
|
101
|
+
try {
|
|
102
|
+
raw = parse(readFileSync(path, 'utf8'));
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
throw new Error(`--loops-file ${path}: ${error.message}`);
|
|
106
|
+
}
|
|
107
|
+
if (!raw || typeof raw !== 'object' || Array.isArray(raw)
|
|
108
|
+
|| !Object.hasOwn(raw, 'loops')
|
|
109
|
+
|| Object.keys(raw).some(key => key !== 'loops'))
|
|
110
|
+
throw new Error(`${path}: expected exactly a top-level loops: mapping`);
|
|
111
|
+
const loops = raw.loops;
|
|
112
|
+
if (!loops || typeof loops !== 'object' || Array.isArray(loops)
|
|
113
|
+
|| !Object.keys(loops).length)
|
|
114
|
+
throw new Error(`${path}: loops must be a non-empty mapping; use --no-loops to disable loops`);
|
|
115
|
+
return structuredClone(loops);
|
|
116
|
+
}
|
|
81
117
|
export function validateSpawnOpts(o) {
|
|
118
|
+
if (o.loopsFile && o.noLoops === true)
|
|
119
|
+
throw new Error('--loops-file and --no-loops are mutually exclusive');
|
|
120
|
+
if ((o.loopsFile || o.noLoops === true || o.agentDefinition?.loops !== undefined) && !o.temp)
|
|
121
|
+
throw new Error('temporary Agent loops require --temp');
|
|
122
|
+
if (o.loopsFile)
|
|
123
|
+
readLoopsFile(o.loopsFile);
|
|
82
124
|
if (o.approval && !['ask', 'auto', 'allow', 'deny'].includes(o.approval))
|
|
83
125
|
throw new Error(`invalid --approval '${o.approval}'; allowed: ask, auto, allow (deprecated alias: deny)`);
|
|
84
126
|
if (o.filesystem && !['read-only', 'workspace', 'unrestricted'].includes(o.filesystem))
|
|
@@ -149,7 +191,7 @@ export function spawnDryRun(o) {
|
|
|
149
191
|
* `env`, `bio`, `persona` and `harness_options` are deliberately absent: the
|
|
150
192
|
* record exists to be read, and must not become a place credentials collect.
|
|
151
193
|
*/
|
|
152
|
-
function provenanceSettings(o, defaults) {
|
|
194
|
+
function provenanceSettings(o, defaults, resolvedLoops = []) {
|
|
153
195
|
const perms = (defaults.permissions ?? {});
|
|
154
196
|
const callerDefaults = new Set(o.inheritedFromCaller ?? []);
|
|
155
197
|
const tagged = (key, entry) => callerDefaults.has(key) ? { ...entry, source: 'caller-role' } : entry;
|
|
@@ -169,6 +211,16 @@ function provenanceSettings(o, defaults) {
|
|
|
169
211
|
? { value: 'declared via --isolation-file', source: 'cli' }
|
|
170
212
|
: { value: defaults.isolation ? 'from fleet defaults' : undefined, source: defaults.isolation ? 'fleet-default' : 'built-in' },
|
|
171
213
|
monitor: tagged('monitorConfig', provenanceOf(o.monitorConfig, defaults.monitor, { mode: 'fleet' })),
|
|
214
|
+
loops: {
|
|
215
|
+
value: o.noLoops ? { enabled: false, loops: [] }
|
|
216
|
+
: resolvedLoops.length ? resolvedLoops.map(loop => ({
|
|
217
|
+
name: loop.name, enabled: loop.enabled, intervalMs: loop.intervalMs,
|
|
218
|
+
initialDelayMs: loop.initialDelayMs, jitterMs: loop.jitterMs,
|
|
219
|
+
prompt: { bytes: loop.promptBytes, sha256: loop.promptHash },
|
|
220
|
+
})) : 'omitted (legacy no temporary loops)',
|
|
221
|
+
source: o.loopSource === 'agent-template' ? 'agent-template'
|
|
222
|
+
: o.loopsFile || o.noLoops || o.loopSource === 'cli' ? 'cli' : 'built-in',
|
|
223
|
+
},
|
|
172
224
|
};
|
|
173
225
|
}
|
|
174
226
|
/** Permanent spawn: persist one bare Agent document under the selected v2 root. */
|
|
@@ -232,7 +284,7 @@ export async function spawnPermanent(o, deps, creation = {}) {
|
|
|
232
284
|
// launch still records how it was asked for.
|
|
233
285
|
const provenance = buildProvenance({
|
|
234
286
|
role: o.name, lifetime: 'permanent', fleetVersion: VERSION,
|
|
235
|
-
settings: provenanceSettings(o, cfg.defaults),
|
|
287
|
+
settings: provenanceSettings(o, cfg.defaults, prepared.role.temporaryLoops),
|
|
236
288
|
surface: o.surface, creationActionId: o.creationActionId, callerRole: o.callerRole,
|
|
237
289
|
});
|
|
238
290
|
mkdirSync(agentDir(o.name), { recursive: true });
|
|
@@ -293,14 +345,19 @@ export async function spawnTemp(o, binPath, launch = independentSupervisor, crea
|
|
|
293
345
|
}
|
|
294
346
|
async function spawnTempInner(o, preparedRole, binPath, launch, tx, onStage) {
|
|
295
347
|
const cfg = loadConfig(o.configPath);
|
|
348
|
+
const { temporaryLoops, ...launchRole } = preparedRole;
|
|
296
349
|
const role = {
|
|
297
|
-
...
|
|
350
|
+
...launchRole, sourceFile: '(temp)',
|
|
351
|
+
...(o.noLoops === true ? { loops: [] }
|
|
352
|
+
: temporaryLoops?.length ? { loops: temporaryLoops } : { loops: undefined }),
|
|
353
|
+
temporaryLoopSource: o.loopSource ?? (temporaryLoops?.length ? 'agent-template' : 'omitted'),
|
|
354
|
+
roomMemberStartup: o.roomMemberStartup,
|
|
298
355
|
};
|
|
299
356
|
onStage?.('writing_role');
|
|
300
357
|
const dir = applyRole(role, { temp: true, identityGuarantee: 'unverified' });
|
|
301
358
|
const provenance = buildProvenance({
|
|
302
359
|
role: o.name, lifetime: 'temporary', fleetVersion: VERSION,
|
|
303
|
-
settings: provenanceSettings(o, cfg.defaults),
|
|
360
|
+
settings: provenanceSettings(o, cfg.defaults, preparedRole.temporaryLoops),
|
|
304
361
|
surface: o.surface, creationActionId: o.creationActionId, callerRole: o.callerRole,
|
|
305
362
|
});
|
|
306
363
|
writeProvenance(dir, provenance);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ours.network/fleet",
|
|
3
|
-
"version": "1.1.0-nightly.
|
|
3
|
+
"version": "1.1.0-nightly.21",
|
|
4
4
|
"description": "Harness-agnostic fleet of persistent, identity-bound AI agents. Declarative fleet.yaml, managed native/ACP sessions, supervision, and ours.network messaging.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "FSL-1.1-Apache-2.0",
|