@ours.network/fleet 1.2.0-nightly.67 → 1.2.0-nightly.68

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.
@@ -1,14 +1,15 @@
1
1
  {
2
- "version": "1.2.0-nightly.67",
3
- "buildId": "d2cb52fe4185",
4
- "commit": "7e8a265a65327167e2b2eb95fc8c246f67cf4460",
2
+ "version": "1.2.0-nightly.68",
3
+ "buildId": "ec9481a2cd81",
4
+ "commit": "7c3095b817127cff76cac375f21e8124558e8f38",
5
5
  "dirty": true,
6
- "builtAt": "2026-10-16T08:04:29.151Z",
6
+ "builtAt": "2026-10-16T12:16:06.490Z",
7
7
  "capabilities": [
8
8
  "service.refresh-v1",
9
9
  "managed-cli.codex-reasoning-effort-v1",
10
10
  "managed-cli.setup-v1",
11
11
  "workspace.enroll.preserve-profile-v1",
12
+ "workspace.enroll.optional-profile-v1",
12
13
  "cowork.http-management-v1",
13
14
  "monitor.interrupt.after_tool"
14
15
  ]
@@ -15,6 +15,7 @@
15
15
  /** `monitor.interrupt: after_tool` — cancel at the next tool boundary (#67). */
16
16
  export declare const CAP_MONITOR_INTERRUPT_AFTER_TOOL = "monitor.interrupt.after_tool";
17
17
  export declare const CAP_HTTP_COWORK = "cowork.http-management-v1";
18
+ export declare const CAP_WORKSPACE_OPTIONAL_PROFILE = "workspace.enroll.optional-profile-v1";
18
19
  export declare const CAP_WORKSPACE_PRESERVE_PROFILE = "workspace.enroll.preserve-profile-v1";
19
20
  /** `ours-fleet managed-cli setup|status` and the `managed_cli` Agent key (#233). */
20
21
  export declare const CAP_MANAGED_CLI_SETUP = "managed-cli.setup-v1";
@@ -22,7 +23,7 @@ export declare const CAP_MANAGED_CLI_SETUP = "managed-cli.setup-v1";
22
23
  export declare const CAP_MANAGED_CLI_CODEX_EFFORT = "managed-cli.codex-reasoning-effort-v1";
23
24
  /** `ours-fleet service check|refresh`: re-point service definitions after an update. */
24
25
  export declare const CAP_SERVICE_REFRESH = "service.refresh-v1";
25
- export declare const CAPABILITIES: readonly ["service.refresh-v1", "managed-cli.codex-reasoning-effort-v1", "managed-cli.setup-v1", "workspace.enroll.preserve-profile-v1", "cowork.http-management-v1", "monitor.interrupt.after_tool"];
26
+ export declare const CAPABILITIES: readonly ["service.refresh-v1", "managed-cli.codex-reasoning-effort-v1", "managed-cli.setup-v1", "workspace.enroll.preserve-profile-v1", "workspace.enroll.optional-profile-v1", "cowork.http-management-v1", "monitor.interrupt.after_tool"];
26
27
  export type Capability = (typeof CAPABILITIES)[number];
27
28
  /** Does this build declare `token`? */
28
29
  export declare const hasCapability: (token: string) => boolean;
@@ -15,6 +15,7 @@
15
15
  /** `monitor.interrupt: after_tool` — cancel at the next tool boundary (#67). */
16
16
  export const CAP_MONITOR_INTERRUPT_AFTER_TOOL = 'monitor.interrupt.after_tool';
17
17
  export const CAP_HTTP_COWORK = 'cowork.http-management-v1';
18
+ export const CAP_WORKSPACE_OPTIONAL_PROFILE = 'workspace.enroll.optional-profile-v1';
18
19
  export const CAP_WORKSPACE_PRESERVE_PROFILE = 'workspace.enroll.preserve-profile-v1';
19
20
  /** `ours-fleet managed-cli setup|status` and the `managed_cli` Agent key (#233). */
20
21
  export const CAP_MANAGED_CLI_SETUP = 'managed-cli.setup-v1';
@@ -27,6 +28,7 @@ export const CAPABILITIES = [
27
28
  CAP_MANAGED_CLI_CODEX_EFFORT,
28
29
  CAP_MANAGED_CLI_SETUP,
29
30
  CAP_WORKSPACE_PRESERVE_PROFILE,
31
+ CAP_WORKSPACE_OPTIONAL_PROFILE,
30
32
  CAP_HTTP_COWORK,
31
33
  CAP_MONITOR_INTERRUPT_AFTER_TOOL,
32
34
  ];
@@ -4,8 +4,9 @@ export interface WorkspacePayload {
4
4
  appOrigin: string;
5
5
  hostname: string;
6
6
  rootName: string;
7
- name: string;
8
- surname: string;
7
+ fullName?: string | null;
8
+ name?: string | null;
9
+ surname?: string | null;
9
10
  connectorToken?: string;
10
11
  invitation: string;
11
12
  serverCid: string;
@@ -25,14 +25,24 @@ export function decodeWorkspacePayload(input, options = {}) {
25
25
  catch {
26
26
  throw Error('Invalid workspace payload');
27
27
  }
28
- if (p.version !== 1 || !['https://app.ours.network', 'https://app.ours-tunnel.com'].includes(p.appOrigin) || !/^[a-z0-9][a-z0-9-]{2,60}\.ours-tunnel\.com$/.test(p.hostname) || !/^[a-z0-9-]{2,30}@[a-z0-9-]{2,30}$/.test(p.rootName) || !/^[a-f0-9]{64}$/i.test(p.serverCid) || !p.challenge || !['nonce', 'accountId', 'workspaceId'].every(k => /^[\w-]{43}$/.test(String(p.challenge[k]))) || (!options.allowExpired && p.challenge.expiresAt <= Date.now()) || !Number.isSafeInteger(p.challenge.expiresAt) || p.challenge.expiresAt > Date.now() + 16 * 60000)
28
+ if (!p || typeof p !== 'object' || Array.isArray(p) || p.version !== 1 || !['https://app.ours.network', 'https://app.ours-tunnel.com'].includes(p.appOrigin) || !/^[a-z0-9][a-z0-9-]{2,60}\.ours-tunnel\.com$/.test(p.hostname) || !/^[a-z0-9-]{2,30}@[a-z0-9-]{2,30}$/.test(p.rootName) || !/^[a-f0-9]{64}$/i.test(p.serverCid) || !p.challenge || !['nonce', 'accountId', 'workspaceId'].every(k => /^[\w-]{43}$/.test(String(p.challenge[k]))) || (!options.allowExpired && p.challenge.expiresAt <= Date.now()) || !Number.isSafeInteger(p.challenge.expiresAt) || p.challenge.expiresAt > Date.now() + 16 * 60000)
29
29
  throw Error('Workspace payload is invalid or expired');
30
- for (const k of ['connectorToken', 'invitation', 'name', 'surname']) {
30
+ for (const k of ['connectorToken', 'invitation']) {
31
31
  if (k === 'connectorToken' && p[k] === undefined)
32
32
  continue;
33
33
  if (typeof p[k] !== 'string' || !p[k] || p[k].length > 8192 || /[\x00-\x1f\x7f]/.test(p[k]))
34
34
  throw Error('Invalid workspace payload');
35
35
  }
36
+ for (const k of ['fullName', 'name', 'surname']) {
37
+ const value = p[k];
38
+ if (value == null || value === '') {
39
+ p[k] = null;
40
+ continue;
41
+ }
42
+ if (typeof value !== 'string' || value.length > 8192 || /[\x00-\x1f\x7f]/.test(value))
43
+ throw Error('Invalid workspace profile');
44
+ p[k] = value.trim() || null;
45
+ }
36
46
  return p;
37
47
  }
38
48
  /** Credential-free renewal is allowed only on the host already holding this private connector. */
@@ -120,11 +130,11 @@ const CID = /^[a-f0-9]{64}$/i, same = (a, b) => a.toLowerCase() === b.toLowerCas
120
130
  async function messengerIdentity(request, attach) {
121
131
  const bound = await request('workspace/enrollment-identity');
122
132
  if (bound.rootCid === undefined)
123
- return { cid: bound.cid, preserveProfile: bound.preserveProfile === true };
133
+ return { cid: bound.cid, preserveProfile: bound.preserveProfile === true, optionalWorkspaceProfileV1: bound.optionalWorkspaceProfileV1 === true };
124
134
  if (typeof bound.cid !== 'string' || !CID.test(bound.cid) || typeof bound.rootCid !== 'string' || !CID.test(bound.rootCid) || same(bound.cid, bound.rootCid))
125
135
  throw Error('Bound Messenger identity unavailable');
126
136
  const root = await humanRoot(attach, bound.rootCid, bound.cid);
127
- return { cid: bound.cid, preserveProfile: bound.preserveProfile === true, root };
137
+ return { cid: bound.cid, preserveProfile: bound.preserveProfile === true, optionalWorkspaceProfileV1: bound.optionalWorkspaceProfileV1 === true, root };
128
138
  }
129
139
  /** This host's only Human root, as its daemon lists it; optionally with a permanent identity that must be listed under it. */
130
140
  async function humanRoot(attach, rootCid, under) {
@@ -280,6 +290,8 @@ export async function enrollWorkspace(payload, configuration = defaultConfigPath
280
290
  && (options.replacingWorkspace !== undefined || JSON.parse(readFileSync(previousFile, 'utf8')).serverCid?.toUpperCase() === payload.serverCid.toUpperCase());
281
291
  if (options.preserveProfile && identity.preserveProfile !== true)
282
292
  throw Error('Existing-host setup requires Messenger profile-preservation support; update the host Messenger before retrying');
293
+ if (!options.preserveProfile && (payload.fullName?.trim() || !payload.name?.trim() || !payload.surname?.trim()) && !identity.optionalWorkspaceProfileV1)
294
+ throw Error('Optional workspace profile requires updated Messenger; update the host Messenger before retrying (the host is not enrolled)');
283
295
  if (loaded.rooms && !lost && !moved && loaded.rooms.owner.expected_cid.toLowerCase() !== identity.cid.toLowerCase())
284
296
  throw Error('Retained room owner CID conflicts with the Human root');
285
297
  const inviteFile = loaded.rooms?.owner.public_invite_file || join(dir, 'owner.invite');
@@ -291,7 +303,7 @@ export async function enrollWorkspace(payload, configuration = defaultConfigPath
291
303
  const devices = new WorkspaceDeviceStore();
292
304
  const hostWorkspaceId = devices.workspaceId;
293
305
  devices.close();
294
- const { name, surname, ...proofPayload } = payload;
306
+ const { fullName, name, surname, ...proofPayload } = payload;
295
307
  let result, ownerCid;
296
308
  // A replacement can stop after its one-time proof but before local files commit. Read the
297
309
  // account's existing signed receipt first; a verified proof must not be sent a second time.
@@ -316,6 +328,8 @@ export async function enrollWorkspace(payload, configuration = defaultConfigPath
316
328
  }
317
329
  }
318
330
  if (identity.root) {
331
+ if (!options.preserveProfile)
332
+ await request('identity/profile', { fullName: fullName ?? null, name: name ?? null, surname: surname ?? null });
319
333
  // The Human root signs the binding; the person's own Messenger identity is the room Owner and issues the Owner invitation.
320
334
  if (!proofAlreadyConfirmed)
321
335
  await proveWithHumanRoot(attach, identity.root, payload, hostWorkspaceId, options.contactWaitMs ?? 10000, options.replacingWorkspace !== undefined);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ours.network/fleet",
3
- "version": "1.2.0-nightly.67",
3
+ "version": "1.2.0-nightly.68",
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",