@ours.network/fleet 1.1.0-nightly.13 → 1.1.0-nightly.15

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.
@@ -2,6 +2,7 @@ import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync } from 'node:f
2
2
  import { join } from 'node:path';
3
3
  import { replaceFileAtomically } from '../atomic-file.js';
4
4
  import { stateRoot } from '../paths.js';
5
+ import { storedRoomLaunchPolicy } from './types.js';
5
6
  import { releaseLaunchSnapshot } from './launch-snapshot.js';
6
7
  export const roomsDir = () => join(stateRoot(), 'rooms');
7
8
  function roomPath(id) { return join(roomsDir(), `${id}.json`); }
@@ -19,8 +20,13 @@ function writeRoom(record) {
19
20
  }
20
21
  export function createRoomRecord(input) {
21
22
  const existing = getRoomRecord(input.room_id);
22
- if (existing)
23
+ if (existing) {
24
+ const before = storedRoomLaunchPolicy(existing.room_policy);
25
+ const requested = storedRoomLaunchPolicy(input.room_policy);
26
+ if (JSON.stringify(before) !== JSON.stringify(requested))
27
+ throw new RoomStateError(`room ${input.room_id} launch policy mismatch`);
23
28
  return existing;
29
+ }
24
30
  const record = {
25
31
  room_id: input.room_id,
26
32
  room_name: input.room_name,
@@ -28,6 +34,7 @@ export function createRoomRecord(input) {
28
34
  room_identity_cid: input.room_identity_cid,
29
35
  task_id: input.task_id,
30
36
  template_snapshot: input.template_snapshot,
37
+ room_policy: input.room_policy,
31
38
  saga: { phase: 'persist_intent', step_index: 0 },
32
39
  member_seats: [],
33
40
  state: 'provisioning',
@@ -3,7 +3,7 @@ import { join } from 'node:path';
3
3
  import { randomUUID } from 'node:crypto';
4
4
  import { replaceFileAtomically } from '../atomic-file.js';
5
5
  import { stateRoot } from '../paths.js';
6
- import { TASK_TERMINAL_STATES, TASK_CANCELLABLE_STATES } from './types.js';
6
+ import { storedRoomLaunchPolicy, TASK_TERMINAL_STATES, TASK_CANCELLABLE_STATES } from './types.js';
7
7
  import { DEFAULT_TASK_LIST_ID, readTaskLists } from './task-lists.js';
8
8
  export const tasksDir = () => join(stateRoot(), 'tasks');
9
9
  function taskPath(id) { return join(tasksDir(), `${id}.json`); }
@@ -131,7 +131,9 @@ export function createTask(input) {
131
131
  if (existing) {
132
132
  const existingPlan = existing.execution_plan?.plan_hash;
133
133
  const requestedPlan = input.execution_plan?.plan_hash;
134
- if (existingPlan !== requestedPlan)
134
+ const existingPolicy = storedRoomLaunchPolicy(existing.execution_plan?.room_policy);
135
+ const requestedPolicy = storedRoomLaunchPolicy(input.execution_plan?.room_policy);
136
+ if (existingPlan !== requestedPlan || JSON.stringify(existingPolicy) !== JSON.stringify(requestedPolicy))
135
137
  throw new TaskStateError(`idempotency key '${key}' was already used with a different execution plan`);
136
138
  return existing;
137
139
  }
@@ -161,8 +163,14 @@ export function updateTaskExecutionPlan(id, executionPlan) {
161
163
  return withTaskLock(id, () => {
162
164
  const task = readTask(id);
163
165
  assertNoPendingDeletion(task);
164
- if (task.execution_plan && task.execution_plan.plan_hash !== executionPlan.plan_hash)
165
- throw new TaskStateError(`task ${id} execution plan mismatch`);
166
+ if (task.execution_plan) {
167
+ if (task.execution_plan.plan_hash !== executionPlan.plan_hash)
168
+ throw new TaskStateError(`task ${id} execution plan mismatch`);
169
+ const before = storedRoomLaunchPolicy(task.execution_plan.room_policy);
170
+ const after = storedRoomLaunchPolicy(executionPlan.room_policy);
171
+ if (task.room_id && JSON.stringify(before) !== JSON.stringify(after))
172
+ throw new TaskStateError(`task ${id} Room launch policy cannot change after Room creation`);
173
+ }
166
174
  task.execution_plan = executionPlan;
167
175
  task.template = { name: executionPlan.snapshot.name, version: executionPlan.snapshot.version,
168
176
  content_hash: executionPlan.snapshot.content_hash };
@@ -101,6 +101,7 @@ export interface TaskRecord {
101
101
  execution_plan?: {
102
102
  schema_version: 1;
103
103
  snapshot: TemplateSnapshot;
104
+ room_policy?: RoomLaunchPolicy;
104
105
  overrides: Record<string, unknown>;
105
106
  overrides_hash: string;
106
107
  plan_hash: string;
@@ -232,6 +233,8 @@ export interface RoomOrchestrationRecord {
232
233
  goal?: string;
233
234
  task_id?: string;
234
235
  template_snapshot?: TemplateSnapshot;
236
+ /** Resolved once at launch; legacy absence means non-anonymous. */
237
+ room_policy?: RoomLaunchPolicy;
235
238
  saga: SagaCursor;
236
239
  provisioning_detail?: ProvisioningDetail;
237
240
  owner_seat_cid?: string;
@@ -256,6 +259,12 @@ export interface TemplateRoomConfig {
256
259
  quiet_membership?: boolean;
257
260
  anonymous?: boolean;
258
261
  }
262
+ export interface RoomLaunchPolicy {
263
+ anonymous: boolean;
264
+ }
265
+ export declare const LEGACY_ROOM_LAUNCH_POLICY: Readonly<RoomLaunchPolicy>;
266
+ /** Validate durable policy before it can influence room creation or recovery. */
267
+ export declare function storedRoomLaunchPolicy(value: RoomLaunchPolicy | undefined): RoomLaunchPolicy;
259
268
  export interface TemplateDefinition {
260
269
  name: string;
261
270
  version: number;
@@ -319,5 +328,6 @@ export declare const ROOMS_COWORK_KEYS: readonly ["config"];
319
328
  export declare const ROOMS_DEFAULTS_KEYS: readonly ["template", "attach_owner", "close_when_task_done"];
320
329
  export declare const TASKS_KEYS: readonly ["default_room_template", "create_mode", "close_room_on_done", "retain_completed_for"];
321
330
  export declare const TEMPLATE_KEYS: readonly ["version", "description", "room", "contract", "members", "override_builtin"];
331
+ export declare const TEMPLATE_ROOM_KEYS: readonly ["quiet_membership", "anonymous"];
322
332
  /** `agent` is accepted only so validation can emit its actionable migration error. */
323
333
  export declare const TEMPLATE_MEMBER_KEYS: readonly ["slot", "role", "count", "agent_template", "agent"];
@@ -7,6 +7,17 @@
7
7
  */
8
8
  export const TASK_TERMINAL_STATES = ['done', 'cancelled', 'failed'];
9
9
  export const TASK_CANCELLABLE_STATES = ['backlog', 'provisioning', 'active', 'review'];
10
+ export const LEGACY_ROOM_LAUNCH_POLICY = Object.freeze({
11
+ anonymous: false,
12
+ });
13
+ /** Validate durable policy before it can influence room creation or recovery. */
14
+ export function storedRoomLaunchPolicy(value) {
15
+ if (value === undefined)
16
+ return { ...LEGACY_ROOM_LAUNCH_POLICY };
17
+ if (typeof value.anonymous !== 'boolean')
18
+ throw new Error('invalid durable Room launch policy; anonymous must be a boolean');
19
+ return { ...value };
20
+ }
10
21
  // ── Validation keys ─────────────────────────────────────────────────────
11
22
  export const ROOMS_KEYS = ['cowork', 'owner', 'defaults'];
12
23
  export const ROOMS_OWNER_KEYS = ['provider', 'public_invite', 'public_invite_file', 'expected_cid', 'role'];
@@ -14,5 +25,6 @@ export const ROOMS_COWORK_KEYS = ['config'];
14
25
  export const ROOMS_DEFAULTS_KEYS = ['template', 'attach_owner', 'close_when_task_done'];
15
26
  export const TASKS_KEYS = ['default_room_template', 'create_mode', 'close_room_on_done', 'retain_completed_for'];
16
27
  export const TEMPLATE_KEYS = ['version', 'description', 'room', 'contract', 'members', 'override_builtin'];
28
+ export const TEMPLATE_ROOM_KEYS = ['quiet_membership', 'anonymous'];
17
29
  /** `agent` is accepted only so validation can emit its actionable migration error. */
18
30
  export const TEMPLATE_MEMBER_KEYS = ['slot', 'role', 'count', 'agent_template', 'agent'];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ours.network/fleet",
3
- "version": "1.1.0-nightly.13",
3
+ "version": "1.1.0-nightly.15",
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",