@ours.network/fleet 1.1.0-nightly.8 → 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/dist/application/role-creation-service.js +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 +2 -9
- package/dist/docs.d.ts +1 -1
- package/dist/docs.js +13 -15
- package/dist/fleet-command-audit.d.ts +37 -8
- package/dist/fleet-command-audit.js +120 -62
- package/dist/owner-channel/channel.d.ts +2 -2
- package/dist/owner-channel/channel.js +31 -38
- package/dist/owner-channel/state.d.ts +2 -0
- package/dist/owner-channel/state.js +8 -2
- package/dist/rooms-tasks/cli.js +117 -38
- package/dist/rooms-tasks/provision.js +28 -1
- package/dist/runner.js +13 -1
- package/dist/session/control.d.ts +1 -1
- package/dist/session/control.js +1 -1
- package/package.json +1 -1
package/dist/rooms-tasks/cli.js
CHANGED
|
@@ -215,35 +215,81 @@ const taskActionMarkdown = (title, task, fields = []) => {
|
|
|
215
215
|
],
|
|
216
216
|
});
|
|
217
217
|
};
|
|
218
|
-
function
|
|
219
|
-
const
|
|
220
|
-
if (!
|
|
218
|
+
function safeSelectionSummary(definition, kind) {
|
|
219
|
+
const selected = definition?.[kind];
|
|
220
|
+
if (!selected || typeof selected !== 'object' || Array.isArray(selected))
|
|
221
221
|
return 'unresolved';
|
|
222
|
-
const selection =
|
|
222
|
+
const selection = selected;
|
|
223
|
+
if (selection.kind === 'ref' && typeof selection.id === 'string')
|
|
224
|
+
return `ref:${selection.id} (reference)`;
|
|
225
|
+
if (selection.kind === 'inline' && typeof selection.fingerprint === 'string')
|
|
226
|
+
return `inline:${selection.fingerprint} (inline)`;
|
|
223
227
|
if (typeof selection.ref === 'string')
|
|
224
228
|
return `ref:${selection.ref}`;
|
|
225
229
|
if (!Object.hasOwn(selection, 'inline'))
|
|
226
230
|
return 'unresolved';
|
|
227
|
-
const
|
|
231
|
+
const canonical = (value) => Array.isArray(value)
|
|
232
|
+
? `[${value.map(canonical).join(',')}]`
|
|
233
|
+
: value && typeof value === 'object'
|
|
234
|
+
? `{${Object.entries(value).sort(([a], [b]) => a.localeCompare(b))
|
|
235
|
+
.map(([key, child]) => `${JSON.stringify(key)}:${canonical(child)}`).join(',')}}`
|
|
236
|
+
: JSON.stringify(value);
|
|
237
|
+
const fingerprint = createHash('sha256').update(canonical(selection.inline)).digest('hex').slice(0, 16);
|
|
228
238
|
return `inline:sha256:${fingerprint}`;
|
|
229
239
|
}
|
|
230
|
-
function
|
|
240
|
+
function safePermissionsSummary(definition) {
|
|
241
|
+
const permissions = definition?.permissions;
|
|
242
|
+
if (!permissions || typeof permissions !== 'object' || Array.isArray(permissions))
|
|
243
|
+
return undefined;
|
|
244
|
+
const policy = permissions;
|
|
245
|
+
const fields = ['approval', 'filesystem', 'unattended'].flatMap(key => typeof policy[key] === 'string' ? [`${key}=${policy[key]}`] : []);
|
|
246
|
+
return fields.length ? fields.join(',') : undefined;
|
|
247
|
+
}
|
|
248
|
+
function auditTask(operation, task, previousState, newState = task.state, revision) {
|
|
249
|
+
const materialSameState = operation === 'block' || operation === 'unblock' || operation === 'settling';
|
|
250
|
+
if (previousState === newState && !materialSameState)
|
|
251
|
+
return;
|
|
231
252
|
recordFleetAuditResource('task', task.task_id);
|
|
232
253
|
if (task.room_id)
|
|
233
254
|
recordFleetAuditResource('room', task.room_id);
|
|
234
255
|
const room = task.room_id ? getRoomRecord(task.room_id) : undefined;
|
|
235
|
-
const
|
|
236
|
-
|
|
256
|
+
const definitions = new Map(room?.member_seats.map(seat => [seat.role_name, seat.launch?.agent_definition]) ?? []);
|
|
257
|
+
const semanticOperation = operation === 'recover'
|
|
258
|
+
? newState === 'active' ? 'work' : newState === 'done' ? 'done'
|
|
259
|
+
: newState === 'cancelled' ? 'cancel' : undefined
|
|
260
|
+
: operation;
|
|
261
|
+
if (!semanticOperation)
|
|
262
|
+
return;
|
|
263
|
+
recordFleetAuditPresentation({ kind: 'task', operation: semanticOperation, id: task.task_id, title: task.title,
|
|
237
264
|
previousState, newState,
|
|
265
|
+
revision: revision ?? task.blocked?.at ?? task.ended_at ?? task.started_at ?? task.created_at,
|
|
266
|
+
list: task.list_name ?? 'default',
|
|
238
267
|
template: task.template ? `${task.template.name}@${task.template.version}` : undefined,
|
|
239
|
-
roomId: task.room_id, agents: task.member_roles.map(member => ({ name: member.name,
|
|
240
|
-
brain:
|
|
268
|
+
roomId: task.room_id, agents: [...task.member_roles].sort((a, b) => a.name.localeCompare(b.name)).map(member => ({ name: member.name,
|
|
269
|
+
brain: safeSelectionSummary(definitions.get(member.name), 'brain'),
|
|
270
|
+
role: safeSelectionSummary(definitions.get(member.name), 'role') === 'unresolved'
|
|
271
|
+
? member.cowork_role : safeSelectionSummary(definitions.get(member.name), 'role'),
|
|
272
|
+
permissions: safePermissionsSummary(definitions.get(member.name)) })) });
|
|
241
273
|
}
|
|
242
274
|
function auditRoom(operation, room, previousState, newState = room.state) {
|
|
275
|
+
if (previousState === newState)
|
|
276
|
+
return;
|
|
243
277
|
recordFleetAuditResource('room', room.room_id);
|
|
244
|
-
|
|
278
|
+
const semanticOperation = operation === 'recover'
|
|
279
|
+
? newState === 'active' ? 'activate' : newState === 'deleted' ? 'delete'
|
|
280
|
+
: newState === 'closing' ? 'close' : undefined
|
|
281
|
+
: operation;
|
|
282
|
+
if (!semanticOperation)
|
|
283
|
+
return;
|
|
284
|
+
recordFleetAuditPresentation({ kind: 'room', operation: semanticOperation, id: room.room_id, previousState, newState,
|
|
285
|
+
revision: room.closed_at ?? room.activated_at ?? room.close?.accepted_at ?? room.created_at,
|
|
286
|
+
name: room.room_name, taskId: room.task_id,
|
|
245
287
|
template: room.template_snapshot ? `${room.template_snapshot.name}@${room.template_snapshot.version}` : undefined,
|
|
246
|
-
participants: room.member_seats.map(member => ({ name: member.role_name,
|
|
288
|
+
participants: [...room.member_seats].sort((a, b) => a.role_name.localeCompare(b.role_name)).map(member => ({ name: member.role_name, id: member.identity_cid,
|
|
289
|
+
brain: safeSelectionSummary(member.launch?.agent_definition, 'brain'),
|
|
290
|
+
role: safeSelectionSummary(member.launch?.agent_definition, 'role') === 'unresolved'
|
|
291
|
+
? member.cowork_role : safeSelectionSummary(member.launch?.agent_definition, 'role'),
|
|
292
|
+
permissions: safePermissionsSummary(member.launch?.agent_definition) })) });
|
|
247
293
|
}
|
|
248
294
|
const roomActionMarkdown = (title, room, fields = []) => {
|
|
249
295
|
recordFleetAuditResource('room', room.room_id);
|
|
@@ -272,6 +318,10 @@ async function launchTaskSettleWorker(taskId, configPath) {
|
|
|
272
318
|
actor: { kind: 'local_control', surface: 'cli' }, taskId, error: errorText(error),
|
|
273
319
|
recoveryHint: `External settle worker failed to start. Retry task recover ${taskId}.`,
|
|
274
320
|
});
|
|
321
|
+
const task = getTask(taskId);
|
|
322
|
+
recordFleetAuditPresentation({ kind: 'lifecycle_failure', resource: 'Task', id: taskId,
|
|
323
|
+
state: task.state, category: 'settlement_failed',
|
|
324
|
+
eventId: task.terminal_intent?.error_at ?? task.terminal_intent?.accepted_at ?? task.created_at });
|
|
275
325
|
throw error;
|
|
276
326
|
}
|
|
277
327
|
const deadline = Date.now() + PUBLIC_SETTLE_WAIT_MS;
|
|
@@ -284,7 +334,11 @@ async function launchTaskSettleWorker(taskId, configPath) {
|
|
|
284
334
|
}
|
|
285
335
|
await sleep(PUBLIC_SETTLE_POLL_MS);
|
|
286
336
|
}
|
|
287
|
-
|
|
337
|
+
const task = getTask(taskId);
|
|
338
|
+
recordFleetAuditPresentation({ kind: 'lifecycle_failure', resource: 'Task', id: taskId,
|
|
339
|
+
state: task.state, category: 'settlement_pending',
|
|
340
|
+
eventId: task.terminal_intent?.accepted_at ?? task.created_at });
|
|
341
|
+
return { task, timedOut: true };
|
|
288
342
|
}
|
|
289
343
|
async function launchRoomDeleteWorker(roomId, configPath) {
|
|
290
344
|
const previousErrorAt = getRoomRecord(roomId)?.close?.error_at;
|
|
@@ -296,6 +350,11 @@ async function launchRoomDeleteWorker(roomId, configPath) {
|
|
|
296
350
|
actor: { kind: 'local_control', surface: 'cli' }, roomId, error: errorText(error),
|
|
297
351
|
recoveryHint: `External delete worker failed to start. Retry room delete ${roomId} ${roomId}.`,
|
|
298
352
|
});
|
|
353
|
+
const room = getRoomRecord(roomId);
|
|
354
|
+
if (room)
|
|
355
|
+
recordFleetAuditPresentation({ kind: 'lifecycle_failure', resource: 'Room', id: roomId,
|
|
356
|
+
state: room.state, category: 'cleanup_failed',
|
|
357
|
+
eventId: room.close?.error_at ?? room.close?.accepted_at ?? room.created_at });
|
|
299
358
|
throw error;
|
|
300
359
|
}
|
|
301
360
|
const deadline = Date.now() + PUBLIC_SETTLE_WAIT_MS;
|
|
@@ -308,7 +367,12 @@ async function launchRoomDeleteWorker(roomId, configPath) {
|
|
|
308
367
|
}
|
|
309
368
|
await sleep(PUBLIC_SETTLE_POLL_MS);
|
|
310
369
|
}
|
|
311
|
-
|
|
370
|
+
const remaining = getRoomRecord(roomId);
|
|
371
|
+
if (remaining)
|
|
372
|
+
recordFleetAuditPresentation({ kind: 'lifecycle_failure', resource: 'Room', id: roomId,
|
|
373
|
+
state: remaining.state, category: 'cleanup_pending',
|
|
374
|
+
eventId: remaining.close?.accepted_at ?? remaining.created_at });
|
|
375
|
+
return { deleted: remaining === undefined, timedOut: true };
|
|
312
376
|
}
|
|
313
377
|
function loadCfg(opts) {
|
|
314
378
|
return loadConfig(opts.configuration);
|
|
@@ -410,21 +474,36 @@ async function provisionRoom(cfg, input) {
|
|
|
410
474
|
setSagaError(record.room_id, error instanceof Error ? error.message : String(error), mismatch
|
|
411
475
|
? 'Verify rooms.owner.expected_cid and rotate the configured invite if necessary.'
|
|
412
476
|
: 'Rotate rooms.owner.public_invite or public_invite_file, then run room recover.', mismatch ? 'owner_cid_mismatch' : 'waiting_owner_invite');
|
|
477
|
+
const failed = getRoomRecord(record.room_id);
|
|
478
|
+
if (failed)
|
|
479
|
+
recordFleetAuditPresentation({ kind: 'lifecycle_failure', resource: 'Room',
|
|
480
|
+
id: failed.room_id, state: failed.state, category: 'provision_failed',
|
|
481
|
+
eventId: `${failed.created_at}:${failed.saga.phase}:${failed.saga.step_index}` });
|
|
413
482
|
throw error;
|
|
414
483
|
}
|
|
415
484
|
}
|
|
416
485
|
record = advanceSaga(record.room_id, 'create_members', 3);
|
|
417
486
|
if (input.template && input.template.members.length > 0) {
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
487
|
+
try {
|
|
488
|
+
record = await provisionMembers({
|
|
489
|
+
cfg,
|
|
490
|
+
cowork,
|
|
491
|
+
roomId: record.room_id,
|
|
492
|
+
taskId: input.taskId,
|
|
493
|
+
template: input.template,
|
|
494
|
+
binPath: getBinPath(),
|
|
495
|
+
brief: input.brief,
|
|
496
|
+
goal: input.goal,
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
catch (error) {
|
|
500
|
+
const failed = getRoomRecord(record.room_id);
|
|
501
|
+
if (failed)
|
|
502
|
+
recordFleetAuditPresentation({ kind: 'lifecycle_failure', resource: 'Room',
|
|
503
|
+
id: failed.room_id, state: failed.state, category: 'provision_failed',
|
|
504
|
+
eventId: `${failed.created_at}:${failed.saga.phase}:${failed.saga.step_index}` });
|
|
505
|
+
throw error;
|
|
506
|
+
}
|
|
428
507
|
}
|
|
429
508
|
else {
|
|
430
509
|
record = activateRoom(record.room_id);
|
|
@@ -529,7 +608,6 @@ export function registerTaskCommands(parent, cOpt) {
|
|
|
529
608
|
idempotencyKey: opts.idempotencyKey, origin: { type: 'cli' },
|
|
530
609
|
list: opts.list,
|
|
531
610
|
});
|
|
532
|
-
auditTask('create', record, 'none');
|
|
533
611
|
if (opts.json) {
|
|
534
612
|
console.log(JSON.stringify({ schema_version: 1, task: record }, null, 2));
|
|
535
613
|
return;
|
|
@@ -716,11 +794,9 @@ export function registerTaskCommands(parent, cOpt) {
|
|
|
716
794
|
.option('--json', 'JSON output')
|
|
717
795
|
.action(async (id, opts) => {
|
|
718
796
|
try {
|
|
719
|
-
const previous = getTask(id).state;
|
|
720
797
|
const t = await taskRoomService(opts.configuration).startTask({
|
|
721
798
|
actor: { kind: 'local_control', surface: 'cli' }, taskId: id,
|
|
722
799
|
});
|
|
723
|
-
auditTask('start', t, previous);
|
|
724
800
|
if (opts.json) {
|
|
725
801
|
console.log(JSON.stringify({ schema_version: 1, task: t }, null, 2));
|
|
726
802
|
return;
|
|
@@ -766,11 +842,12 @@ export function registerTaskCommands(parent, cOpt) {
|
|
|
766
842
|
.option('--json', 'JSON output')
|
|
767
843
|
.action((id, opts) => {
|
|
768
844
|
try {
|
|
769
|
-
const
|
|
845
|
+
const prior = getTask(id);
|
|
846
|
+
const previous = prior.state;
|
|
770
847
|
const t = taskRoomService().unblockTask({
|
|
771
848
|
actor: { kind: 'local_control', surface: 'cli' }, taskId: id,
|
|
772
849
|
});
|
|
773
|
-
auditTask('unblock', t, previous);
|
|
850
|
+
auditTask('unblock', t, previous, t.state, prior.blocked?.at);
|
|
774
851
|
if (opts.json) {
|
|
775
852
|
console.log(JSON.stringify({ schema_version: 1, task: t }, null, 2));
|
|
776
853
|
return;
|
|
@@ -820,6 +897,8 @@ export function registerTaskCommands(parent, cOpt) {
|
|
|
820
897
|
const plan = await taskRoomService(opts.configuration).completeTask({
|
|
821
898
|
actor: { kind: 'local_control', surface: 'cli' }, taskId: id, outcome,
|
|
822
899
|
});
|
|
900
|
+
if (plan.task.terminal_intent?.status === 'pending')
|
|
901
|
+
auditTask('settling', plan.task, previous, previous, plan.task.terminal_intent.accepted_at);
|
|
823
902
|
const settled = plan.settlementRequired
|
|
824
903
|
? await launchTaskSettleWorker(id, opts.configuration)
|
|
825
904
|
: { task: plan.task, timedOut: false };
|
|
@@ -856,6 +935,8 @@ export function registerTaskCommands(parent, cOpt) {
|
|
|
856
935
|
const plan = await taskRoomService(opts.configuration).cancelTask({
|
|
857
936
|
actor: { kind: 'local_control', surface: 'cli' }, taskId: id,
|
|
858
937
|
});
|
|
938
|
+
if (plan.task.terminal_intent?.status === 'pending')
|
|
939
|
+
auditTask('settling', plan.task, previous, previous, plan.task.terminal_intent.accepted_at);
|
|
859
940
|
const settled = plan.settlementRequired
|
|
860
941
|
? await launchTaskSettleWorker(id, opts.configuration)
|
|
861
942
|
: { task: plan.task, timedOut: false };
|
|
@@ -1037,12 +1118,10 @@ export function registerTaskCommands(parent, cOpt) {
|
|
|
1037
1118
|
.option('--json', 'JSON output')
|
|
1038
1119
|
.action(async (id, opts) => {
|
|
1039
1120
|
try {
|
|
1040
|
-
const previous = getTask(id).state;
|
|
1041
1121
|
const result = await taskRoomService(opts.configuration).ensureTaskWork({
|
|
1042
1122
|
actor: { kind: 'local_control', surface: 'cli' }, taskId: id, template: opts.template,
|
|
1043
1123
|
});
|
|
1044
1124
|
const t = result.task;
|
|
1045
|
-
auditTask('work', t, previous);
|
|
1046
1125
|
if (result.status === 'already_active') {
|
|
1047
1126
|
if (opts.json) {
|
|
1048
1127
|
console.log(JSON.stringify({ schema_version: 1, task: t, status: 'already_active' }, null, 2));
|
|
@@ -1089,6 +1168,8 @@ export function registerTaskCommands(parent, cOpt) {
|
|
|
1089
1168
|
const plan = await taskRoomService(opts.configuration).finishTask({
|
|
1090
1169
|
actor: { kind: 'local_control', surface: 'cli' }, taskId: id, outcome,
|
|
1091
1170
|
});
|
|
1171
|
+
if (plan.task.terminal_intent?.status === 'pending')
|
|
1172
|
+
auditTask('settling', plan.task, previous, previous, plan.task.terminal_intent.accepted_at);
|
|
1092
1173
|
const settled = plan.settlementRequired
|
|
1093
1174
|
? await launchTaskSettleWorker(id, opts.configuration)
|
|
1094
1175
|
: { task: plan.task, timedOut: false };
|
|
@@ -1133,7 +1214,6 @@ export function registerRoomCommands(parent, cOpt) {
|
|
|
1133
1214
|
actor: { kind: 'local_control', surface: 'cli' }, name: opts.name,
|
|
1134
1215
|
template: opts.template, goal: opts.goal, brief: opts.brief, briefFile: opts.briefFile,
|
|
1135
1216
|
});
|
|
1136
|
-
auditRoom('create', record, 'none');
|
|
1137
1217
|
if (opts.json) {
|
|
1138
1218
|
console.log(JSON.stringify({ schema_version: 1, room: record }, null, 2));
|
|
1139
1219
|
return;
|
|
@@ -1219,8 +1299,6 @@ export function registerRoomCommands(parent, cOpt) {
|
|
|
1219
1299
|
.action(async (id, opts) => {
|
|
1220
1300
|
try {
|
|
1221
1301
|
const { room, orchestration } = await taskRoomService(opts.configuration).getRoomDetail(id);
|
|
1222
|
-
if (orchestration)
|
|
1223
|
-
auditRoom('open', orchestration, orchestration.state);
|
|
1224
1302
|
const url = `http://localhost:4460/room/${id}`;
|
|
1225
1303
|
if (opts.json) {
|
|
1226
1304
|
console.log(JSON.stringify({ schema_version: 1, room_id: id, url, room_name: room.room_name }, null, 2));
|
|
@@ -1247,8 +1325,6 @@ export function registerRoomCommands(parent, cOpt) {
|
|
|
1247
1325
|
.action(async (id, opts) => {
|
|
1248
1326
|
try {
|
|
1249
1327
|
const { orchestration: r, members } = await taskRoomService(opts.configuration).getRoomMembers(id);
|
|
1250
|
-
if (r)
|
|
1251
|
-
auditRoom('members', r, r.state);
|
|
1252
1328
|
if (opts.json) {
|
|
1253
1329
|
console.log(JSON.stringify({
|
|
1254
1330
|
schema_version: 1,
|
|
@@ -1286,9 +1362,12 @@ export function registerRoomCommands(parent, cOpt) {
|
|
|
1286
1362
|
await taskRoomService(opts.configuration).requestRoomDeletion({
|
|
1287
1363
|
actor: { kind: 'local_control', surface: 'cli' }, roomId: id,
|
|
1288
1364
|
});
|
|
1365
|
+
const closing = getRoomRecord(id);
|
|
1366
|
+
if (previous && closing && previous.state !== closing.state)
|
|
1367
|
+
auditRoom('close', closing, previous.state, closing.state);
|
|
1289
1368
|
const settled = await launchRoomDeleteWorker(id, opts.configuration);
|
|
1290
|
-
if (
|
|
1291
|
-
auditRoom('delete',
|
|
1369
|
+
if (closing)
|
|
1370
|
+
auditRoom('delete', closing, closing.state, settled.deleted ? 'deleted' : 'closing');
|
|
1292
1371
|
if (opts.json) {
|
|
1293
1372
|
console.log(JSON.stringify({ schema_version: 1, room_id: id, deleted: settled.deleted }, null, 2));
|
|
1294
1373
|
return;
|
|
@@ -13,6 +13,7 @@ import { buildRoomMemberTask, sha256Text } from './member-startup.js';
|
|
|
13
13
|
import { agentDir } from '../paths.js';
|
|
14
14
|
import { readProvenance } from '../creation.js';
|
|
15
15
|
import { readTempSupervisor, secureStoppedTempArchive, tempArchiveForCreationAction, tempSupervisorLiveness, } from '../temp-lifecycle.js';
|
|
16
|
+
import { recordFleetAuditPresentation } from '../fleet-command-audit.js';
|
|
16
17
|
export function getBinPath() {
|
|
17
18
|
try {
|
|
18
19
|
return realpathSync(process.argv[1]);
|
|
@@ -30,11 +31,30 @@ function canonicalJson(value) {
|
|
|
30
31
|
.map(([key, item]) => `${JSON.stringify(key)}:${canonicalJson(item)}`).join(',')}}`;
|
|
31
32
|
return JSON.stringify(value);
|
|
32
33
|
}
|
|
34
|
+
function selectionSummary(selection) {
|
|
35
|
+
if ('ref' in selection)
|
|
36
|
+
return `ref:${selection.ref} (reference)`;
|
|
37
|
+
return `inline:sha256:${createHash('sha256').update(canonicalJson(selection.inline)).digest('hex').slice(0, 16)} (inline)`;
|
|
38
|
+
}
|
|
39
|
+
function permissionSummary(definition) {
|
|
40
|
+
const permissions = definition.permissions;
|
|
41
|
+
if (!permissions)
|
|
42
|
+
return undefined;
|
|
43
|
+
return `approval=${permissions.approval},filesystem=${permissions.filesystem},unattended=${permissions.unattended}`;
|
|
44
|
+
}
|
|
33
45
|
function launchDefinition(definition) {
|
|
34
|
-
const selectionEvidence = (selection) => 'ref' in selection ? { kind: 'ref', id: selection.ref } : {
|
|
46
|
+
const selectionEvidence = (selection) => 'ref' in selection ? { kind: 'ref', id: selection.ref } : {
|
|
47
|
+
kind: 'inline', fingerprint: `sha256:${createHash('sha256')
|
|
48
|
+
.update(canonicalJson(selection.inline)).digest('hex').slice(0, 16)}`,
|
|
49
|
+
};
|
|
35
50
|
return {
|
|
36
51
|
projection: {
|
|
37
52
|
brain: selectionEvidence(definition.brain), role: selectionEvidence(definition.role),
|
|
53
|
+
...(definition.permissions ? { permissions: {
|
|
54
|
+
approval: definition.permissions.approval,
|
|
55
|
+
filesystem: definition.permissions.filesystem,
|
|
56
|
+
unattended: definition.permissions.unattended,
|
|
57
|
+
} } : {}),
|
|
38
58
|
operationalFields: Object.keys(definition)
|
|
39
59
|
.filter(key => key !== 'brain' && key !== 'role').sort(),
|
|
40
60
|
},
|
|
@@ -260,6 +280,11 @@ async function launchMember(input) {
|
|
|
260
280
|
...(launched.callerRole ? { caller_role: launched.callerRole } : {}),
|
|
261
281
|
launch_id: supervisor.launchId, updated_at: new Date().toISOString(),
|
|
262
282
|
} });
|
|
283
|
+
recordFleetAuditPresentation({ kind: 'agent_started', id: member.name, name: member.name,
|
|
284
|
+
lifetime: 'temporary', brain: selectionSummary(settings.definition.brain),
|
|
285
|
+
role: selectionSummary(settings.definition.role), harness: 'resolved', session: 'acp',
|
|
286
|
+
permissions: permissionSummary(settings.definition), parent: provision.roomId,
|
|
287
|
+
actionId: launched.creationActionId, inherited: [] });
|
|
263
288
|
}
|
|
264
289
|
catch (error) {
|
|
265
290
|
updateMemberStartup(provision.roomId, member.name, { launch: {
|
|
@@ -269,6 +294,8 @@ async function launchMember(input) {
|
|
|
269
294
|
updated_at: new Date().toISOString(),
|
|
270
295
|
error: error instanceof Error ? error.message : String(error),
|
|
271
296
|
} });
|
|
297
|
+
recordFleetAuditPresentation({ kind: 'lifecycle_failure', resource: 'Agent', id: member.name,
|
|
298
|
+
state: 'failed', category: 'readiness_failed', eventId: effectiveActionId });
|
|
272
299
|
throw error;
|
|
273
300
|
}
|
|
274
301
|
}
|
package/dist/runner.js
CHANGED
|
@@ -621,7 +621,19 @@ export async function runOnce(name, opts = {}, partialDeps = {}) {
|
|
|
621
621
|
throw error;
|
|
622
622
|
}
|
|
623
623
|
control.setFleetSpawner(async (requested) => {
|
|
624
|
-
|
|
624
|
+
const event = await executeManagedSpawn(role, configPath, requested, deps.log);
|
|
625
|
+
// Room-member launches are collected with their Task/Room transaction so
|
|
626
|
+
// causal delivery is Task → Room → Agents → Room active → Task active.
|
|
627
|
+
if (!requested.roomMemberStartup && role.owner_channel && ownerChannel?.notifyFleetSpawn) {
|
|
628
|
+
try {
|
|
629
|
+
await ownerChannel.notifyFleetSpawn(event);
|
|
630
|
+
}
|
|
631
|
+
catch (error) {
|
|
632
|
+
deps.log(`[${name}] Agent lifecycle notice delivery failed: `
|
|
633
|
+
+ `${error?.message ?? String(error)}`);
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
return event;
|
|
625
637
|
});
|
|
626
638
|
resolvedMonitorDeps.delivery = {
|
|
627
639
|
// A wake is only delivered when its turn TERMINATES successfully. A
|
|
@@ -39,7 +39,7 @@ export interface ControlRequest {
|
|
|
39
39
|
exitCode?: number;
|
|
40
40
|
effect?: 'not_started' | 'completed' | 'unknown';
|
|
41
41
|
resourceIds?: Record<string, string>;
|
|
42
|
-
|
|
42
|
+
presentations?: FleetAuditPresentation[];
|
|
43
43
|
};
|
|
44
44
|
}
|
|
45
45
|
export interface ControlResponse {
|
package/dist/session/control.js
CHANGED
|
@@ -341,7 +341,7 @@ export class RoleControlServer {
|
|
|
341
341
|
correlationId: audit.correlationId, class: audit.class, effect: audit.effect,
|
|
342
342
|
...(audit.exitCode === undefined ? {} : { exitCode: audit.exitCode }),
|
|
343
343
|
...(audit.resourceIds ? { resourceIds: audit.resourceIds } : {}),
|
|
344
|
-
...(audit.
|
|
344
|
+
...(audit.presentations ? { presentations: audit.presentations } : {}),
|
|
345
345
|
});
|
|
346
346
|
this.write(socket, { version: 1, id: request.id, ok: true, result });
|
|
347
347
|
return;
|
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.9",
|
|
4
4
|
"description": "Harness-agnostic fleet of persistent, identity-bound AI agents. Declarative fleet.yaml, ACP sessions, supervision, and ours.network messaging.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "FSL-1.1-Apache-2.0",
|