@codemem/server 0.32.2 → 0.33.0-alpha.1

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/static/app.js CHANGED
@@ -3200,21 +3200,6 @@
3200
3200
  async function loadPairing(includeDiagnostics = false) {
3201
3201
  return fetchJson(`/api/sync/pairing${includeDiagnostics ? "?includeDiagnostics=1" : ""}`);
3202
3202
  }
3203
- async function updatePeerScope(peerDeviceId, include, exclude, inheritGlobal = false) {
3204
- const resp = await fetch("/api/sync/peers/scope", {
3205
- method: "POST",
3206
- headers: { "Content-Type": "application/json" },
3207
- body: JSON.stringify({
3208
- peer_device_id: peerDeviceId,
3209
- include,
3210
- exclude,
3211
- inherit_global: inheritGlobal
3212
- })
3213
- });
3214
- const { text, payload } = await readJsonPayload(resp);
3215
- if (!resp.ok) throw new Error(payloadError(payload) || text || "request failed");
3216
- return payload;
3217
- }
3218
3203
  async function assignPeerActor(peerDeviceId, actorId) {
3219
3204
  const resp = await fetch("/api/sync/peers/identity", {
3220
3205
  method: "POST",
@@ -3248,13 +3233,12 @@
3248
3233
  return payload;
3249
3234
  }
3250
3235
  async function acceptDiscoveredPeer(peerDeviceId, fingerprint) {
3236
+ const body = { peer_device_id: peerDeviceId };
3237
+ if (fingerprint) body.fingerprint = fingerprint;
3251
3238
  const resp = await fetch("/api/sync/peers/accept-discovered", {
3252
3239
  method: "POST",
3253
3240
  headers: { "Content-Type": "application/json" },
3254
- body: JSON.stringify({
3255
- peer_device_id: peerDeviceId,
3256
- fingerprint
3257
- })
3241
+ body: JSON.stringify(body)
3258
3242
  });
3259
3243
  const text = await resp.text();
3260
3244
  let payload = {};
@@ -3461,11 +3445,16 @@
3461
3445
  if (!resp.ok) throw new Error(payloadError(payload) || text || "request failed");
3462
3446
  return payload;
3463
3447
  }
3464
- async function triggerSync(address) {
3448
+ async function triggerSync(target) {
3449
+ const address = typeof target === "string" ? target.trim() : target?.address?.trim();
3450
+ const peerDeviceId = typeof target === "string" ? "" : target?.peerDeviceId?.trim();
3451
+ const payload = {};
3452
+ if (address) payload.address = address;
3453
+ if (peerDeviceId) payload.peer_device_id = peerDeviceId;
3465
3454
  const resp = await fetch("/api/sync/run", {
3466
3455
  method: "POST",
3467
3456
  headers: { "Content-Type": "application/json" },
3468
- body: JSON.stringify(address ? { address } : {})
3457
+ body: JSON.stringify(payload)
3469
3458
  });
3470
3459
  const { text, payload: body } = await readJsonPayload(resp);
3471
3460
  if (!resp.ok) throw new Error(payloadError(body) || text || "request failed");
@@ -3493,7 +3482,7 @@
3493
3482
  function escapeHtml(value) {
3494
3483
  return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
3495
3484
  }
3496
- function escapeRegExp(value) {
3485
+ function escapeRegExp$1(value) {
3497
3486
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3498
3487
  }
3499
3488
  function highlightText(text, query) {
@@ -3501,7 +3490,7 @@
3501
3490
  if (!q) return escapeHtml(text);
3502
3491
  const safe = escapeHtml(text);
3503
3492
  try {
3504
- const re = new RegExp(`(${escapeRegExp(q)})`, "ig");
3493
+ const re = new RegExp(`(${escapeRegExp$1(q)})`, "ig");
3505
3494
  return safe.replace(re, "<mark class=\"match\">$1</mark>");
3506
3495
  } catch {
3507
3496
  return safe;
@@ -4099,6 +4088,23 @@
4099
4088
  return /* @__PURE__ */ u("input", { ...props });
4100
4089
  }
4101
4090
  //#endregion
4091
+ //#region src/tabs/coordinator-admin/data/device-card.ts
4092
+ function coordinatorAdminDeviceCardCopy(device, fallbackTeamId) {
4093
+ const deviceId = String(device.device_id || "").trim();
4094
+ const teamId = String(device.group_id || fallbackTeamId || "").trim();
4095
+ const displayName = String(device.display_name || deviceId || "Unnamed device");
4096
+ const enabled = device.enabled !== false && device.enabled !== 0;
4097
+ const advancedParts = [`Device ID ${deviceId || "unknown"}`];
4098
+ if (teamId) advancedParts.push(`Team ID ${teamId}`);
4099
+ return {
4100
+ advancedDetail: `Advanced: ${advancedParts.join(" · ")}`,
4101
+ deviceId,
4102
+ displayName,
4103
+ statusLabel: enabled ? "Enabled in this Team" : "Disabled in this Team",
4104
+ teamId
4105
+ };
4106
+ }
4107
+ //#endregion
4102
4108
  //#region src/tabs/coordinator-admin/data/state.ts
4103
4109
  var ADMIN_TARGET_GROUP_KEY = "codemem-coordinator-admin-target-group";
4104
4110
  var coordinatorAdminState = {
@@ -4134,17 +4140,16 @@
4134
4140
  return _$3(RadixTabsContent, {
4135
4141
  className: "coordinator-admin-panel",
4136
4142
  value: "devices"
4137
- }, _$3("h3", null, "Enrolled devices"), _$3("p", { class: "peer-submeta" }, summary.readiness === "ready" ? "Rename, disable, re-enable, or remove enrolled devices from the operator surface without confusing this with direct sync state." : "Finish setup first. Device administration stays disabled until coordinator admin is ready."), !items.length ? _$3("div", { class: "peer-meta" }, summary.readiness === "ready" ? "No enrolled devices found for the active coordinator group." : "Device administration will appear here once setup is complete.") : _$3("div", { class: "peer-list" }, items.map((item) => {
4138
- const deviceId = String(item.device_id || "").trim();
4139
- const groupId = String(item.group_id || state.lastCoordinatorAdminStatus?.active_group || "").trim();
4140
- const displayName = String(item.display_name || deviceId || "Unnamed device");
4143
+ }, _$3("h3", null, "Enrolled devices"), _$3("p", { class: "peer-submeta" }, summary.readiness === "ready" ? "Rename, disable, re-enable, or remove Team devices here. Space access is granted from Spaces below; Team membership alone does not share memories." : "Finish setup first. Device administration stays disabled until coordinator admin is ready."), !items.length ? _$3("div", { class: "peer-meta" }, summary.readiness === "ready" ? "No enrolled devices found for the active coordinator group." : "Device administration will appear here once setup is complete.") : _$3("div", { class: "peer-list" }, items.map((item) => {
4144
+ const copy = coordinatorAdminDeviceCardCopy(item, String(state.lastCoordinatorAdminStatus?.active_group || ""));
4145
+ const { deviceId, displayName, teamId } = copy;
4141
4146
  const pending = coordinatorAdminState.deviceActionPendingId === deviceId;
4142
4147
  const draft = coordinatorAdminState.deviceRenameDrafts.get(deviceId) ?? String(item.display_name || "");
4143
4148
  const enabled = item.enabled !== false && item.enabled !== 0;
4144
4149
  return _$3("div", {
4145
4150
  class: "peer-card peer-card--padded",
4146
4151
  key: deviceId || String(item.fingerprint || "unknown")
4147
- }, _$3("div", { class: "peer-title" }, _$3("strong", null, draft || displayName)), _$3("div", { class: "peer-meta" }, `Device: ${deviceId || "unknown"}`), groupId ? _$3("div", { class: "peer-submeta" }, `Group: ${groupId}`) : null, _$3("div", { class: "peer-submeta" }, enabled ? "Enabled" : "Disabled"), _$3("div", { class: "coordinator-admin-form-grid" }, _$3("label", { class: "coordinator-admin-field" }, _$3("span", null, "Display name"), _$3(TextInput, {
4152
+ }, _$3("div", { class: "peer-title" }, _$3("strong", null, draft || displayName)), _$3("div", { class: "peer-submeta" }, copy.statusLabel), _$3("div", { class: "peer-meta" }, copy.advancedDetail), _$3("div", { class: "coordinator-admin-form-grid" }, _$3("label", { class: "coordinator-admin-field" }, _$3("span", null, "Display name"), _$3(TextInput, {
4148
4153
  class: "peer-scope-input",
4149
4154
  disabled: summary.readiness !== "ready" || pending,
4150
4155
  onInput: (event) => {
@@ -4155,22 +4160,22 @@
4155
4160
  }))), _$3("div", { class: "peer-actions" }, _$3("button", {
4156
4161
  class: "settings-button",
4157
4162
  disabled: !deviceId || pending || summary.readiness !== "ready",
4158
- onClick: () => runDevice(deviceId, groupId, displayName, "rename"),
4163
+ onClick: () => runDevice(deviceId, teamId, displayName, "rename"),
4159
4164
  type: "button"
4160
4165
  }, pending && coordinatorAdminState.deviceActionPendingKind === "rename" ? "Renaming…" : "Rename"), enabled ? _$3("button", {
4161
4166
  class: "settings-button danger",
4162
4167
  disabled: !deviceId || pending || summary.readiness !== "ready",
4163
- onClick: () => runDevice(deviceId, groupId, displayName, "disable"),
4168
+ onClick: () => runDevice(deviceId, teamId, displayName, "disable"),
4164
4169
  type: "button"
4165
4170
  }, pending && coordinatorAdminState.deviceActionPendingKind === "disable" ? "Disabling…" : "Disable") : _$3("button", {
4166
4171
  class: "settings-button",
4167
4172
  disabled: !deviceId || pending || summary.readiness !== "ready",
4168
- onClick: () => runDevice(deviceId, groupId, displayName, "enable"),
4173
+ onClick: () => runDevice(deviceId, teamId, displayName, "enable"),
4169
4174
  type: "button"
4170
4175
  }, pending && coordinatorAdminState.deviceActionPendingKind === "enable" ? "Enabling…" : "Enable"), _$3("button", {
4171
4176
  class: "settings-button danger",
4172
4177
  disabled: !deviceId || pending || summary.readiness !== "ready",
4173
- onClick: () => runDevice(deviceId, groupId, displayName, "remove"),
4178
+ onClick: () => runDevice(deviceId, teamId, displayName, "remove"),
4174
4179
  type: "button"
4175
4180
  }, pending && coordinatorAdminState.deviceActionPendingKind === "remove" ? "Removing…" : "Remove")));
4176
4181
  })));
@@ -10053,6 +10058,36 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
10053
10058
  const value = String(status || "active").trim();
10054
10059
  return value ? value.replaceAll("_", " ") : "active";
10055
10060
  }
10061
+ function spaceCardCopy(scope) {
10062
+ const title = String(scope.label || "").trim() || "Untitled Space";
10063
+ const status = scopeStatusLabel(scope.status);
10064
+ const kind = String(scope.kind || "user").replaceAll("_", " ");
10065
+ const scopeId = String(scope.scope_id || "").trim() || "unknown";
10066
+ const epoch = scope.membership_epoch == null ? "—" : String(scope.membership_epoch);
10067
+ return {
10068
+ title,
10069
+ summary: `${status} Space · ${kind}`,
10070
+ advancedDetail: `Advanced: Space ID ${scopeId} · Membership epoch ${epoch}`
10071
+ };
10072
+ }
10073
+ function spaceAccessDeviceCopy(row) {
10074
+ const statusLabel = {
10075
+ active: "Space access active",
10076
+ revoked: "Space access revoked",
10077
+ not_member: "No Space access"
10078
+ };
10079
+ const role = row.role.replaceAll("_", " ");
10080
+ const detail = row.status === "not_member" ? statusLabel[row.status] : `${statusLabel[row.status]} · ${role}`;
10081
+ const epoch = row.membershipEpoch == null ? "—" : String(row.membershipEpoch);
10082
+ return {
10083
+ statusLabel: statusLabel[row.status],
10084
+ detail,
10085
+ advancedDetail: `Advanced: membership epoch ${epoch}`
10086
+ };
10087
+ }
10088
+ function spaceRevokeMemberTitle(scope, displayName, deviceId) {
10089
+ return `Revoke ${displayName || deviceId || "this device"} from ${spaceCardCopy(scope).title}?`;
10090
+ }
10056
10091
  function deriveScopeMembershipDeviceRows(devices, members) {
10057
10092
  const memberByDevice = new Map(members.map((member) => [String(member.device_id || "").trim(), member]).filter(([deviceId]) => deviceId.length > 0));
10058
10093
  return devices.map((device) => {
@@ -10247,7 +10282,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
10247
10282
  const scopeId = String(scope.scope_id || "").trim();
10248
10283
  if (!scopeId) return;
10249
10284
  if (!await openSyncConfirmDialog({
10250
- title: `Revoke ${displayName || deviceId} from ${scope.label || scopeId}?`,
10285
+ title: spaceRevokeMemberTitle(scope, displayName, deviceId),
10251
10286
  description: "Revocation only blocks future sync for this Space. Data already copied to that device can remain there.",
10252
10287
  confirmLabel: "Revoke membership",
10253
10288
  cancelLabel: "Keep membership",
@@ -10286,31 +10321,30 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
10286
10321
  const pending = draft.actionPendingKey === pendingKey;
10287
10322
  const canGrant = row.enabled && row.status !== "active";
10288
10323
  const canRevoke = row.status === "active";
10289
- const statusCopy = row.status === "not_member" ? "Not a member" : row.status === "revoked" ? "Revoked" : "Active member";
10290
- const epochCopy = row.membershipEpoch == null ? "epoch —" : `epoch ${row.membershipEpoch}`;
10324
+ const copy = spaceAccessDeviceCopy(row);
10291
10325
  return _$3("div", {
10292
10326
  class: "coordinator-admin-scope-member-row",
10293
10327
  key: row.deviceId
10294
- }, _$3("div", { class: "coordinator-admin-scope-member-copy" }, _$3("strong", null, row.displayName), _$3("span", null, `${statusCopy} · ${row.role} · ${epochCopy}`), row.enabled ? null : _$3("span", null, "Device is disabled in this Team.")), _$3("div", { class: "peer-actions" }, canGrant ? _$3("button", {
10328
+ }, _$3("div", { class: "coordinator-admin-scope-member-copy" }, _$3("strong", null, row.displayName), _$3("span", null, copy.detail), _$3("span", { class: "peer-meta" }, copy.advancedDetail), row.enabled ? null : _$3("span", null, "Device is disabled in this Team.")), _$3("div", { class: "peer-actions" }, canGrant ? _$3("button", {
10295
10329
  class: "settings-button",
10296
10330
  disabled: !ready || Boolean(draft.actionPendingKey),
10297
10331
  onClick: () => void grantMember(groupId, scopeId, row.deviceId, renderShell),
10298
10332
  type: "button"
10299
- }, pending && draft.actionPendingKind === "grant" ? "Granting…" : "Grant") : null, canRevoke ? _$3("button", {
10333
+ }, pending && draft.actionPendingKind === "grant" ? "Granting…" : "Grant access") : null, canRevoke ? _$3("button", {
10300
10334
  class: "settings-button danger",
10301
10335
  disabled: !ready || Boolean(draft.actionPendingKey),
10302
10336
  onClick: () => void revokeMember(groupId, scope, row.deviceId, row.displayName, renderShell),
10303
10337
  type: "button"
10304
- }, pending && draft.actionPendingKind === "revoke" ? "Revoking…" : "Revoke") : null));
10338
+ }, pending && draft.actionPendingKind === "revoke" ? "Revoking…" : "Revoke access") : null));
10305
10339
  }));
10306
10340
  }
10307
10341
  function renderScopeCard(groupId, scope, draft, ready, renderShell) {
10308
10342
  const scopeId = String(scope.scope_id || "").trim();
10309
- const label = String(scope.label || scopeId || "Untitled Space");
10343
+ const copy = spaceCardCopy(scope);
10310
10344
  return _$3("div", {
10311
10345
  class: "peer-card peer-card--padded coordinator-admin-scope-card",
10312
- key: scopeId || label
10313
- }, _$3("div", { class: "peer-title" }, _$3("strong", null, label)), _$3("div", { class: "peer-submeta" }, `Status: ${scopeStatusLabel(scope.status)} · Kind: ${scope.kind || "user"}`), _$3("div", { class: "peer-meta" }, `Advanced: Space ID ${scopeId || "unknown"} · Membership epoch ${scope.membership_epoch ?? 0}`), _$3("div", { class: "peer-submeta" }, "Devices below are enrolled in the Team; only active Space members can sync this data boundary."), renderMembershipRows(groupId, scope, draft, ready, renderShell));
10346
+ key: scopeId || copy.title
10347
+ }, _$3("div", { class: "peer-title" }, _$3("strong", null, copy.title)), _$3("div", { class: "peer-submeta" }, copy.summary), _$3("div", { class: "peer-meta" }, copy.advancedDetail), _$3("div", { class: "peer-submeta" }, "Devices below are enrolled in the Team; only active Space members can sync this data boundary."), renderMembershipRows(groupId, scope, draft, ready, renderShell));
10314
10348
  }
10315
10349
  function renderGroupScopeManagementPanel(deps) {
10316
10350
  const { groupId, ready, summary, renderShell } = deps;
@@ -10478,10 +10512,10 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
10478
10512
  const includeLabelId = `coord-admin-scope-include-${groupId}`;
10479
10513
  const excludeLabelId = `coord-admin-scope-exclude-${groupId}`;
10480
10514
  const fieldsDisabled = !ready || draft.saving || !draft.auto_seed_scope;
10481
- return _$3(k$3, null, _$3("h4", { class: "coordinator-admin-drawer-title" }, "Team defaults"), _$3("div", { class: "peer-submeta" }, "Project filters only narrow what future writes sync; they never grant Space access by themselves. Use the default Space toggle to control whether newly joined devices receive the Team's default Space."), _$3("label", { class: "coordinator-admin-inline-filter" }, _$3("span", {
10515
+ return _$3(k$3, null, _$3("h4", { class: "coordinator-admin-drawer-title" }, "Team defaults"), _$3("div", { class: "peer-submeta" }, "Space access grants decide what new devices can sync. Advanced sharing rules only narrow future writes after Space access already exists."), _$3("label", { class: "coordinator-admin-inline-filter" }, _$3("span", {
10482
10516
  class: "section-meta",
10483
10517
  id: autoSeedLabelId
10484
- }, "Auto-seed project filters"), _$3(RadixSwitch, {
10518
+ }, "Auto-apply advanced rules to new devices"), _$3(RadixSwitch, {
10485
10519
  "aria-labelledby": autoSeedLabelId,
10486
10520
  checked: draft.auto_seed_scope,
10487
10521
  className: "coordinator-admin-switch",
@@ -10512,7 +10546,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
10512
10546
  renderShell();
10513
10547
  },
10514
10548
  thumbClassName: "coordinator-admin-switch-thumb"
10515
- })), draft.default_space_scope_id ? _$3("div", { class: "peer-submeta" }, "Default Space configured.") : _$3("div", { class: "peer-submeta" }, "No default Space has been created yet."), _$3("div", { class: "coordinator-admin-field" }, _$3("span", { id: includeLabelId }, "Include projects"), _$3(ProjectScopePicker, {
10549
+ })), draft.default_space_scope_id ? _$3("div", { class: "peer-submeta" }, "Default Space configured.") : _$3("div", { class: "peer-submeta" }, "No default Space has been created yet."), _$3("div", { class: "coordinator-admin-field" }, _$3("span", { id: includeLabelId }, "Include template"), _$3(ProjectScopePicker, {
10516
10550
  "aria-labelledby": includeLabelId,
10517
10551
  availableProjects: coordinatorAdminState.availableProjects,
10518
10552
  disabled: fieldsDisabled,
@@ -10527,7 +10561,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
10527
10561
  },
10528
10562
  placeholder: "Add project",
10529
10563
  values: draft.projects_include
10530
- }), _$3("span", { class: "peer-submeta" }, "Leave empty to allow every project. Type to search or create a new name.")), _$3("div", { class: "coordinator-admin-field" }, _$3("span", { id: excludeLabelId }, "Exclude projects"), _$3(ProjectScopePicker, {
10564
+ }), _$3("span", { class: "peer-submeta" }, "Leave empty to allow every project inside the granted Space. Type to search or create a new name.")), _$3("div", { class: "coordinator-admin-field" }, _$3("span", { id: excludeLabelId }, "Exclude template"), _$3(ProjectScopePicker, {
10531
10565
  "aria-labelledby": excludeLabelId,
10532
10566
  availableProjects: coordinatorAdminState.availableProjects,
10533
10567
  disabled: fieldsDisabled,
@@ -10833,7 +10867,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
10833
10867
  return _$3(RadixTabsContent, {
10834
10868
  className: "coordinator-admin-panel",
10835
10869
  value: "join-requests"
10836
- }, _$3("h3", null, "Pending join requests"), _$3("p", { class: "peer-submeta" }, summary.readiness === "ready" ? "Approve or deny teammate join requests from the dedicated operator surface instead of burying them in Sync." : "Finish setup first. Join request review stays disabled until coordinator admin is ready."), !items.length ? _$3("div", { class: "peer-meta" }, summary.readiness === "ready" ? "No pending join requests right now." : "Join request review will appear here once setup is complete.") : _$3("div", { class: "peer-list" }, items.map((item) => {
10870
+ }, _$3("h3", null, "Pending join requests"), _$3("p", { class: "peer-submeta" }, summary.readiness === "ready" ? "Approve or deny devices that want to join this Team. Default Space access is handled by Team defaults and can be reviewed in Spaces." : "Finish setup first. Join request review stays disabled until coordinator admin is ready."), !items.length ? _$3("div", { class: "peer-meta" }, summary.readiness === "ready" ? "No pending join requests right now." : "Join request review will appear here once setup is complete.") : _$3("div", { class: "peer-list" }, items.map((item) => {
10837
10871
  const requestId = String(item.request_id || "").trim();
10838
10872
  const deviceId = String(item.device_id || "unknown-device");
10839
10873
  const displayName = String(item.display_name || deviceId);
@@ -10841,7 +10875,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
10841
10875
  return _$3("div", {
10842
10876
  class: "peer-card peer-card--padded",
10843
10877
  key: requestId || deviceId
10844
- }, _$3("div", { class: "peer-title" }, _$3("strong", null, displayName)), _$3("div", { class: "peer-meta" }, `Device: ${deviceId}`), _$3("div", { class: "peer-actions" }, _$3("button", {
10878
+ }, _$3("div", { class: "peer-title" }, _$3("strong", null, displayName)), _$3("div", { class: "peer-meta" }, `Advanced: Device ID ${deviceId}`), _$3("div", { class: "peer-actions" }, _$3("button", {
10845
10879
  class: "settings-button",
10846
10880
  disabled: !requestId || pending,
10847
10881
  onClick: () => reviewJoinRequest(requestId, "approve"),
@@ -10959,7 +10993,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
10959
10993
  state.lastTeamInvite = result;
10960
10994
  coordinatorAdminState.inviteGroup = groupId;
10961
10995
  const warnings = Array.isArray(result.warnings) ? result.warnings : [];
10962
- showGlobalNotice(warnings.length ? `Invite created. Review ${warnings.length === 1 ? "the warning" : `${warnings.length} warnings`} before sharing it.` : "Invite created. Copy it from Coordinator Admin and share it with your teammate.", warnings.length ? "warning" : "success");
10996
+ showGlobalNotice(warnings.length ? `Invite created. Review ${warnings.length === 1 ? "the warning" : `${warnings.length} warnings`} before sharing it.` : "Invite created. Copy it from Teams and share it with your teammate.", warnings.length ? "warning" : "success");
10963
10997
  } catch (error) {
10964
10998
  showGlobalNotice(error instanceof Error ? error.message : "Failed to create invite.", "warning");
10965
10999
  } finally {
@@ -18061,9 +18095,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
18061
18095
  }
18062
18096
  function shouldShowCoordinatorReviewAction(input) {
18063
18097
  const approvalSummary = deriveCoordinatorApprovalSummary(input);
18064
- const deviceId = cleanText(input.device?.device_id);
18065
- const fingerprint = cleanText(input.device?.fingerprint);
18066
- if (!deviceId || !fingerprint) return false;
18098
+ if (!cleanText(input.device?.device_id)) return false;
18067
18099
  if (input.device?.stale || input.hasAmbiguousCoordinatorGroup) return false;
18068
18100
  if (!input.pairedLocally) return true;
18069
18101
  return approvalSummary.state === "needs-your-approval";
@@ -18093,11 +18125,16 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
18093
18125
  warning: false
18094
18126
  };
18095
18127
  }
18096
- if (failedItems.filter((item) => cleanText(item.error).toLowerCase().includes("401") && cleanText(item.error).toLowerCase().includes("unauthorized")).length === failedItems.length) return {
18128
+ if (failedItems.filter((item) => isTrustFailureError(cleanText(item.error))).length === failedItems.length) return {
18097
18129
  ok: false,
18098
18130
  message: "This device no longer has two-way trust with the peer. Pair it again from the other device, or remove the stale local record if it should be gone.",
18099
18131
  warning: true
18100
18132
  };
18133
+ if (failedItems.filter((item) => isScopeMembershipFailureError(cleanText(item.error))).length === failedItems.length) return {
18134
+ ok: false,
18135
+ message: "Sync ran, but the peer is not authorized for one or more Spaces. Review Space access for this device in Teams, then sync again.",
18136
+ warning: true
18137
+ };
18101
18138
  if (failedItems.length < items.length) return {
18102
18139
  ok: false,
18103
18140
  message: `${failedItems.length} of ${items.length} device sync attempts failed. Open the affected device cards for the specific errors.`,
@@ -18109,6 +18146,41 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
18109
18146
  warning: true
18110
18147
  };
18111
18148
  }
18149
+ /**
18150
+ * True when a sync-failure error string looks like a two-way-trust failure
18151
+ * (401 unauthorized on the peer's /v1/status), which is the only class of
18152
+ * failure that should trigger the "no longer has two-way trust" toast. Any
18153
+ * other class — connectivity, scope/membership, generation mismatch — flows
18154
+ * through the generic mixed-failure or scoped paths so users do not get
18155
+ * pointed at re-pairing for problems that re-pairing will not fix.
18156
+ */
18157
+ function isTrustFailureError(error) {
18158
+ const lower = error.toLowerCase();
18159
+ return lower.includes("401") && lower.includes("unauthorized");
18160
+ }
18161
+ /**
18162
+ * True when a sync-failure error string indicates the peer is not authorized
18163
+ * for a Space (or is authorized at a stale epoch). These failures map to the
18164
+ * scoped-sync protocol classes documented in
18165
+ * docs/plans/2026-05-25-scoped-sync-protocol.md:
18166
+ *
18167
+ * - `403: scope_rejected` from POST /v1/ops outbound scope filter.
18168
+ * - `409: reset_required` with `reason=missing_scope` or `stale_epoch`
18169
+ * from GET /v1/ops or GET /v1/snapshot.
18170
+ * - syncOneScope's aggregate string only when it includes one of those wire
18171
+ * reasons. Other scoped-sync failures remain generic sync failures.
18172
+ *
18173
+ * Detection is intentionally substring-based because the wire reason can
18174
+ * arrive embedded in a longer error string from the address-fallback loop.
18175
+ */
18176
+ function isScopeMembershipFailureError(error) {
18177
+ const lower = error.toLowerCase();
18178
+ if (lower.includes("scope_rejected")) return true;
18179
+ if (lower.includes("missing_scope")) return true;
18180
+ if (lower.includes("stale_epoch")) return true;
18181
+ if (lower.includes("scope_inactive")) return true;
18182
+ return false;
18183
+ }
18112
18184
  function outboundFilterLabel(detail) {
18113
18185
  const reason = cleanText(detail?.reason);
18114
18186
  return OUTBOUND_FILTER_LABELS[reason] ?? (reason.replaceAll("_", " ") || "sync filters");
@@ -18259,8 +18331,9 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
18259
18331
  function derivePeerAuthorizedDomainsView(peer) {
18260
18332
  const domains = (Array.isArray(peer.authorized_scopes) ? peer.authorized_scopes : []).map((scope) => {
18261
18333
  const scopeId = cleanText(scope.scope_id);
18262
- const label = cleanText(scope.label) ?? scopeId;
18263
- if (!scopeId && !label) return null;
18334
+ const rawLabel = cleanText(scope.label);
18335
+ if (!scopeId && !rawLabel) return null;
18336
+ const label = rawLabel || "Untitled Space";
18264
18337
  const detail = [
18265
18338
  labelPart(cleanText(scope.kind), "user"),
18266
18339
  labelPart(cleanText(scope.authority_type), "local"),
@@ -18268,17 +18341,17 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
18268
18341
  ].filter(Boolean).join(" · ");
18269
18342
  return {
18270
18343
  scopeId: scopeId || label,
18271
- label: label || scopeId,
18344
+ label,
18272
18345
  detail
18273
18346
  };
18274
18347
  }).filter((item) => item != null);
18275
18348
  const total = domains.length;
18276
18349
  return {
18277
18350
  total,
18278
- badgeLabel: total === 1 ? "1 Sharing domain" : total > 1 ? `${total} Sharing domains` : "No Sharing domains",
18351
+ badgeLabel: total === 1 ? "1 Space" : total > 1 ? `${total} Spaces` : "No Space access",
18279
18352
  isWarning: total === 0,
18280
18353
  domains,
18281
- emptyMessage: "No Sharing-domain grants exist for this device yet. Project filters below cannot send data by themselves."
18354
+ emptyMessage: "No Space access grants exist for this device yet. Advanced project filters cannot send data by themselves."
18282
18355
  };
18283
18356
  }
18284
18357
  var PERSONAL_SCOPE_WORDS = [
@@ -18358,25 +18431,32 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
18358
18431
  title: ""
18359
18432
  };
18360
18433
  return {
18361
- badgeLabel: "Review domain fit",
18362
- detail: "Coordinator/group context helps find the device, and project filters only narrow already-authorized domains. Neither one grants a missing work/client domain.",
18434
+ badgeLabel: "Review Space fit",
18435
+ detail: "Team discovery helps find the device, and advanced project filters only narrow already-authorized Spaces. Neither one grants missing work/client Space access.",
18363
18436
  isVisible: true,
18364
- message: "This coordinator-discovered device has personal or OSS Sharing-domain grants, but no separate work/client-like domain grant. If this device is meant for work/client sync, add that Sharing domain explicitly before treating sync as validated.",
18365
- title: "Check this device's Sharing-domain role"
18437
+ message: "This Team-discovered device has personal or OSS Space access, but no separate work/client-like Space access. If this device is meant for work/client sync, grant that Space explicitly before treating sync as validated.",
18438
+ title: "Check this device's Space access"
18366
18439
  };
18367
18440
  }
18441
+ function hasActiveProjectNarrowing(include, exclude) {
18442
+ const normalizedInclude = include.map((item) => item.trim()).filter(Boolean);
18443
+ return normalizedInclude.length > 0 && !normalizedInclude.includes("*") || exclude.length > 0;
18444
+ }
18368
18445
  function derivePeerProjectNarrowingView(scope) {
18369
18446
  const effectiveInclude = cleanList(scope?.effective_include);
18370
18447
  const effectiveExclude = cleanList(scope?.effective_exclude);
18371
18448
  const sourceLabel = scope?.inherits_global ? "Global defaults" : "Device override";
18372
18449
  const includeLabel = `Include filter: ${shortList(effectiveInclude, "all projects")}`;
18373
18450
  const excludeLabel = `Exclude filter: ${shortList(effectiveExclude, "no exclusions")}`;
18451
+ const hasAdvancedFilters = hasActiveProjectNarrowing(effectiveInclude, effectiveExclude);
18374
18452
  return {
18453
+ hasAdvancedFilters,
18454
+ statusLabel: hasAdvancedFilters ? "Advanced filters active" : "No advanced filters",
18375
18455
  sourceLabel,
18376
18456
  includeLabel,
18377
18457
  excludeLabel,
18378
18458
  summary: `${sourceLabel}. ${includeLabel}; ${excludeLabel}.`,
18379
- note: "Project filters only narrow data after Sharing domain authorization; they never grant access to another domain."
18459
+ note: "Advanced filters only narrow data after Space access; they never grant access to another Space."
18380
18460
  };
18381
18461
  }
18382
18462
  //#endregion
@@ -18683,6 +18763,18 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
18683
18763
  const unique = Array.from(new Set(addresses.filter(Boolean)));
18684
18764
  return typeof unique[0] === "string" ? unique[0] : "";
18685
18765
  }
18766
+ /**
18767
+ * Returns true when a "Removed peer X" style feedback message is stale
18768
+ * because the related peer has reappeared in the loaded peers list (e.g.
18769
+ * the user re-paired the same device they just removed). Used by
18770
+ * renderSyncPeers to clear the persistent banner once the user's view
18771
+ * of reality contradicts it.
18772
+ */
18773
+ function shouldClearStalePeersFeedback(feedback, peers) {
18774
+ const id = String(feedback?.relatedPeerDeviceId ?? "").trim();
18775
+ if (!id) return false;
18776
+ return peers.some((peer) => String(peer?.peer_device_id ?? "").trim() === id);
18777
+ }
18686
18778
  function actorLabel(actor) {
18687
18779
  if (!actor || typeof actor !== "object") return "Unknown person";
18688
18780
  const displayName = String(actor.display_name || "").trim();
@@ -18728,208 +18820,6 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
18728
18820
  if (!targetActorId || !target) return "Choose which person should keep these devices.";
18729
18821
  return `Merge into ${actorDisplayLabel(target)}. Assigned devices move now; existing memories keep their current provenance.`;
18730
18822
  }
18731
- function createChipEditor(initialValues, placeholder, emptyLabel, availableProjects = []) {
18732
- let values = [...initialValues];
18733
- let query = "";
18734
- let activeIndex = 0;
18735
- let popoverOpen = false;
18736
- const container = el$1("div", "project-scope-picker");
18737
- const selectedList = el$1("ul", "project-scope-picker-selected");
18738
- const trigger = el$1("button", "project-scope-picker-trigger settings-button");
18739
- trigger.type = "button";
18740
- const triggerLabel = el$1("span", null, placeholder || "Add project");
18741
- const triggerPlus = el$1("span", null, "+");
18742
- triggerPlus.setAttribute("aria-hidden", "true");
18743
- trigger.append(triggerPlus, triggerLabel);
18744
- trigger.setAttribute("aria-haspopup", "listbox");
18745
- trigger.setAttribute("aria-expanded", "false");
18746
- const popover = el$1("div", "project-scope-picker-popover project-scope-picker-popover--inline");
18747
- popover.hidden = true;
18748
- const search = el$1("input", "project-scope-picker-search");
18749
- search.type = "text";
18750
- search.placeholder = "Search or create…";
18751
- search.setAttribute("aria-label", "Search projects");
18752
- const results = el$1("div", "project-scope-picker-results");
18753
- results.setAttribute("role", "listbox");
18754
- popover.append(search, results);
18755
- const knownPool = () => {
18756
- const seen = /* @__PURE__ */ new Set();
18757
- const out = [];
18758
- for (const item of [...availableProjects, ...values]) {
18759
- const trimmed = item.trim();
18760
- if (!trimmed || seen.has(trimmed)) continue;
18761
- seen.add(trimmed);
18762
- out.push(trimmed);
18763
- }
18764
- return out.sort((a, b) => a.localeCompare(b));
18765
- };
18766
- const filteredRows = () => {
18767
- const all = knownPool();
18768
- const q = query.trim().toLowerCase();
18769
- if (!q) return all;
18770
- return all.filter((project) => project.toLowerCase().includes(q));
18771
- };
18772
- const shouldShowCreateRow = () => {
18773
- const q = query.trim();
18774
- if (!q) return false;
18775
- return !knownPool().some((project) => project.toLowerCase() === q.toLowerCase());
18776
- };
18777
- const toggleValue = (project) => {
18778
- if (values.includes(project)) values = values.filter((value) => value !== project);
18779
- else values = Array.from(new Set([...values, project]));
18780
- renderSelected();
18781
- renderResults();
18782
- };
18783
- const createFromQuery = () => {
18784
- const trimmed = query.trim();
18785
- if (!trimmed) return;
18786
- values = Array.from(new Set([...values, trimmed]));
18787
- query = "";
18788
- search.value = "";
18789
- activeIndex = 0;
18790
- renderSelected();
18791
- renderResults();
18792
- };
18793
- const activateRow = (index) => {
18794
- const rows = filteredRows();
18795
- if (index < rows.length) {
18796
- const project = rows[index];
18797
- if (project) toggleValue(project);
18798
- return;
18799
- }
18800
- if (shouldShowCreateRow()) createFromQuery();
18801
- };
18802
- const renderSelected = () => {
18803
- selectedList.textContent = "";
18804
- if (!values.length) {
18805
- const note = el$1("li", "project-scope-picker-selected-empty", emptyLabel);
18806
- selectedList.appendChild(note);
18807
- return;
18808
- }
18809
- values.forEach((value) => {
18810
- const chip = el$1("li", "project-scope-picker-selected-chip");
18811
- const label = el$1("span", null, value);
18812
- const remove = el$1("button", "project-scope-picker-selected-remove", "×");
18813
- remove.type = "button";
18814
- remove.setAttribute("aria-label", `Remove ${value}`);
18815
- remove.addEventListener("click", () => {
18816
- values = values.filter((existing) => existing !== value);
18817
- renderSelected();
18818
- renderResults();
18819
- });
18820
- chip.append(label, remove);
18821
- selectedList.appendChild(chip);
18822
- });
18823
- };
18824
- const renderResults = () => {
18825
- results.textContent = "";
18826
- const rows = filteredRows();
18827
- const withCreate = shouldShowCreateRow();
18828
- const rowCount = rows.length + (withCreate ? 1 : 0);
18829
- if (activeIndex >= rowCount) activeIndex = Math.max(0, rowCount - 1);
18830
- if (!rows.length && !withCreate) {
18831
- const empty = el$1("div", "project-scope-picker-empty-row", "No projects yet. Type a name to create one.");
18832
- results.appendChild(empty);
18833
- return;
18834
- }
18835
- rows.forEach((project, index) => {
18836
- const selected = values.includes(project);
18837
- const row = el$1("button", index === activeIndex ? "project-scope-picker-row project-scope-picker-row--active" : "project-scope-picker-row");
18838
- row.type = "button";
18839
- row.setAttribute("role", "option");
18840
- row.setAttribute("aria-selected", selected ? "true" : "false");
18841
- const check = el$1("span", "project-scope-picker-row-check", selected ? "✓" : "");
18842
- check.setAttribute("aria-hidden", "true");
18843
- const labelEl = el$1("span", "project-scope-picker-row-label", project);
18844
- row.append(check, labelEl);
18845
- row.addEventListener("click", () => {
18846
- activeIndex = index;
18847
- toggleValue(project);
18848
- });
18849
- row.addEventListener("mouseenter", () => {
18850
- activeIndex = index;
18851
- renderResults();
18852
- });
18853
- results.appendChild(row);
18854
- });
18855
- if (withCreate) {
18856
- const index = rows.length;
18857
- const row = el$1("button", index === activeIndex ? "project-scope-picker-row project-scope-picker-row--create project-scope-picker-row--active" : "project-scope-picker-row project-scope-picker-row--create");
18858
- row.type = "button";
18859
- row.setAttribute("role", "option");
18860
- const check = el$1("span", "project-scope-picker-row-check", "+");
18861
- check.setAttribute("aria-hidden", "true");
18862
- const labelEl = el$1("span", "project-scope-picker-row-label", `Create "${query.trim()}"`);
18863
- row.append(check, labelEl);
18864
- row.addEventListener("click", () => createFromQuery());
18865
- row.addEventListener("mouseenter", () => {
18866
- activeIndex = index;
18867
- renderResults();
18868
- });
18869
- results.appendChild(row);
18870
- }
18871
- };
18872
- const setOpen = (next) => {
18873
- popoverOpen = next;
18874
- popover.hidden = !next;
18875
- trigger.setAttribute("aria-expanded", next ? "true" : "false");
18876
- if (next) {
18877
- query = "";
18878
- search.value = "";
18879
- activeIndex = 0;
18880
- renderResults();
18881
- requestAnimationFrame(() => search.focus());
18882
- }
18883
- };
18884
- trigger.addEventListener("click", (event) => {
18885
- event.stopPropagation();
18886
- setOpen(!popoverOpen);
18887
- });
18888
- search.addEventListener("input", () => {
18889
- query = search.value;
18890
- activeIndex = 0;
18891
- renderResults();
18892
- });
18893
- search.addEventListener("keydown", (event) => {
18894
- const rowCount = filteredRows().length + (shouldShowCreateRow() ? 1 : 0);
18895
- if (event.key === "ArrowDown") {
18896
- event.preventDefault();
18897
- activeIndex = Math.min(rowCount - 1, activeIndex + 1);
18898
- renderResults();
18899
- return;
18900
- }
18901
- if (event.key === "ArrowUp") {
18902
- event.preventDefault();
18903
- activeIndex = Math.max(0, activeIndex - 1);
18904
- renderResults();
18905
- return;
18906
- }
18907
- if (event.key === "Enter") {
18908
- event.preventDefault();
18909
- activateRow(activeIndex);
18910
- return;
18911
- }
18912
- if (event.key === "Escape") {
18913
- event.preventDefault();
18914
- setOpen(false);
18915
- }
18916
- });
18917
- const onDocumentClick = (event) => {
18918
- if (!popoverOpen) return;
18919
- const target = event.target;
18920
- if (!target) return;
18921
- if (container.contains(target)) return;
18922
- setOpen(false);
18923
- };
18924
- document.addEventListener("click", onDocumentClick);
18925
- container.append(selectedList, trigger, popover);
18926
- renderSelected();
18927
- renderResults();
18928
- return {
18929
- element: container,
18930
- values: () => [...values]
18931
- };
18932
- }
18933
18823
  function renderActionList(container, actions) {
18934
18824
  if (!container) return;
18935
18825
  container.textContent = "";
@@ -19526,46 +19416,6 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
19526
19416
  });
19527
19417
  }
19528
19418
  //#endregion
19529
- //#region src/tabs/sync/peer-scope-collapsible.tsx
19530
- function PeerScopeCollapsible({ contentHost, initialOpen, onOpenChange, children }) {
19531
- const [open, setOpen] = d$2(initialOpen);
19532
- const contentRef = A$1(null);
19533
- y$2(() => {
19534
- setOpen(initialOpen);
19535
- }, [initialOpen]);
19536
- _$2(() => {
19537
- onOpenChange(open);
19538
- }, [onOpenChange, open]);
19539
- y$2(() => {
19540
- if (!open) return;
19541
- queueMicrotask(() => {
19542
- (contentRef.current?.querySelector("button:not([disabled]), input:not([disabled]), textarea:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex=\"-1\"])"))?.focus();
19543
- });
19544
- }, [open]);
19545
- return /* @__PURE__ */ u(Root$3, {
19546
- open,
19547
- onOpenChange: setOpen,
19548
- children: [/* @__PURE__ */ u(Trigger$4, {
19549
- asChild: true,
19550
- children: /* @__PURE__ */ u("button", {
19551
- type: "button",
19552
- className: "settings-button",
19553
- children: open ? "Hide sharing scope" : "Show sharing scope"
19554
- })
19555
- }), contentHost ? $$1(/* @__PURE__ */ u(Content$2, {
19556
- forceMount: true,
19557
- ref: contentRef,
19558
- className: `peer-scope-editor-wrap${open ? "" : " collapsed"}`,
19559
- hidden: !open,
19560
- inert: !open,
19561
- children: /* @__PURE__ */ u(ScopeEditorContent, { children })
19562
- }), contentHost) : null]
19563
- });
19564
- }
19565
- function ScopeEditorContent({ children }) {
19566
- return /* @__PURE__ */ u("div", { children });
19567
- }
19568
- //#endregion
19569
19419
  //#region src/tabs/sync/components/sync-inline-feedback.tsx
19570
19420
  function SyncInlineFeedback({ feedback }) {
19571
19421
  if (!feedback?.message) return null;
@@ -19634,53 +19484,38 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
19634
19484
  },
19635
19485
  none: null
19636
19486
  };
19637
- function listText(value) {
19638
- return Array.isArray(value) ? value.map((item) => String(item || "").trim()).filter(Boolean) : [];
19639
- }
19640
19487
  function claimedLocalActorScopeMessage(scope) {
19641
- const scopeId = String(scope?.scope_id || "").trim();
19642
- if (!scope) return "Private same-person sync is limited to an allowed personal Sharing domain; team or org domains do not carry your private memories.";
19643
- if (scope.authorized) return `Private same-person sync is limited to personal Sharing domain ${scopeId || "this device's personal domain"}. Team and org domains do not carry your private memories.`;
19644
- return `Private same-person sync is blocked until this device is granted ${scopeId || "its personal Sharing domain"}.`;
19488
+ if (!scope) return "Private same-person sync is limited to an allowed personal Space; team or org Spaces do not carry your private memories.";
19489
+ if (scope.authorized) return "Private same-person sync is limited to your personal Space. Team and org Spaces do not carry your private memories.";
19490
+ return "Private same-person sync is blocked until this device is granted an allowed personal Space.";
19645
19491
  }
19646
- function ExistingElementSlot$1({ element }) {
19647
- const hostRef = A$1(null);
19648
- _$2(() => {
19649
- const host = hostRef.current;
19650
- if (!host) return;
19651
- if (element.parentElement !== host) host.appendChild(element);
19652
- return () => {
19653
- if (element.parentElement === host) host.removeChild(element);
19654
- };
19655
- }, [element]);
19656
- return /* @__PURE__ */ u("div", { ref: hostRef });
19492
+ function openTeamsAccessManagement() {
19493
+ window.location.hash = "coordinator-admin";
19657
19494
  }
19658
- function SyncPeerCard({ peer, onAssignActor, onRemove, onRename, onResetScope, onSaveScope, onSync }) {
19495
+ function SyncPeerCard({ peer, onAssignActor, onRemove, onRename, onSync }) {
19659
19496
  const peerId = String(peer.peer_device_id || "");
19660
19497
  const displayName = peer.name || (peerId ? peerId.slice(0, 8) : "unknown");
19661
19498
  const destructiveLabel = peer.name || peerId || displayName;
19662
- const pendingScopeReview = isPeerScopeReviewPending(peerId);
19663
19499
  const trustSummary = derivePeerTrustSummary(peer);
19664
19500
  const directionHint = DIRECTION_GLYPH[derivePeerDirection(peer)];
19665
19501
  const peerStatus = peer.status || {};
19666
19502
  const authorizedDomains = derivePeerAuthorizedDomainsView(peer);
19503
+ const rawPendingScopeReview = isPeerScopeReviewPending(peerId);
19504
+ const pendingScopeReview = rawPendingScopeReview && authorizedDomains.total === 0;
19667
19505
  const grantRoleMismatch = derivePeerGrantRoleMismatchView(peer);
19668
19506
  const scopeRejections = derivePeerScopeRejectionsView(peer);
19669
- const scope = peer.project_scope || {};
19670
- const projectNarrowing = derivePeerProjectNarrowingView(scope);
19671
- const includeList = listText(scope.include);
19672
- const excludeList = listText(scope.exclude);
19507
+ const projectNarrowing = derivePeerProjectNarrowingView(peer.project_scope || {});
19673
19508
  const primaryAddress = pickPrimaryAddress(peer.addresses);
19674
19509
  const peerAddresses = Array.isArray(peer.addresses) ? Array.from(new Set(peer.addresses.filter(Boolean).map((value) => String(value)))) : [];
19675
- const addressLine = peerAddresses.length ? peerAddresses.map((address) => isSyncRedactionEnabled() ? redactAddress(address) : address).join(" · ") : "No addresses";
19510
+ const rawHiddenAddressCount = Number(peer.address_count ?? 0);
19511
+ const hiddenAddressCount = Number.isFinite(rawHiddenAddressCount) && rawHiddenAddressCount > 0 ? rawHiddenAddressCount : 0;
19512
+ const addressLine = peerAddresses.length ? peerAddresses.map((address) => isSyncRedactionEnabled() ? redactAddress(address) : address).join(" · ") : hiddenAddressCount > 0 ? `${hiddenAddressCount} ${hiddenAddressCount === 1 ? "address" : "addresses"} hidden` : "No addresses";
19676
19513
  const assignmentSummary = peer.actor_display_name ? `This device belongs to ${peer.claimed_local_actor ? "you" : String(peer.actor_display_name)}.` : "This device is not assigned to anyone yet.";
19677
19514
  const lastSyncAt = String(peerStatus.last_sync_at || peerStatus.last_sync_at_utc || "");
19678
19515
  const lastPingAt = String(peerStatus.last_ping_at || peerStatus.last_ping_at_utc || "");
19679
- const discoverySummary = peer.discovered_via_group_id ? `Discovery: seen through coordinator group ${peer.discovered_via_group_id}. Discovery helps find devices; Sharing-domain grants above decide data access.` : peer.discovered_via_coordinator_id ? `Discovery: seen through coordinator ${peer.discovered_via_coordinator_id}. Discovery helps find devices; Sharing-domain grants above decide data access.` : null;
19680
- const scopeEditorOpen = openPeerScopeEditors.has(peerId);
19516
+ const discoverySummary = peer.discovered_via_group_id ? `Discovery: seen through Team ${peer.discovered_via_group_id}. Discovery helps find devices; Space access above decides data access.` : peer.discovered_via_coordinator_id ? `Discovery: seen through coordinator ${peer.discovered_via_coordinator_id}. Discovery helps find devices; Space access above decides data access.` : null;
19681
19517
  const scopeReviewRequested = consumePeerScopeReviewRequest(peerId);
19682
19518
  const cardRef = A$1(null);
19683
- const [scopeHost, setScopeHost] = d$2(null);
19684
19519
  const [renameValue, setRenameValue] = d$2(displayName);
19685
19520
  const [feedback, setFeedback] = d$2(() => state.syncPeerFeedbackById.get(peerId) ?? null);
19686
19521
  const [renameBusy, setRenameBusy] = d$2(false);
@@ -19691,10 +19526,6 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
19691
19526
  const [selectedActorId, setSelectedActorId] = d$2(String(peer.actor_id || ""));
19692
19527
  const [applyActorBusy, setApplyActorBusy] = d$2(false);
19693
19528
  const [applyActorLabel, setApplyActorLabel] = d$2("Save assignment");
19694
- const [saveScopeBusy, setSaveScopeBusy] = d$2(false);
19695
- const [saveScopeLabel, setSaveScopeLabel] = d$2("Save scope");
19696
- const [resetScopeBusy, setResetScopeBusy] = d$2(false);
19697
- const [resetScopeLabel, setResetScopeLabel] = d$2("Reset to global scope");
19698
19529
  const actorSelectOptions = T$1(() => {
19699
19530
  const options = buildActorSelectOptions(selectedActorId);
19700
19531
  const hasSelected = options.some((option) => option.value === selectedActorId);
@@ -19711,15 +19542,12 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
19711
19542
  state.lastSyncPeers,
19712
19543
  state.lastSyncViewModel
19713
19544
  ]);
19714
- const includeEditor = T$1(() => createChipEditor(includeList, "Add project", "All projects", state.knownProjects), [
19715
- peerId,
19716
- includeList.join("|"),
19717
- state.knownProjects.join("|")
19718
- ]);
19719
- const excludeEditor = T$1(() => createChipEditor(excludeList, "Add project", "No exclusions", state.knownProjects), [
19545
+ y$2(() => {
19546
+ if (rawPendingScopeReview && authorizedDomains.total > 0) clearPeerScopeReview(peerId);
19547
+ }, [
19548
+ authorizedDomains.total,
19720
19549
  peerId,
19721
- excludeList.join("|"),
19722
- state.knownProjects.join("|")
19550
+ rawPendingScopeReview
19723
19551
  ]);
19724
19552
  y$2(() => {
19725
19553
  setRenameValue(displayName);
@@ -19732,16 +19560,10 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
19732
19560
  setSelectedActorId(String(peer.actor_id || ""));
19733
19561
  setApplyActorBusy(false);
19734
19562
  setApplyActorLabel("Save assignment");
19735
- setSaveScopeBusy(false);
19736
- setSaveScopeLabel("Save scope");
19737
- setResetScopeBusy(false);
19738
- setResetScopeLabel("Reset to global scope");
19739
19563
  }, [
19740
19564
  displayName,
19741
19565
  peer.actor_id,
19742
- peerId,
19743
- includeList.join("|"),
19744
- excludeList.join("|")
19566
+ peerId
19745
19567
  ]);
19746
19568
  y$2(() => {
19747
19569
  if (!scopeReviewRequested || !cardRef.current) return;
@@ -19777,13 +19599,12 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
19777
19599
  }
19778
19600
  }
19779
19601
  async function sync() {
19780
- if (!primaryAddress) return;
19781
19602
  if (pendingScopeReview) {
19782
19603
  if (!await openSyncConfirmDialog({
19783
- title: `Sync ${displayName} before scope review?`,
19784
- description: "This manual sync will use the current effective scope until you finish reviewing and saving the device scope.",
19604
+ title: `Sync ${displayName} before advanced rule review?`,
19605
+ description: "This manual sync will use the current Space access and advanced filters until you review them in Teams.",
19785
19606
  confirmLabel: "Sync anyway",
19786
- cancelLabel: "Review scope first"
19607
+ cancelLabel: "Review in Teams first"
19787
19608
  })) return;
19788
19609
  }
19789
19610
  setSyncBusy(true);
@@ -19832,36 +19653,6 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
19832
19653
  setApplyActorBusy(false);
19833
19654
  }
19834
19655
  }
19835
- async function saveScope() {
19836
- if (!peerId) return;
19837
- setSaveScopeBusy(true);
19838
- setSaveScopeLabel("Saving…");
19839
- try {
19840
- const nextFeedback = await onSaveScope(peerId, includeEditor.values(), excludeEditor.values());
19841
- setFeedback(nextFeedback);
19842
- state.syncPeerFeedbackById.set(peerId, nextFeedback);
19843
- setSaveScopeLabel("Save scope");
19844
- } catch {
19845
- setSaveScopeLabel("Retry");
19846
- } finally {
19847
- setSaveScopeBusy(false);
19848
- }
19849
- }
19850
- async function resetScope() {
19851
- if (!peerId) return;
19852
- setResetScopeBusy(true);
19853
- setResetScopeLabel("Resetting…");
19854
- try {
19855
- const nextFeedback = await onResetScope(peerId);
19856
- setFeedback(nextFeedback);
19857
- state.syncPeerFeedbackById.set(peerId, nextFeedback);
19858
- setResetScopeLabel("Reset to global scope");
19859
- } catch {
19860
- setResetScopeLabel("Retry");
19861
- } finally {
19862
- setResetScopeBusy(false);
19863
- }
19864
- }
19865
19656
  const isExpanded = useExpandedPeer() === peerId && peerId !== "";
19866
19657
  const drawerId = `device-drawer-${peerId || "unknown"}`;
19867
19658
  const presenceState = presenceForPeer(peer);
@@ -19908,11 +19699,11 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
19908
19699
  }),
19909
19700
  pendingScopeReview ? /* @__PURE__ */ u("span", {
19910
19701
  className: "badge actor-badge",
19911
- children: "Needs scope review"
19702
+ children: "Review advanced rules"
19912
19703
  }) : null,
19913
19704
  /* @__PURE__ */ u("span", {
19914
19705
  className: `badge ${authorizedDomains.isWarning ? "badge-offline" : "actor-badge"}`,
19915
- title: "Sharing domains are the hard access boundary; project filters only narrow inside them.",
19706
+ title: "Spaces are the hard access boundary; advanced filters only narrow inside them.",
19916
19707
  children: authorizedDomains.badgeLabel
19917
19708
  }),
19918
19709
  scopeRejections.badgeLabel ? /* @__PURE__ */ u("span", {
@@ -19922,7 +19713,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
19922
19713
  }) : null,
19923
19714
  grantRoleMismatch.badgeLabel ? /* @__PURE__ */ u("span", {
19924
19715
  className: "badge badge-offline",
19925
- title: "Review whether explicit Sharing-domain grants match this device's intended role.",
19716
+ title: "Review whether explicit Space access matches this device's intended role.",
19926
19717
  children: grantRoleMismatch.badgeLabel
19927
19718
  }) : null,
19928
19719
  peer.discovered_via_group_id ? /* @__PURE__ */ u("span", {
@@ -19953,8 +19744,9 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
19953
19744
  /* @__PURE__ */ u("button", {
19954
19745
  type: "button",
19955
19746
  className: "settings-button",
19956
- disabled: !primaryAddress || syncBusy,
19747
+ disabled: syncBusy,
19957
19748
  onClick: () => void sync(),
19749
+ title: primaryAddress ? void 0 : "Address details are hidden; sync will target this device by ID.",
19958
19750
  children: syncBusy ? "Syncing…" : "Sync now"
19959
19751
  }),
19960
19752
  /* @__PURE__ */ u("div", {
@@ -19995,10 +19787,10 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
19995
19787
  children: [
19996
19788
  scopeReviewRequested ? /* @__PURE__ */ u("div", {
19997
19789
  className: "peer-meta",
19998
- children: "Review this device's sharing rules now if the defaults are too broad."
19790
+ children: "Review this device's Space access and advanced rules in Teams if the defaults are too broad."
19999
19791
  }) : pendingScopeReview ? /* @__PURE__ */ u("div", {
20000
19792
  className: "peer-meta",
20001
- children: "Sharing rule review is still pending for this device."
19793
+ children: "Advanced rule review is still pending for this device."
20002
19794
  }) : null,
20003
19795
  /* @__PURE__ */ u("div", {
20004
19796
  className: "peer-scope-summary",
@@ -20018,15 +19810,15 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
20018
19810
  }),
20019
19811
  /* @__PURE__ */ u("div", {
20020
19812
  className: "peer-scope-summary",
20021
- children: "Authorized Sharing domains"
19813
+ children: "Space access"
20022
19814
  }),
20023
19815
  /* @__PURE__ */ u("div", {
20024
19816
  className: "peer-meta",
20025
- children: authorizedDomains.total > 0 ? "These Sharing domains are the hard access boundary for this device." : authorizedDomains.emptyMessage
19817
+ children: authorizedDomains.total > 0 ? "These Spaces are the hard access boundary for this device." : authorizedDomains.emptyMessage
20026
19818
  }),
20027
19819
  authorizedDomains.domains.length > 0 ? /* @__PURE__ */ u("ul", {
20028
19820
  className: "peer-scope-chips",
20029
- "aria-label": "Authorized Sharing domains",
19821
+ "aria-label": "Authorized Spaces",
20030
19822
  children: authorizedDomains.domains.map((domain) => /* @__PURE__ */ u("li", {
20031
19823
  className: "peer-scope-chip",
20032
19824
  title: domain.detail,
@@ -20112,41 +19904,28 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
20112
19904
  }),
20113
19905
  /* @__PURE__ */ u("div", {
20114
19906
  className: "peer-scope-summary",
20115
- children: "Project filters (narrowing only)"
19907
+ children: "Advanced sharing rules"
20116
19908
  }),
20117
19909
  /* @__PURE__ */ u("div", {
20118
19910
  className: "peer-meta",
20119
- children: [projectNarrowing.summary, " Review or tighten these filters when this device should receive fewer projects from its authorized Sharing domains."]
19911
+ children: [
19912
+ projectNarrowing.statusLabel,
19913
+ ". ",
19914
+ projectNarrowing.summary,
19915
+ " ",
19916
+ projectNarrowing.note
19917
+ ]
20120
19918
  }),
20121
- /* @__PURE__ */ u(PeerScopeCollapsible, {
20122
- contentHost: scopeHost,
20123
- initialOpen: scopeEditorOpen,
20124
- onOpenChange: (open) => {
20125
- if (open) openPeerScopeEditors.add(peerId);
20126
- else openPeerScopeEditors.delete(peerId);
20127
- },
20128
- children: /* @__PURE__ */ u("div", { children: [/* @__PURE__ */ u("div", {
20129
- className: "peer-scope-row",
20130
- children: [/* @__PURE__ */ u(ExistingElementSlot$1, { element: includeEditor.element }), /* @__PURE__ */ u(ExistingElementSlot$1, { element: excludeEditor.element })]
20131
- }), /* @__PURE__ */ u("div", {
20132
- className: "peer-scope-actions",
20133
- children: [/* @__PURE__ */ u("button", {
20134
- type: "button",
20135
- className: "settings-button",
20136
- disabled: saveScopeBusy,
20137
- onClick: () => void saveScope(),
20138
- children: saveScopeLabel
20139
- }), /* @__PURE__ */ u("button", {
20140
- type: "button",
20141
- className: "settings-button",
20142
- disabled: resetScopeBusy,
20143
- onClick: () => void resetScope(),
20144
- children: resetScopeLabel
20145
- })]
20146
- })] })
19919
+ /* @__PURE__ */ u("div", {
19920
+ className: "peer-actions",
19921
+ children: /* @__PURE__ */ u("button", {
19922
+ type: "button",
19923
+ className: "settings-button",
19924
+ onClick: openTeamsAccessManagement,
19925
+ children: "Manage Spaces in Teams"
19926
+ })
20147
19927
  }),
20148
- /* @__PURE__ */ u(SyncInlineFeedback, { feedback }),
20149
- /* @__PURE__ */ u("div", { ref: setScopeHost })
19928
+ /* @__PURE__ */ u(SyncInlineFeedback, { feedback })
20150
19929
  ]
20151
19930
  })]
20152
19931
  })]
@@ -20245,8 +20024,10 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
20245
20024
  if (!syncPeers) return;
20246
20025
  hideSkeleton("syncPeersSkeleton");
20247
20026
  const peers = state.lastSyncPeers;
20027
+ const peersArray = Array.isArray(peers) ? peers : [];
20028
+ if (shouldClearStalePeersFeedback(state.syncPeersSectionFeedback, peersArray)) state.syncPeersSectionFeedback = null;
20248
20029
  renderSyncPeersList(syncPeers, {
20249
- peers: Array.isArray(peers) ? peers : [],
20030
+ peers: peersArray,
20250
20031
  onRename: async (peerId, nextName) => {
20251
20032
  try {
20252
20033
  await renamePeer(peerId, nextName);
@@ -20264,15 +20045,18 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
20264
20045
  },
20265
20046
  onSync: async (peer, address) => {
20266
20047
  try {
20267
- const summary = summarizeSyncRunResult(await triggerSync(address));
20268
20048
  const peerId = String(peer?.peer_device_id || "");
20049
+ const summary = summarizeSyncRunResult(await triggerSync({
20050
+ address,
20051
+ peerDeviceId: peerId || void 0
20052
+ }));
20269
20053
  let feedback;
20270
20054
  if (!summary.ok) feedback = {
20271
20055
  message: summary.message,
20272
20056
  tone: "warning"
20273
20057
  };
20274
20058
  else if (peerId && isPeerScopeReviewPending(peerId)) feedback = {
20275
- message: `Triggered sync for ${peer?.name || (peerId ? peerId.slice(0, 8) : "unknown")}. Review scope in this card if you want tighter sharing rules.`,
20059
+ message: `Triggered sync for ${peer?.name || (peerId ? peerId.slice(0, 8) : "unknown")}. Review Space access and advanced rules in Teams if this device needs tighter sharing.`,
20276
20060
  tone: "warning"
20277
20061
  };
20278
20062
  else feedback = {
@@ -20303,7 +20087,10 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
20303
20087
  tone: "success"
20304
20088
  };
20305
20089
  state.syncPeerFeedbackById.delete(peerId);
20306
- state.syncPeersSectionFeedback = feedback;
20090
+ state.syncPeersSectionFeedback = {
20091
+ ...feedback,
20092
+ relatedPeerDeviceId: peerId
20093
+ };
20307
20094
  await _loadSyncData();
20308
20095
  return feedback;
20309
20096
  } catch (error) {
@@ -20327,38 +20114,6 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
20327
20114
  tone: "warning"
20328
20115
  };
20329
20116
  }
20330
- },
20331
- onSaveScope: async (peerId, include, exclude) => {
20332
- try {
20333
- await updatePeerScope(peerId, include, exclude);
20334
- clearPeerScopeReview(peerId);
20335
- await _loadSyncData();
20336
- return {
20337
- message: "Device sync scope saved.",
20338
- tone: "success"
20339
- };
20340
- } catch (error) {
20341
- return {
20342
- message: friendlyError(error, "Failed to save device scope. The current sharing rules are still active."),
20343
- tone: "warning"
20344
- };
20345
- }
20346
- },
20347
- onResetScope: async (peerId) => {
20348
- try {
20349
- await updatePeerScope(peerId, null, null, true);
20350
- clearPeerScopeReview(peerId);
20351
- await _loadSyncData();
20352
- return {
20353
- message: "Device sync scope reset to global defaults.",
20354
- tone: "success"
20355
- };
20356
- } catch (error) {
20357
- return {
20358
- message: friendlyError(error, "Failed to reset device scope. The current sharing rules are still active."),
20359
- tone: "warning"
20360
- };
20361
- }
20362
20117
  }
20363
20118
  });
20364
20119
  }
@@ -21347,7 +21102,9 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
21347
21102
  pairedLocally: Boolean(pairedPeer)
21348
21103
  });
21349
21104
  const addresses = Array.isArray(device.addresses) ? device.addresses : [];
21350
- const noteParts = [addresses.length ? addresses.map((address) => isSyncRedactionEnabled() ? redactAddress(String(address || "")) : String(address || "")).filter(Boolean).join(" · ") : "No fresh addresses"];
21105
+ const rawHiddenAddressCount = Number(device.address_count ?? 0);
21106
+ const hiddenAddressCount = Number.isFinite(rawHiddenAddressCount) && rawHiddenAddressCount > 0 ? rawHiddenAddressCount : 0;
21107
+ const noteParts = [addresses.length ? addresses.map((address) => isSyncRedactionEnabled() ? redactAddress(String(address || "")) : String(address || "")).filter(Boolean).join(" · ") : hiddenAddressCount > 0 ? `${hiddenAddressCount} ${device.stale ? "last-known" : "fresh"} ${hiddenAddressCount === 1 ? "address" : "addresses"} hidden` : "No fresh addresses"];
21351
21108
  if (!addresses.length && displayTitle) noteParts.push(`device id: ${deviceId}`);
21352
21109
  let actionMessage = null;
21353
21110
  let mode = canAccept ? "accept" : "none";
@@ -21357,7 +21114,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
21357
21114
  actionMessage = "This device appears in multiple coordinator groups. Review team setup first or ask an admin to clean up the duplicate enrollment before approving it here.";
21358
21115
  mode = "ambiguous";
21359
21116
  } else if (pairedPeer && isPeerScopeReviewPending(deviceId)) {
21360
- actionMessage = `Finish this device's scope review in People & devices before you sync it.`;
21117
+ actionMessage = "Review this device's Space access and advanced rules in Teams before you sync it.";
21361
21118
  mode = "scope-pending";
21362
21119
  } else if (pairedPeer?.last_error) {
21363
21120
  noteParts.push(`error: ${String(pairedPeer.last_error)}`);
@@ -21545,14 +21302,41 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
21545
21302
  const label = group.displayProject.toLowerCase();
21546
21303
  return label.includes("fatal:") || label.includes("not a git repository") || label === "unknown project" || group.identitySource === "unmapped";
21547
21304
  }
21305
+ function displayProjectTitle(group) {
21306
+ return needsProjectCleanup(group) ? "Unclear project identity" : group.displayProject;
21307
+ }
21548
21308
  function canReassignLegacyGroup(group) {
21549
21309
  return (group.reassignableMemoryCount ?? group.memoryCount) > 0;
21550
21310
  }
21551
- function groupSearchText(group) {
21311
+ function suggestedScopeText(group, targetScopes) {
21312
+ if (!group.suggestedScopeId) return "";
21313
+ return ` · suggested ${targetScopes.find((scope) => scope.scopeId === group.suggestedScopeId)?.label || "Space"}`;
21314
+ }
21315
+ function escapeRegExp(value) {
21316
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
21317
+ }
21318
+ function targetScopeLabel(scopeId, targetScopes) {
21319
+ return targetScopes.find((scope) => scope.scopeId === scopeId)?.label || "selected Space";
21320
+ }
21321
+ function authorityLabel(authorityType) {
21322
+ switch (authorityType) {
21323
+ case "coordinator": return "Team Space";
21324
+ case "local": return "Local Space";
21325
+ default: return "Other Space";
21326
+ }
21327
+ }
21328
+ function formatLegacyReviewError(error, scopeId, targetScopes) {
21329
+ let result = (error instanceof Error ? error.message : "Unable to reassign legacy review memories.").replace(/sharing domain/gi, "Space");
21330
+ if (scopeId) result = result.replace(new RegExp(`\\b${escapeRegExp(scopeId)}\\b`, "g"), targetScopeLabel(scopeId, targetScopes));
21331
+ return result;
21332
+ }
21333
+ function groupSearchText(group, targetScopes) {
21334
+ const suggestedScopeLabel = group.suggestedScopeId ? targetScopes.find((scope) => scope.scopeId === group.suggestedScopeId)?.label : "";
21552
21335
  return [
21553
21336
  group.displayProject,
21554
21337
  group.identitySource,
21555
21338
  group.suggestedScopeId ?? "",
21339
+ suggestedScopeLabel ?? "",
21556
21340
  group.suggestionReason ?? "",
21557
21341
  group.workspaceIdentity
21558
21342
  ].join(" ").toLowerCase();
@@ -21613,7 +21397,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
21613
21397
  if (filter === "cleanup" && !cleanup) return false;
21614
21398
  if (filter === "no-suggestion" && (group.suggestedScopeId || cleanup)) return false;
21615
21399
  if (filter === "selected" && !selectedGroups[group.workspaceIdentity]) return false;
21616
- return !query.trim() || groupSearchText(group).includes(query.trim().toLowerCase());
21400
+ return !query.trim() || groupSearchText(group, targetScopes).includes(query.trim().toLowerCase());
21617
21401
  });
21618
21402
  async function applyGroup(group) {
21619
21403
  const selectedScopeId = selectedScopes[group.workspaceIdentity] || group.suggestedScopeId || "";
@@ -21638,7 +21422,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
21638
21422
  } catch (error) {
21639
21423
  setErrors((current) => ({
21640
21424
  ...current,
21641
- [group.workspaceIdentity]: error instanceof Error ? error.message : "Unable to reassign legacy review memories."
21425
+ [group.workspaceIdentity]: formatLegacyReviewError(error, selectedScopeId, targetScopes)
21642
21426
  }));
21643
21427
  } finally {
21644
21428
  setBusyIdentity(null);
@@ -21702,7 +21486,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
21702
21486
  } catch (error) {
21703
21487
  setErrors((current) => ({
21704
21488
  ...current,
21705
- [group.workspaceIdentity]: error instanceof Error ? error.message : "Unable to reassign legacy review memories."
21489
+ [group.workspaceIdentity]: formatLegacyReviewError(error, selectedScopeId, targetScopes)
21706
21490
  }));
21707
21491
  }
21708
21492
  }
@@ -21730,7 +21514,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
21730
21514
  " older",
21731
21515
  " ",
21732
21516
  groupCount === 1 ? "project needs" : "projects need",
21733
- " a Sharing domain. They contain",
21517
+ " a Space. They contain",
21734
21518
  " ",
21735
21519
  item.memoryCount.toLocaleString(),
21736
21520
  " older shared memories total; review the projects, not individual memories.",
@@ -21803,7 +21587,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
21803
21587
  children: busyIdentity === "bulk" ? "Previewing…" : `Preview ${selectedCount.toLocaleString()} selected`
21804
21588
  }) : onReassign && selectedReassignableCount > 0 ? /* @__PURE__ */ u("span", {
21805
21589
  className: "legacy-review-cleanup-note",
21806
- children: "Add or join a Sharing domain before bulk reassignment."
21590
+ children: "Add or join a Space before bulk reassignment."
21807
21591
  }) : null,
21808
21592
  selectedCount > 0 && groups.filter((group) => selectedGroups[group.workspaceIdentity] && canReassignLegacyGroup(group) && Boolean(selectedScopes[group.workspaceIdentity])).every((group) => pending[group.workspaceIdentity]) ? /* @__PURE__ */ u("span", {
21809
21593
  className: "legacy-review-cleanup-note",
@@ -21823,7 +21607,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
21823
21607
  children: [/* @__PURE__ */ u("div", {
21824
21608
  className: "legacy-review-title-wrap",
21825
21609
  children: [/* @__PURE__ */ u("input", {
21826
- "aria-label": `Select ${group.displayProject}`,
21610
+ "aria-label": `Select ${displayProjectTitle(group)}`,
21827
21611
  checked: Boolean(selectedGroups[group.workspaceIdentity]),
21828
21612
  className: "cm-checkbox",
21829
21613
  disabled: !canReassignLegacyGroup(group),
@@ -21832,15 +21616,15 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
21832
21616
  }), /* @__PURE__ */ u("div", { children: [
21833
21617
  /* @__PURE__ */ u("div", {
21834
21618
  className: "legacy-review-group-title",
21835
- children: group.displayProject
21619
+ children: displayProjectTitle(group)
21836
21620
  }),
21837
21621
  /* @__PURE__ */ u("div", {
21838
21622
  className: "legacy-review-group-meta",
21839
- children: [formatIdentitySource(group.identitySource), group.suggestedScopeId ? ` · suggested ${group.suggestedScopeId}` : ""]
21623
+ children: [formatIdentitySource(group.identitySource), suggestedScopeText(group, targetScopes)]
21840
21624
  }),
21841
21625
  !canReassignLegacyGroup(group) ? /* @__PURE__ */ u("div", {
21842
21626
  className: "legacy-review-cleanup-note",
21843
- children: "Peer-owned only. These older memories were received from another device, so this device cannot reassign them to a Sharing domain."
21627
+ children: "Peer-owned only. These older memories were received from another device, so this device cannot reassign them to a Space."
21844
21628
  }) : group.peerOwnedMemoryCount ? /* @__PURE__ */ u("div", {
21845
21629
  className: "legacy-review-cleanup-note",
21846
21630
  children: [
@@ -21858,7 +21642,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
21858
21642
  }) : null,
21859
21643
  needsProjectCleanup(group) ? /* @__PURE__ */ u("div", {
21860
21644
  className: "legacy-review-cleanup-note",
21861
- children: "Unclear project identity. Inspect or correct the project before trusting a domain."
21645
+ children: "Inspect or correct the project identity before assigning old shared data to a Space."
21862
21646
  }) : null
21863
21647
  ] })]
21864
21648
  }), /* @__PURE__ */ u("span", {
@@ -21897,20 +21681,20 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
21897
21681
  }) : null,
21898
21682
  targetScopes.length > 0 && onReassign && canReassignLegacyGroup(group) ? /* @__PURE__ */ u("label", {
21899
21683
  className: "legacy-review-target",
21900
- children: [/* @__PURE__ */ u("span", { children: "Destination Sharing domain" }), /* @__PURE__ */ u("select", {
21684
+ children: [/* @__PURE__ */ u("span", { children: "Destination Space" }), /* @__PURE__ */ u("select", {
21901
21685
  className: "peer-scope-input",
21902
21686
  disabled: busyIdentity != null,
21903
21687
  onChange: (event) => updateSelectedScope(group, event.currentTarget.value),
21904
21688
  value: selectedScopes[group.workspaceIdentity] || "",
21905
21689
  children: [/* @__PURE__ */ u("option", {
21906
21690
  value: "",
21907
- children: "Choose domain…"
21691
+ children: "Choose Space…"
21908
21692
  }), targetScopes.map((scope) => /* @__PURE__ */ u("option", {
21909
21693
  value: scope.scopeId,
21910
21694
  children: [
21911
21695
  scope.label,
21912
21696
  " · ",
21913
- scope.authorityType,
21697
+ authorityLabel(scope.authorityType),
21914
21698
  scope.scopeId === group.suggestedScopeId ? " · suggested" : ""
21915
21699
  ]
21916
21700
  }, scope.scopeId))]
@@ -22213,7 +21997,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
22213
21997
  try {
22214
21998
  const runtime = await loadRuntimeInfo();
22215
21999
  if (!runtime?.version) return;
22216
- setRuntimeLabel(runtime.version, "b4af8ab");
22000
+ setRuntimeLabel(runtime.version, "2eab49c");
22217
22001
  } catch {}
22218
22002
  }
22219
22003
  var lastAnnouncedRefreshState = null;