@ours.network/fleet 1.1.0-nightly.27 → 1.1.0-nightly.28

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.
@@ -22,7 +22,7 @@ export interface TaskSettlementPlan {
22
22
  settlementRequired: boolean;
23
23
  }
24
24
  export type TaskRecoveryIssue = {
25
- code: 'terminal_pending' | 'waiting_cowork' | 'waiting_owner_invite' | 'owner_cid_mismatch' | 'waiting_seats' | 'provisioning_resumed';
25
+ code: 'terminal_pending' | 'waiting_cowork' | 'waiting_owner_authorization' | 'waiting_owner_invite' | 'owner_cid_mismatch' | 'waiting_seats' | 'provisioning_resumed';
26
26
  } | {
27
27
  code: 'member_failed';
28
28
  stepIndex: number;
@@ -16,6 +16,7 @@ import { withFileLock } from '../atomic-file.js';
16
16
  import { launchFleetWorker } from '../rooms-tasks/external-worker.js';
17
17
  import { storedRoomLaunchPolicy, TASK_CANCELLABLE_STATES, TASK_TERMINAL_STATES } from '../rooms-tasks/types.js';
18
18
  import { deriveTaskRoomName } from '../rooms-tasks/task-room-name.js';
19
+ const OWNER_ROOM_COMMANDS = ['list-members', 'remove-member'];
19
20
  function recordCanonicalRoomCreated(room) {
20
21
  recordFleetAuditPresentation({ kind: 'room', operation: 'create',
21
22
  eventId: `room-created:${room.created_at}`, id: room.room_id,
@@ -235,15 +236,17 @@ export class TaskRoomApplicationService {
235
236
  const ready = task.state === 'active' && room?.state === 'active'
236
237
  && active === expected && launched === expected;
237
238
  const blocker = task.outcome?.summary ?? task.blocked?.reason ?? room?.saga.error;
238
- const nextAction = room?.provisioning_detail === 'waiting_owner_invite'
239
- ? 'Rotate rooms.owner.public_invite, then await the same task.'
240
- : room?.provisioning_detail === 'owner_cid_mismatch'
241
- ? 'Verify rooms.owner.expected_cid, rotate the Owner invite, then await the same task.'
242
- : room?.provisioning_detail === 'waiting_cowork'
243
- ? 'Restore ours-cowork, then await the same task.'
244
- : failed
245
- ? `Correct the blocker, then run ours-fleet task recover ${task.task_id}.`
246
- : undefined;
239
+ const nextAction = room?.provisioning_detail === 'waiting_owner_authorization'
240
+ ? 'Restore ours-cowork, then await the same task to retry Owner command authorization.'
241
+ : room?.provisioning_detail === 'waiting_owner_invite'
242
+ ? 'Rotate rooms.owner.public_invite, then await the same task.'
243
+ : room?.provisioning_detail === 'owner_cid_mismatch'
244
+ ? 'Verify rooms.owner.expected_cid, rotate the Owner invite, then await the same task.'
245
+ : room?.provisioning_detail === 'waiting_cowork'
246
+ ? 'Restore ours-cowork, then await the same task.'
247
+ : failed
248
+ ? `Correct the blocker, then run ours-fleet task recover ${task.task_id}.`
249
+ : undefined;
247
250
  return {
248
251
  kind: failed ? 'failed' : ready ? 'ready' : 'in_progress', task, room,
249
252
  handle: { command: `ours-fleet task await ${task.task_id}`, task_id: task.task_id },
@@ -462,7 +465,9 @@ export class TaskRoomApplicationService {
462
465
  return { kind: 'provisioning_resume_failed', task, room, issues };
463
466
  }
464
467
  }
465
- if (room.provisioning_detail === 'waiting_owner_invite'
468
+ if (room.saga.phase === 'attach_owner'
469
+ || room.provisioning_detail === 'waiting_owner_authorization'
470
+ || room.provisioning_detail === 'waiting_owner_invite'
466
471
  || room.provisioning_detail === 'owner_cid_mismatch') {
467
472
  try {
468
473
  await this.recoverRoom({ actor: input.actor, roomId: room.room_id });
@@ -484,6 +489,8 @@ export class TaskRoomApplicationService {
484
489
  };
485
490
  if (room.provisioning_detail === 'waiting_cowork')
486
491
  issues.push({ code: 'waiting_cowork' });
492
+ if (room.provisioning_detail === 'waiting_owner_authorization')
493
+ issues.push({ code: 'waiting_owner_authorization' });
487
494
  if (room.provisioning_detail === 'waiting_owner_invite')
488
495
  issues.push({ code: 'waiting_owner_invite' });
489
496
  if (room.provisioning_detail === 'owner_cid_mismatch')
@@ -631,14 +638,27 @@ export class TaskRoomApplicationService {
631
638
  throw new Error(error);
632
639
  }
633
640
  }
641
+ if (orchestration?.owner_seat_cid) {
642
+ const ownerCid = orchestration.owner_seat_cid.toLowerCase();
643
+ const ownerSeat = room.seats.find(seat => seat.identity_cid.toLowerCase() === ownerCid && seat.seat_state !== 'removed');
644
+ if (ownerSeat)
645
+ await adapter.setRoleCommands(input.roomId, {
646
+ role: ownerSeat.role, commands: [...OWNER_ROOM_COMMANDS],
647
+ });
648
+ }
634
649
  if (orchestration && !orchestration.owner_seat_cid
635
- && (orchestration.provisioning_detail === 'waiting_owner_invite'
650
+ && (orchestration.saga.phase === 'attach_owner'
651
+ || orchestration.provisioning_detail === 'waiting_owner_authorization'
652
+ || orchestration.provisioning_detail === 'waiting_owner_invite'
636
653
  || orchestration.provisioning_detail === 'owner_cid_mismatch')) {
637
654
  const expected = cfg.rooms.owner.expected_cid.toLowerCase();
638
655
  const existing = (await adapter.getSeats(input.roomId))
639
656
  .find(seat => seat.identity_cid.toLowerCase() === expected && seat.seat_state !== 'removed');
640
657
  if (!existing && !cfg.ownerInvite)
641
658
  throw new ConfigError('rooms.owner: configure public_invite or public_invite_file before recovery');
659
+ await adapter.setRoleCommands(input.roomId, {
660
+ role: existing?.role ?? cfg.rooms.owner.role, commands: [...OWNER_ROOM_COMMANDS],
661
+ });
642
662
  let acceptedCid = existing?.identity_cid;
643
663
  if (!acceptedCid)
644
664
  acceptedCid = (await adapter.acceptInvite(input.roomId, cfg.ownerInvite, {
@@ -654,6 +674,8 @@ export class TaskRoomApplicationService {
654
674
  issues.push('Recovery guidance is recorded; inspect role logs for diagnostics.');
655
675
  if (orchestration?.provisioning_detail === 'waiting_cowork')
656
676
  issues.push('Check ours-cowork service status');
677
+ if (orchestration?.provisioning_detail === 'waiting_owner_authorization')
678
+ issues.push('Restore Cowork availability, then re-run recover');
657
679
  if (orchestration?.provisioning_detail === 'waiting_owner_invite')
658
680
  issues.push('Rotate rooms.owner.public_invite in config, then re-run recover');
659
681
  if (orchestration?.provisioning_detail === 'waiting_seats')
@@ -951,8 +973,17 @@ export class TaskRoomApplicationService {
951
973
  await checkpointFleetAuditPresentations();
952
974
  room = advanceSaga(room.room_id, 'create_room', 1);
953
975
  if (attachOwner) {
976
+ room = advanceSaga(room.room_id, 'attach_owner', 2);
977
+ try {
978
+ await cowork.setRoleCommands(room.room_id, {
979
+ role: rooms.owner.role, commands: [...OWNER_ROOM_COMMANDS],
980
+ });
981
+ }
982
+ catch (error) {
983
+ setSagaError(room.room_id, error instanceof Error ? error.message : String(error), 'Restore Cowork availability, then run room recover to configure Owner command authorization.', 'waiting_owner_authorization');
984
+ throw error;
985
+ }
954
986
  try {
955
- room = advanceSaga(room.room_id, 'attach_owner', 2);
956
987
  const accepted = await cowork.acceptInvite(room.room_id, cfg.ownerInvite, {
957
988
  role: rooms.owner.role, expected_cid: rooms.owner.expected_cid,
958
989
  });
@@ -1,9 +1,9 @@
1
1
  {
2
- "version": "1.1.0-nightly.27",
3
- "buildId": "188ee458bb22",
4
- "commit": "0ace1644c06e5c637efccced7b39bdfa03260cf6",
2
+ "version": "1.1.0-nightly.28",
3
+ "buildId": "761c569df66f",
4
+ "commit": "c03ee4d380feb9e12fcc503785f3d81203026c2a",
5
5
  "dirty": true,
6
- "builtAt": "2026-09-03T18:06:33.704Z",
6
+ "builtAt": "2026-09-04T14:31:02.540Z",
7
7
  "capabilities": [
8
8
  "monitor.interrupt.after_tool"
9
9
  ]
@@ -1648,12 +1648,13 @@ export class OwnerChannel {
1648
1648
  }
1649
1649
  const { task, room, issues } = begin.result;
1650
1650
  const hints = issues.map(issue => issue.code === 'waiting_cowork' ? 'Cowork socket unreachable'
1651
- : issue.code === 'waiting_owner_invite' ? 'Owner invite missing or invalid'
1652
- : issue.code === 'owner_cid_mismatch' ? 'Owner CID mismatch'
1653
- : issue.code === 'member_failed' ? `Member failed at step ${issue.stepIndex}`
1654
- : issue.code === 'resume_failed' ? `Resume failed: ${issue.error}`
1655
- : issue.code === 'provisioning_resumed' ? 'Provisioning resumed successfully'
1656
- : issue.code);
1651
+ : issue.code === 'waiting_owner_authorization' ? 'Owner command authorization is not configured'
1652
+ : issue.code === 'waiting_owner_invite' ? 'Owner invite missing or invalid'
1653
+ : issue.code === 'owner_cid_mismatch' ? 'Owner CID mismatch'
1654
+ : issue.code === 'member_failed' ? `Member failed at step ${issue.stepIndex}`
1655
+ : issue.code === 'resume_failed' ? `Resume failed: ${issue.error}`
1656
+ : issue.code === 'provisioning_resumed' ? 'Provisioning resumed successfully'
1657
+ : issue.code);
1657
1658
  await this.send(sender.id, renderMarkdownResult({
1658
1659
  icon: '🛟', title: 'Task recovery',
1659
1660
  fields: [{ label: 'Task', value: task.task_id, kind: 'code' },
@@ -1196,6 +1196,8 @@ export function registerTaskCommands(parent, cOpt) {
1196
1196
  return 'Cowork management socket unreachable — check ours-cowork service';
1197
1197
  if (issue.code === 'waiting_owner_invite')
1198
1198
  return 'Owner invite invalid or expired — rotate rooms.owner.public_invite in config';
1199
+ if (issue.code === 'waiting_owner_authorization')
1200
+ return 'Owner command authorization is not configured — restore ours-cowork and retry';
1199
1201
  if (issue.code === 'owner_cid_mismatch')
1200
1202
  return 'Owner CID mismatch — verify rooms.owner.expected_cid matches Messenger identity';
1201
1203
  if (issue.code === 'member_failed')
@@ -73,6 +73,10 @@ export interface CoworkAdapter {
73
73
  role: string;
74
74
  text: string;
75
75
  }): Promise<CoworkRoleBriefingInfo>;
76
+ setRoleCommands(roomId: string, opts: {
77
+ role: string;
78
+ commands: Array<'list-members' | 'remove-member'>;
79
+ }): Promise<void>;
76
80
  getHistory(roomId: string, opts?: {
77
81
  after?: number;
78
82
  limit?: number;
@@ -390,6 +390,13 @@ export function createCoworkAdapter(options = {}) {
390
390
  throw new CoworkProtocolError('room.participants', 'result must be an array');
391
391
  return result.map((seat) => projectSeat(seat, 'room.participants'));
392
392
  },
393
+ async setRoleCommands(roomId, opts) {
394
+ const result = await call('room.command.role.set', {
395
+ room_id: roomId, role: opts.role, commands: opts.commands,
396
+ });
397
+ if (!Array.isArray(result))
398
+ throw new CoworkProtocolError('room.command.role.set', 'result must be an array');
399
+ },
393
400
  async recoverRoom(roomId) {
394
401
  // Cowork performs packet/state reconciliation during daemon recovery.
395
402
  // Its `room.recover` RPC is specifically invite-receipt recovery, so a
@@ -133,7 +133,7 @@ export interface SagaCursor {
133
133
  error?: string;
134
134
  recovery_hint?: string;
135
135
  }
136
- export type ProvisioningDetail = 'waiting_cowork' | 'waiting_owner_invite' | 'owner_cid_mismatch' | 'member_failed' | 'waiting_seats' | 'uncertain';
136
+ export type ProvisioningDetail = 'waiting_cowork' | 'waiting_owner_authorization' | 'waiting_owner_invite' | 'owner_cid_mismatch' | 'member_failed' | 'waiting_seats' | 'uncertain';
137
137
  export interface RoomRoleBriefingDefinition {
138
138
  role: string;
139
139
  text: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ours.network/fleet",
3
- "version": "1.1.0-nightly.27",
3
+ "version": "1.1.0-nightly.28",
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",