@codemem/server 0.47.1 → 0.47.3

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/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { closeSync, existsSync, fstatSync, fsyncSync, linkSync, lstatSync, mkdirSync, openSync, readFileSync, statSync, unlinkSync, writeFileSync } from "node:fs";
2
2
  import { basename, dirname, isAbsolute, join, posix, resolve, win32 } from "node:path";
3
- import { ACCESS_CLEANUP_OP_TYPE, CodememConfigMutationError, CoordinatorAccountLinkError, CoordinatorReciprocalApprovalRequestChangedError, DEFAULT_TIME_WINDOW_S, FILE_CONTEXT_TRANSPORT_MAX_BYTES, LEGACY_SHARED_REVIEW_SCOPE_ID, LOCAL_DEFAULT_SCOPE_ID, LOCAL_SYNC_CAPABILITY, LOCAL_SYNC_FEATURES, LegacyTeamSetupAdditiveConvergenceError, LegacyTeamSetupRosterCapacityError, MEMORY_FILTER_FIELD_TYPES, MEMORY_FILTER_NAMES, MEMORY_KIND_DESCRIPTIONS, MemoryStore, ObserverClient, PROMPT_TRANSPORT_PROTOCOL_RANGE, REMEMBER_MEMORY_KINDS, RICH_TIER_ANTHROPIC_DEFAULTS, RICH_TIER_DEFAULTS, RawEventIngestValidationError, RecipientPolicyEdgeRequestError, RecipientPolicyTeamRenameError, RemoteCoordinatorRequestError, SCOPE_MEMBERSHIP_REVOCATION_LIMITATION, SIMPLE_TIER_ANTHROPIC_DEFAULTS, SIMPLE_TIER_DEFAULTS, SYNC_AUTHORIZATION_REFRESH_HEADER, SYNC_CAPABILITY_HEADER, SYNC_FEATURES_HEADER, SYNC_SCOPE_QUERY_PARAM, VERSION, VERSION as VERSION$1, VIEWER_IDENTITY_TARGET_KEYS, VIEWER_SERVICE_DISCRIMINATOR, addSyncScopeToBoundary, analyzeProjectScopeMappingChangeGuardrails, analyzeProjectScopeMappingChangesGuardrails, analyzeProjectScopeMappingDeletionGuardrails, applyLegacyTeamSetupCompletionManifest, applyLegacyTeamSetupCompletionManifestAndReturnActivation, applyLocalIdentitySwitch, applyReplicationOps, areLegacyTeamSetupCompletionPolicyFactsAdditivelyCompatible, assertNoLocalIdentityOverride, automaticRecallHealth, buildAuthHeaders, buildBaseUrl, buildDirectPeerAuthHeaders, buildDistillReport, buildFilterBranchesWithContext, buildFilterClausesWithContext, buildPiFlushSignalFromEvent, buildRawEventEnvelopeFromCodexHook, buildRawEventEnvelopeFromHook, buildRawEventEnvelopeFromPiEvent, buildViewerIdentityTarget, canonicalCoordinatorLegacyTeamCompletionManifestJson, canonicalMemoryKind, canonicalRepositoryProjectIdentity, canonicalWorkspaceIdentity, captureAccountOwnerProfilePins, changeCoordinatorAccount, claimRecipientPolicyActorMutations, claimRecipientPolicyPublicationMutation, classifyRecordedSyncFailure, cleanupNonces, clearLegacyTeamSetupDeviceDecision, clonePromptPackAttempt, coerceObserverCommand, commitDeviceIdentityBindings, commitRecipientPolicyEdges, completeLocalIdentitySwitch, containLegacyTeamSetupCompletionConflict, coordinatorArchiveGroupAction, coordinatorAuthControllerReviewAction, coordinatorCreateAddDeviceInviteAction, coordinatorCreateGroupAction, coordinatorCreateInviteAction, coordinatorCreateLegacyTeamCompletionAction, coordinatorCreateScopeAction, coordinatorDisableDeviceAction, coordinatorEnableDeviceAction, coordinatorGetLegacyTeamCompletionAction, coordinatorGrantScopeMembershipAction, coordinatorImportInviteAction, coordinatorListBootstrapGrantsAction, coordinatorListConsumedTeamInvitesAction, coordinatorListDevicesAction, coordinatorListGroupsAction, coordinatorListJoinRequestsAction, coordinatorListLegacyTeamCompletionsAction, coordinatorListReviewedRecipientInviteEvidenceAction, coordinatorListScopeMembershipsAction, coordinatorListScopesAction, coordinatorRemoveDeviceAction, coordinatorRenameDeviceAction, coordinatorRenameGroupAction, coordinatorReviewJoinRequestAction, coordinatorRevokeBootstrapGrantAction, coordinatorRevokeScopeMembershipAction, coordinatorStatusSnapshot, coordinatorUnarchiveGroupAction, coordinatorUpdateScopeAction, countShareableProjectMemories, createCoordinatorReciprocalApproval, decodeInvitePayload, dedupeOrderedIds, defaultSpaceScopeIdForGroup, deleteProjectScopeSettingsMapping, deriveLegacyTeamSetupCompletionManifest, detectInstallKind, deterministicPolicyTeamId, diagnoseStalePeerReceivedRows, discoverLegacyTeamCandidates, dispatchFileContextTransport, ensureDeviceIdentity, executeShareProvisioning, extractInvitePayload, extractReplicationOps, filterReplicationOpsForSyncWithStatus, fingerprintPublicKey, formatHostPort, friendlyDeviceName, fromJson, generateWithOpenCodeV2, getCodememConfigPath, getCodememEnvOverrideValues, getCodememEnvOverrides, getCoordinatorEnrollmentReconciliationIssueSummary, getCoordinatorGroupPreference, getEffectiveCachedScopeAuthorization, getLegacyTeamSetupDraft, getRecipientPolicyAuthorityState, getSemanticIndexDiagnostics, getSyncResetState, getUpdateStatus, ingestRawEvents, inspectFreshLegacyTeamSetupActivation, inspectLegacyTeamSetupActivation, inviteTokenDigest, isAutomaticRecallMeasurement, isLegacyTeamCandidateSelectable, isLegacyTeamSetupProjectMappingIdentity, isPeerTrustBindingCompatible, isProjectSyncEnablementError, isScopedSyncCapability, judgeDistillReport, legacyDecisionCountsAsOwner, legacyTeamCandidateId, legacyTeamCandidateProjectInventory, legacyTeamCanonicalProjectRef, legacyTeamDeviceRef, legacyTeamResolvedProjectRef, legacyTeamSetupApiErrorCode, linkCoordinatorAccount, listAuthorizedScopesForPeer, listCoordinatorJoinRequests, listDeviceIdentityInventory, listInboundScopeRejections, listLegacyRecipientPolicyProjections, listMaintenanceJobs, listObserverProviderOptions, listOpenCodeV2Models, listPerPeerScopeSyncState, listProjectScopeCandidates, listProjectScopeInventory, listProjectScopeSettingsMappings, listRecipientPolicyIntent, listRecipientPolicyReview, listSharingDomainSettingsScopes, loadMemorySnapshotPageForPeer, loadPublicKey, loadReplicationOpsForPeer, loadRuntimeSigningPublicKey, lookupCoordinatorPeers, markLocalIdentitySwitchBound, markLocalIdentitySwitchNotCommitted, memoryFilterValueMatchesType, mergeAddresses, migrateRecipientPolicyIntent, mutateCodememConfigFile, negotiateSyncCapability, normalizeAddress, normalizeCoordinatorAccountLinkOrigin, normalizeCoordinatorLegacyTeamCompletionManifest, normalizeHumanPresentationName, normalizeSyncCapability, normalizeSyncFeatures, normalizeTeammateName, parseAcceptedProjectIntent, parseBootstrapExpiry, parseFileContextTransportRequest, parsePositiveMemoryId, parseReassignScopePayload, parseRecipientPolicyEdgeCommitRequest, parseStrictInteger, parseSyncScopeRequest, persistShareOperation, personalScopeGrantStatusForPeer, planShareOperation, previewDeviceIdentityBindings, previewLegacyTeamSetupActivation, previewRecipientPolicyEdges, previewRecipientPolicyOnboarding, previewRecipientPolicyOnboardingFromReviewedIntent, probeAvailableCredentials, projectMatchesFilter, projectShareLifecycle, promptPackArtifactFingerprint, readAccountLinkDevice, readAccountOwnerAssociation, readAccountOwnerAssociationRetainedAtMs, readCodememConfigFile, readCompletedLocalIdentitySwitches, readCoordinatorAccountChangeCapabilities, readCoordinatorAccountLinkStatus, readCoordinatorResolvedAccountOwnerReceiptStatus, readCoordinatorResolvedAccountOwnerStatus, readCoordinatorSyncConfig, readOpenLocalIdentitySwitch, reassignProjectScopeInventoryProject, recipientInviteAuthoritativeIdentityId, recipientPolicyDigest, recipientReviewedIntentDigest, reconcileCoordinatorEnrollmentSnapshot, reconcileRecipientPolicyProject, reconcileShareOperationAcceptance, reconstructLegacyTeamSetupCompletionManifest, recordAutomaticRecall, recordHighestObservedDirectSignatureVersion, recordLocalIdentitySwitch, recordNonce, recordPromptPackArtifacts, recordPromptPackTerminal, refreshAuthorizedCoordinatorPeerTrust, refreshConfiguredScopeMembershipCache, refreshLegacyTeamCandidate, rejectInboundScopeFailures, renameRecipientPolicyTeam, replayLegacyTeamSetupActivation, repositoryIdentitiesByWorkspace, repositoryIdentityForWorkspace, requestJson, requireLegacyTeamSetupAccessDeltaWithinLimit, resolveCodememConfigPath, resolveDbPath, resolveObserverDefaultModel, resolveObserverRuntime, resolveProject, resolveProjectRoot, resolveRecipientPolicyReview, resolveRecipientPolicyReviewBulk, resolveVisibleScopeIds, resumeDeviceRebind, runDeviceRebind, runSyncPass, schema, scopeAuthorizationFailureReason, searchPiSessions, serializeRecipientPolicyCoordinatorGroupMutation, serializeRecipientPolicyPublicationMutation, serializeRecipientPolicyTeamMutation, setLegacyTeamSetupDeviceAssignment, setLegacyTeamSetupDeviceDecision, setLegacyTeamSetupProjectMapping, summarizeInboundScopeRejections, summaryLikeSqlPredicate, supportsSyncFeature, syncScopeResetRequiredPayload, toJson, tryUpdateRetrievalDelivery, updatePeerAddresses, upsertCoordinatorGroupPreference, upsertProjectScopeSettingsMappings, validateLegacyTeamSetupCompletionManifest, validateLegacyTeamSetupCompletionManifestBinding, validateRawEvents, verifyDirectPeerSignature, verifyRecipientReviewedIntent, verifySignature, wakeRecipientPoliciesForIdentities, wakeRecipientPoliciesForProjectIdentities } from "@codemem/core";
3
+ import { ACCESS_CLEANUP_OP_TYPE, CodememConfigMutationError, CoordinatorAccountLinkError, CoordinatorReciprocalApprovalRequestChangedError, DEFAULT_TIME_WINDOW_S, FILE_CONTEXT_TRANSPORT_MAX_BYTES, LEGACY_SHARED_REVIEW_SCOPE_ID, LOCAL_DEFAULT_SCOPE_ID, LOCAL_SYNC_CAPABILITY, LOCAL_SYNC_FEATURES, LegacyTeamSetupAdditiveConvergenceError, LegacyTeamSetupRosterCapacityError, MEMORY_FILTER_FIELD_TYPES, MEMORY_FILTER_NAMES, MEMORY_KIND_DESCRIPTIONS, MemoryStore, ObserverClient, PROMPT_TRANSPORT_PROTOCOL_RANGE, REMEMBER_MEMORY_KINDS, RICH_TIER_ANTHROPIC_DEFAULTS, RICH_TIER_DEFAULTS, RawEventIngestValidationError, RecipientPolicyEdgeRequestError, RecipientPolicyTeamRenameError, RemoteCoordinatorRequestError, SCOPE_MEMBERSHIP_REVOCATION_LIMITATION, SIMPLE_TIER_ANTHROPIC_DEFAULTS, SIMPLE_TIER_DEFAULTS, SYNC_AUTHORIZATION_REFRESH_HEADER, SYNC_CAPABILITY_HEADER, SYNC_FEATURES_HEADER, SYNC_SCOPE_QUERY_PARAM, VERSION, VERSION as VERSION$1, VIEWER_IDENTITY_TARGET_KEYS, VIEWER_SERVICE_DISCRIMINATOR, addSyncScopeToBoundary, analyzeProjectScopeMappingChangeGuardrails, analyzeProjectScopeMappingChangesGuardrails, analyzeProjectScopeMappingDeletionGuardrails, applyLegacyTeamSetupCompletionManifest, applyLegacyTeamSetupCompletionManifestAndReturnActivation, applyLocalIdentitySwitch, applyReplicationOps, areLegacyTeamSetupCompletionPolicyFactsAdditivelyCompatible, assertNoLocalIdentityOverride, automaticRecallHealth, buildAuthHeaders, buildBaseUrl, buildDirectPeerAuthHeaders, buildDistillReport, buildFilterBranchesWithContext, buildFilterClausesWithContext, buildPiFlushSignalFromEvent, buildRawEventEnvelopeFromCodexHook, buildRawEventEnvelopeFromHook, buildRawEventEnvelopeFromPiEvent, buildViewerIdentityTarget, canonicalCoordinatorLegacyTeamCompletionManifestJson, canonicalMemoryKind, canonicalRepositoryProjectIdentity, canonicalWorkspaceIdentity, captureAccountOwnerProfilePins, changeCoordinatorAccount, claimRecipientPolicyActorMutations, claimRecipientPolicyPublicationMutation, classifyRecordedSyncFailure, cleanupNonces, clearLegacyTeamSetupDeviceDecision, clonePromptPackAttempt, coerceObserverCommand, commitDeviceIdentityBindings, commitRecipientPolicyEdges, completeLocalIdentitySwitch, containLegacyTeamSetupCompletionConflict, coordinatorArchiveGroupAction, coordinatorAuthControllerReviewAction, coordinatorCreateAddDeviceInviteAction, coordinatorCreateGroupAction, coordinatorCreateInviteAction, coordinatorCreateLegacyTeamCompletionAction, coordinatorCreateScopeAction, coordinatorDisableDeviceAction, coordinatorEnableDeviceAction, coordinatorGetLegacyTeamCompletionAction, coordinatorGrantScopeMembershipAction, coordinatorImportInviteAction, coordinatorListBootstrapGrantsAction, coordinatorListConsumedTeamInvitesAction, coordinatorListDevicesAction, coordinatorListGroupsAction, coordinatorListJoinRequestsAction, coordinatorListLegacyTeamCompletionsAction, coordinatorListReviewedRecipientInviteEvidenceAction, coordinatorListScopeMembershipsAction, coordinatorListScopesAction, coordinatorRemoveDeviceAction, coordinatorRenameDeviceAction, coordinatorRenameGroupAction, coordinatorReviewJoinRequestAction, coordinatorRevokeBootstrapGrantAction, coordinatorRevokeScopeMembershipAction, coordinatorStatusSnapshot, coordinatorUnarchiveGroupAction, coordinatorUpdateScopeAction, countShareableProjectMemories, createCoordinatorReciprocalApproval, decodeInvitePayload, dedupeOrderedIds, defaultSpaceScopeIdForGroup, deleteProjectScopeSettingsMapping, deriveLegacyTeamSetupCompletionManifest, detectInstallKind, deterministicPolicyTeamId, diagnoseStalePeerReceivedRows, discoverLegacyTeamCandidates, dispatchFileContextTransport, ensureDeviceIdentity, executeShareProvisioning, extractInvitePayload, extractReplicationOps, filterReplicationOpsForSyncWithStatus, fingerprintPublicKey, formatHostPort, friendlyDeviceName, fromJson, generateWithOpenCodeV2, getCodememConfigPath, getCodememEnvOverrideValues, getCodememEnvOverrides, getCoordinatorEnrollmentReconciliationIssueSummary, getCoordinatorGroupPreference, getEffectiveCachedScopeAuthorization, getLegacyTeamSetupDraft, getRecipientPolicyAuthorityState, getSemanticIndexDiagnostics, getSyncResetState, getUpdateStatus, ingestRawEvents, inspectFreshLegacyTeamSetupActivation, inspectLegacyTeamSetupActivation, inviteTokenDigest, isAutomaticRecallMeasurement, isLegacyTeamCandidateSelectable, isLegacyTeamSetupProjectMappingIdentity, isPeerTrustBindingCompatible, isProjectSyncEnablementError, isScopedSyncCapability, judgeDistillReport, legacyDecisionCountsAsOwner, legacyTeamCandidateId, legacyTeamCandidateProjectInventory, legacyTeamCanonicalProjectRef, legacyTeamDeviceRef, legacyTeamResolvedProjectRef, legacyTeamSetupApiErrorCode, legacyTeamSetupCompletionActorIds, legacyTeamSetupFailureCheck, linkCoordinatorAccount, listAuthorizedScopesForPeer, listCoordinatorJoinRequests, listDeviceIdentityInventory, listInboundScopeRejections, listLegacyRecipientPolicyProjections, listMaintenanceJobs, listObserverProviderOptions, listOpenCodeV2Models, listPerPeerScopeSyncState, listProjectScopeCandidates, listProjectScopeInventory, listProjectScopeSettingsMappings, listRecipientPolicyIntent, listRecipientPolicyReview, listSharingDomainSettingsScopes, loadMemorySnapshotPageForPeer, loadPublicKey, loadReplicationOpsForPeer, loadRuntimeSigningPublicKey, lookupCoordinatorPeers, markLocalIdentitySwitchBound, markLocalIdentitySwitchNotCommitted, memoryFilterValueMatchesType, mergeAddresses, migrateRecipientPolicyIntent, mutateCodememConfigFile, negotiateSyncCapability, normalizeAddress, normalizeCoordinatorAccountLinkOrigin, normalizeCoordinatorLegacyTeamCompletionManifest, normalizeHumanPresentationName, normalizeSyncCapability, normalizeSyncFeatures, normalizeTeammateName, parseAcceptedProjectIntent, parseBootstrapExpiry, parseFileContextTransportRequest, parsePositiveMemoryId, parseReassignScopePayload, parseRecipientPolicyEdgeCommitRequest, parseStrictInteger, parseSyncScopeRequest, persistShareOperation, personalScopeGrantStatusForPeer, planShareOperation, previewDeviceIdentityBindings, previewLegacyTeamSetupActivation, previewRecipientPolicyEdges, previewRecipientPolicyOnboarding, previewRecipientPolicyOnboardingFromReviewedIntent, probeAvailableCredentials, projectMatchesFilter, projectShareLifecycle, promptPackArtifactFingerprint, readAccountLinkDevice, readAccountOwnerAssociation, readAccountOwnerAssociationRetainedAtMs, readCodememConfigFile, readCompletedLocalIdentitySwitches, readCoordinatorAccountChangeCapabilities, readCoordinatorAccountLinkStatus, readCoordinatorResolvedAccountOwnerReceiptStatus, readCoordinatorResolvedAccountOwnerStatus, readCoordinatorSyncConfig, readOpenLocalIdentitySwitch, reassignProjectScopeInventoryProject, recipientInviteAuthoritativeIdentityId, recipientPolicyDigest, recipientReviewedIntentDigest, reconcileCoordinatorEnrollmentSnapshot, reconcileRecipientPolicyProject, reconcileShareOperationAcceptance, reconstructLegacyTeamSetupCompletionManifest, recordAutomaticRecall, recordHighestObservedDirectSignatureVersion, recordLocalIdentitySwitch, recordNonce, recordPromptPackArtifacts, recordPromptPackTerminal, refreshAuthorizedCoordinatorPeerTrust, refreshConfiguredScopeMembershipCache, refreshLegacyTeamCandidate, rejectInboundScopeFailures, renameRecipientPolicyTeam, replayLegacyTeamSetupActivation, repositoryIdentitiesByWorkspace, repositoryIdentityForWorkspace, requestJson, requireLegacyTeamSetupAccessDeltaWithinLimit, resolveCodememConfigPath, resolveDbPath, resolveObserverDefaultModel, resolveObserverRuntime, resolveProject, resolveProjectRoot, resolveRecipientPolicyReview, resolveRecipientPolicyReviewBulk, resolveVisibleScopeIds, resumeDeviceRebind, runDeviceRebind, runSyncPass, schema, scopeAuthorizationFailureReason, searchPiSessions, serializeRecipientPolicyActorMutations, serializeRecipientPolicyCoordinatorGroupMutation, serializeRecipientPolicyPublicationMutation, serializeRecipientPolicyTeamMutation, setLegacyTeamSetupDeviceAssignment, setLegacyTeamSetupDeviceDecision, setLegacyTeamSetupProjectMapping, summarizeInboundScopeRejections, summaryLikeSqlPredicate, supportsSyncFeature, syncScopeResetRequiredPayload, toJson, tryUpdateRetrievalDelivery, updatePeerAddresses, upsertCoordinatorGroupPreference, upsertProjectScopeSettingsMappings, validateLegacyTeamSetupCompletionManifest, validateLegacyTeamSetupCompletionManifestBinding, validateRawEvents, verifyDirectPeerSignature, verifyRecipientReviewedIntent, verifySignature, wakeRecipientPoliciesForIdentities, wakeRecipientPoliciesForProjectIdentities } from "@codemem/core";
4
4
  import { serveStatic } from "@hono/node-server/serve-static";
5
5
  import { Hono } from "hono";
6
6
  import { createCipheriv, createDecipheriv, createHash, randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
@@ -435,11 +435,13 @@ var InProcessAccountLinkRegistry = class {
435
435
  };
436
436
  const intent = job.operation.intent ?? "connect";
437
437
  if (intent === "sign_in") return await this.#runSignIn(job, options.onBrowserStart);
438
- if (intent === "connect") await (this.#options.link ?? linkCoordinatorAccount)({
439
- ...options,
440
- expectedAuthority
441
- });
442
- else {
438
+ if (intent === "connect") {
439
+ if (expectedAuthority === null) throw new CoordinatorAccountLinkError("invalid_options");
440
+ await (this.#options.link ?? linkCoordinatorAccount)({
441
+ ...options,
442
+ expectedAuthority
443
+ });
444
+ } else {
443
445
  const result = await (this.#options.change ?? changeCoordinatorAccount)({
444
446
  ...options,
445
447
  intent,
@@ -481,6 +483,7 @@ var InProcessAccountLinkRegistry = class {
481
483
  }
482
484
  start({ intent = "connect", context, expectedCoordinatorId, expectedAuthority, expectedLifecycleToken, expectedLinkId }) {
483
485
  if (this.#shuttingDown) return { error: "account_shutting_down" };
486
+ if (expectedAuthority !== "controller_attestation" && expectedAuthority !== "device_claim" && !(intent === "sign_in" && expectedAuthority === null)) return { error: "account_context_changed" };
484
487
  const pending = this.#inProgress();
485
488
  if (pending) return pending;
486
489
  if (expectedLifecycleToken && expectedLifecycleToken !== this.lifecycleToken()) return { error: "account_context_changed" };
@@ -791,11 +794,11 @@ function associatedOwnerView(view, context, association, remote) {
791
794
  //#region src/account/status.ts
792
795
  /**
793
796
  * The owner-only state offers sign-in when the owner's Google account is
794
- * linked and this device's own Identity has none: signing in switches the
795
- * device to the owner's Identity.
797
+ * linked and this device is unlinked or still needs enrollment review.
798
+ * Rebind verifies the Google account before switching the device's Identity.
796
799
  */
797
800
  function withOwnerSignIn(owner, own, context, remote) {
798
- if (owner.ownerProjection?.status.profileState !== "linked" || own.canSignIn !== true || own.remote.kind !== "status" || own.remote.state !== "unlinked") return owner;
801
+ if (owner.ownerProjection?.status.profileState !== "linked" || own.remote.kind !== "status" || own.remote.state !== "review_required" && !(own.canSignIn === true && own.remote.state === "unlinked")) return owner;
799
802
  return {
800
803
  ...owner,
801
804
  canSignIn: true,
@@ -1010,7 +1013,10 @@ async function finishAccountStatus(options, context, fresh, remote, read) {
1010
1013
  const associated = await readAssociatedOwner(options, context, currentView, remote, signal);
1011
1014
  if (associated) {
1012
1015
  const owner = withOwnerSignIn(associated, currentView, context, remote);
1013
- if (read.ownIdentityOnly && owner.signIn?.contextId === context.contextId) return attachOperation(currentView, options.registry);
1016
+ if (read.ownIdentityOnly && owner.signIn?.contextId === context.contextId) return attachOperation({
1017
+ ...currentView,
1018
+ canSignIn: true
1019
+ }, options.registry);
1014
1020
  return attachOperation(owner, options.registry, context.contextId);
1015
1021
  }
1016
1022
  }
@@ -1147,7 +1153,7 @@ function isCurrent(options, operation) {
1147
1153
  return !("error" in fresh) && fresh.setup === "ready" && fresh.context?.groupId === operation.groupId && fresh.context.contextId === operation.contextId;
1148
1154
  }
1149
1155
  function validStartBody(body) {
1150
- return body !== null && validString(body.groupId) && validString(body.contextId) && validString(body.coordinatorId) && (body.intent !== "disconnect" || validString(body.expectedLinkId)) && (body.intent === void 0 || body.intent === "connect" || body.intent === "refresh" || body.intent === "disconnect" || body.intent === "sign_in") && (body.authority === "controller_attestation" || body.authority === "device_claim");
1156
+ return body !== null && validString(body.groupId) && validString(body.contextId) && validString(body.coordinatorId) && (body.intent !== "disconnect" || validString(body.expectedLinkId)) && (body.intent === void 0 || body.intent === "connect" || body.intent === "refresh" || body.intent === "disconnect" || body.intent === "sign_in") && (body.authority === "controller_attestation" || body.authority === "device_claim" || body.intent === "sign_in" && body.authority === null);
1151
1157
  }
1152
1158
  function unlinkTargetChanged(body, view) {
1153
1159
  return body.intent === "disconnect" && body.expectedLinkId !== view.unlinkTarget;
@@ -6354,6 +6360,23 @@ var MAX_PROJECT_MAPPING_METADATA_ROWS = 1e4;
6354
6360
  var MAX_MUTATION_BODY_BYTES = 8192;
6355
6361
  var SUMMARY_SNAPSHOT_CACHE_TTL_MS = 3e4;
6356
6362
  var COORDINATOR_ROSTER_READ_BUDGET_MS = 6e4;
6363
+ function serializeCanonicalManifestMutation(store, groupId, manifest, operation) {
6364
+ return serializeRecipientPolicyPublicationMutation(store.db, () => {
6365
+ const actorIds = legacyTeamSetupCompletionActorIds(store.db, manifest);
6366
+ return serializeRecipientPolicyActorMutations(store.db, actorIds, () => serializeRecipientPolicyTeamMutation(store.db, manifest.team_id, () => serializeRecipientPolicyCoordinatorGroupMutation(store.db, groupId, () => operation(actorIds))));
6367
+ });
6368
+ }
6369
+ function finishDraftActorIds(draft) {
6370
+ return draft.devices.flatMap((device) => [...device.decision === "included" && device.targetIdentityId ? [device.targetIdentityId] : [], ...device.existingIdentityId ? [device.existingIdentityId] : []]);
6371
+ }
6372
+ function completionRecipientPolicyLocksHeld(actorIds) {
6373
+ return {
6374
+ publication: true,
6375
+ actors: actorIds,
6376
+ team: true,
6377
+ coordinatorGroup: true
6378
+ };
6379
+ }
6357
6380
  var TEAM_SETUP_ROUTE_PREFIX = "/api/sync/team-setup/v1";
6358
6381
  var CANDIDATE_REF_PATTERN = /^legacy-team-candidate:[0-9a-f]{32}$/u;
6359
6382
  var DEVICE_REF_PATTERN = /^legacy-team-device-ref-v1:[0-9a-f]{64}$/u;
@@ -6399,6 +6422,47 @@ async function runCandidateMutation(db, candidateRef, operation) {
6399
6422
  release();
6400
6423
  }
6401
6424
  }
6425
+ function completionFailureHandlers(options) {
6426
+ const handledGroupIds = /* @__PURE__ */ new Set();
6427
+ const reportFailure = (group, error) => {
6428
+ handledGroupIds.add(group.groupId);
6429
+ options.onFailure?.(group, error);
6430
+ };
6431
+ const handleFailure = (group, error) => {
6432
+ if (!options.isolateApplyFailures) throw error;
6433
+ reportFailure(group, error);
6434
+ };
6435
+ return {
6436
+ handledGroupIds,
6437
+ handleFailure,
6438
+ reportFailure
6439
+ };
6440
+ }
6441
+ function completionConflictError(cause) {
6442
+ return new Error("team_setup_completion_conflict", { cause });
6443
+ }
6444
+ function addFreshCompletionGroups(index, groups, keyOf) {
6445
+ for (const group of groups) {
6446
+ if (!group) continue;
6447
+ const key = keyOf(group);
6448
+ if (index.has(key)) throw new Error("team_setup_completion_invalid");
6449
+ index.set(key, group);
6450
+ }
6451
+ }
6452
+ function groupCompletionFailureHandler(groups, handleFailure) {
6453
+ return (groupId, error) => {
6454
+ const group = groups.get(groupId);
6455
+ if (group) handleFailure(group, error);
6456
+ };
6457
+ }
6458
+ function summaryReconciliationOptions(blockingErrors) {
6459
+ return {
6460
+ isolateApplyFailures: true,
6461
+ onFailure: (group, error) => {
6462
+ blockingErrors.set(legacyTeamCandidateId(group.coordinatorId, group.groupId), apiErrorCode(new Error(completionApiError(error))));
6463
+ }
6464
+ };
6465
+ }
6402
6466
  var defaultCompletionDependencies = {
6403
6467
  create: coordinatorCreateLegacyTeamCompletionAction,
6404
6468
  get: coordinatorGetLegacyTeamCompletionAction,
@@ -6490,13 +6554,17 @@ function isUnsupportedCompletionQuery(error) {
6490
6554
  function isUnattributableCompletionBatchError(error) {
6491
6555
  return error instanceof Error && (error.message === "coordinator_completion_response_malformed" || error.message === "coordinator_completion_group_mismatch");
6492
6556
  }
6493
- async function listCompletionRecordsPerGroup(groupIds, list) {
6494
- if (groupIds.length < 2) return [];
6557
+ async function listCompletionRecordsPerGroup(groupIds, list, onFailure, batchError) {
6558
+ if (groupIds.length === 1) {
6559
+ onFailure(groupIds[0], batchError);
6560
+ return [];
6561
+ }
6495
6562
  const records = [];
6496
6563
  for (const groupId of groupIds) try {
6497
6564
  records.push(...await list([groupId]));
6498
6565
  } catch (error) {
6499
6566
  if (!isUnattributableCompletionBatchError(error)) throw error;
6567
+ onFailure(groupId, error);
6500
6568
  }
6501
6569
  return records;
6502
6570
  }
@@ -6730,6 +6798,55 @@ function candidateFromDraft(draft) {
6730
6798
  unresolvedProjectCount: draft.unresolvedProjectCount
6731
6799
  };
6732
6800
  }
6801
+ function blockedCandidateSummary(store, group, blockingError) {
6802
+ const candidateRef = legacyTeamCandidateId(group.coordinatorId, group.groupId);
6803
+ const draft = getLegacyTeamSetupDraft(store.db, candidateRef);
6804
+ if (draft) return {
6805
+ ...candidateSummary(candidateFromDraft(draft)),
6806
+ status: "stale",
6807
+ blockingError
6808
+ };
6809
+ const forbiddenIds = [
6810
+ group.coordinatorId,
6811
+ group.groupId,
6812
+ candidateRef,
6813
+ ...group.devices.flatMap((device) => [
6814
+ device.deviceId,
6815
+ device.fingerprint,
6816
+ ...device.labelRedactionIds ?? []
6817
+ ])
6818
+ ];
6819
+ return {
6820
+ candidateRef,
6821
+ displayName: safeChoiceLabel(group.displayName, "Legacy Team", forbiddenIds),
6822
+ status: "stale",
6823
+ blockingError,
6824
+ deviceCount: group.devices.length,
6825
+ projectCount: 0,
6826
+ unresolvedDeviceCount: 0,
6827
+ unresolvedProjectCount: 0
6828
+ };
6829
+ }
6830
+ function pendingCandidateSummaries(store, groups, blockingErrors) {
6831
+ const pending = readPureCandidateViews(store, groups.filter((group) => !blockingErrors.has(legacyTeamCandidateId(group.coordinatorId, group.groupId)))).map(candidateSummary).filter((candidate) => candidate.status !== "ready");
6832
+ const blocked = groups.flatMap((group) => {
6833
+ const error = blockingErrors.get(legacyTeamCandidateId(group.coordinatorId, group.groupId));
6834
+ return error ? [blockedCandidateSummary(store, group, error)] : [];
6835
+ });
6836
+ return [...pending, ...blocked].toSorted((left, right) => {
6837
+ if (left.candidateRef < right.candidateRef) return -1;
6838
+ return left.candidateRef > right.candidateRef ? 1 : 0;
6839
+ });
6840
+ }
6841
+ async function reconcileSummaryCandidates(store, groups, reconcile) {
6842
+ readPureCandidateViews(store, groups);
6843
+ const blockingErrors = /* @__PURE__ */ new Map();
6844
+ await reconcile(store, groups, summaryReconciliationOptions(blockingErrors));
6845
+ return {
6846
+ version: TEAM_SETUP_VERSION,
6847
+ candidates: pendingCandidateSummaries(store, groups, blockingErrors)
6848
+ };
6849
+ }
6733
6850
  function readPureCandidateViews(store, groups) {
6734
6851
  const existing = [];
6735
6852
  const discoverable = [];
@@ -7094,6 +7211,20 @@ function apiErrorCode(error) {
7094
7211
  const code = legacyTeamSetupApiErrorCode(error);
7095
7212
  return code === "team_setup_projection_changed" ? "team_setup_conflict" : code;
7096
7213
  }
7214
+ /**
7215
+ * Maps a setup failure to its API code and logs the internal check that raised
7216
+ * it. Dozens of checks share each public code, so the log is the only place an
7217
+ * operator can see which one failed.
7218
+ */
7219
+ function teamSetupFailure(error, route) {
7220
+ const code = apiErrorCode(error);
7221
+ const check = legacyTeamSetupFailureCheck(error);
7222
+ console.warn(`[team-setup] route=${route} apiCode=${code}${check ? ` check=${check}` : ""}`);
7223
+ return {
7224
+ code,
7225
+ status: errorStatus$1(code)
7226
+ };
7227
+ }
7097
7228
  function completionApiError(error) {
7098
7229
  const code = legacyTeamSetupApiErrorCode(error);
7099
7230
  return code === "team_setup_failed" ? "team_setup_completion_unavailable" : code;
@@ -7432,29 +7563,26 @@ function teamSetupRoutes(options) {
7432
7563
  */
7433
7564
  async function applyCanonicalManifestIfStillAuthorized(store, authorization, group, manifest, freshRoster) {
7434
7565
  let outcome = "unauthorized";
7435
- return await runCandidateMutation(store.db, legacyTeamCandidateId(group.coordinatorId, group.groupId), () => serializeRecipientPolicyPublicationMutation(store.db, () => serializeRecipientPolicyTeamMutation(store.db, manifest.team_id, () => serializeRecipientPolicyCoordinatorGroupMutation(store.db, group.groupId, async () => {
7566
+ return await runCandidateMutation(store.db, legacyTeamCandidateId(group.coordinatorId, group.groupId), () => serializeCanonicalManifestMutation(store, group.groupId, manifest, async (actorIds) => {
7436
7567
  if (!stillAuthorizesGroup(authorization, group)) return;
7437
7568
  await applyLegacyTeamSetupCompletionManifest(store.db, {
7438
7569
  coordinatorId: group.coordinatorId,
7439
7570
  groupId: group.groupId,
7440
7571
  manifest,
7441
7572
  freshRoster,
7442
- recipientPolicyLocksHeld: {
7443
- publication: true,
7444
- team: true,
7445
- coordinatorGroup: true
7446
- }
7573
+ recipientPolicyLocksHeld: completionRecipientPolicyLocksHeld(actorIds)
7447
7574
  });
7448
7575
  outcome = "applied";
7449
- })))) ? outcome : "claimed";
7576
+ })) ? outcome : "claimed";
7450
7577
  }
7451
7578
  async function reconcileCompletions(store, groups, reconciliationOptions = {}) {
7579
+ const { handledGroupIds, handleFailure, reportFailure } = completionFailureHandlers(reconciliationOptions);
7452
7580
  if (!completionDependencies || groups.length === 0) return;
7453
7581
  let authorization;
7454
7582
  try {
7455
7583
  authorization = currentCompletionAuthorization();
7456
7584
  } catch (error) {
7457
- throw new Error(completionApiError(error));
7585
+ throw new Error(completionApiError(error), { cause: error });
7458
7586
  }
7459
7587
  if (!authorization) return;
7460
7588
  const { config, remoteUrl } = authorization;
@@ -7476,30 +7604,25 @@ function teamSetupRoutes(options) {
7476
7604
  records = await listCompletionRecords(scopedGroupIds);
7477
7605
  } catch (error) {
7478
7606
  if (isUnsupportedCompletionQuery(error)) records = [];
7479
- else if (reconciliationOptions.isolateApplyFailures && isUnattributableCompletionBatchError(error)) records = await listCompletionRecordsPerGroup(scopedGroupIds, listCompletionRecords);
7607
+ else if (reconciliationOptions.isolateApplyFailures && isUnattributableCompletionBatchError(error)) records = await listCompletionRecordsPerGroup(scopedGroupIds, listCompletionRecords, groupCompletionFailureHandler(groupById, handleFailure), error);
7480
7608
  else throw error;
7481
7609
  }
7482
7610
  const freshGroupByKey = /* @__PURE__ */ new Map();
7483
7611
  if (records.length > 0) try {
7484
7612
  const onlyGroup = scopedGroups.length === 1 ? scopedGroups[0] : void 0;
7485
- const freshGroups = onlyGroup ? [await loadFreshGroupSnapshot(onlyGroup, remoteUrl, reconciliationDeadlineMs)] : await loadedSnapshots(void 0, reconciliationDeadlineMs);
7486
- for (const freshGroup of freshGroups) {
7487
- if (!freshGroup) continue;
7488
- const key = freshGroupKey(freshGroup);
7489
- if (freshGroupByKey.has(key)) throw new Error("team_setup_completion_invalid");
7490
- freshGroupByKey.set(key, freshGroup);
7491
- }
7613
+ addFreshCompletionGroups(freshGroupByKey, onlyGroup ? [await loadFreshGroupSnapshot(onlyGroup, remoteUrl, reconciliationDeadlineMs)] : await loadedSnapshots(void 0, reconciliationDeadlineMs), freshGroupKey);
7492
7614
  } catch (error) {
7493
7615
  for (const record of records) {
7494
7616
  const group = groupById.get(record.group_id);
7495
7617
  if (!group || !belongsToGroupCandidate(record, group)) continue;
7618
+ reportFailure(group, error);
7496
7619
  try {
7497
7620
  validateLegacyTeamSetupCompletionManifestBinding(record.manifest, {
7498
7621
  coordinatorId: group.coordinatorId,
7499
7622
  groupId: group.groupId
7500
7623
  });
7501
7624
  } catch (bindingError) {
7502
- if (!reconciliationOptions.isolateApplyFailures) throw bindingError;
7625
+ handleFailure(group, bindingError);
7503
7626
  continue;
7504
7627
  }
7505
7628
  let canonicalManifest;
@@ -7509,16 +7632,17 @@ function teamSetupRoutes(options) {
7509
7632
  groupId: group.groupId
7510
7633
  });
7511
7634
  } catch (validationError) {
7635
+ reportFailure(group, validationError);
7512
7636
  try {
7513
7637
  if (!await runCandidateMutation(store.db, legacyTeamCandidateId(group.coordinatorId, group.groupId), () => containLegacyTeamSetupCompletionConflict(store.db, {
7514
7638
  coordinatorId: group.coordinatorId,
7515
7639
  groupId: group.groupId
7516
7640
  }))) {
7517
- if (!reconciliationOptions.isolateApplyFailures) throw validationError;
7641
+ handleFailure(group, validationError);
7518
7642
  continue;
7519
7643
  }
7520
7644
  } catch (containmentError) {
7521
- if (!reconciliationOptions.isolateApplyFailures) throw containmentError;
7645
+ handleFailure(group, containmentError);
7522
7646
  }
7523
7647
  if (!reconciliationOptions.isolateApplyFailures) throw validationError;
7524
7648
  continue;
@@ -7534,13 +7658,12 @@ function teamSetupRoutes(options) {
7534
7658
  groupId: group.groupId
7535
7659
  }));
7536
7660
  } catch (containmentError) {
7537
- if (!reconciliationOptions.isolateApplyFailures) throw containmentError;
7661
+ handleFailure(group, containmentError);
7538
7662
  }
7539
7663
  }
7540
7664
  if (!reconciliationOptions.isolateApplyFailures) throw error;
7541
7665
  return;
7542
7666
  }
7543
- const coordinatorCompletionGroupIds = /* @__PURE__ */ new Set();
7544
7667
  for (const record of records) {
7545
7668
  const group = groupById.get(record.group_id);
7546
7669
  if (!group) throw new Error("team_setup_completion_invalid");
@@ -7551,7 +7674,7 @@ function teamSetupRoutes(options) {
7551
7674
  groupId: group.groupId
7552
7675
  });
7553
7676
  } catch (bindingError) {
7554
- if (!reconciliationOptions.isolateApplyFailures) throw bindingError;
7677
+ handleFailure(group, bindingError);
7555
7678
  continue;
7556
7679
  }
7557
7680
  let canonicalManifest;
@@ -7561,18 +7684,19 @@ function teamSetupRoutes(options) {
7561
7684
  groupId: group.groupId
7562
7685
  });
7563
7686
  } catch (validationError) {
7687
+ reportFailure(group, validationError);
7564
7688
  try {
7565
7689
  await runCandidateMutation(store.db, legacyTeamCandidateId(group.coordinatorId, group.groupId), () => containLegacyTeamSetupCompletionConflict(store.db, {
7566
7690
  coordinatorId: group.coordinatorId,
7567
7691
  groupId: group.groupId
7568
7692
  }));
7569
7693
  } catch (containmentError) {
7570
- if (!reconciliationOptions.isolateApplyFailures) throw containmentError;
7694
+ handleFailure(group, containmentError);
7571
7695
  }
7572
7696
  if (!reconciliationOptions.isolateApplyFailures) throw validationError;
7573
7697
  continue;
7574
7698
  }
7575
- coordinatorCompletionGroupIds.add(record.group_id);
7699
+ handledGroupIds.add(record.group_id);
7576
7700
  try {
7577
7701
  let freshGroup = freshGroupByKey.get(freshGroupKey(group));
7578
7702
  if (!freshGroup && scopedGroups.length > 1) {
@@ -7584,18 +7708,18 @@ function teamSetupRoutes(options) {
7584
7708
  coordinatorId: group.coordinatorId,
7585
7709
  groupId: group.groupId
7586
7710
  }))) {
7587
- if (!reconciliationOptions.isolateApplyFailures) throw new Error("team_setup_completion_invalid");
7711
+ handleFailure(group, /* @__PURE__ */ new Error("team_setup_completion_invalid"));
7588
7712
  continue;
7589
7713
  }
7590
7714
  throw new Error("team_setup_completion_invalid");
7591
7715
  }
7592
7716
  if (await applyCanonicalManifestIfStillAuthorized(store, authorization, group, canonicalManifest, freshGroup.devices) !== "applied") continue;
7593
7717
  } catch (error) {
7594
- if (!reconciliationOptions.isolateApplyFailures) throw error;
7718
+ handleFailure(group, error);
7595
7719
  }
7596
7720
  }
7597
7721
  for (const group of scopedGroups) {
7598
- if (coordinatorCompletionGroupIds.has(group.groupId)) continue;
7722
+ if (handledGroupIds.has(group.groupId)) continue;
7599
7723
  const publicationTimeoutS = remainingCoordinatorTimeoutS(timeoutS, reconciliationDeadlineMs);
7600
7724
  if (publicationTimeoutS == null) break;
7601
7725
  if (!hasCompletedLegacyTeamSetup(store, legacyTeamCandidateId(group.coordinatorId, group.groupId))) continue;
@@ -7642,14 +7766,14 @@ function teamSetupRoutes(options) {
7642
7766
  coordinatorId: group.coordinatorId,
7643
7767
  groupId: group.groupId
7644
7768
  }))) {
7645
- if (!reconciliationOptions.isolateApplyFailures) throw new Error("team_setup_completion_conflict");
7769
+ handleFailure(group, /* @__PURE__ */ new Error("team_setup_completion_conflict"));
7646
7770
  continue;
7647
7771
  }
7648
7772
  } catch (containmentError) {
7649
- if (!reconciliationOptions.isolateApplyFailures) throw containmentError;
7773
+ handleFailure(group, containmentError);
7650
7774
  continue;
7651
7775
  }
7652
- if (!reconciliationOptions.isolateApplyFailures) throw new Error("team_setup_completion_conflict");
7776
+ handleFailure(group, /* @__PURE__ */ new Error("team_setup_completion_conflict"));
7653
7777
  continue;
7654
7778
  }
7655
7779
  try {
@@ -7658,7 +7782,7 @@ function teamSetupRoutes(options) {
7658
7782
  if (await applyCanonicalManifestIfStillAuthorized(store, authorization, group, canonicalManifest, freshGroup.devices) !== "applied") continue;
7659
7783
  } catch (applyError) {
7660
7784
  if (applyError instanceof LegacyTeamSetupAdditiveConvergenceError) {
7661
- if (!reconciliationOptions.isolateApplyFailures) throw applyError;
7785
+ handleFailure(group, applyError);
7662
7786
  continue;
7663
7787
  }
7664
7788
  try {
@@ -7666,19 +7790,19 @@ function teamSetupRoutes(options) {
7666
7790
  coordinatorId: group.coordinatorId,
7667
7791
  groupId: group.groupId
7668
7792
  }))) {
7669
- if (!reconciliationOptions.isolateApplyFailures) throw new Error("team_setup_completion_conflict", { cause: applyError });
7793
+ handleFailure(group, completionConflictError(applyError));
7670
7794
  continue;
7671
7795
  }
7672
7796
  } catch (containmentError) {
7673
- if (!reconciliationOptions.isolateApplyFailures) throw containmentError;
7797
+ handleFailure(group, containmentError);
7674
7798
  continue;
7675
7799
  }
7676
- if (!reconciliationOptions.isolateApplyFailures) throw new Error("team_setup_completion_conflict", { cause: applyError });
7800
+ handleFailure(group, completionConflictError(applyError));
7677
7801
  }
7678
7802
  }
7679
7803
  }
7680
7804
  } catch (error) {
7681
- throw new Error(completionApiError(error));
7805
+ throw new Error(completionApiError(error), { cause: error });
7682
7806
  }
7683
7807
  }
7684
7808
  async function loadedCandidateSnapshots(candidateRef, loadOptions = {}) {
@@ -7712,21 +7836,15 @@ function teamSetupRoutes(options) {
7712
7836
  try {
7713
7837
  groups = await loadedSummarySnapshots();
7714
7838
  } catch (error) {
7715
- const code = apiErrorCode(error);
7716
- return c.json({ error: code }, errorStatus$1(code));
7839
+ const failure = teamSetupFailure(error, `${c.req.method} ${c.req.routePath}`);
7840
+ return c.json({ error: failure.code }, failure.status);
7717
7841
  }
7718
7842
  try {
7719
- const store = options.getStore();
7720
- readPureCandidateViews(store, groups);
7721
- await reconcileCompletions(store, groups, { isolateApplyFailures: true });
7722
- const response = {
7723
- version: TEAM_SETUP_VERSION,
7724
- candidates: readPureCandidateViews(store, groups).map(candidateSummary).filter((candidate) => candidate.status !== "ready")
7725
- };
7843
+ const response = await reconcileSummaryCandidates(options.getStore(), groups, reconcileCompletions);
7726
7844
  return c.json(response);
7727
7845
  } catch (error) {
7728
- const code = apiErrorCode(error);
7729
- return c.json({ error: code }, errorStatus$1(code));
7846
+ const failure = teamSetupFailure(error, `${c.req.method} ${c.req.routePath}`);
7847
+ return c.json({ error: failure.code }, failure.status);
7730
7848
  }
7731
7849
  });
7732
7850
  app.get("/api/sync/team-setup/v1/:candidateRef", async (c) => {
@@ -7772,8 +7890,8 @@ function teamSetupRoutes(options) {
7772
7890
  if (completionGroups) candidate = candidateFromDraft(draft);
7773
7891
  return c.json(detailResponse(store, draft, candidate));
7774
7892
  } catch (error) {
7775
- const code = apiErrorCode(error);
7776
- return c.json({ error: code }, errorStatus$1(code));
7893
+ const failure = teamSetupFailure(error, `${c.req.method} ${c.req.routePath}`);
7894
+ return c.json({ error: failure.code }, failure.status);
7777
7895
  }
7778
7896
  });
7779
7897
  app.put("/api/sync/team-setup/v1/:candidateRef/devices/:deviceRef/assignment", async (c) => {
@@ -7818,8 +7936,8 @@ function teamSetupRoutes(options) {
7818
7936
  releaseMutation();
7819
7937
  }
7820
7938
  } catch (error) {
7821
- const code = apiErrorCode(error);
7822
- return c.json({ error: code }, errorStatus$1(code));
7939
+ const failure = teamSetupFailure(error, `${c.req.method} ${c.req.routePath}`);
7940
+ return c.json({ error: failure.code }, failure.status);
7823
7941
  }
7824
7942
  });
7825
7943
  app.put("/api/sync/team-setup/v1/:candidateRef/devices/:deviceRef/decision", async (c) => {
@@ -7860,8 +7978,8 @@ function teamSetupRoutes(options) {
7860
7978
  releaseMutation();
7861
7979
  }
7862
7980
  } catch (error) {
7863
- const code = apiErrorCode(error);
7864
- return c.json({ error: code }, errorStatus$1(code));
7981
+ const failure = teamSetupFailure(error, `${c.req.method} ${c.req.routePath}`);
7982
+ return c.json({ error: failure.code }, failure.status);
7865
7983
  }
7866
7984
  });
7867
7985
  app.delete("/api/sync/team-setup/v1/:candidateRef/devices/:deviceRef/decision", async (c) => {
@@ -7891,8 +8009,8 @@ function teamSetupRoutes(options) {
7891
8009
  releaseMutation();
7892
8010
  }
7893
8011
  } catch (error) {
7894
- const code = apiErrorCode(error);
7895
- return c.json({ error: code }, errorStatus$1(code));
8012
+ const failure = teamSetupFailure(error, `${c.req.method} ${c.req.routePath}`);
8013
+ return c.json({ error: failure.code }, failure.status);
7896
8014
  }
7897
8015
  });
7898
8016
  app.put("/api/sync/team-setup/v1/:candidateRef/projects/:projectRef/mapping", async (c) => {
@@ -7927,8 +8045,8 @@ function teamSetupRoutes(options) {
7927
8045
  releaseMutation();
7928
8046
  }
7929
8047
  } catch (error) {
7930
- const code = apiErrorCode(error);
7931
- return c.json({ error: code }, errorStatus$1(code));
8048
+ const failure = teamSetupFailure(error, `${c.req.method} ${c.req.routePath}`);
8049
+ return c.json({ error: failure.code }, failure.status);
7932
8050
  }
7933
8051
  });
7934
8052
  app.post("/api/sync/team-setup/v1/:candidateRef/refresh", async (c) => {
@@ -7941,15 +8059,15 @@ function teamSetupRoutes(options) {
7941
8059
  store = options.getStore();
7942
8060
  if (getLegacyTeamSetupDraft(store.db, candidateRef)?.state === "completed" || hasCompletedLegacyTeamSetup(store, candidateRef)) return c.json({ error: "team_setup_confirmation_stale" }, 409);
7943
8061
  } catch (error) {
7944
- const code = apiErrorCode(error);
7945
- return c.json({ error: code }, errorStatus$1(code));
8062
+ const failure = teamSetupFailure(error, `${c.req.method} ${c.req.routePath}`);
8063
+ return c.json({ error: failure.code }, failure.status);
7946
8064
  }
7947
8065
  let groups;
7948
8066
  try {
7949
8067
  groups = await loadMutableCandidateSnapshots(store, candidateRef, () => loadedCandidateSnapshots(candidateRef));
7950
8068
  } catch (error) {
7951
- const code = apiErrorCode(error);
7952
- return c.json({ error: code }, errorStatus$1(code));
8069
+ const failure = teamSetupFailure(error, `${c.req.method} ${c.req.routePath}`);
8070
+ return c.json({ error: failure.code }, failure.status);
7953
8071
  }
7954
8072
  if (groups.filter((group) => legacyTeamCandidateId(group.coordinatorId, group.groupId) === candidateRef).length === 0) return c.json({ error: "team_setup_confirmation_stale" }, 404);
7955
8073
  try {
@@ -7967,8 +8085,8 @@ function teamSetupRoutes(options) {
7967
8085
  releaseMutation();
7968
8086
  }
7969
8087
  } catch (error) {
7970
- const code = apiErrorCode(error);
7971
- return c.json({ error: code }, errorStatus$1(code));
8088
+ const failure = teamSetupFailure(error, `${c.req.method} ${c.req.routePath}`);
8089
+ return c.json({ error: failure.code }, failure.status);
7972
8090
  }
7973
8091
  });
7974
8092
  app.post("/api/sync/team-setup/v1/:candidateRef/finish", async (c) => {
@@ -8040,13 +8158,14 @@ function teamSetupRoutes(options) {
8040
8158
  if (!claimedDraft || claimedDraft.attemptId !== attemptId) return c.json({ error: "team_setup_confirmation_stale" }, 409);
8041
8159
  const publicationDeadlineMs = Date.now() + COORDINATOR_ROSTER_READ_BUDGET_MS;
8042
8160
  releasePublicationMutation = await claimRecipientPolicyPublicationMutation(store.db);
8043
- releaseActorMutations = await claimRecipientPolicyActorMutations(store.db, claimedDraft.devices.flatMap((device) => [...device.decision === "included" && device.targetIdentityId ? [device.targetIdentityId] : [], ...device.existingIdentityId ? [device.existingIdentityId] : []]));
8161
+ const claimedActorIds = finishDraftActorIds(claimedDraft);
8162
+ releaseActorMutations = await claimRecipientPolicyActorMutations(store.db, claimedActorIds);
8044
8163
  return await serializeRecipientPolicyTeamMutation(store.db, deterministicPolicyTeamId(candidateRef), () => serializeRecipientPolicyCoordinatorGroupMutation(store.db, group.groupId, async () => {
8045
8164
  let authorization;
8046
8165
  try {
8047
8166
  authorization = currentCompletionAuthorization();
8048
8167
  } catch (error) {
8049
- throw new Error(completionApiError(error));
8168
+ throw new Error(completionApiError(error), { cause: error });
8050
8169
  }
8051
8170
  if (!completionDependencies || !authorization) throw new Error("team_setup_completion_unavailable");
8052
8171
  if (!authorizesGroup(authorization, group)) throw new Error("team_setup_completion_invalid");
@@ -8137,11 +8256,7 @@ function teamSetupRoutes(options) {
8137
8256
  freshRoster: postPublicationGroup.devices,
8138
8257
  manifest: canonicalManifest,
8139
8258
  expectedDraftManifest: proposedManifest,
8140
- recipientPolicyLocksHeld: {
8141
- publication: true,
8142
- team: true,
8143
- coordinatorGroup: true
8144
- }
8259
+ recipientPolicyLocksHeld: completionRecipientPolicyLocksHeld(claimedActorIds)
8145
8260
  });
8146
8261
  return c.json(finishResponse(candidateRef, result));
8147
8262
  }));