@codemem/server 0.44.0-alpha.1 → 0.44.0-beta.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/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { existsSync, readFileSync } from "node:fs";
2
2
  import { dirname, isAbsolute, join, posix, resolve, win32 } from "node:path";
3
- import { ACCESS_CLEANUP_OP_TYPE, CODEMEM_CONFIG_ENV_OVERRIDES, CoordinatorReciprocalApprovalRequestChangedError, DEFAULT_TIME_WINDOW_S, LEGACY_SHARED_REVIEW_SCOPE_ID, LOCAL_DEFAULT_SCOPE_ID, LOCAL_SYNC_CAPABILITY, LOCAL_SYNC_FEATURES, LegacyTeamSetupAdditiveConvergenceError, MemoryStore, PROMPT_TRANSPORT_PROTOCOL_RANGE, RawEventIngestValidationError, RecipientPolicyEdgeRequestError, RecipientPolicyTeamRenameError, SCOPE_MEMBERSHIP_REVOCATION_LIMITATION, 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, applyLegacyTeamSetupCompletionManifest, applyLegacyTeamSetupCompletionManifestAndReturnActivation, applyReplicationOps, areLegacyTeamSetupCompletionPolicyFactsAdditivelyCompatible, buildAuthHeaders, buildBaseUrl, buildDirectPeerAuthHeaders, buildFilterClausesWithContext, buildRawEventEnvelopeFromCodexHook, buildRawEventEnvelopeFromHook, buildViewerIdentityTarget, canonicalCoordinatorLegacyTeamCompletionManifestJson, canonicalMemoryKind, canonicalWorkspaceIdentity, claimRecipientPolicyActorMutations, claimRecipientPolicyPublicationMutation, cleanupNonces, clearLegacyTeamSetupDeviceDecision, clonePromptPackAttempt, coerceObserverCommand, commitDeviceIdentityBindings, commitRecipientPolicyEdges, containLegacyTeamSetupCompletionConflict, coordinatorArchiveGroupAction, 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, defaultSpaceScopeIdForGroup, deleteProjectScopeSettingsMapping, deriveLegacyTeamSetupCompletionManifest, detectInstallKind, deterministicPolicyTeamId, diagnoseStalePeerReceivedRows, discoverLegacyTeamCandidates, ensureDeviceIdentity, executeShareProvisioning, extractInvitePayload, extractReplicationOps, filterReplicationOpsForSyncWithStatus, fingerprintPublicKey, formatHostPort, friendlyDeviceName, fromJson, getCodememConfigPath, getCodememEnvOverrides, getCoordinatorEnrollmentReconciliationIssueSummary, getCoordinatorGroupPreference, getLegacyTeamSetupDraft, getRecipientPolicyAuthorityState, getSemanticIndexDiagnostics, getSyncResetState, getUpdateStatus, ingestRawEvents, inspectFreshLegacyTeamSetupActivation, inspectLegacyTeamSetupActivation, inviteTokenDigest, isLegacyTeamCandidateSelectable, isLegacyTeamSetupProjectMappingIdentity, isPeerTrustBindingCompatible, isProjectSyncEnablementError, isScopedSyncCapability, legacyTeamCandidateId, legacyTeamCandidateProjectInventory, legacyTeamCanonicalProjectRef, legacyTeamDeviceRef, legacyTeamResolvedProjectRef, legacyTeamSetupApiErrorCode, listAuthorizedScopesForPeer, listCoordinatorJoinRequests, listDeviceIdentityInventory, listInboundScopeRejections, listLegacyRecipientPolicyProjections, listMaintenanceJobs, listObserverProviderOptions, listPerPeerScopeSyncState, listProjectScopeCandidates, listProjectScopeInventory, listProjectScopeSettingsMappings, listRecipientPolicyIntent, listRecipientPolicyReview, listSharingDomainSettingsScopes, loadMemorySnapshotPageForPeer, loadReplicationOpsForPeer, lookupCoordinatorPeers, mergeAddresses, migrateRecipientPolicyIntent, negotiateSyncCapability, normalizeAddress, normalizeHumanPresentationName, normalizeSyncCapability, normalizeSyncFeatures, normalizeTeammateName, parseAcceptedProjectIntent, parsePositiveMemoryId, parseReassignScopePayload, parseRecipientPolicyEdgeCommitRequest, parseStrictInteger, parseSyncScopeRequest, persistShareOperation, personalScopeGrantStatusForPeer, planShareOperation, previewDeviceIdentityBindings, previewLegacyTeamSetupActivation, previewRecipientPolicyEdges, previewRecipientPolicyOnboarding, previewRecipientPolicyOnboardingFromReviewedIntent, probeAvailableCredentials, projectShareLifecycle, promptPackArtifactFingerprint, readCodememConfigFile, readCoordinatorSyncConfig, reassignProjectScopeInventoryProject, recipientInviteAuthoritativeIdentityId, recipientPolicyDigest, recipientReviewedIntentDigest, reconcileCoordinatorEnrollmentSnapshot, reconcileRecipientPolicyProject, reconcileShareOperationAcceptance, reconstructLegacyTeamSetupCompletionManifest, recordHighestObservedDirectSignatureVersion, recordNonce, recordPromptPackArtifacts, recordPromptPackTerminal, refreshAuthorizedCoordinatorPeerTrust, refreshConfiguredScopeMembershipCache, refreshLegacyTeamCandidate, rejectInboundScopeFailures, renameRecipientPolicyTeam, replayLegacyTeamSetupActivation, requestJson, requireLegacyTeamSetupAccessDeltaWithinLimit, resolveDbPath, resolveProject, resolveRecipientPolicyReview, resolveRecipientPolicyReviewBulk, runSyncPass, schema, serializeRecipientPolicyCoordinatorGroupMutation, serializeRecipientPolicyPublicationMutation, serializeRecipientPolicyTeamMutation, setLegacyTeamSetupDeviceAssignment, setLegacyTeamSetupDeviceDecision, setLegacyTeamSetupProjectMapping, summarizeInboundScopeRejections, supportsSyncFeature, syncScopeResetRequiredPayload, tryUpdateRetrievalDelivery, updatePeerAddresses, upsertCoordinatorGroupPreference, upsertProjectScopeSettingsMapping, validateLegacyTeamSetupCompletionManifest, validateLegacyTeamSetupCompletionManifestBinding, verifyDirectPeerSignature, verifyRecipientReviewedIntent, verifySignature, writeCodememConfigFile } from "@codemem/core";
3
+ import { ACCESS_CLEANUP_OP_TYPE, CODEMEM_CONFIG_ENV_OVERRIDES, CoordinatorReciprocalApprovalRequestChangedError, DEFAULT_TIME_WINDOW_S, LEGACY_SHARED_REVIEW_SCOPE_ID, LOCAL_DEFAULT_SCOPE_ID, LOCAL_SYNC_CAPABILITY, LOCAL_SYNC_FEATURES, LegacyTeamSetupAdditiveConvergenceError, LegacyTeamSetupRosterCapacityError, MemoryStore, PROMPT_TRANSPORT_PROTOCOL_RANGE, RawEventIngestValidationError, RecipientPolicyEdgeRequestError, RecipientPolicyTeamRenameError, RemoteCoordinatorRequestError, SCOPE_MEMBERSHIP_REVOCATION_LIMITATION, 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, applyLegacyTeamSetupCompletionManifest, applyLegacyTeamSetupCompletionManifestAndReturnActivation, applyReplicationOps, areLegacyTeamSetupCompletionPolicyFactsAdditivelyCompatible, buildAuthHeaders, buildBaseUrl, buildDirectPeerAuthHeaders, buildFilterClausesWithContext, buildRawEventEnvelopeFromCodexHook, buildRawEventEnvelopeFromHook, buildViewerIdentityTarget, canonicalCoordinatorLegacyTeamCompletionManifestJson, canonicalMemoryKind, canonicalWorkspaceIdentity, claimRecipientPolicyActorMutations, claimRecipientPolicyPublicationMutation, cleanupNonces, clearLegacyTeamSetupDeviceDecision, clonePromptPackAttempt, coerceObserverCommand, commitDeviceIdentityBindings, commitRecipientPolicyEdges, containLegacyTeamSetupCompletionConflict, coordinatorArchiveGroupAction, 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, defaultSpaceScopeIdForGroup, deleteProjectScopeSettingsMapping, deriveLegacyTeamSetupCompletionManifest, detectInstallKind, deterministicPolicyTeamId, diagnoseStalePeerReceivedRows, discoverLegacyTeamCandidates, ensureDeviceIdentity, executeShareProvisioning, extractInvitePayload, extractReplicationOps, filterReplicationOpsForSyncWithStatus, fingerprintPublicKey, formatHostPort, friendlyDeviceName, fromJson, getCodememConfigPath, getCodememEnvOverrides, getCoordinatorEnrollmentReconciliationIssueSummary, getCoordinatorGroupPreference, getLegacyTeamSetupDraft, getRecipientPolicyAuthorityState, getSemanticIndexDiagnostics, getSyncResetState, getUpdateStatus, ingestRawEvents, inspectFreshLegacyTeamSetupActivation, inspectLegacyTeamSetupActivation, inviteTokenDigest, isLegacyTeamCandidateSelectable, isLegacyTeamSetupProjectMappingIdentity, isPeerTrustBindingCompatible, isProjectSyncEnablementError, isScopedSyncCapability, legacyTeamCandidateId, legacyTeamCandidateProjectInventory, legacyTeamCanonicalProjectRef, legacyTeamDeviceRef, legacyTeamResolvedProjectRef, legacyTeamSetupApiErrorCode, listAuthorizedScopesForPeer, listCoordinatorJoinRequests, listDeviceIdentityInventory, listInboundScopeRejections, listLegacyRecipientPolicyProjections, listMaintenanceJobs, listObserverProviderOptions, listPerPeerScopeSyncState, listProjectScopeCandidates, listProjectScopeInventory, listProjectScopeSettingsMappings, listRecipientPolicyIntent, listRecipientPolicyReview, listSharingDomainSettingsScopes, loadMemorySnapshotPageForPeer, loadReplicationOpsForPeer, lookupCoordinatorPeers, mergeAddresses, migrateRecipientPolicyIntent, negotiateSyncCapability, normalizeAddress, normalizeHumanPresentationName, normalizeSyncCapability, normalizeSyncFeatures, normalizeTeammateName, parseAcceptedProjectIntent, parsePositiveMemoryId, parseReassignScopePayload, parseRecipientPolicyEdgeCommitRequest, parseStrictInteger, parseSyncScopeRequest, persistShareOperation, personalScopeGrantStatusForPeer, planShareOperation, previewDeviceIdentityBindings, previewLegacyTeamSetupActivation, previewRecipientPolicyEdges, previewRecipientPolicyOnboarding, previewRecipientPolicyOnboardingFromReviewedIntent, probeAvailableCredentials, projectShareLifecycle, promptPackArtifactFingerprint, readCodememConfigFile, readCoordinatorSyncConfig, reassignProjectScopeInventoryProject, recipientInviteAuthoritativeIdentityId, recipientPolicyDigest, recipientReviewedIntentDigest, reconcileCoordinatorEnrollmentSnapshot, reconcileRecipientPolicyProject, reconcileShareOperationAcceptance, reconstructLegacyTeamSetupCompletionManifest, recordHighestObservedDirectSignatureVersion, recordNonce, recordPromptPackArtifacts, recordPromptPackTerminal, refreshAuthorizedCoordinatorPeerTrust, refreshConfiguredScopeMembershipCache, refreshLegacyTeamCandidate, rejectInboundScopeFailures, renameRecipientPolicyTeam, replayLegacyTeamSetupActivation, requestJson, requireLegacyTeamSetupAccessDeltaWithinLimit, resolveDbPath, resolveProject, resolveRecipientPolicyReview, resolveRecipientPolicyReviewBulk, runSyncPass, schema, serializeRecipientPolicyCoordinatorGroupMutation, serializeRecipientPolicyPublicationMutation, serializeRecipientPolicyTeamMutation, setLegacyTeamSetupDeviceAssignment, setLegacyTeamSetupDeviceDecision, setLegacyTeamSetupProjectMapping, summarizeInboundScopeRejections, supportsSyncFeature, syncScopeResetRequiredPayload, tryUpdateRetrievalDelivery, updatePeerAddresses, upsertCoordinatorGroupPreference, upsertProjectScopeSettingsMapping, validateLegacyTeamSetupCompletionManifest, validateLegacyTeamSetupCompletionManifestBinding, verifyDirectPeerSignature, verifyRecipientReviewedIntent, verifySignature, writeCodememConfigFile } from "@codemem/core";
4
4
  import { serveStatic } from "@hono/node-server/serve-static";
5
5
  import { Hono } from "hono";
6
6
  import { createMiddleware } from "hono/factory";
@@ -2922,10 +2922,10 @@ function projectMappingChoices(store) {
2922
2922
  excludePeerReceived: true
2923
2923
  }).filter((candidate) => !candidate.read_only && isLegacyTeamSetupProjectMappingIdentity(candidate.workspace_identity, store.db));
2924
2924
  } catch (error) {
2925
- if (error instanceof Error && (error.message === "project_scope_candidate_scan_too_large" || error.message === "project_scope_candidate_metadata_too_large")) throw new Error("legacy_team_setup_roster_too_large");
2925
+ if (error instanceof Error && (error.message === "project_scope_candidate_scan_too_large" || error.message === "project_scope_candidate_metadata_too_large")) throw new LocalProjectScanBudgetError(error);
2926
2926
  throw error;
2927
2927
  }
2928
- if (candidates.length > MAX_PROJECT_MAPPING_CHOICES) throw new Error("legacy_team_setup_roster_too_large");
2928
+ if (candidates.length > MAX_PROJECT_MAPPING_CHOICES) throw new LocalProjectScanBudgetError();
2929
2929
  return candidates.map((candidate) => ({
2930
2930
  projectIdentity: candidate.workspace_identity,
2931
2931
  sourceDisplayName: candidate.display_project
@@ -3001,6 +3001,156 @@ function completionApiError(error) {
3001
3001
  const code = legacyTeamSetupApiErrorCode(error);
3002
3002
  return code === "team_setup_failed" ? "team_setup_completion_unavailable" : code;
3003
3003
  }
3004
+ var TeamSetupFinishError = class extends Error {
3005
+ reason;
3006
+ alreadyLogged;
3007
+ constructor(code, reason, alreadyLogged, cause) {
3008
+ super(code, cause === void 0 ? void 0 : { cause });
3009
+ this.reason = reason;
3010
+ this.alreadyLogged = alreadyLogged;
3011
+ this.name = "TeamSetupFinishError";
3012
+ }
3013
+ };
3014
+ /**
3015
+ * The generic `legacy_team_setup_roster_too_large` string is thrown by ten
3016
+ * sites for unrelated limits (coordinator roster size, draft device count,
3017
+ * local project choices). Only the local project-candidate scan is a
3018
+ * "this device's history is too large" condition, so it carries a distinct
3019
+ * type. The message is kept identical so the frozen API-code mapping and every
3020
+ * existing caller that matches on it are unchanged.
3021
+ */
3022
+ var LocalProjectScanBudgetError = class extends Error {
3023
+ constructor(cause) {
3024
+ super("legacy_team_setup_roster_too_large", cause === void 0 ? void 0 : { cause });
3025
+ this.name = "LocalProjectScanBudgetError";
3026
+ }
3027
+ };
3028
+ /**
3029
+ * Remote error codes the finish path can legitimately receive from the
3030
+ * coordinator. Anything else — including identifier-shaped values, which a
3031
+ * hostile or misconfigured upstream could use to smuggle a credential through
3032
+ * a JSON `error` field — is reduced to a fixed classification before logging.
3033
+ * A shape check is not an allowlist; only this set is echoed verbatim.
3034
+ */
3035
+ var LOGGABLE_REMOTE_ERROR_CODES = /* @__PURE__ */ new Set([
3036
+ "body_too_large",
3037
+ "completion_manifest_invalid",
3038
+ "completion_manifest_unavailable",
3039
+ "completion_not_found",
3040
+ "completion_query_invalid",
3041
+ "group_archived",
3042
+ "group_id_and_candidate_ref_required",
3043
+ "group_not_found",
3044
+ "invalid_json",
3045
+ "missing_admin_header",
3046
+ "not_found",
3047
+ "rate_limited",
3048
+ "response_too_large",
3049
+ "unauthorized"
3050
+ ]);
3051
+ function loggableRemoteCode(code) {
3052
+ if (LOGGABLE_REMOTE_ERROR_CODES.has(code)) return code;
3053
+ if (code.startsWith("non_json_response")) return "non_json_response";
3054
+ return "unclassified_remote_error";
3055
+ }
3056
+ /**
3057
+ * Local causes the finish path can produce. Same allowlist discipline as
3058
+ * remote codes: everything else, including transport messages that embed the
3059
+ * target URL, is classified rather than echoed.
3060
+ */
3061
+ var LOGGABLE_LOCAL_CAUSES = /* @__PURE__ */ new Set([
3062
+ "completion_conflict",
3063
+ "legacy_team_setup_roster_too_large",
3064
+ "project_scope_candidate_metadata_too_large",
3065
+ "project_scope_candidate_scan_too_large",
3066
+ "team_setup_completion_conflict",
3067
+ "team_setup_completion_invalid",
3068
+ "team_setup_completion_unavailable",
3069
+ "team_setup_confirmation_stale",
3070
+ "team_setup_roster_unavailable"
3071
+ ]);
3072
+ function loggableLocalCause(error) {
3073
+ if (LOGGABLE_LOCAL_CAUSES.has(error.message)) return error.message;
3074
+ if (error.name === "AbortError" || error.name === "TimeoutError") return error.name;
3075
+ if (/fetch failed/iu.test(error.message)) return "fetch_failed";
3076
+ if (/timed out|timeout/iu.test(error.message)) return "timed_out";
3077
+ return `unclassified_error:${error.name || "Error"}`;
3078
+ }
3079
+ function errorChain(error) {
3080
+ const chain = [];
3081
+ let current = error;
3082
+ while (current && !chain.includes(current)) {
3083
+ chain.push(current);
3084
+ current = current instanceof Error ? current.cause : void 0;
3085
+ }
3086
+ return chain;
3087
+ }
3088
+ function remoteFailureReason(remote, fallback) {
3089
+ if (remote.status === 404 && (remote.code === "not_found" || remote.code.startsWith("non_json"))) return "coordinator_route_missing";
3090
+ if (remote.status === 400 && remote.code === "completion_manifest_invalid") return "coordinator_rejected_manifest";
3091
+ return fallback;
3092
+ }
3093
+ function teamSetupFinishFailureDetails(error) {
3094
+ const chain = errorChain(error);
3095
+ const wrapper = chain.find((item) => item instanceof TeamSetupFinishError);
3096
+ const remote = chain.find((item) => item instanceof RemoteCoordinatorRequestError);
3097
+ if (remote) {
3098
+ const reason = remoteFailureReason(remote, wrapper?.reason);
3099
+ return {
3100
+ ...reason ? { reason } : {},
3101
+ cause: "remote coordinator request failed",
3102
+ status: remote.status,
3103
+ code: loggableRemoteCode(remote.code)
3104
+ };
3105
+ }
3106
+ if (chain.some((item) => item instanceof LegacyTeamSetupRosterCapacityError || item instanceof LocalProjectScanBudgetError)) return {
3107
+ reason: "local_candidate_scan_budget_exceeded",
3108
+ cause: "local project candidate scan exceeded budget"
3109
+ };
3110
+ const errorCause = chain.find((item) => item instanceof Error && !(item instanceof TeamSetupFinishError));
3111
+ const cause = errorCause ? loggableLocalCause(errorCause) : "unclassified_non_error";
3112
+ if (wrapper?.reason) return {
3113
+ reason: wrapper.reason,
3114
+ cause
3115
+ };
3116
+ if (errorCause && (errorCause.name === "AbortError" || errorCause.name === "TimeoutError" || /fetch failed|timed out|timeout/iu.test(errorCause.message))) return {
3117
+ reason: "coordinator_unreachable",
3118
+ cause
3119
+ };
3120
+ return { cause };
3121
+ }
3122
+ function warnTeamSetupFinishFailure(error, details, context) {
3123
+ if (errorChain(error).some((item) => item instanceof TeamSetupFinishError && item.alreadyLogged)) return;
3124
+ const safe = (value) => {
3125
+ let sanitized = String(value).replace(/[\r\n\t]+/gu, " ");
3126
+ for (const secret of context.redactValues ?? []) if (secret) sanitized = sanitized.replaceAll(secret, "[redacted]");
3127
+ return sanitized.replace(/https?:\/\/[^\s]+/giu, (value) => {
3128
+ try {
3129
+ const url = new URL(value);
3130
+ return `${url.protocol}//${url.host}`;
3131
+ } catch {
3132
+ return "[redacted-url]";
3133
+ }
3134
+ });
3135
+ };
3136
+ const fields = [
3137
+ `candidateRef=${safe(context.candidateRef)}`,
3138
+ `attemptId=${safe(context.attemptId)}`,
3139
+ `apiCode=${safe(context.apiCode)}`,
3140
+ `cause=${safe(details.cause)}`,
3141
+ ...details.reason ? [`reason=${safe(details.reason)}`] : [],
3142
+ ...details.status == null ? [] : [`status=${safe(details.status)}`],
3143
+ ...details.code == null ? [] : [`code=${safe(details.code)}`]
3144
+ ];
3145
+ console.warn(`[team-setup finish] ${fields.join(" ")}`);
3146
+ }
3147
+ function finishErrorResponse(code, details) {
3148
+ const reason = details.reason;
3149
+ return reason ? {
3150
+ error: code,
3151
+ reason
3152
+ } : { error: code };
3153
+ }
3004
3154
  async function parseBoundedJsonObject(c) {
3005
3155
  if (c.req.header("content-type")?.split(";", 1)[0]?.trim().toLowerCase() !== "application/json") return { ok: false };
3006
3156
  const lengthHeader = c.req.header("content-length");
@@ -3741,6 +3891,7 @@ function teamSetupRoutes(options) {
3741
3891
  let releaseMutation;
3742
3892
  let releasePublicationMutation;
3743
3893
  let releaseActorMutations;
3894
+ let finishLogRedactValues = [];
3744
3895
  try {
3745
3896
  const store = options.getStore();
3746
3897
  const draft = getLegacyTeamSetupDraft(store.db, candidateRef);
@@ -3803,8 +3954,9 @@ function teamSetupRoutes(options) {
3803
3954
  if (!completionDependencies || !authorization) throw new Error("team_setup_completion_unavailable");
3804
3955
  if (!authorizesGroup(authorization, group)) throw new Error("team_setup_completion_invalid");
3805
3956
  const { config, remoteUrl } = authorization;
3957
+ finishLogRedactValues = [config.syncCoordinatorAdminSecret];
3806
3958
  const freshGroup = await loadFreshGroupSnapshot(group, remoteUrl, publicationDeadlineMs);
3807
- if (!freshGroup) throw new Error("team_setup_roster_unavailable");
3959
+ if (!freshGroup) throw new TeamSetupFinishError("team_setup_roster_unavailable", "coordinator_roster_unavailable", false, /* @__PURE__ */ new Error("coordinator roster unavailable"));
3808
3960
  if (!stillAuthorizesGroup(authorization, group)) throw new Error("team_setup_completion_invalid");
3809
3961
  const projectInventory = legacyTeamCandidateProjectInventory(store.db, projectionOptions(store), candidateRef);
3810
3962
  const freshPreview = inspectFreshLegacyTeamSetupActivation(store.db, {
@@ -3845,8 +3997,17 @@ function teamSetupRoutes(options) {
3845
3997
  if (canonicalCoordinatorLegacyTeamCompletionManifestJson(canonicalManifest) !== canonicalCoordinatorLegacyTeamCompletionManifestJson(proposedManifest)) throw new Error("team_setup_completion_conflict");
3846
3998
  } catch (error) {
3847
3999
  const code = completionApiError(error);
4000
+ const details = teamSetupFinishFailureDetails(error);
3848
4001
  const getCompletion = completionDependencies.get;
3849
- if (code !== "team_setup_completion_conflict" || !getCompletion) throw new Error(code);
4002
+ if (code !== "team_setup_completion_conflict" || !getCompletion) {
4003
+ warnTeamSetupFinishFailure(error, details, {
4004
+ candidateRef,
4005
+ attemptId,
4006
+ apiCode: code,
4007
+ redactValues: finishLogRedactValues
4008
+ });
4009
+ throw new TeamSetupFinishError(code, details.reason, true, error);
4010
+ }
3850
4011
  try {
3851
4012
  const existing = await retryTransientCoordinatorRead((attemptTimeoutS) => getCompletion({
3852
4013
  groupId: group.groupId,
@@ -3862,17 +4023,17 @@ function teamSetupRoutes(options) {
3862
4023
  });
3863
4024
  if (canonicalManifest.finish_digest !== proposedManifest.finish_digest) throw new Error("team_setup_completion_conflict");
3864
4025
  } catch (recoveryError) {
3865
- throw new Error(completionApiError(recoveryError));
4026
+ throw new TeamSetupFinishError(completionApiError(recoveryError), teamSetupFinishFailureDetails(recoveryError).reason, false, recoveryError);
3866
4027
  }
3867
4028
  }
3868
4029
  let postPublicationGroup;
3869
4030
  try {
3870
4031
  postPublicationGroup = await loadFreshGroupSnapshot(group, remoteUrl, publicationDeadlineMs);
3871
4032
  } catch (error) {
3872
- if (legacyTeamSetupApiErrorCode(error) === "team_setup_completion_invalid") throw new Error("team_setup_roster_unavailable");
4033
+ if (legacyTeamSetupApiErrorCode(error) === "team_setup_completion_invalid") throw new TeamSetupFinishError("team_setup_roster_unavailable", "coordinator_roster_unavailable", false, error);
3873
4034
  throw error;
3874
4035
  }
3875
- if (!postPublicationGroup) throw new Error("team_setup_roster_unavailable");
4036
+ if (!postPublicationGroup) throw new TeamSetupFinishError("team_setup_roster_unavailable", "coordinator_roster_unavailable", false, /* @__PURE__ */ new Error("coordinator roster unavailable"));
3876
4037
  const result = await applyLegacyTeamSetupCompletionManifestAndReturnActivation(store.db, {
3877
4038
  coordinatorId: group.coordinatorId,
3878
4039
  groupId: group.groupId,
@@ -3889,7 +4050,14 @@ function teamSetupRoutes(options) {
3889
4050
  }));
3890
4051
  } catch (error) {
3891
4052
  const code = apiErrorCode(error);
3892
- return c.json({ error: code }, errorStatus$1(code));
4053
+ const details = teamSetupFinishFailureDetails(error);
4054
+ warnTeamSetupFinishFailure(error, details, {
4055
+ candidateRef,
4056
+ attemptId,
4057
+ apiCode: code,
4058
+ redactValues: finishLogRedactValues
4059
+ });
4060
+ return c.json(finishErrorResponse(code, details), errorStatus$1(code));
3893
4061
  } finally {
3894
4062
  releaseActorMutations?.();
3895
4063
  releasePublicationMutation?.();