@ours.network/fleet 2.2.3 → 2.2.4
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 +2 -0
- package/dist/application/task-room-service.d.ts +2 -2
- package/dist/application/task-room-service.js +4 -5
- package/dist/build-info.json +4 -4
- package/dist/notifications/mission-ownership.d.ts +3 -4
- package/dist/notifications/mission-ownership.js +4 -5
- package/dist/notifications/task-producer.d.ts +1 -2
- package/dist/notifications/task-producer.js +9 -17
- package/dist/rooms-tasks/layout-service.d.ts +1 -1
- package/dist/rooms-tasks/layout-service.js +1 -1
- package/dist/rooms-tasks/layout.d.ts +2 -2
- package/dist/rooms-tasks/layout.js +9 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1863,6 +1863,8 @@ See [Room layouts](docs/room-layouts.md) for YAML examples and CLI commands.
|
|
|
1863
1863
|
|
|
1864
1864
|
### Dedicated notification service
|
|
1865
1865
|
|
|
1866
|
+
Task room notification registration is attempted before admitting the Owner. If registration fails, room admission and task work continue; unassociated room messages may arrive as ordinary notifications even when mission-room messages are off. Existing-room registration is attempted once per Fleet startup, without automatic registration retries. The installer configures the trusted Fleet producer's mission ownership permission automatically.
|
|
1867
|
+
|
|
1866
1868
|
Configure the web backend with `OURS_NOTIFICATIONS_ORIGIN` and a Fleet-scoped `OURS_NOTIFICATIONS_PRODUCER_TOKEN`. It reads each local supervisor ledger incrementally with persistent checkpoints under web state, correlating owner browser/channel prompts with terminal completion. Monitor/startup turns do not notify. First installation skips existing history; no agent restart or conversation controller is needed. Persist web state across deployment/restarts. Reads are bounded to 256 KiB per role per tick, and oversized model/media records are skipped across chunks. Expose `/notifications` through the generic authenticated prefix gateway with a server-side user token and `stripBrowserContext: true`; producer tokens stay server-side. All browser sessions of this single-owner console map to one stable notification account. See [ours-notifications](https://github.com/adapt-toolkit/ours-notifications) for configuration/API/release details.
|
|
1867
1869
|
|
|
1868
1870
|
New agent notifications and confirmed role retirement use `/fleet/chats/agents/:roleId`, with each ID escaped once and matching message anchors retained. Deploy the app, notification service, and producers together: old query-based conversation links are intentionally unsupported. Existing pending payload URLs are replayed unchanged for idempotency; obsolete cleanup targets remain stored without being sent or treated as proof of absence. Task management notification paths are unchanged.
|
|
@@ -328,8 +328,8 @@ export declare class TaskRoomApplicationService {
|
|
|
328
328
|
taskId: string;
|
|
329
329
|
}): Promise<TaskProvisioningContinuationResult>;
|
|
330
330
|
/**
|
|
331
|
-
*
|
|
332
|
-
*
|
|
331
|
+
* Attempt registration before the Owner joins. A notification failure must not become a room saga error:
|
|
332
|
+
* admission continues and unassociated room messages may arrive as ordinary notifications.
|
|
333
333
|
*/
|
|
334
334
|
private associateRoomNotifications;
|
|
335
335
|
private setOwnerRoomCommands;
|
|
@@ -707,8 +707,8 @@ export class TaskRoomApplicationService {
|
|
|
707
707
|
}
|
|
708
708
|
}
|
|
709
709
|
/**
|
|
710
|
-
*
|
|
711
|
-
*
|
|
710
|
+
* Attempt registration before the Owner joins. A notification failure must not become a room saga error:
|
|
711
|
+
* admission continues and unassociated room messages may arrive as ordinary notifications.
|
|
712
712
|
*/
|
|
713
713
|
async associateRoomNotifications(room) {
|
|
714
714
|
if (!room.task_id || !room.room_identity_cid)
|
|
@@ -716,9 +716,8 @@ export class TaskRoomApplicationService {
|
|
|
716
716
|
try {
|
|
717
717
|
await (this.deps.associateRoomNotifications ?? associateRoomNotifications)(room.task_id, room.room_identity_cid);
|
|
718
718
|
}
|
|
719
|
-
catch
|
|
720
|
-
|
|
721
|
-
throw error;
|
|
719
|
+
catch {
|
|
720
|
+
console.warn('Mission room notification registration failed; continuing room admission with ordinary notifications');
|
|
722
721
|
}
|
|
723
722
|
}
|
|
724
723
|
async setOwnerRoomCommands(cowork, roomId, role) {
|
package/dist/build-info.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "2.2.
|
|
3
|
-
"buildId": "
|
|
4
|
-
"commit": "
|
|
2
|
+
"version": "2.2.4",
|
|
3
|
+
"buildId": "009d1f631eac",
|
|
4
|
+
"commit": "ed07f55a29a98edfbcadccb5ebc07fccaa24adae",
|
|
5
5
|
"dirty": false,
|
|
6
|
-
"builtAt": "2026-10-
|
|
6
|
+
"builtAt": "2026-10-29T09:19:37.374Z",
|
|
7
7
|
"capabilities": [
|
|
8
8
|
"service.refresh-v1",
|
|
9
9
|
"managed-cli.codex-reasoning-effort-v1",
|
|
@@ -10,10 +10,9 @@ export declare function missionRequest(config: ProducerConfig, path: string, bod
|
|
|
10
10
|
data: any;
|
|
11
11
|
}>;
|
|
12
12
|
/**
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* admitted with a warning (it cannot suppress own-room messages either).
|
|
13
|
+
* Attempt to register the task room before the Owner joins, so preferences apply from its first message when confirmed.
|
|
14
|
+
* Unreachable service or unconfirmed coverage throws to the caller, which continues admission with ordinary notifications.
|
|
15
|
+
* No producer configured → nothing to register. An older service without the route returns with a warning.
|
|
17
16
|
*/
|
|
18
17
|
export declare function associateRoomNotifications(taskId: string, roomCid: string, options?: {
|
|
19
18
|
config?: ProducerConfig | undefined;
|
|
@@ -47,10 +47,9 @@ async function registerMission(config, taskId, manifest, request) {
|
|
|
47
47
|
throw new MissionOwnershipUnavailable(`notification service refused room registration (HTTP ${status})`);
|
|
48
48
|
}
|
|
49
49
|
/**
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
* admitted with a warning (it cannot suppress own-room messages either).
|
|
50
|
+
* Attempt to register the task room before the Owner joins, so preferences apply from its first message when confirmed.
|
|
51
|
+
* Unreachable service or unconfirmed coverage throws to the caller, which continues admission with ordinary notifications.
|
|
52
|
+
* No producer configured → nothing to register. An older service without the route returns with a warning.
|
|
54
53
|
*/
|
|
55
54
|
export async function associateRoomNotifications(taskId, roomCid, options = {}) {
|
|
56
55
|
const manifest = recordNotificationRooms(taskId, [roomCid]);
|
|
@@ -66,7 +65,7 @@ export async function associateRoomNotifications(taskId, roomCid, options = {})
|
|
|
66
65
|
return;
|
|
67
66
|
}
|
|
68
67
|
if (error instanceof MissionOwnershipUnavailable)
|
|
69
|
-
throw new Error('
|
|
68
|
+
throw new Error('Notification service did not register the mission room');
|
|
70
69
|
throw error;
|
|
71
70
|
}
|
|
72
71
|
}
|
|
@@ -15,8 +15,7 @@ export declare class TaskNotificationProducer {
|
|
|
15
15
|
private offset;
|
|
16
16
|
private stopped;
|
|
17
17
|
private readonly shutdown;
|
|
18
|
-
private
|
|
19
|
-
private bootstrapAt;
|
|
18
|
+
private bootstrapAttempted;
|
|
20
19
|
constructor(config: ProducerConfig, warn?: (line: string) => void, request?: typeof fetch,
|
|
21
20
|
/** Existing tasks and their layout room CIDs, registered once per start before own-room OFF can be chosen. */
|
|
22
21
|
missions?: {
|
|
@@ -16,8 +16,7 @@ export class TaskNotificationProducer {
|
|
|
16
16
|
offset = 0;
|
|
17
17
|
stopped = false;
|
|
18
18
|
shutdown = new AbortController();
|
|
19
|
-
|
|
20
|
-
bootstrapAt = 0;
|
|
19
|
+
bootstrapAttempted = false;
|
|
21
20
|
constructor(config, warn = () => { }, request = fetch,
|
|
22
21
|
/** Existing tasks and their layout room CIDs, registered once per start before own-room OFF can be chosen. */
|
|
23
22
|
missions) {
|
|
@@ -68,21 +67,14 @@ export class TaskNotificationProducer {
|
|
|
68
67
|
// Deletion first: a retired task's notifications go before any other delivery work in this pass.
|
|
69
68
|
if (convertRetiredTaskNotifications() || await drainMissionCleanup(this.config, this.warn, this.request))
|
|
70
69
|
this.retry();
|
|
71
|
-
if (this.missions && !this.
|
|
72
|
-
//
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
this.
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
this.bootstrapped = true;
|
|
80
|
-
}
|
|
81
|
-
catch {
|
|
82
|
-
this.warn('Mission room registration pending; own-room message preference stays unavailable');
|
|
83
|
-
this.bootstrapAt = Date.now() + 30_000;
|
|
84
|
-
this.retry(30_000);
|
|
85
|
-
}
|
|
70
|
+
if (this.missions && !this.bootstrapAttempted && !this.stopped) {
|
|
71
|
+
// One startup attempt. Association failure neither blocks task events nor schedules another registration.
|
|
72
|
+
this.bootstrapAttempted = true;
|
|
73
|
+
try {
|
|
74
|
+
await bootstrapMissionRooms(this.config, this.missions.tasks, this.missions.layoutRooms, this.request);
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
this.warn('Mission room registration failed; own-room message preference may be unavailable');
|
|
86
78
|
}
|
|
87
79
|
}
|
|
88
80
|
const pending = pendingTaskNotifications(this.warn);
|
|
@@ -30,7 +30,7 @@ export declare class RoomLayoutService {
|
|
|
30
30
|
supervisor(id?: string, templates?: Record<string, AgentTemplateDefinition>): NativeLayoutSupervisor;
|
|
31
31
|
private owner;
|
|
32
32
|
private engine;
|
|
33
|
-
/** A task's layout rooms
|
|
33
|
+
/** A task's layout rooms attempt notification registration before Owner admission. */
|
|
34
34
|
private beforeOwner;
|
|
35
35
|
open(id: string, cleanup?: boolean): RoomLayout;
|
|
36
36
|
/** `scope` lets an owner (e.g. a Task) snapshot an adjusted definition and pin agent working directories. */
|
|
@@ -180,7 +180,7 @@ export class RoomLayoutService {
|
|
|
180
180
|
assertLayoutFile(path);
|
|
181
181
|
return new RoomLayout(path, () => createCoworkAdapter(), this.supervisor(id), cleanup ? undefined : this.owner(), this.beforeOwner(id));
|
|
182
182
|
}
|
|
183
|
-
/** A task's layout rooms
|
|
183
|
+
/** A task's layout rooms attempt notification registration before Owner admission. */
|
|
184
184
|
beforeOwner(id) {
|
|
185
185
|
const taskId = /^task-([0-9a-z]{9}[0-9a-f]{8})$/.exec(id)?.[1];
|
|
186
186
|
return taskId ? async (roomCid) => (await import('../notifications/mission-ownership.js')).associateRoomNotifications(taskId, roomCid) : undefined;
|
|
@@ -104,12 +104,12 @@ export declare class RoomLayout {
|
|
|
104
104
|
private file;
|
|
105
105
|
private supervisor;
|
|
106
106
|
private owner?;
|
|
107
|
-
/**
|
|
107
|
+
/** Best-effort notification registration before the Owner joins each room. */
|
|
108
108
|
private beforeOwner?;
|
|
109
109
|
private coworkAdapter?;
|
|
110
110
|
private readonly coworkFactory;
|
|
111
111
|
constructor(file: string, cowork: CoworkAdapter | (() => CoworkAdapter), supervisor: LayoutSupervisor, owner?: LayoutOwner | undefined,
|
|
112
|
-
/**
|
|
112
|
+
/** Best-effort notification registration before the Owner joins each room. */
|
|
113
113
|
beforeOwner?: ((roomCid: string) => Promise<void>) | undefined);
|
|
114
114
|
private get cowork();
|
|
115
115
|
snapshot(): RoomLayoutState;
|
|
@@ -43,7 +43,7 @@ export class RoomLayout {
|
|
|
43
43
|
coworkAdapter;
|
|
44
44
|
coworkFactory;
|
|
45
45
|
constructor(file, cowork, supervisor, owner,
|
|
46
|
-
/**
|
|
46
|
+
/** Best-effort notification registration before the Owner joins each room. */
|
|
47
47
|
beforeOwner) {
|
|
48
48
|
this.file = file;
|
|
49
49
|
this.supervisor = supervisor;
|
|
@@ -165,8 +165,14 @@ export class RoomLayout {
|
|
|
165
165
|
activation_requirements: !spec.members.length && s.owner ? [{ role: s.owner.role, count: 1 }]
|
|
166
166
|
: [...new Set(spec.members.map(roleFor))].map(role => ({ role, count: spec.members.filter(m => roleFor(m) === role).length })),
|
|
167
167
|
}), native => { room.native = native; s.order.push(key); });
|
|
168
|
-
if (s.owner && !room.owner_ready && this.beforeOwner)
|
|
169
|
-
|
|
168
|
+
if (s.owner && !room.owner_ready && this.beforeOwner) {
|
|
169
|
+
try {
|
|
170
|
+
await this.beforeOwner(room.native.identity_cid);
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
console.warn('Mission room notification registration failed; continuing room admission with ordinary notifications');
|
|
174
|
+
}
|
|
175
|
+
}
|
|
170
176
|
if (s.owner && !room.owner_ready)
|
|
171
177
|
await this.mutation(s, `owner:${key}`, async () => {
|
|
172
178
|
await this.cowork.acceptInvite(room.native.room_id, this.owner.invite, { role: s.owner.role, expected_cid: s.owner.cid });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ours.network/fleet",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.4",
|
|
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",
|