@ours.network/fleet 1.1.0-nightly.7 → 1.1.0-nightly.9
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 +23 -4
- package/dist/application/role-creation-service.js +1 -1
- package/dist/application/task-room-service.d.ts +1 -1
- package/dist/application/task-room-service.js +147 -14
- package/dist/briefing.js +6 -6
- package/dist/build-info.json +4 -4
- package/dist/cli.js +8 -11
- package/dist/config-yaml.d.ts +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.js +49 -2
- package/dist/docs.d.ts +1 -1
- package/dist/docs.js +39 -18
- package/dist/fleet-command-audit.d.ts +37 -8
- package/dist/fleet-command-audit.js +120 -62
- package/dist/init-guidance.d.ts +1 -1
- package/dist/init-guidance.js +4 -3
- package/dist/owner-channel/channel.d.ts +2 -2
- package/dist/owner-channel/channel.js +31 -38
- package/dist/owner-channel/commands.js +3 -9
- 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/rooms-tasks/cli.js +121 -42
- package/dist/rooms-tasks/config.js +0 -4
- package/dist/rooms-tasks/provision.js +28 -1
- package/dist/rooms-tasks/templates.d.ts +0 -1
- package/dist/rooms-tasks/templates.js +9 -76
- package/dist/rooms-tasks/types.d.ts +2 -1
- package/dist/runner.js +13 -1
- package/dist/session/control.d.ts +1 -1
- package/dist/session/control.js +1 -1
- package/dist/web/fleet-config-service.js +4 -3
- package/package.json +2 -1
- 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/README.md
CHANGED
|
@@ -43,7 +43,7 @@ brain: { inline: { harness: codex, session: acp } }
|
|
|
43
43
|
## How it works
|
|
44
44
|
|
|
45
45
|
```
|
|
46
|
-
~/fleet.yaml + ~/fleet/{agents,roles,brains}/*.yaml your declaration
|
|
46
|
+
~/fleet.yaml + ~/fleet/{agents,roles,brains,room_templates}/*.yaml your declaration
|
|
47
47
|
│ ours-fleet up
|
|
48
48
|
▼
|
|
49
49
|
briefing.md per role ──► agent session adapter ──► ACP client/session ──► ACP agent
|
|
@@ -91,7 +91,7 @@ equivalent); logs land in `~/.ours-fleet/logs/`.
|
|
|
91
91
|
|
|
92
92
|
```sh
|
|
93
93
|
npm i -g @ours.network/fleet
|
|
94
|
-
ours-fleet init # units/dirs/linger
|
|
94
|
+
ours-fleet init # units/dirs/linger + missing standard presets
|
|
95
95
|
ours-fleet doctor # verifies everything above, with actionable messages
|
|
96
96
|
```
|
|
97
97
|
|
|
@@ -107,8 +107,8 @@ become that account and repeat.
|
|
|
107
107
|
## Quickstart
|
|
108
108
|
|
|
109
109
|
```sh
|
|
110
|
-
|
|
111
|
-
|
|
110
|
+
ours-fleet init # safe to repeat: creates missing files, never replaces edits
|
|
111
|
+
ours-fleet config # validates Agents, Roles, Brains, and Room templates
|
|
112
112
|
$EDITOR ~/fleet/agents/*.yaml # compose Role + Brain and operational settings
|
|
113
113
|
ours-fleet up # boot the fleet (staggered)
|
|
114
114
|
ours-fleet ls # running consoles
|
|
@@ -442,6 +442,25 @@ installer/setup flows remain responsible for starting it.
|
|
|
442
442
|
|
|
443
443
|
### Rooms and tasks
|
|
444
444
|
|
|
445
|
+
Init installs editable `single`, `pair`, and `team` definitions under
|
|
446
|
+
`~/fleet/room_templates/`, plus every exact-cased Agent, Role, and Brain they
|
|
447
|
+
reference. Inspect them with `ours-fleet template list` and
|
|
448
|
+
`ours-fleet template show team`. After configuring the authenticated owner below:
|
|
449
|
+
|
|
450
|
+
```sh
|
|
451
|
+
ours-fleet task create --title "Solo task" --template single
|
|
452
|
+
ours-fleet task create --title "Reviewed change" --template pair
|
|
453
|
+
ours-fleet task create --title "Phased delivery" --template team
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
`ours-fleet init -c /path/custom.yaml` seeds `/path/custom/` instead. Init reports
|
|
457
|
+
the packaged preset revision and source directory and only seeds missing files.
|
|
458
|
+
It never upgrades an edited preset. To adopt a newer packaged file explicitly,
|
|
459
|
+
copy the reported source file beside the existing target as `.new-default`, review
|
|
460
|
+
`diff -u`, then replace the target yourself. This is the sole adoption operation;
|
|
461
|
+
rerunning init is not an update. Users upgrading from hardcoded templates should
|
|
462
|
+
run init once (with the same `-c` selection they normally use).
|
|
463
|
+
|
|
445
464
|
Rooms always use `ours-cowork`; there is no room-provider selector. Configure
|
|
446
465
|
the cowork daemon connection and the room owner directly:
|
|
447
466
|
|
|
@@ -68,7 +68,7 @@ export class RoleCreationService {
|
|
|
68
68
|
return `unresolved (${provenance})`;
|
|
69
69
|
if ('ref' in value)
|
|
70
70
|
return `ref:${value.ref} (${provenance})`;
|
|
71
|
-
const fingerprint =
|
|
71
|
+
const fingerprint = hash(value.inline).slice(0, 16);
|
|
72
72
|
return `inline:sha256:${fingerprint} (${provenance})`;
|
|
73
73
|
};
|
|
74
74
|
return {
|
|
@@ -179,7 +179,7 @@ export declare class TaskRoomApplicationService {
|
|
|
179
179
|
name: string;
|
|
180
180
|
version: number;
|
|
181
181
|
description: string;
|
|
182
|
-
|
|
182
|
+
sourceFile?: string;
|
|
183
183
|
room?: import("../rooms-tasks/types.js").TemplateRoomConfig;
|
|
184
184
|
contract?: string;
|
|
185
185
|
members: import("../rooms-tasks/types.js").TemplateMemberSlot[];
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
2
3
|
import { ConfigError, loadConfig } from '../config.js';
|
|
3
4
|
import { CoworkProtocolError, CoworkUnavailableError, createCoworkAdapter, } from '../rooms-tasks/cowork-adapter.js';
|
|
4
5
|
import { getBinPath, provisionMembers } from '../rooms-tasks/provision.js';
|
|
5
6
|
import { activateRoom, advanceSaga, createRoomRecord, getRoomRecord, listRoomRecords, setOwnerSeat, setSagaError, } from '../rooms-tasks/room-state.js';
|
|
6
7
|
import { activateTask, blockTask as persistBlockTask, createTask as persistTask, deleteTask as persistDeleteTask, getTask as readTask, listTasks as readTasks, moveTaskToList, reviewTask as persistReviewTask, startTask as transitionTask, TaskStateError, unblockTask as persistUnblockTask, updateTaskRoom, updateTaskTemplate, } from '../rooms-tasks/task-state.js';
|
|
7
8
|
import { createTaskListLocked, DEFAULT_TASK_LIST_ID, deleteTaskListRecordLocked, readTaskLists, renameTaskListLocked, resolveTaskList, TaskListError, withTaskListsLock, } from '../rooms-tasks/task-lists.js';
|
|
9
|
+
import { recordFleetAuditPresentation } from '../fleet-command-audit.js';
|
|
8
10
|
import { hashTemplate, listTemplates, resolveTemplate, snapshotTemplate } from '../rooms-tasks/templates.js';
|
|
9
11
|
import { acceptManagedRoomClose, deleteLegacyClosedRooms, deleteManagedRoom, recordManagedRoomCloseError, } from '../rooms-tasks/close.js';
|
|
10
12
|
import { acceptTaskTerminalIntent, recordTaskTerminalIntentError, settleTaskTerminalIntent, } from '../rooms-tasks/terminal.js';
|
|
@@ -19,6 +21,47 @@ export class TaskRoomApplicationError extends Error {
|
|
|
19
21
|
this.name = 'TaskRoomApplicationError';
|
|
20
22
|
}
|
|
21
23
|
}
|
|
24
|
+
function launchSelection(definition, key) {
|
|
25
|
+
const selected = definition?.[key];
|
|
26
|
+
if (!selected || typeof selected !== 'object' || Array.isArray(selected))
|
|
27
|
+
return undefined;
|
|
28
|
+
const value = selected;
|
|
29
|
+
if (value.kind === 'ref' && typeof value.id === 'string')
|
|
30
|
+
return `ref:${value.id} (reference)`;
|
|
31
|
+
if (value.kind === 'inline' && typeof value.fingerprint === 'string')
|
|
32
|
+
return `inline:${value.fingerprint} (inline)`;
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
function launchPermissions(definition) {
|
|
36
|
+
const selected = definition?.permissions;
|
|
37
|
+
if (!selected || typeof selected !== 'object' || Array.isArray(selected))
|
|
38
|
+
return undefined;
|
|
39
|
+
const value = selected;
|
|
40
|
+
const fields = ['approval', 'filesystem', 'unattended'].flatMap(key => typeof value[key] === 'string' ? [`${key}=${value[key]}`] : []);
|
|
41
|
+
return fields.length ? fields.join(',') : undefined;
|
|
42
|
+
}
|
|
43
|
+
function roomParticipants(room) {
|
|
44
|
+
return [...room.member_seats].sort((a, b) => a.role_name.localeCompare(b.role_name)).map(seat => ({
|
|
45
|
+
name: seat.role_name, id: seat.identity_cid,
|
|
46
|
+
brain: launchSelection(seat.launch?.agent_definition, 'brain'),
|
|
47
|
+
role: launchSelection(seat.launch?.agent_definition, 'role') ?? seat.cowork_role,
|
|
48
|
+
permissions: launchPermissions(seat.launch?.agent_definition),
|
|
49
|
+
}));
|
|
50
|
+
}
|
|
51
|
+
function taskAgents(task) {
|
|
52
|
+
const room = task.room_id ? getRoomRecord(task.room_id) : undefined;
|
|
53
|
+
const seats = new Map(room?.member_seats.map(seat => [seat.role_name, seat]) ?? []);
|
|
54
|
+
return [...task.member_roles].sort((a, b) => a.name.localeCompare(b.name)).map(member => {
|
|
55
|
+
const seat = seats.get(member.name);
|
|
56
|
+
return { name: member.name, brain: launchSelection(seat?.launch?.agent_definition, 'brain'),
|
|
57
|
+
role: launchSelection(seat?.launch?.agent_definition, 'role') ?? member.cowork_role,
|
|
58
|
+
permissions: launchPermissions(seat?.launch?.agent_definition) };
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
function roomFailureEventId(room) {
|
|
62
|
+
const evidence = `${room.created_at}\0${room.saga.phase}\0${room.saga.step_index}\0${room.saga.error ?? ''}`;
|
|
63
|
+
return `sha256:${createHash('sha256').update(evidence).digest('hex')}`;
|
|
64
|
+
}
|
|
22
65
|
/** Exact extraction of the previously CLI-owned task create/start behavior. */
|
|
23
66
|
export class TaskRoomApplicationService {
|
|
24
67
|
configurationPath;
|
|
@@ -43,16 +86,34 @@ export class TaskRoomApplicationService {
|
|
|
43
86
|
start: !request.backlog, no_room: request.noRoom, listId: list.list_id,
|
|
44
87
|
});
|
|
45
88
|
});
|
|
89
|
+
recordFleetAuditPresentation({ kind: 'task', operation: 'create', id: task.task_id,
|
|
90
|
+
title: task.title, previousState: 'none', newState: task.state, revision: task.created_at,
|
|
91
|
+
list: task.list_name ?? 'default', roomId: task.room_id,
|
|
92
|
+
template: task.template ? `${task.template.name}@${task.template.version}` : undefined,
|
|
93
|
+
agents: [] });
|
|
46
94
|
if (!request.backlog && !request.noRoom && ref && !task.room_id) {
|
|
47
95
|
try {
|
|
48
|
-
await this.provisionRoom(cfg, task, template,
|
|
49
|
-
task = updateTaskRoom(task.task_id,
|
|
96
|
+
const room = await this.provisionRoom(cfg, task, template, created => {
|
|
97
|
+
task = updateTaskRoom(task.task_id, created.room_id, created.room_identity_cid);
|
|
50
98
|
});
|
|
51
99
|
task = readTask(task.task_id);
|
|
100
|
+
if (task.state === 'active' && room.state === 'active')
|
|
101
|
+
recordFleetAuditPresentation({ kind: 'task', operation: 'work', id: task.task_id,
|
|
102
|
+
title: task.title, previousState: 'provisioning', newState: task.state,
|
|
103
|
+
revision: getRoomRecord(task.room_id)?.activated_at ?? task.started_at ?? task.created_at,
|
|
104
|
+
list: task.list_name ?? 'default', roomId: task.room_id,
|
|
105
|
+
template: task.template ? `${task.template.name}@${task.template.version}` : undefined,
|
|
106
|
+
agents: taskAgents(task) });
|
|
107
|
+
else
|
|
108
|
+
recordFleetAuditPresentation({ kind: 'lifecycle_failure', resource: 'Task', id: task.task_id,
|
|
109
|
+
state: task.state, category: 'provision_pending', eventId: roomFailureEventId(room) });
|
|
52
110
|
}
|
|
53
111
|
catch (error) {
|
|
54
112
|
if (error instanceof CoworkUnavailableError)
|
|
55
113
|
persistBlockTask(task.task_id, 'Cowork management socket is unavailable');
|
|
114
|
+
recordFleetAuditPresentation({ kind: 'lifecycle_failure', resource: 'Task',
|
|
115
|
+
id: task.task_id, state: readTask(task.task_id).state, category: 'provision_failed',
|
|
116
|
+
eventId: task.room_id ? roomFailureEventId(getRoomRecord(task.room_id)) : task.created_at });
|
|
56
117
|
throw error;
|
|
57
118
|
}
|
|
58
119
|
}
|
|
@@ -75,12 +136,36 @@ export class TaskRoomApplicationService {
|
|
|
75
136
|
async startTask(input) {
|
|
76
137
|
const cfg = (this.deps.loadConfiguration ?? loadConfig)(this.configurationPath);
|
|
77
138
|
let task = transitionTask(input.taskId);
|
|
139
|
+
recordFleetAuditPresentation({ kind: 'task', operation: 'start', id: task.task_id,
|
|
140
|
+
title: task.title, previousState: 'backlog', newState: task.state,
|
|
141
|
+
revision: task.started_at ?? task.created_at, list: task.list_name ?? 'default',
|
|
142
|
+
template: task.template ? `${task.template.name}@${task.template.version}` : undefined,
|
|
143
|
+
agents: [] });
|
|
78
144
|
if (task.template && !task.room_id) {
|
|
79
145
|
const template = this.existingTemplate(cfg, task);
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
146
|
+
try {
|
|
147
|
+
const room = await this.provisionRoom(cfg, task, template, created => {
|
|
148
|
+
task = updateTaskRoom(task.task_id, created.room_id, created.room_identity_cid);
|
|
149
|
+
});
|
|
150
|
+
task = readTask(task.task_id);
|
|
151
|
+
if (task.state === 'active' && room.state === 'active')
|
|
152
|
+
recordFleetAuditPresentation({ kind: 'task', operation: 'work', id: task.task_id,
|
|
153
|
+
title: task.title, previousState: 'provisioning', newState: task.state,
|
|
154
|
+
revision: getRoomRecord(task.room_id)?.activated_at ?? task.started_at ?? task.created_at,
|
|
155
|
+
list: task.list_name ?? 'default', roomId: task.room_id,
|
|
156
|
+
template: task.template ? `${task.template.name}@${task.template.version}` : undefined,
|
|
157
|
+
agents: taskAgents(task) });
|
|
158
|
+
else
|
|
159
|
+
recordFleetAuditPresentation({ kind: 'lifecycle_failure', resource: 'Task', id: task.task_id,
|
|
160
|
+
state: task.state, category: 'provision_pending', eventId: roomFailureEventId(room) });
|
|
161
|
+
}
|
|
162
|
+
catch (error) {
|
|
163
|
+
const failed = task.room_id ? getRoomRecord(task.room_id) : undefined;
|
|
164
|
+
recordFleetAuditPresentation({ kind: 'lifecycle_failure', resource: 'Task', id: task.task_id,
|
|
165
|
+
state: readTask(task.task_id).state, category: 'provision_failed',
|
|
166
|
+
eventId: failed ? roomFailureEventId(failed) : task.created_at });
|
|
167
|
+
throw error;
|
|
168
|
+
}
|
|
84
169
|
}
|
|
85
170
|
return task;
|
|
86
171
|
}
|
|
@@ -466,8 +551,13 @@ export class TaskRoomApplicationService {
|
|
|
466
551
|
});
|
|
467
552
|
if (!task.template || task.template.name !== snapshot.name || task.template.content_hash !== snapshot.content_hash)
|
|
468
553
|
task = updateTaskTemplate(task.task_id, { name: snapshot.name, version: snapshot.version, content_hash: snapshot.content_hash });
|
|
469
|
-
if (task.state === 'backlog')
|
|
554
|
+
if (task.state === 'backlog') {
|
|
470
555
|
task = transitionTask(task.task_id);
|
|
556
|
+
recordFleetAuditPresentation({ kind: 'task', operation: 'start', id: task.task_id,
|
|
557
|
+
title: task.title, previousState: 'backlog', newState: task.state,
|
|
558
|
+
revision: task.started_at ?? task.created_at, list: task.list_name ?? 'default',
|
|
559
|
+
template: `${snapshot.name}@${snapshot.version}`, agents: [] });
|
|
560
|
+
}
|
|
471
561
|
if (!task.room_id) {
|
|
472
562
|
try {
|
|
473
563
|
await this.provisionRoom(cfg, task, snapshot, created => {
|
|
@@ -478,6 +568,9 @@ export class TaskRoomApplicationService {
|
|
|
478
568
|
catch (error) {
|
|
479
569
|
if (error instanceof CoworkUnavailableError)
|
|
480
570
|
persistBlockTask(task.task_id, 'Cowork management socket is unavailable');
|
|
571
|
+
recordFleetAuditPresentation({ kind: 'lifecycle_failure', resource: 'Task',
|
|
572
|
+
id: task.task_id, state: readTask(task.task_id).state, category: 'provision_failed',
|
|
573
|
+
eventId: task.room_id ? roomFailureEventId(getRoomRecord(task.room_id)) : task.created_at });
|
|
481
574
|
throw error;
|
|
482
575
|
}
|
|
483
576
|
}
|
|
@@ -496,9 +589,23 @@ export class TaskRoomApplicationService {
|
|
|
496
589
|
catch (error) {
|
|
497
590
|
if (error instanceof CoworkUnavailableError)
|
|
498
591
|
persistBlockTask(task.task_id, 'Cowork management socket is unavailable');
|
|
592
|
+
recordFleetAuditPresentation({ kind: 'lifecycle_failure', resource: 'Task',
|
|
593
|
+
id: task.task_id, state: readTask(task.task_id).state, category: 'provision_failed',
|
|
594
|
+
eventId: roomFailureEventId(getRoomRecord(task.room_id)) });
|
|
499
595
|
throw error;
|
|
500
596
|
}
|
|
501
597
|
}
|
|
598
|
+
const resultingRoom = getRoomRecord(task.room_id);
|
|
599
|
+
if (task.state === 'active' && resultingRoom?.state === 'active')
|
|
600
|
+
recordFleetAuditPresentation({ kind: 'task', operation: 'work', id: task.task_id,
|
|
601
|
+
title: task.title, previousState: 'provisioning', newState: task.state,
|
|
602
|
+
revision: getRoomRecord(task.room_id)?.activated_at ?? task.started_at ?? task.created_at,
|
|
603
|
+
list: task.list_name ?? 'default', roomId: task.room_id,
|
|
604
|
+
template: task.template ? `${task.template.name}@${task.template.version}` : undefined,
|
|
605
|
+
agents: taskAgents(task) });
|
|
606
|
+
else if (resultingRoom)
|
|
607
|
+
recordFleetAuditPresentation({ kind: 'lifecycle_failure', resource: 'Task',
|
|
608
|
+
id: task.task_id, state: task.state, category: 'provision_pending', eventId: roomFailureEventId(resultingRoom) });
|
|
502
609
|
return { task, status: 'ready' };
|
|
503
610
|
}
|
|
504
611
|
createTemplate(cfg, requested, noRoom) {
|
|
@@ -544,6 +651,11 @@ export class TaskRoomApplicationService {
|
|
|
544
651
|
room_id: created.room_id, room_name: task.title, room_identity_cid: created.identity_cid,
|
|
545
652
|
task_id: task.task_id, template_snapshot: template,
|
|
546
653
|
});
|
|
654
|
+
recordFleetAuditPresentation({ kind: 'room', operation: 'create', id: room.room_id,
|
|
655
|
+
name: room.room_name, previousState: 'none', newState: 'provisioning',
|
|
656
|
+
revision: room.created_at, taskId: room.task_id,
|
|
657
|
+
template: room.template_snapshot ? `${room.template_snapshot.name}@${room.template_snapshot.version}` : undefined,
|
|
658
|
+
participants: [] });
|
|
547
659
|
onCreated(room);
|
|
548
660
|
room = advanceSaga(room.room_id, 'create_room', 1);
|
|
549
661
|
if (attachOwner) {
|
|
@@ -558,18 +670,39 @@ export class TaskRoomApplicationService {
|
|
|
558
670
|
const mismatch = error instanceof CoworkProtocolError && /CID|expected/i.test(error.message);
|
|
559
671
|
setSagaError(room.room_id, error instanceof Error ? error.message : String(error), mismatch ? 'Verify rooms.owner.expected_cid and rotate the configured invite if necessary.'
|
|
560
672
|
: 'Rotate rooms.owner.public_invite or public_invite_file, then run room recover.', mismatch ? 'owner_cid_mismatch' : 'waiting_owner_invite');
|
|
673
|
+
recordFleetAuditPresentation({ kind: 'lifecycle_failure', resource: 'Room',
|
|
674
|
+
id: room.room_id, state: getRoomRecord(room.room_id)?.state ?? 'provisioning',
|
|
675
|
+
category: 'provision_failed', eventId: roomFailureEventId(getRoomRecord(room.room_id) ?? room) });
|
|
561
676
|
throw error;
|
|
562
677
|
}
|
|
563
678
|
}
|
|
564
679
|
room = advanceSaga(room.room_id, 'create_members', 3);
|
|
565
680
|
if (template?.members.length)
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
681
|
+
try {
|
|
682
|
+
room = await (this.deps.provisionMembers ?? provisionMembers)({
|
|
683
|
+
cfg, cowork, roomId: room.room_id, taskId: task.task_id, template,
|
|
684
|
+
binPath: (this.deps.binPath ?? getBinPath)(), brief: task.brief,
|
|
685
|
+
goal: task.task_id ? task.title : task.goal,
|
|
686
|
+
});
|
|
687
|
+
}
|
|
688
|
+
catch (error) {
|
|
689
|
+
const failed = getRoomRecord(room.room_id) ?? room;
|
|
690
|
+
recordFleetAuditPresentation({ kind: 'lifecycle_failure', resource: 'Room', id: failed.room_id,
|
|
691
|
+
state: failed.state, category: 'provision_failed', eventId: roomFailureEventId(failed) });
|
|
692
|
+
throw error;
|
|
693
|
+
}
|
|
694
|
+
else
|
|
695
|
+
room = activateRoom(room.room_id);
|
|
696
|
+
if (room.state === 'active')
|
|
697
|
+
recordFleetAuditPresentation({ kind: 'room', operation: 'activate', id: room.room_id,
|
|
698
|
+
name: room.room_name, previousState: 'provisioning', newState: 'active',
|
|
699
|
+
revision: room.activated_at ?? room.created_at, taskId: room.task_id,
|
|
700
|
+
template: room.template_snapshot ? `${room.template_snapshot.name}@${room.template_snapshot.version}` : undefined,
|
|
701
|
+
participants: roomParticipants(room) });
|
|
702
|
+
else
|
|
703
|
+
recordFleetAuditPresentation({ kind: 'lifecycle_failure', resource: 'Room', id: room.room_id,
|
|
704
|
+
state: room.state, category: 'provision_pending', eventId: roomFailureEventId(room) });
|
|
705
|
+
if (task.task_id && !template?.members.length)
|
|
573
706
|
activateTask(task.task_id);
|
|
574
707
|
return room;
|
|
575
708
|
}
|
package/dist/briefing.js
CHANGED
|
@@ -172,16 +172,16 @@ export function generateBriefing(role, v, opts) {
|
|
|
172
172
|
if (role.session === 'acp') {
|
|
173
173
|
L.push('', '### Managed fleet commands');
|
|
174
174
|
L.push('This ACP role has a supervisor-scoped ours-fleet proxy. Use the ordinary');
|
|
175
|
-
L.push('`ours-fleet` command; the CLI
|
|
176
|
-
L.push('
|
|
177
|
-
L.push('
|
|
178
|
-
L.push('Owner
|
|
175
|
+
L.push('`ours-fleet` command; the CLI routes public commands through your live supervisor.');
|
|
176
|
+
L.push('Your existing OS sandbox remains the executor and ordinary CLI validation still applies.');
|
|
177
|
+
L.push('Reads, help, validation failures, retries, and command invocations are silent in the');
|
|
178
|
+
L.push('Owner channel. Only confirmed Agent, Task, and Room lifecycle changes are announced.');
|
|
179
179
|
L.push('A minimal call is `ours-fleet spawn DeveloperName --temp`.');
|
|
180
180
|
L.push('For omitted settings, the supervisor inherits your canonical Brain and Role selections,');
|
|
181
181
|
L.push('working directory, neutral permissions, coordinator, and fleet monitor policy. Every');
|
|
182
182
|
L.push('explicit option wins; identity/session-local and secret material never inherit.');
|
|
183
|
-
L.push('
|
|
184
|
-
L.push('
|
|
183
|
+
L.push('All public CLI surfaces are available; hidden worker entry points remain internal.');
|
|
184
|
+
L.push('Lifecycle notice delivery failures stay in service diagnostics and never rerun effects.');
|
|
185
185
|
L.push('This proxy is attribution and convenience, not a security boundary for unisolated roles.');
|
|
186
186
|
}
|
|
187
187
|
if (role.coordinator) {
|
package/dist/build-info.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "1.1.0-nightly.
|
|
3
|
-
"buildId": "
|
|
4
|
-
"commit": "
|
|
2
|
+
"version": "1.1.0-nightly.9",
|
|
3
|
+
"buildId": "949ece807b97",
|
|
4
|
+
"commit": "c88941e2850b865432a850db957c806acf10406c",
|
|
5
5
|
"dirty": true,
|
|
6
|
-
"builtAt": "2026-08-
|
|
6
|
+
"builtAt": "2026-08-31T09:53:12.340Z",
|
|
7
7
|
"capabilities": [
|
|
8
8
|
"monitor.interrupt.after_tool"
|
|
9
9
|
]
|
package/dist/cli.js
CHANGED
|
@@ -8,6 +8,7 @@ import { createInterface } from 'node:readline';
|
|
|
8
8
|
import { Command } from 'commander';
|
|
9
9
|
import { VERSION } from './version.js';
|
|
10
10
|
import { INIT_COMPLETION_GUIDANCE } from './init-guidance.js';
|
|
11
|
+
import { bootstrapPresets } from './preset-bootstrap.js';
|
|
11
12
|
import { analyzeInstalls, buildInfo, buildLabel, discoverInstalls, runningLabel, } from './provenance.js';
|
|
12
13
|
import { agentDir, agentsRoot, tmpRoot, logsRoot, deriveXdgRuntimeDir } from './paths.js';
|
|
13
14
|
import { findRole, loadConfig, ROLE_NAME_RE } from './config.js';
|
|
@@ -38,7 +39,7 @@ import { requestWebControl } from './web/control.js';
|
|
|
38
39
|
import { WebServiceManager } from './web/service.js';
|
|
39
40
|
import { WebAccessStore, passwordAccess, validatePublicOrigin } from './web/access.js';
|
|
40
41
|
import { FLEET_PROXY_CALLER_ENV, FLEET_PROXY_STATE_DIR_ENV, } from './fleet-proxy.js';
|
|
41
|
-
import { beginFleetAuditCollection, consumeFleetAuditCollection, FleetCliExit,
|
|
42
|
+
import { beginFleetAuditCollection, consumeFleetAuditCollection, FleetCliExit, } from './fleet-command-audit.js';
|
|
42
43
|
import './harness/claude-code.js'; // registers the claude-code adapter
|
|
43
44
|
import './harness/codex.js'; // registers the codex adapter
|
|
44
45
|
import { registerTemplateCommands, registerTaskCommands, registerRoomCommands } from './rooms-tasks/cli.js';
|
|
@@ -1198,13 +1199,6 @@ cOpt(program.command('spawn [name]').description('spawn a new agent (permanent b
|
|
|
1198
1199
|
if (!response.ok)
|
|
1199
1200
|
throw new SessionControlError(response.kind ?? 'backend', response.error ?? 'managed spawn failed');
|
|
1200
1201
|
const result = response.result;
|
|
1201
|
-
recordFleetAuditResource('agent', result.role);
|
|
1202
|
-
recordFleetAuditPresentation({ kind: 'agent_started', name: result.role,
|
|
1203
|
-
lifetime: result.lifetime, brain: result.brainSummary, role: result.roleSummary,
|
|
1204
|
-
harness: result.harness, session: result.session, model: result.model,
|
|
1205
|
-
permissions: result.permissionMode
|
|
1206
|
-
? `${result.permissionMode.fleetMode}/${result.permissionMode.nativeMode}` : undefined,
|
|
1207
|
-
parent: result.caller, actionId: result.creationActionId, inherited: result.inherited });
|
|
1208
1202
|
const expectedCaller = process.env[FLEET_PROXY_CALLER_ENV];
|
|
1209
1203
|
if (expectedCaller && result.caller !== expectedCaller)
|
|
1210
1204
|
throw new Error(`fleet proxy caller mismatch: expected '${expectedCaller}', got '${result.caller}'`);
|
|
@@ -1264,12 +1258,15 @@ cOpt(program.command('doctor').description('prerequisite report'))
|
|
|
1264
1258
|
if (!rep.ok)
|
|
1265
1259
|
throw new FleetCliExit(1);
|
|
1266
1260
|
});
|
|
1267
|
-
program.command('init').description('
|
|
1268
|
-
.action(async () => {
|
|
1261
|
+
cOpt(program.command('init').description('idempotent host setup plus missing packaged presets'))
|
|
1262
|
+
.action(async (opts) => {
|
|
1269
1263
|
for (const d of [agentsRoot(), tmpRoot(), logsRoot()])
|
|
1270
1264
|
mkdirSync(d, { recursive: true });
|
|
1271
1265
|
for (const m of await pickBackend().init(binPath))
|
|
1272
1266
|
console.log(m);
|
|
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).`);
|
|
1273
1270
|
console.log(INIT_COMPLETION_GUIDANCE);
|
|
1274
1271
|
});
|
|
1275
1272
|
const webCommand = cOpt(program.command('web').description('start or open the secure localhost fleet web console'))
|
|
@@ -1581,7 +1578,7 @@ async function runFleetCli() {
|
|
|
1581
1578
|
const finished = await controlRequest(stateDir, { command: 'fleet_audit_finish', audit: {
|
|
1582
1579
|
correlationId: attempt.correlationId, class: outcomeClass, exitCode, effect,
|
|
1583
1580
|
...(metadata.resourceIds ? { resourceIds: metadata.resourceIds } : {}),
|
|
1584
|
-
...(metadata.
|
|
1581
|
+
...(metadata.presentations ? { presentations: metadata.presentations } : {}),
|
|
1585
1582
|
} });
|
|
1586
1583
|
if (!finished.ok)
|
|
1587
1584
|
throw new SessionControlError(finished.kind ?? 'backend', finished.error ?? `audit delivery failed after execution; effect status=${effect}`);
|
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
|
@@ -220,7 +220,7 @@ export interface FleetConfig {
|
|
|
220
220
|
files: string[];
|
|
221
221
|
configMode?: 'split-v2';
|
|
222
222
|
sourceDocuments?: Array<{
|
|
223
|
-
kind: 'Manifest' | 'Agent' | 'Role' | 'Brain';
|
|
223
|
+
kind: 'Manifest' | 'Agent' | 'Role' | 'Brain' | 'RoomTemplate';
|
|
224
224
|
id?: string;
|
|
225
225
|
path: string;
|
|
226
226
|
}>;
|
package/dist/config.js
CHANGED
|
@@ -265,6 +265,35 @@ function readKindDirectory(root, kind, required, yamlMode, diagnostics, files) {
|
|
|
265
265
|
}
|
|
266
266
|
return result;
|
|
267
267
|
}
|
|
268
|
+
function readRoomTemplateDirectory(root, yamlMode, diagnostics, files) {
|
|
269
|
+
if (!existsSync(root))
|
|
270
|
+
return {};
|
|
271
|
+
assertTrustedPath(root, 'directory');
|
|
272
|
+
const result = {};
|
|
273
|
+
const sources = new Map();
|
|
274
|
+
const names = readdirSync(root)
|
|
275
|
+
.filter(name => ['.yaml', '.yml'].includes(extname(name).toLowerCase())).sort();
|
|
276
|
+
for (const filename of names) {
|
|
277
|
+
const file = join(root, filename);
|
|
278
|
+
assertTrustedPath(file, 'file');
|
|
279
|
+
const id = basename(filename, extname(filename));
|
|
280
|
+
const previous = sources.get(id);
|
|
281
|
+
if (previous)
|
|
282
|
+
throw new ConfigError(`E_DUPLICATE_ID: room template '${id}' defined by both ${previous} and ${file}`);
|
|
283
|
+
const parsed = parseFleetDocument(file, readFileSync(file, 'utf8'), yamlMode);
|
|
284
|
+
diagnostics.push(...parsed.diagnostics);
|
|
285
|
+
if (parsed.value.override_builtin === true)
|
|
286
|
+
diagnostics.push({
|
|
287
|
+
severity: 'warning', kind: 'deprecated-field', file, line: 1, column: 1,
|
|
288
|
+
message: `${file}: override_builtin is deprecated and ignored for a file-backed Room template`,
|
|
289
|
+
});
|
|
290
|
+
files.push(file);
|
|
291
|
+
const validated = validateRoomTemplatesConfig({ [id]: parsed.value }, file)[id];
|
|
292
|
+
result[id] = { ...validated, sourceFile: file };
|
|
293
|
+
sources.set(id, file);
|
|
294
|
+
}
|
|
295
|
+
return result;
|
|
296
|
+
}
|
|
268
297
|
function selection(raw, kind, agentFile, presets, allowed) {
|
|
269
298
|
if (!isPlainObject(raw))
|
|
270
299
|
schemaError(agentFile, `/${kind}`, 'must be { ref } or { inline }');
|
|
@@ -586,6 +615,9 @@ export function loadConfig(configPath, options = {}) {
|
|
|
586
615
|
let ownerInvite;
|
|
587
616
|
let roomTemplates;
|
|
588
617
|
let tasks;
|
|
618
|
+
const fileTemplates = readRoomTemplateDirectory(join(splitRootFor(base), 'room_templates'), options.yamlMode ?? 'compat', diagnostics, files);
|
|
619
|
+
if (Object.keys(fileTemplates).length)
|
|
620
|
+
roomTemplates = fileTemplates;
|
|
589
621
|
for (const { file, doc } of docs) {
|
|
590
622
|
if (doc.rooms !== undefined) {
|
|
591
623
|
if (rooms)
|
|
@@ -598,7 +630,21 @@ export function loadConfig(configPath, options = {}) {
|
|
|
598
630
|
}
|
|
599
631
|
if (doc.room_templates !== undefined) {
|
|
600
632
|
const validated = validateRoomTemplatesConfig(deepSub(doc.room_templates, vars), file);
|
|
601
|
-
|
|
633
|
+
const raw = doc.room_templates;
|
|
634
|
+
for (const [name, template] of Object.entries(validated)) {
|
|
635
|
+
const shadowed = roomTemplates?.[name];
|
|
636
|
+
const marker = raw[name]?.override_builtin;
|
|
637
|
+
if (shadowed && (marker !== true || template.version <= shadowed.version))
|
|
638
|
+
throw new ConfigError(`${file}: room_templates.${name} shadows file preset ${shadowed.sourceFile}; `
|
|
639
|
+
+ 'set override_builtin: true and use a higher version');
|
|
640
|
+
if (marker === true)
|
|
641
|
+
diagnostics.push({
|
|
642
|
+
severity: 'warning', kind: 'deprecated-field', file, line: 1, column: 1,
|
|
643
|
+
message: `${file}: room_templates.${name}.override_builtin is deprecated; `
|
|
644
|
+
+ 'it is retained only as an explicit manifest-over-file migration marker',
|
|
645
|
+
});
|
|
646
|
+
roomTemplates = { ...(roomTemplates ?? {}), [name]: { ...template, sourceFile: file } };
|
|
647
|
+
}
|
|
602
648
|
}
|
|
603
649
|
if (doc.tasks !== undefined) {
|
|
604
650
|
if (tasks)
|
|
@@ -616,7 +662,8 @@ export function loadConfig(configPath, options = {}) {
|
|
|
616
662
|
return { kind: 'Manifest', path };
|
|
617
663
|
const parent = basename(dirname(path));
|
|
618
664
|
const kind = parent === 'agents' ? 'Agent'
|
|
619
|
-
: parent === 'roles' ? 'Role'
|
|
665
|
+
: parent === 'roles' ? 'Role'
|
|
666
|
+
: parent === 'room_templates' ? 'RoomTemplate' : 'Brain';
|
|
620
667
|
return { kind, id: basename(path, extname(path)), path };
|
|
621
668
|
}),
|
|
622
669
|
};
|