@gitroll/tacitt-cli 0.4.1 → 0.4.2

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.
Files changed (37) hide show
  1. package/dist/{agent-setup-Cb_spmGb.mjs → agent-setup-Bpsi-BCv.mjs} +2 -2
  2. package/dist/agent-setup-worker.mjs +5 -5
  3. package/dist/cli.mjs +36 -10
  4. package/dist/codex-title-worker.mjs +1 -1
  5. package/dist/configured-preview-worker.mjs +5 -5
  6. package/dist/configured-session-worker.mjs +5 -5
  7. package/dist/{frame-waS5FnN5.mjs → frame-IeDOLy1o.mjs} +1 -1
  8. package/dist/{heap-eZTo0VYj.mjs → heap-DiOlSa8T.mjs} +6 -6
  9. package/dist/inspection-worker.mjs +4 -4
  10. package/dist/{job-Bi57uRU-.mjs → job-BII94L2c.mjs} +3 -3
  11. package/dist/{locate-U3-nkird.mjs → locate-CBiw-CJK.mjs} +5 -0
  12. package/dist/{model-BDrntocp.mjs → model-DGTsARtV.mjs} +1 -1
  13. package/dist/operation-executor.mjs +3 -3
  14. package/dist/preview-worker.mjs +1 -1
  15. package/dist/privacy-validator.mjs +1 -1
  16. package/dist/{query-7uY266qY.mjs → query-CVqWNFYf.mjs} +822 -244
  17. package/dist/{run-CV6Io7CS.mjs → run-BGlf-W0M.mjs} +4 -4
  18. package/dist/{src-BESjwh7X.mjs → src-37kIclGK.mjs} +5 -4
  19. package/dist/{src-DSDRF7i6.mjs → src-CBTvOa44.mjs} +45 -2
  20. package/dist/{src-DIuh0LHk.mjs → src-D4nrfC2S.mjs} +165 -5
  21. package/dist/store-database-worker.mjs +1 -1
  22. package/dist/web/assets/index-D3g_ATU-.js +69 -0
  23. package/dist/web/index.html +1 -1
  24. package/package.json +1 -1
  25. package/skills/tacitt/SKILL.md +2 -2
  26. package/skills/tacitt/references/INDEX.md +10 -10
  27. package/skills/tacitt/references/journey.md +3 -3
  28. package/skills/tacitt/references/send.md +38 -12
  29. package/skills/tacitt/scripts/INDEX.md +36 -36
  30. package/skills/tacitt/scripts/journey-card-consolidation.mjs +2 -4
  31. package/skills/tacitt/scripts/journey-state.d.mts +30 -0
  32. package/skills/tacitt/scripts/journey-state.mjs +56 -3
  33. package/skills/tacitt/scripts/journey-tasks.d.mts +8 -0
  34. package/skills/tacitt/scripts/journey-tasks.mjs +4 -1
  35. package/skills/tacitt/scripts/privacy-validator.mjs +2 -0
  36. package/skills/tacitt/scripts/review.mjs +36 -5
  37. package/dist/web/assets/index-Dh4iG6x2.js +0 -69
@@ -1,10 +1,10 @@
1
- import { A as runConfiguredSessionJob, C as readConfiguredSearchFields, D as captureLogicalJourneySources, F as sessionWorkerFailure, G as staticMaskCategory, H as readLogicalJourneyFinalMasks, I as privateCollectionDirectory, K as readWorkspacePair, L as defaultParallelism, M as parseCollectionWorkspaceSummary, N as createSharedSessionPool, O as capturedJourneyScope, P as SessionWorkerError, Q as createCleanup, R as gitSeedsProvider, S as readOriginalSearchFields, T as checkReusableBundle, U as readConfiguredOutputInfo, V as privateJourneyDirectory, W as processJourneyMetadataFields, X as createTemporary, Z as removeNamedTemporarySpools, _ as readStaticSessionMasks, a as scanReviewFields, at as isWindowsAclFailure, c as readSessionScanRecords, g as processJourneyMetadata, h as readCapturedOriginalRows, i as buildWorkScanBatches, it as windowsOwnerOnly, j as readCollectionIndex, l as scanPreparedSession, n as verifyPreparedSessionSources, nt as openPrivateFile, o as createWorkPieces, ot as windowsAclCodes, p as openConfiguredUpload, r as prepareSessionScans, rt as restrictToOwner, s as readSessionScanFields, t as preparedScanConfiguration, tt as makePrivateDirectory, v as staticSessionJobs, w as createCollectionArtifacts, z as parallelismLimits } from "./src-DIuh0LHk.mjs";
2
- import { Bt as sessionPeriodSchema, Dn as entityDictionaryVersion, F as bClassificationKey, Fn as frameRecords, Ft as termTypeInfo, Gt as safeField, H as codePointLength, Ht as ApplicationError, Jt as reviewFamilies, K as utf16Offset, N as bUnitFragments, Ot as privacyTermKey, Qn as admitGitName, T as bLiteralTerms, Ut as UnfinishedJourneyError, V as bDigest, Wt as safeCode, Xn as createEntityMatcher, Zn as admitCustomTerm, Zt as observeSource, b as validateBReview, c as textDigest, h as reviewFindingsDigest, ht as journeyDescribeSchema, jt as reviewTermsInputSchema, l as validateReview, mn as currentDetectorVersion, n as compileBFindings, r as reviewBatchChunks, s as reviewSubmitSchema, t as locateTerms, yn as createStaticProcessor, z as bAnchorKey } from "./locate-U3-nkird.mjs";
3
- import { a as mainTextChunks, c as previewInWorker, i as nearestSortingRepository, l as createNativeAdapters, n as createProjectEvidenceCollector, o as mainTextFields, r as sortingRepositoryId, s as textChunks, t as createHarnesses, u as createGitNameSeedProvider } from "./src-BESjwh7X.mjs";
4
- import { $ as settingsQuerySchema, $r as packageCommandSchema, A as deviceProfileScopeSchema, B as validateInspectionBatch, Bn as journeyConfirmedHides, Br as hasCompleteWorkDescription, Bt as newJourneyWork, C as rebindReviewSubmit, Cr as privacyDecisionDigest, Ct as sendStartSchema, D as createAccount, Dr as hasCurrentStaticReview, E as staticallyHiddenFindings, Er as nativeRespondSchema, Et as obsoleteJourney, Fn as confirmedHideGroupIds, Fr as sendArtifactSchema, Gr as withReadingProgress, Hr as isReadingComplete, Ht as journeyContextQuerySchema, Ir as sendReceiptSchema, Jn as reviewCandidateResourceSchema, Jr as journeyFindingsSchema, Kn as workCorrectionBinding, L as createApplication, Ln as confirmedHideKinds, Lr as sendReplaceResultSchema, Lt as classificationPartition, M as readAccountAvatarUrl, Mr as agentSourceSchema, Mt as applyClassification, N as storedCredentialSchema, Nn as bTermMatcher, Nr as agentSourcesSchema, O as accountProfileSchema, Or as staticReviewDigest, Pr as journeySendSchema, Pt as journeyReviewStorageQuerySchema, Q as settingsBatchSchema, Qn as bReviewDigest, Qr as commandSchema, Qt as journeyResourceQuerySchema, R as inspectionCancelSchema, Rr as sendResolutionSchema, Rt as classificationWorks, S as rebindReviewOverlay, Sn as readConfirmedHideChange, Sr as assertJourneyPrivacyReady, St as sendPrepareSchema, T as guardedFindingText, Tn as termHideAdditions, Tr as nativeGetSchema, Tt as currentJourneyAdapters, U as inspectionQuerySchema, Un as retireWorkDescription, Ur as isSelectedWork, Vn as maskedHitsStale, Vr as isClassifying, Vt as reviewCompileSchema, Wn as workDecisionDigest, Xn as reviewRepairResourceSchema, Xr as journeyRenderInputSchema, Xt as journeyPolicyRespondSchema, Yn as reviewRepairInputSchema, Yr as journeyPreviewSchema, Yt as searchScopeDigest, Zr as journeyRenderSchema, _t as maximumTermsFileBytes, a as bCrossSegmentReviewSchema, ar as scanGitSeedsId, b as bReviewScope, bn as confirmedHideAdditions, br as journeyClassificationSessionsSchema, bt as sendActionSchema, c as reconstructBFields, cr as scanExclusionResourceSchema, ct as validateFailureLocation, d as selectedValueMembers, dn as sessionReviewInputSchema, dr as journeyInventoryQuerySchema, dt as manualUploadChoiceSchema, ei as selectionBatchSchema, er as planBRepair, et as readWorkspaceOutcomes, f as splitSelectorValues, fn as sessionReviewQuerySchema, ft as manualUploadPrepareSchema, g as changeJourney, gt as emptyTermsRevision, hn as sessionUsefulnessInputSchema, hr as emptyPreparationFacts, i as validateBCrossSegmentReview, ii as stateSchema, ir as scanSessionResourceSchema, j as profileUrlMatchesIdentity, k as deviceAuthorizationSchema, ln as sessionRereadInputSchema, lt as manualUploadActionSchema, m as hideScopeDigest, mt as manualUploadStartSchema, n as scanBatchEquivalenceDigest, nt as appendSensitiveTerms, o as bRereadDigest, oi as recommendParallelism, on as sessionReconcileInputSchema, or as scanGitSeedsResource, ot as artifactFailureDetailSchema, p as activeJourneyTerms, pt as manualUploadSchema, qn as reviewCandidateInputSchema, qr as journeyFindingImpactSchema, qt as journeySearchResultSchema, r as locateTermEvidence, ri as producerVersion, rn as reviewAttemptResourceSchema, rr as scanPreparationDigest, s as reconcileBSession, sr as verifiedScanGitSeeds, st as resolveFailureLocation, t as scanBatchContextDigest, ti as selectionSchema, tn as journeyPlaceSchema, vt as normalizeSensitiveTerms, w as reviewBatchReuse, wr as nativeEventsSchema, x as rebindReviewAlignment, xt as sendChoiceSchema, y as isGenericPublicTerm, yt as replaceSensitiveTermsSchema, z as inspectionStartSchema, zr as sendSettingsSchema, zt as retireScanGeneration } from "./src-DSDRF7i6.mjs";
5
- import { $ as checkVersion, A as resourceSelectionQuerySchema, B as termReuseQuerySchema, C as loadAcceptedReview, D as aggregateUsefulness, E as loadPrivateResource, F as validateUnitCandidate, G as requireActiveBatch, H as batchHeader, I as reuseQuerySchema, J as validateDatabase, K as initializeAndClaim, L as batchQuery, M as evaluateUnitEvidence, N as sameCandidateRequest, O as sessionUsefulnessRecommendation, P as validateCandidateAttempt, Q as validateState, R as isPrivateResourceKind, S as validateSessionBindings, T as loadBatch, U as createJourneyPayloadFiles, V as privateReviewHash, W as validateBatchBody, X as readBundleDirectory, Y as withDatabase, Z as readStoredState, _ as loadReconciliation, _t as isCoordinationBusy, a as decodeError, at as hasResetMarker, b as validatePreparationSelection, c as dropsAnchor, ct as privatePath, d as parseDelivery, dt as stateDirectory, et as schemaVersion, f as validateCandidate, ft as syncDirectory, g as loadPreparedBodies, gt as errorCode$1, h as loadPreparation, ht as StoreError, i as createExecutor, it as finishReset, j as inspectionQuerySchema$1, k as sessionQuery, l as replySchema, lt as readResetMarker, m as loadStatus, mt as withFileCheckTimings, n as releaseInheritedHeapFlags, nt as createDatabaseFile, o as ProtocolError, ot as inspectStateFiles, p as classificationJourneyCommit, pt as withStateSnapshot, q as ownership, r as storeHeapLimits, rt as databaseName, s as allowedReplies, st as present, tt as beginReset, u as operations, ut as recheckPrivatePath, v as loadRereadBodies, vt as isSqliteCorruption, w as loadAuthenticatedReview, x as validateSessionReconciliation, y as loadSessionBodies, z as privateResourceHeader } from "./heap-eZTo0VYj.mjs";
6
- import { _ as staticBaseProduction, a as writeJourneyOutput, c as mergeHideIndexTokens, f as writeJourneyBase, g as staticBaseInputs, h as staticSessions, l as packRenderHideIndex, m as saveStaticSession, o as createHideIndexTokens, p as readStaticResult, s as hideIndexTexts, t as journeyOutputJob, u as journeyBaseJob, v as prepareSourcePlan, y as workApproval } from "./job-Bi57uRU-.mjs";
7
- import { t as createAgentSetupPort } from "./agent-setup-Cb_spmGb.mjs";
1
+ import { $ as createCleanup, B as parallelismLimits, C as readOriginalSearchFields, E as checkReusableBundle, F as SessionWorkerError, G as processJourneyMetadataFields, H as privateJourneyDirectory, I as sessionWorkerFailure, K as staticMaskCategory, L as privateCollectionDirectory, M as readCollectionIndex, N as parseCollectionWorkspaceSummary, O as captureLogicalJourneySources, P as createSharedSessionPool, Q as removeNamedTemporarySpools, R as defaultParallelism, T as createCollectionArtifacts, U as readLogicalJourneyFinalMasks, W as readConfiguredOutputInfo, Z as createTemporary, _ as processJourneyMetadata, a as scanReviewFields, at as windowsOwnerOnly, c as readSessionScanRecords, g as readCapturedOriginalRows, i as buildWorkScanBatches, it as restrictToOwner, j as runConfiguredSessionJob, k as capturedJourneyScope, l as scanPreparedSession, m as openConfiguredUpload, n as verifyPreparedSessionSources, nt as makePrivateDirectory, o as createWorkPieces, ot as isWindowsAclFailure, p as configuredSessionUploads, q as readWorkspacePair, r as prepareSessionScans, rt as openPrivateFile, s as readSessionScanFields, st as windowsAclCodes, t as preparedScanConfiguration, v as readStaticSessionMasks, w as readConfiguredSearchFields, y as staticSessionJobs, z as gitSeedsProvider } from "./src-D4nrfC2S.mjs";
2
+ import { Bt as sessionPeriodSchema, Dn as entityDictionaryVersion, F as bClassificationKey, Fn as frameRecords, Ft as termTypeInfo, Gt as safeField, H as codePointLength, Ht as ApplicationError, Jt as reviewFamilies, K as utf16Offset, N as bUnitFragments, Ot as privacyTermKey, Qn as admitGitName, T as bLiteralTerms, Ut as UnfinishedJourneyError, V as bDigest, Wt as safeCode, Xn as createEntityMatcher, Zn as admitCustomTerm, Zt as observeSource, b as validateBReview, c as textDigest, h as reviewFindingsDigest, ht as journeyDescribeSchema, jt as reviewTermsInputSchema, l as validateReview, mn as currentDetectorVersion, n as compileBFindings, r as reviewBatchChunks, s as reviewSubmitSchema, t as locateTerms, yn as createStaticProcessor, z as bAnchorKey } from "./locate-CBiw-CJK.mjs";
3
+ import { a as mainTextChunks, c as previewInWorker, i as nearestSortingRepository, l as createNativeAdapters, n as createProjectEvidenceCollector, o as mainTextFields, r as sortingRepositoryId, s as textChunks, t as createHarnesses, u as createGitNameSeedProvider } from "./src-37kIclGK.mjs";
4
+ import { $ as settingsQuerySchema, $r as packageCommandSchema, A as deviceProfileScopeSchema, B as validateInspectionBatch, Bn as journeyConfirmedHides, Br as hasCompleteWorkDescription, Bt as newJourneyWork, C as rebindReviewSubmit, Cr as privacyDecisionDigest, Ct as sendStartSchema, D as createAccount, Dr as hasCurrentStaticReview, E as staticallyHiddenFindings, Er as nativeRespondSchema, Et as obsoleteJourney, Fn as confirmedHideGroupIds, Fr as sendArtifactSchema, Gr as withReadingProgress, Hr as isReadingComplete, Ht as journeyContextQuerySchema, Ir as sendReceiptSchema, Jn as reviewCandidateResourceSchema, Jr as journeyFindingsSchema, Kn as workCorrectionBinding, L as createApplication, Ln as confirmedHideKinds, Lr as sendReplaceResultSchema, Lt as classificationPartition, M as readAccountAvatarUrl, Mr as agentSourceSchema, Mt as applyClassification, N as storedCredentialSchema, Nn as bTermMatcher, Nr as agentSourcesSchema, O as accountProfileSchema, Or as staticReviewDigest, Pr as journeySendSchema, Pt as journeyReviewStorageQuerySchema, Q as settingsBatchSchema, Qn as bReviewDigest, Qr as commandSchema, Qt as journeyResourceQuerySchema, R as inspectionCancelSchema, Rr as sendResolutionSchema, Rt as classificationWorks, S as rebindReviewOverlay, Sn as readConfirmedHideChange, Sr as assertJourneyPrivacyReady, St as sendPrepareSchema, T as guardedFindingText, Tn as termHideAdditions, Tr as nativeGetSchema, Tt as currentJourneyAdapters, U as inspectionQuerySchema, Un as retireWorkDescription, Ur as isSelectedWork, Vn as maskedHitsStale, Vr as isClassifying, Vt as reviewCompileSchema, Wn as workDecisionDigest, Xn as reviewRepairResourceSchema, Xr as journeyRenderInputSchema, Xt as journeyPolicyRespondSchema, Yn as reviewRepairInputSchema, Yr as journeyPreviewSchema, Yt as searchScopeDigest, Zr as journeyRenderSchema, _t as maximumTermsFileBytes, a as bCrossSegmentReviewSchema, ar as scanGitSeedsId, b as bReviewScope, bn as confirmedHideAdditions, br as journeyClassificationSessionsSchema, bt as sendActionSchema, c as reconstructBFields, cr as scanExclusionResourceSchema, ct as validateFailureLocation, d as selectedValueMembers, dn as sessionReviewInputSchema, dr as journeyInventoryQuerySchema, dt as manualUploadChoiceSchema, ei as selectionBatchSchema, er as planBRepair, et as readWorkspaceOutcomes, f as splitSelectorValues, fn as sessionReviewQuerySchema, ft as manualUploadPrepareSchema, g as changeJourney, gt as emptyTermsRevision, hn as sessionUsefulnessInputSchema, hr as emptyPreparationFacts, i as validateBCrossSegmentReview, ii as stateSchema, ir as scanSessionResourceSchema, j as profileUrlMatchesIdentity, k as deviceAuthorizationSchema, ln as sessionRereadInputSchema, lt as manualUploadActionSchema, m as hideScopeDigest, mt as manualUploadStartSchema, n as scanBatchEquivalenceDigest, nt as appendSensitiveTerms, o as bRereadDigest, oi as recommendParallelism, on as sessionReconcileInputSchema, or as scanGitSeedsResource, ot as artifactFailureDetailSchema, p as activeJourneyTerms, pt as manualUploadSchema, qn as reviewCandidateInputSchema, qr as journeyFindingImpactSchema, qt as journeySearchResultSchema, r as locateTermEvidence, ri as producerVersion, rn as reviewAttemptResourceSchema, rr as scanPreparationDigest, s as reconcileBSession, sr as verifiedScanGitSeeds, st as resolveFailureLocation, t as scanBatchContextDigest, ti as selectionSchema, tn as journeyPlaceSchema, vt as normalizeSensitiveTerms, w as reviewBatchReuse, wr as nativeEventsSchema, x as rebindReviewAlignment, xt as sendChoiceSchema, y as isGenericPublicTerm, yt as replaceSensitiveTermsSchema, z as inspectionStartSchema, zr as sendSettingsSchema, zt as retireScanGeneration } from "./src-CBTvOa44.mjs";
5
+ import { $ as checkVersion, A as resourceSelectionQuerySchema, B as termReuseQuerySchema, C as loadAcceptedReview, D as aggregateUsefulness, E as loadPrivateResource, F as validateUnitCandidate, G as requireActiveBatch, H as batchHeader, I as reuseQuerySchema, J as validateDatabase, K as initializeAndClaim, L as batchQuery, M as evaluateUnitEvidence, N as sameCandidateRequest, O as sessionUsefulnessRecommendation, P as validateCandidateAttempt, Q as validateState, R as isPrivateResourceKind, S as validateSessionBindings, T as loadBatch, U as createJourneyPayloadFiles, V as privateReviewHash, W as validateBatchBody, X as readBundleDirectory, Y as withDatabase, Z as readStoredState, _ as loadReconciliation, _t as isCoordinationBusy, a as decodeError, at as hasResetMarker, b as validatePreparationSelection, c as dropsAnchor, ct as privatePath, d as parseDelivery, dt as stateDirectory, et as schemaVersion, f as validateCandidate, ft as syncDirectory, g as loadPreparedBodies, gt as errorCode$1, h as loadPreparation, ht as StoreError, i as createExecutor, it as finishReset, j as inspectionQuerySchema$1, k as sessionQuery, l as replySchema, lt as readResetMarker, m as loadStatus, mt as withFileCheckTimings, n as releaseInheritedHeapFlags, nt as createDatabaseFile, o as ProtocolError, ot as inspectStateFiles, p as classificationJourneyCommit, pt as withStateSnapshot, q as ownership, r as storeHeapLimits, rt as databaseName, s as allowedReplies, st as present, tt as beginReset, u as operations, ut as recheckPrivatePath, v as loadRereadBodies, vt as isSqliteCorruption, w as loadAuthenticatedReview, x as validateSessionReconciliation, y as loadSessionBodies, z as privateResourceHeader } from "./heap-DiOlSa8T.mjs";
6
+ import { _ as staticBaseProduction, a as writeJourneyOutput, c as mergeHideIndexTokens, f as writeJourneyBase, g as staticBaseInputs, h as staticSessions, l as packRenderHideIndex, m as saveStaticSession, o as createHideIndexTokens, p as readStaticResult, s as hideIndexTexts, t as journeyOutputJob, u as journeyBaseJob, v as prepareSourcePlan, y as workApproval } from "./job-BII94L2c.mjs";
7
+ import { t as createAgentSetupPort } from "./agent-setup-Bpsi-BCv.mjs";
8
8
  import { t as workerMessageSchema } from "./messages-D3m3bhva.mjs";
9
9
  import { constants, write } from "node:fs";
10
10
  import { z } from "zod";
@@ -166,14 +166,14 @@ async function readThroughContention(read, signal, delaysMs = [
166
166
  }
167
167
  //#endregion
168
168
  //#region packages/local-host/src/native-inspection.ts
169
- const count$4 = z.number().int().nonnegative();
169
+ const count$5 = z.number().int().nonnegative();
170
170
  const originalViewSchema = z.object({
171
171
  journeyId: z.uuid(),
172
172
  workId: z.uuid(),
173
173
  cursor: z.object({
174
174
  snapshotId: z.string().regex(/^[a-f0-9]{64}$/u),
175
- inputRevision: count$4,
176
- partIndex: count$4,
175
+ inputRevision: count$5,
176
+ partIndex: count$5,
177
177
  afterOrdinal: z.number().int().min(-1)
178
178
  }).strict().optional()
179
179
  }).strict();
@@ -195,7 +195,7 @@ const outputViewSchema = z.object({
195
195
  const sourceViewSchema = z.object({
196
196
  journeyId: z.uuid(),
197
197
  workId: z.uuid(),
198
- inputRevision: count$4.positive(),
198
+ inputRevision: count$5.positive(),
199
199
  sessionId: z.string().min(1).max(128)
200
200
  }).strict();
201
201
  const masksViewSchema = z.object({
@@ -563,8 +563,8 @@ function createDeliveryService(application) {
563
563
  //#endregion
564
564
  //#region packages/local-host/src/current-view/model.ts
565
565
  const sequenceHeader = "x-sess-pilot-ci-current-sequence";
566
- const count$3 = z.number().int().min(0).max(Number.MAX_SAFE_INTEGER);
567
- const identifier$1 = count$3.min(1);
566
+ const count$4 = z.number().int().min(0).max(Number.MAX_SAFE_INTEGER);
567
+ const identifier$1 = count$4.min(1);
568
568
  const requestId = z.string().regex(/^req-[a-z0-9]{1,7}$/u);
569
569
  const leg = z.enum(["ownership", "procedure"]);
570
570
  const operation = z.enum([
@@ -708,27 +708,27 @@ const rowSchema = z.strictObject({
708
708
  "stopped"
709
709
  ]),
710
710
  phase: phase.nullable(),
711
- elapsedMs: count$3,
712
- queueMs: count$3.nullable(),
713
- activeMs: count$3.nullable(),
714
- phaseMs: count$3.nullable()
711
+ elapsedMs: count$4,
712
+ queueMs: count$4.nullable(),
713
+ activeMs: count$4.nullable(),
714
+ phaseMs: count$4.nullable()
715
715
  });
716
716
  const pendingSchema = z.strictObject({
717
717
  accessId: identifier$1,
718
718
  operation,
719
719
  write: z.boolean(),
720
720
  phase,
721
- queueDepth: count$3,
722
- queueMs: count$3,
723
- activeMs: count$3,
724
- phaseMs: count$3,
721
+ queueDepth: count$4,
722
+ queueMs: count$4,
723
+ activeMs: count$4,
724
+ phaseMs: count$4,
725
725
  association: associationSchema.nullable()
726
726
  });
727
727
  const storeSchema = z.strictObject({
728
728
  executor: z.enum(["worker", "inline"]),
729
- lastAccessId: count$3,
730
- dequeuedAccessId: count$3,
731
- queueDepth: count$3,
729
+ lastAccessId: count$4,
730
+ dequeuedAccessId: count$4,
731
+ queueDepth: count$4,
732
732
  pending: pendingSchema.nullable()
733
733
  });
734
734
  const legState = z.enum([
@@ -743,17 +743,17 @@ const disconnectSchema = z.strictObject({
743
743
  procedure: z.literal("workflow.getCurrent"),
744
744
  requestId,
745
745
  sequence: identifier$1.nullable(),
746
- elapsedMs: count$3,
746
+ elapsedMs: count$4,
747
747
  ownership: legState,
748
748
  procedureState: legState,
749
- pendingAccesses: count$3,
750
- truncatedAccesses: count$3,
751
- stoppedBeforeEnqueue: count$3,
749
+ pendingAccesses: count$4,
750
+ truncatedAccesses: count$4,
751
+ stoppedBeforeEnqueue: count$4,
752
752
  accesses: z.array(rowSchema).max(16),
753
753
  store: storeSchema.nullable(),
754
- droppedFrames: count$3,
755
- droppedSnapshots: count$3,
756
- observerFailures: count$3
754
+ droppedFrames: count$4,
755
+ droppedSnapshots: count$4,
756
+ observerFailures: count$4
757
757
  });
758
758
  function parseSequence(value) {
759
759
  if (typeof value !== "string" || !/^[1-9][0-9]{0,15}$/u.test(value)) return null;
@@ -5078,10 +5078,10 @@ async function readStateDump(stateDir) {
5078
5078
  //#endregion
5079
5079
  //#region packages/local-host/src/execution/progress.ts
5080
5080
  const maximumBytes = 65536;
5081
- const count$2 = z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER);
5081
+ const count$3 = z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER);
5082
5082
  const scope = z.object({
5083
- completed: count$2,
5084
- total: count$2
5083
+ completed: count$3,
5084
+ total: count$3
5085
5085
  }).strict().nullable();
5086
5086
  const recordSchema = z.object({
5087
5087
  databaseId: z.string(),
@@ -5090,14 +5090,14 @@ const recordSchema = z.object({
5090
5090
  operationId: z.string(),
5091
5091
  step: z.enum(["preparing", "processing"]),
5092
5092
  activeWorkspace: z.string().nullable(),
5093
- workspaceSequence: count$2,
5093
+ workspaceSequence: count$3,
5094
5094
  workspaces: scope,
5095
5095
  sessions: scope,
5096
5096
  sourceBytes: z.object({
5097
- processed: count$2,
5098
- total: count$2
5097
+ processed: count$3,
5098
+ total: count$3
5099
5099
  }).strict().nullable(),
5100
- archiveBytes: count$2
5100
+ archiveBytes: count$3
5101
5101
  }).strict()
5102
5102
  }).strict();
5103
5103
  /** Disposable progress only. The workflow claim serializes publishers and matching clears. */
@@ -8201,7 +8201,7 @@ async function draftSend(journey, dependencies, signal) {
8201
8201
  const omitted = [];
8202
8202
  for (const work of journey.works) {
8203
8203
  signal.throwIfAborted();
8204
- const reason = !isSelectedWork(work) ? work.access === "drop" ? "drop" : "empty" : work.status !== "ready" || !hasCompleteWorkDescription(work) || !work.accessConfirmed || !work.artifact || work.artifact.revision !== work.revision ? "not-ready" : work.binding?.sentBundleId === work.artifact.bundleId && work.binding.sentAccess === work.access ? "sent" : work.artifact.archiveBytes > 5368709120 ? "too-large" : null;
8204
+ const reason = !isSelectedWork(work) ? work.access === "drop" ? "drop" : "empty" : work.status !== "ready" || !hasCompleteWorkDescription(work) || !work.accessConfirmed || !work.artifact || work.artifact.revision !== work.revision ? "not-ready" : !dependencies.artifacts.sessions && work.binding?.sentBundleId === work.artifact.bundleId && work.binding.sentAccess === work.access ? "sent" : work.artifact.archiveBytes > 5368709120 ? "too-large" : null;
8205
8205
  if (reason !== null || work.access === "drop") {
8206
8206
  omitted.push({
8207
8207
  workId: work.id,
@@ -8779,18 +8779,18 @@ function createFrozenPathRouter(repositories, folderPaths) {
8779
8779
  }
8780
8780
  //#endregion
8781
8781
  //#region apps/cli/src/journey/works/project-evidence.ts
8782
- const count$1 = z.number().int().nonnegative();
8782
+ const count$2 = z.number().int().nonnegative();
8783
8783
  const resultSchema = z.object({
8784
8784
  repositories: z.array(z.object({
8785
8785
  id: z.string(),
8786
8786
  path: z.string(),
8787
- references: count$1,
8788
- reads: count$1,
8789
- changes: count$1,
8790
- commands: count$1
8787
+ references: count$2,
8788
+ reads: count$2,
8789
+ changes: count$2,
8790
+ commands: count$2
8791
8791
  })),
8792
8792
  incomplete: z.boolean(),
8793
- ignored: count$1
8793
+ ignored: count$2
8794
8794
  });
8795
8795
  const cacheSchema = z.object({
8796
8796
  key: z.string(),
@@ -9005,7 +9005,7 @@ function createClassificationSessions(storage, processing, stateDir) {
9005
9005
  }
9006
9006
  //#endregion
9007
9007
  //#region apps/cli/src/journey/works/projection.ts
9008
- const digest = (bytes) => createHash("sha256").update(bytes).digest();
9008
+ const digest$1 = (bytes) => createHash("sha256").update(bytes).digest();
9009
9009
  async function buildProjection(directory, key, source, role, signal) {
9010
9010
  const temporary = await mkdtemp(join(directory, ".classification-"));
9011
9011
  const target = join(directory, `classification-${key}`);
@@ -9038,7 +9038,7 @@ async function buildProjection(directory, key, source, role, signal) {
9038
9038
  const entry = Buffer.alloc(48);
9039
9039
  entry.writeBigUInt64LE(BigInt(offset), 0);
9040
9040
  entry.writeBigUInt64LE(BigInt(bytes.length), 8);
9041
- digest(bytes).copy(entry, 16);
9041
+ digest$1(bytes).copy(entry, 16);
9042
9042
  await data.writeFile(bytes);
9043
9043
  await index.writeFile(entry);
9044
9044
  offset += bytes.length;
@@ -9089,7 +9089,7 @@ async function buildProjection(directory, key, source, role, signal) {
9089
9089
  /** Persisted derived content, never a scan receipt. Only the first read replays the capture. */
9090
9090
  async function readClassificationProjection(source, options, signal) {
9091
9091
  signal.throwIfAborted();
9092
- const key = digest(JSON.stringify([
9092
+ const key = digest$1(JSON.stringify([
9093
9093
  1,
9094
9094
  options.snapshotId,
9095
9095
  source,
@@ -9116,7 +9116,7 @@ async function readClassificationProjection(source, options, signal) {
9116
9116
  const offset = Number(entry.readBigUInt64LE(0)), length = Number(entry.readBigUInt64LE(8));
9117
9117
  if (!Number.isSafeInteger(offset) || !Number.isSafeInteger(length) || length > 1048576) throw new ApplicationError("CORRUPTED_STATE");
9118
9118
  const bytes = await readProjectionBytes(dataPath, offset, length, manifest.data);
9119
- if (!digest(bytes).equals(entry.subarray(16))) throw new ApplicationError("CORRUPTED_STATE");
9119
+ if (!digest$1(bytes).equals(entry.subarray(16))) throw new ApplicationError("CORRUPTED_STATE");
9120
9120
  const row = projectionRowSchema.parse(JSON.parse(bytes.toString("utf8")));
9121
9121
  if (row.sessionId !== source.sessionId || row.sourcePartId !== source.capture.partId) throw new ApplicationError("CORRUPTED_STATE");
9122
9122
  rows.push(row);
@@ -9784,6 +9784,82 @@ function protectedSourceText(field, hidden) {
9784
9784
  return text;
9785
9785
  }
9786
9786
  //#endregion
9787
+ //#region apps/cli/src/account/journey/remote-error.ts
9788
+ const refusalSchema = z.object({
9789
+ error: z.string(),
9790
+ preview: sendSettingsSchema.nullable().optional(),
9791
+ resolution: sendResolutionSchema.optional(),
9792
+ /** `unknown_keep_work_ids`: the kept IDs Passport did not accept (ADR 0204). */
9793
+ workIds: z.array(z.string()).optional()
9794
+ });
9795
+ var JourneyRemoteError = class extends Error {
9796
+ status;
9797
+ code;
9798
+ details;
9799
+ constructor(status, code, details, options) {
9800
+ super(code, options);
9801
+ this.name = "JourneyRemoteError";
9802
+ this.status = status;
9803
+ this.code = code;
9804
+ this.details = details;
9805
+ }
9806
+ };
9807
+ //#endregion
9808
+ //#region apps/cli/src/journey/send-sessions.ts
9809
+ /** Build upload identities from frozen local metadata and bytes from the final primary only. */
9810
+ async function* sessionArtifacts(storage, find, journeyId, artifact, signal) {
9811
+ const output = await find(journeyId, artifact.workId, artifact.revision);
9812
+ if (output.bundleId !== artifact.bundleId || output.primary.sha256 !== artifact.sha256 || output.primary.archiveBytes !== artifact.byteLength) throw new ApplicationError("ARTIFACT_IDENTITY_MISMATCH");
9813
+ const base = (await storage.resources({
9814
+ journeyId,
9815
+ kind: "base"
9816
+ })).find((item) => item.kind === "base" && item.workId === artifact.workId && item.inputRevision === output.finalMaskLedger.inputRevision && item.primary.sha256 === output.finalMaskLedger.basePrimarySha256);
9817
+ const plan = (await storage.resources({
9818
+ journeyId,
9819
+ kind: "plan"
9820
+ })).find((item) => item.kind === "plan");
9821
+ if (base?.kind !== "base" || plan?.kind !== "plan") throw new ApplicationError("ARTIFACT_IDENTITY_MISMATCH");
9822
+ const identities = base.sessions.map((binding) => {
9823
+ const session = plan.approval.inventory.sessions.find((item) => item.id === binding.applicationSessionId);
9824
+ const part = session?.parts[0];
9825
+ const lastAt = session?.lastInteractionAt ?? session?.createdAt;
9826
+ if (!session || !part || !lastAt || !Number.isFinite(Date.parse(lastAt))) throw new ApplicationError("ARTIFACT_IDENTITY_MISMATCH");
9827
+ const nativeId = part.nativeEvidence?.identity?.sessionId ?? session.sourceSessionId;
9828
+ const path = part.harnessRelativePath ?? "";
9829
+ if (!path && !nativeId?.trim() || isAbsolute(path) || path.includes("\\") || path.split("/").some((segment) => segment === ".." || segment === ".")) throw new ApplicationError("ARTIFACT_IDENTITY_MISMATCH");
9830
+ return {
9831
+ archiveSessionId: binding.archiveSessionId,
9832
+ relativePath: path,
9833
+ lastAt: new Date(lastAt).toISOString()
9834
+ };
9835
+ });
9836
+ for await (const session of configuredSessionUploads(output.primary, identities, signal)) yield {
9837
+ ...session,
9838
+ async upload(upload, uploadSignal) {
9839
+ await find(journeyId, artifact.workId, artifact.revision);
9840
+ const url = new URL(upload.uploadUrl);
9841
+ if (url.protocol !== "https:" || url.username || url.password || url.hash) throw new ApplicationError("INVALID_ARGUMENTS");
9842
+ const request = {
9843
+ method: "PUT",
9844
+ body: session.stream(),
9845
+ signal: uploadSignal,
9846
+ redirect: "error",
9847
+ credentials: "omit",
9848
+ referrerPolicy: "no-referrer",
9849
+ headers: {
9850
+ "content-type": "application/zip",
9851
+ "content-length": String(session.byteLength),
9852
+ ...upload.headers
9853
+ },
9854
+ duplex: "half"
9855
+ };
9856
+ const response = await fetch(url, request);
9857
+ await response.body?.cancel();
9858
+ if (!response.ok) throw new JourneyRemoteError(response.status, "UPLOAD_FAILED", null);
9859
+ }
9860
+ };
9861
+ }
9862
+ //#endregion
9787
9863
  //#region apps/cli/src/journey/send-artifacts.ts
9788
9864
  function createJourneySendArtifacts(storage) {
9789
9865
  async function find(journeyId, workId, revision) {
@@ -9801,6 +9877,7 @@ function createJourneySendArtifacts(storage) {
9801
9877
  return output;
9802
9878
  }
9803
9879
  return {
9880
+ sessions: (journeyId, artifact, signal) => sessionArtifacts(storage, find, journeyId, artifact, signal),
9804
9881
  async describe(journey, work, signal) {
9805
9882
  const output = await find(journey.journeyId, work.id, work.revision);
9806
9883
  const info = await readConfiguredOutputInfo(output.primary, signal);
@@ -9884,69 +9961,61 @@ async function putPrimary(primary, byteLength, upload, signal) {
9884
9961
  }
9885
9962
  }
9886
9963
  //#endregion
9887
- //#region apps/cli/src/account/journey/state.ts
9888
- function requireSend(snapshot) {
9889
- if (!snapshot?.send) throw new ApplicationError("UNKNOWN_ITEM");
9890
- return {
9891
- ...snapshot,
9892
- send: snapshot.send
9893
- };
9894
- }
9895
- function matchesAction(snapshot, action) {
9896
- return snapshot.journey.revision === action.expectedJourneyRevision && snapshot.send.revision === action.expectedSendRevision;
9897
- }
9898
- async function saveSend(dependencies, snapshot, next, action) {
9899
- return dependencies.storage.commitSend({
9900
- journeyId: snapshot.journey.journeyId,
9901
- expectedJourneyRevision: action?.expectedJourneyRevision ?? snapshot.journey.revision,
9902
- expectedSendRevision: action?.expectedSendRevision ?? (snapshot.send?.id === next.id ? snapshot.send.revision : null),
9903
- requestKey: action?.idempotencyKey ?? randomUUID(),
9904
- requestHash: hash$1(action ?? next),
9905
- state: journeySendSchema.parse({
9906
- ...next,
9907
- status: next.status === "completed" && !next.replaced ? "sending" : next.status,
9908
- revision: (snapshot.send?.id === next.id ? snapshot.send.revision : -1) + 1
9909
- }),
9910
- at: dependencies.now()
9911
- });
9912
- }
9913
- function aggregate(send) {
9914
- if (send.status === "paused") return send;
9915
- const statuses = send.items.map((item) => item.status);
9916
- return {
9917
- ...send,
9918
- status: statuses.length === 0 ? "blocked" : statuses.every((status) => status === "completed") ? "completed" : statuses.some((status) => status === "target" || status === "settings") ? "review" : statuses.some((status) => status === "ready" || status === "uploading") ? "sending" : statuses.some((status) => status === "assessing") ? "waiting" : "blocked"
9919
- };
9964
+ //#region apps/cli/src/account/journey/session-key.ts
9965
+ /** Engine native identity, or harness-root-relative path identity when native identity is absent. */
9966
+ async function sessionKeyFor(input) {
9967
+ const native = input.nativeSessionId;
9968
+ const path = input.relativePath?.replaceAll("\\", "/");
9969
+ if (!input.harness || (!native || !native.trim()) && !path) throw new TypeError("SESSION_RELATIVE_PATH_REQUIRED");
9970
+ const source = native && native.trim() ? JSON.stringify([
9971
+ "native-v1",
9972
+ input.harness,
9973
+ "session",
9974
+ native
9975
+ ]) : `path-v1|${input.harness}|${path}`;
9976
+ return createHash("sha256").update(source, "utf8").digest("hex");
9977
+ }
9978
+ //#endregion
9979
+ //#region apps/cli/src/account/journey/session-access.ts
9980
+ /** Check the entire pending Send before admitting any session mutation. */
9981
+ async function sessionAccessBlocked(dependencies, journeyId, items, inventory, signal) {
9982
+ const sessions = dependencies.artifacts.sessions;
9983
+ if (!sessions) return false;
9984
+ const requestedAccess = /* @__PURE__ */ new Map();
9985
+ const requestedDigests = /* @__PURE__ */ new Map();
9986
+ for (const item of items) {
9987
+ if (item.sessionAssembly || item.status === "completed") continue;
9988
+ const destination = inventory.works.find((work) => work.logicalWorkId === item.workId);
9989
+ if (destination?.access === "removed") throw new ApplicationError("WORK_REMOVED");
9990
+ for await (const session of sessions(journeyId, item.artifact, signal)) {
9991
+ const key = await sessionKeyFor(session);
9992
+ const otherAccess = requestedAccess.get(key);
9993
+ if (otherAccess !== void 0 && otherAccess !== item.access) return true;
9994
+ requestedAccess.set(key, item.access);
9995
+ const otherDigest = requestedDigests.get(key);
9996
+ if (otherDigest !== void 0 && otherDigest !== session.sha256) throw new ApplicationError("SESSION_CONTENT_CONFLICT");
9997
+ requestedDigests.set(key, session.sha256);
9998
+ const source = inventory.works.find((work) => work.sessions.some((entry) => entry.key === key));
9999
+ if (source && source.logicalWorkId !== item.workId && source.access !== item.access) return true;
10000
+ if (destination && destination.access !== item.access && !destination.sessions.some((entry) => entry.key === key && entry.sha256 === session.sha256)) return true;
10001
+ }
10002
+ }
10003
+ return false;
9920
10004
  }
9921
- /** Explicit resume retries recoverable items and recaptures a pending sign-in or profile. */
9922
- function resumeState(send) {
9923
- const profileRequired = ["paused", "sign-in"].includes(send.status) || send.subject === null || ["ACCOUNT_PROFILE_UNAVAILABLE", "ACCOUNT_CHANGED"].includes(send.error ?? "") || send.status === "blocked" && send.items.some((item) => [
9924
- "ready",
9925
- "uploading",
9926
- "assessing",
9927
- "target",
9928
- "settings"
9929
- ].includes(item.status));
9930
- const next = aggregate({
9931
- ...send,
9932
- status: "sending",
9933
- error: null,
9934
- items: send.items.map((item) => item.status === "blocked" && [
9935
- "OPERATION_FAILED",
9936
- "UPLOAD_FAILED",
9937
- "SEND_REMOTE_REFUSED",
9938
- "IDENTITY_UNAVAILABLE",
9939
- "ACCOUNT_SIGNED_OUT"
9940
- ].includes(item.error ?? "") ? {
9941
- ...item,
9942
- status: item.receipt ? item.receipt.state === "awaiting_upload" ? "uploading" : "assessing" : "ready",
9943
- error: null
9944
- } : item)
9945
- });
9946
- return profileRequired && !["blocked", "completed"].includes(next.status) ? {
9947
- ...next,
9948
- status: "sign-in"
9949
- } : next;
10005
+ /** Keep overlapping session ownership serial through membership, including retained members. */
10006
+ async function sessionIndependentItems(dependencies, journeyId, items, inventory, signal) {
10007
+ const sessions = dependencies.artifacts.sessions;
10008
+ if (!sessions) return [];
10009
+ const pending = items.filter((item) => item.status === "uploading" || item.status === "ready").toSorted((left, right) => Number(right.status === "uploading") - Number(left.status === "uploading") || right.artifact.byteLength - left.artifact.byteLength);
10010
+ const claimed = /* @__PURE__ */ new Set();
10011
+ const independent = [];
10012
+ for (const item of pending) {
10013
+ const keys = /* @__PURE__ */ new Set([...inventory.works.find((work) => work.logicalWorkId === item.workId)?.sessions.map((session) => session.key) ?? [], ...item.sessionMembership?.keys ?? []]);
10014
+ for await (const session of sessions(journeyId, item.artifact, signal)) keys.add(await sessionKeyFor(session));
10015
+ if (![...keys].some((key) => claimed.has(key))) independent.push(item);
10016
+ for (const key of keys) claimed.add(key);
10017
+ }
10018
+ return independent;
9950
10019
  }
9951
10020
  //#endregion
9952
10021
  //#region apps/cli/src/account/journey/receipt.ts
@@ -10070,14 +10139,154 @@ async function checkPublicAgentSources(candidates, requestFetch = fetch, signal)
10070
10139
  return results.filter((item) => item !== null);
10071
10140
  }
10072
10141
  //#endregion
10073
- //#region apps/cli/src/account/journey/client.ts
10074
- const refusalSchema = z.object({
10075
- error: z.string(),
10076
- preview: sendSettingsSchema.nullable().optional(),
10077
- resolution: sendResolutionSchema.optional(),
10078
- /** `unknown_keep_work_ids`: the kept IDs Passport did not accept (ADR 0204). */
10079
- workIds: z.array(z.string()).optional()
10142
+ //#region apps/cli/src/account/journey/session-schema.ts
10143
+ const digest = z.string().regex(/^[0-9a-f]{64}$/u);
10144
+ const stored = z.strictObject({
10145
+ key: digest,
10146
+ sessionKey: z.string().regex(/^sess-pilot\/[^/]+\/[^/]+\/[^/]+$/u).nullable(),
10147
+ sha256: digest,
10148
+ byteLength: z.number().int().positive(),
10149
+ lastAt: z.iso.datetime()
10150
+ });
10151
+ const work = z.strictObject({
10152
+ logicalWorkId: z.uuid(),
10153
+ access: z.enum([
10154
+ "public",
10155
+ "private",
10156
+ "removed"
10157
+ ]),
10158
+ sessions: z.array(stored)
10080
10159
  });
10160
+ const cursor = z.string().nullable();
10161
+ const inventory$1 = z.strictObject({
10162
+ works: z.array(work),
10163
+ unassigned: z.array(stored),
10164
+ nextCursor: cursor,
10165
+ unassignedNextCursor: cursor
10166
+ });
10167
+ const reserve = z.discriminatedUnion("state", [z.strictObject({ state: z.literal("already_stored") }), z.strictObject({
10168
+ state: z.literal("awaiting_upload"),
10169
+ uploadUrl: z.url({ protocol: /^https?$/u }),
10170
+ expiresAt: z.iso.datetime(),
10171
+ headers: z.strictObject({ "x-amz-checksum-sha256": z.base64().length(44) })
10172
+ })]);
10173
+ const count$1 = z.number().int().nonnegative();
10174
+ const assembly = z.strictObject({
10175
+ revision: count$1,
10176
+ completedRevision: count$1,
10177
+ scanId: z.uuid().nullable(),
10178
+ assembledByteCount: count$1.nullable(),
10179
+ wallMs: count$1.nullable(),
10180
+ attempts: count$1,
10181
+ error: z.string().nullable()
10182
+ });
10183
+ //#endregion
10184
+ //#region apps/cli/src/account/journey/session-client.ts
10185
+ function continuation(seen, next) {
10186
+ if (next !== null && seen.has(next)) throw new JourneyRemoteError(200, "REMOTE_CURSOR_CYCLE", null);
10187
+ if (next !== null) seen.add(next);
10188
+ return next;
10189
+ }
10190
+ /** Session endpoints share the owner client's authentication, bounds and refusal classification. */
10191
+ function createSessionClient(request) {
10192
+ let writes = Promise.resolve();
10193
+ function mutate(signal, effect) {
10194
+ const result = writes.then(() => {
10195
+ signal.throwIfAborted();
10196
+ return effect();
10197
+ });
10198
+ writes = result.then(() => void 0, () => void 0);
10199
+ return result;
10200
+ }
10201
+ return {
10202
+ async inventory(signal) {
10203
+ const works = /* @__PURE__ */ new Map();
10204
+ const unassigned = /* @__PURE__ */ new Map();
10205
+ let workCursor = null;
10206
+ let unassignedCursor = null;
10207
+ let workDone = false;
10208
+ let unassignedDone = false;
10209
+ const workSeen = /* @__PURE__ */ new Set();
10210
+ const unassignedSeen = /* @__PURE__ */ new Set();
10211
+ do {
10212
+ const query = new URLSearchParams({ limit: "500" });
10213
+ if (workCursor !== null) query.set("cursor", workCursor);
10214
+ if (unassignedCursor !== null) query.set("unassignedCursor", unassignedCursor);
10215
+ const page = await request(`/me/sessions?${query}`, inventory$1, signal, void 0, "GET");
10216
+ if (!workDone) {
10217
+ for (const item of page.works) {
10218
+ const sessions = new Map((works.get(item.logicalWorkId)?.sessions ?? []).map((session) => [session.key, session]));
10219
+ for (const session of item.sessions) sessions.set(session.key, session);
10220
+ works.set(item.logicalWorkId, {
10221
+ ...item,
10222
+ sessions: [...sessions.values()]
10223
+ });
10224
+ }
10225
+ workCursor = continuation(workSeen, page.nextCursor);
10226
+ workDone = workCursor === null;
10227
+ }
10228
+ if (!unassignedDone) {
10229
+ for (const session of page.unassigned) unassigned.set(session.key, session);
10230
+ unassignedCursor = continuation(unassignedSeen, page.unassignedNextCursor);
10231
+ unassignedDone = unassignedCursor === null;
10232
+ }
10233
+ } while (!workDone || !unassignedDone);
10234
+ return {
10235
+ works: [...works.values()],
10236
+ unassigned: [...unassigned.values()]
10237
+ };
10238
+ },
10239
+ async work(id, signal) {
10240
+ let next = null;
10241
+ const seen = /* @__PURE__ */ new Set();
10242
+ const sessions = /* @__PURE__ */ new Map();
10243
+ let value;
10244
+ do {
10245
+ const query = new URLSearchParams({ limit: "500" });
10246
+ if (next !== null) query.set("cursor", next);
10247
+ const page = await request(`/me/works/${encodeURIComponent(id)}/sessions?${query}`, work.extend({ nextCursor: cursor }).refine((item) => item.logicalWorkId === id), signal, void 0, "GET");
10248
+ for (const session of page.sessions) sessions.set(session.key, session);
10249
+ value = {
10250
+ logicalWorkId: page.logicalWorkId,
10251
+ access: page.access,
10252
+ sessions: [...sessions.values()]
10253
+ };
10254
+ next = continuation(seen, page.nextCursor);
10255
+ } while (next !== null);
10256
+ return value;
10257
+ },
10258
+ reserve: (input, signal) => request("/me/sessions/reserve", reserve, signal, input),
10259
+ complete: (input, signal) => mutate(signal, () => request("/me/sessions/complete", z.strictObject({ state: z.literal("stored") }), signal, input, "POST", void 0, 6e5)),
10260
+ membership: (id, input, signal) => mutate(signal, () => request(`/me/works/${encodeURIComponent(id)}/sessions`, z.strictObject({
10261
+ revision: count$1,
10262
+ affectedWorks: z.array(z.uuid())
10263
+ }), signal, input, "PUT", void 0, void 0, false, 202)),
10264
+ async assembly(id, signal) {
10265
+ try {
10266
+ return await request(`/me/works/${encodeURIComponent(id)}/assembly`, assembly, signal, void 0, "GET");
10267
+ } catch (error) {
10268
+ if (error instanceof JourneyRemoteError && error.status === 404 && error.code === "not_found") return null;
10269
+ throw error;
10270
+ }
10271
+ },
10272
+ async assessment(scanId, signal) {
10273
+ return (await request(`/me/tasks/${encodeURIComponent(scanId)}/log`, z.strictObject({
10274
+ taskId: z.literal(scanId),
10275
+ state: z.enum([
10276
+ "accepted",
10277
+ "running",
10278
+ "succeeded",
10279
+ "partial",
10280
+ "failed"
10281
+ ]),
10282
+ progress: z.number().min(0).max(1),
10283
+ lines: z.array(z.string())
10284
+ }), signal, void 0, "GET")).state;
10285
+ }
10286
+ };
10287
+ }
10288
+ //#endregion
10289
+ //#region apps/cli/src/account/journey/client.ts
10081
10290
  const publicProfileSchema = z.strictObject({
10082
10291
  displayName: z.string().trim().min(1).max(300).nullable(),
10083
10292
  avatarUrl: z.url().max(2048).refine((value) => {
@@ -10086,18 +10295,6 @@ const publicProfileSchema = z.strictObject({
10086
10295
  return url.protocol === "https:" && !url.username && !url.password;
10087
10296
  }).nullable()
10088
10297
  });
10089
- var JourneyRemoteError = class extends Error {
10090
- status;
10091
- code;
10092
- details;
10093
- constructor(status, code, details, options) {
10094
- super(code, options);
10095
- this.name = "JourneyRemoteError";
10096
- this.status = status;
10097
- this.code = code;
10098
- this.details = details;
10099
- }
10100
- };
10101
10298
  function apiBase(value) {
10102
10299
  const url = new URL(value);
10103
10300
  const local = [
@@ -10118,7 +10315,7 @@ const completeTimeoutMs = 6e5;
10118
10315
  function createJourneyOwnerClient(options) {
10119
10316
  const base = apiBase(options.apiUrl);
10120
10317
  const requestFetch = options.fetch ?? fetch;
10121
- async function request(path, schema, signal, body, method = "POST", key, timeoutMs = requestTimeoutMs$2, checksumUpload = false) {
10318
+ async function requestWithHeaders(path, schema, signal, body, method = "POST", key, timeoutMs = requestTimeoutMs$2, checksumUpload = false, expectedStatus = 200) {
10122
10319
  const token = await options.authorize();
10123
10320
  signal.throwIfAborted();
10124
10321
  const response = await requestFetch(new URL(path, base), {
@@ -10146,7 +10343,7 @@ function createJourneyOwnerClient(options) {
10146
10343
  throw new JourneyRemoteError(response.status, "REMOTE_PROTOCOL", null);
10147
10344
  }
10148
10345
  const value = await readOwnerJson(response);
10149
- if (response.status !== 200) {
10346
+ if (response.status !== expectedStatus) {
10150
10347
  const refusal = refusalSchema.safeParse(value);
10151
10348
  throw new JourneyRemoteError(response.status, refusal.success ? refusal.data.error : "REMOTE_PROTOCOL", refusal.success ? refusal.data : null);
10152
10349
  }
@@ -10154,11 +10351,18 @@ function createJourneyOwnerClient(options) {
10154
10351
  if (!parsed.success) throw new JourneyRemoteError(response.status, "REMOTE_PROTOCOL", null, { cause: parsed.error });
10155
10352
  await options.authorize();
10156
10353
  signal.throwIfAborted();
10157
- return parsed.data;
10354
+ return {
10355
+ data: parsed.data,
10356
+ headers: response.headers
10357
+ };
10358
+ }
10359
+ async function request(...args) {
10360
+ return (await requestWithHeaders(...args)).data;
10158
10361
  }
10159
10362
  const receipt = (id) => sendReceiptSchema.refine((value) => value.receiptId === id);
10160
10363
  return {
10161
10364
  subject: options.subject,
10365
+ sessions: createSessionClient(request),
10162
10366
  checkAgentSources(candidates, signal) {
10163
10367
  return checkPublicAgentSources(candidates, requestFetch, signal);
10164
10368
  },
@@ -10167,7 +10371,8 @@ function createJourneyOwnerClient(options) {
10167
10371
  signal.throwIfAborted();
10168
10372
  },
10169
10373
  async syncProfile(signal) {
10170
- await request("/me/profile", publicProfileSchema, signal);
10374
+ const { headers } = await requestWithHeaders("/me/profile", publicProfileSchema, signal);
10375
+ return { parallelCompletes: (headers.get("passport-capabilities") ?? "").split(",").some((token) => token.trim() === "parallel-completes") };
10171
10376
  },
10172
10377
  resolve(input, signal) {
10173
10378
  return request("/me/work-bundles/resolve", sendResolutionSchema.refine((value) => value.logicalWorkId === input.logicalWorkId), signal, input);
@@ -10265,6 +10470,23 @@ async function applySettings(item, connection, signal, guard, auto = false) {
10265
10470
  }
10266
10471
  //#endregion
10267
10472
  //#region apps/cli/src/account/journey/resolve.ts
10473
+ /**
10474
+ * Whether the reservation's target survives other Works' completions, which move the owner's
10475
+ * scope version: Passport then still admits a new Work that resolves new with no candidate, or
10476
+ * the same matched binding at the requested access (ADR 0235).
10477
+ */
10478
+ function keepsTarget(resolution, reservation) {
10479
+ const [only] = resolution.candidates;
10480
+ return reservation.target.kind === "new" ? resolution.status === "new" && only === void 0 : resolution.status === "matched" && only?.bindingId === reservation.target.bindingId && only.access === reservation.access;
10481
+ }
10482
+ /**
10483
+ * Whether Passport still admits the reservation under this fresh resolution: the access version
10484
+ * must not move, and a moved scope version needs a kept target (ADR 0235).
10485
+ */
10486
+ function admits(resolution, reservation) {
10487
+ if (resolution.accessVersion !== reservation.expectedAccessVersion) return false;
10488
+ return resolution.scopeVersion === reservation.expectedScopeVersion || keepsTarget(resolution, reservation);
10489
+ }
10268
10490
  function resolveInput(item, bindingId) {
10269
10491
  return {
10270
10492
  logicalWorkId: item.workId,
@@ -10399,6 +10621,7 @@ async function perform(context, item, connection, signal) {
10399
10621
  return next;
10400
10622
  }
10401
10623
  if (!item.receipt) {
10624
+ if (item.resolution && !keepsTarget(item.resolution, item.reservation) && !admits(await connection.resolve(item.reservation.work, signal), item.reservation)) return renewed(item, null);
10402
10625
  const reserved = await connection.reserve(item.operationKey, item.reservation, signal, context.checksumUploads !== false);
10403
10626
  const next = await observe(item, reserved, connection, signal, auto);
10404
10627
  if (context.fresh?.has(item.workId) && reserved.upload && next.status === "uploading") context.fresh.set(item.workId, {
@@ -10427,24 +10650,24 @@ async function perform(context, item, connection, signal) {
10427
10650
  return withoutUpload(item);
10428
10651
  }
10429
10652
  }
10430
- const current = await connection.resolve(item.reservation.work, signal);
10431
- if (current.scopeVersion !== item.reservation.expectedScopeVersion || current.accessVersion !== item.reservation.expectedAccessVersion) return renewed(item, null);
10653
+ if (!admits(await connection.resolve(item.reservation.work, signal), item.reservation)) return renewed(item, null);
10432
10654
  return put(context, item, receipt.receiptId, receipt.upload, connection, signal, guard);
10433
10655
  }
10434
10656
  /** Uploads the bytes to the receipt's signed URL and completes it. */
10435
10657
  async function put(context, item, receiptId, upload, connection, signal, guard) {
10436
10658
  await guard();
10437
10659
  await context.upload(item.artifact, upload, signal);
10660
+ const sent = withoutBusy(item);
10438
10661
  await guard();
10439
10662
  try {
10440
- return observe(item, await connection.complete(receiptId, signal), connection, signal, context.snapshot === true);
10663
+ return observe(sent, await connection.complete(receiptId, signal), connection, signal, context.snapshot === true);
10441
10664
  } catch (error) {
10442
10665
  if (upload.headers !== void 0 && error instanceof JourneyRemoteError && error.code === "bundle_mismatch") return {
10443
10666
  ...renewed(withoutUpload(item), null),
10444
10667
  checksumUploadRefused: true
10445
10668
  };
10446
10669
  if (error instanceof Object) uploaded.set(error, {
10447
- ...item,
10670
+ ...sent,
10448
10671
  uploadedReceiptId: receiptId
10449
10672
  });
10450
10673
  throw error;
@@ -10456,10 +10679,24 @@ function uploadedItem(error) {
10456
10679
  return error instanceof Object ? uploaded.get(error) : void 0;
10457
10680
  }
10458
10681
  function withoutUpload(item) {
10459
- if (item.uploadedReceiptId === void 0) return item;
10460
- const { uploadedReceiptId: _, ...rest } = item;
10682
+ if (item.uploadedReceiptId === void 0 && item.busyRefusals === void 0) return item;
10683
+ const { uploadedReceiptId: _, busyRefusals: __, ...rest } = item;
10461
10684
  return rest;
10462
10685
  }
10686
+ /** The busy count spans one upload and its completion: a Work that leaves the upload keeps none (ADR 0235). */
10687
+ function withoutBusy(item) {
10688
+ const { busyRefusals: _busy, ...rest } = item;
10689
+ return rest;
10690
+ }
10691
+ /**
10692
+ * A `settings_changed` without a resolution after the bytes were staged: other activations or
10693
+ * assessments moved the scope until Passport's retry budget ran out, which says nothing about this
10694
+ * Work's target. Completing again needs no second upload; the advance records `SEND_SCOPE_BUSY`
10695
+ * and waits longer before each further try (ADR 0235).
10696
+ */
10697
+ function stagedRetry(error, item) {
10698
+ return error instanceof JourneyRemoteError && error.code === "settings_changed" && error.details?.resolution === void 0 && item.uploadedReceiptId !== void 0;
10699
+ }
10463
10700
  /** A definite stale refusal can renew the intent; unknown outcomes always retry the same key. */
10464
10701
  async function stepItem(context, item, connection, signal) {
10465
10702
  try {
@@ -10472,10 +10709,11 @@ async function stepItem(context, item, connection, signal) {
10472
10709
  "target_conflict",
10473
10710
  "work_removed"
10474
10711
  ].includes(error.code)) throw error;
10712
+ if (stagedRetry(error, uploadedItem(error) ?? item)) throw error;
10475
10713
  if (item.receipt) {
10476
10714
  const observed = await connection.status(item.receipt.receiptId, signal);
10477
10715
  await connection.assertCurrent(signal);
10478
- if (observed.state !== "awaiting_upload") return observe(item, observed, connection, signal, context.snapshot === true);
10716
+ if (observed.state !== "awaiting_upload") return withoutBusy(await observe(item, observed, connection, signal, context.snapshot === true));
10479
10717
  }
10480
10718
  const candidate = error.details?.resolution?.candidates[0];
10481
10719
  if (error.code === "target_conflict" && item.target?.kind === "existing" && candidate?.bindingId === item.target.bindingId) return {
@@ -10488,6 +10726,17 @@ async function stepItem(context, item, connection, signal) {
10488
10726
  status: "blocked",
10489
10727
  error: "WORK_REMOVED"
10490
10728
  };
10729
+ if (error.code === "settings_changed" && error.details?.resolution === void 0) {
10730
+ const busyRefusals = (item.busyRefusals ?? 0) + 1;
10731
+ if (item.receipt) return {
10732
+ ...item,
10733
+ busyRefusals
10734
+ };
10735
+ return {
10736
+ ...renewed(item, null),
10737
+ busyRefusals
10738
+ };
10739
+ }
10491
10740
  return renewed(item, "SETTINGS_CHANGED");
10492
10741
  }
10493
10742
  }
@@ -10504,18 +10753,268 @@ function renewed(item, error) {
10504
10753
  error
10505
10754
  };
10506
10755
  }
10756
+ //#endregion
10757
+ //#region apps/cli/src/account/journey/session-step.ts
10758
+ /** Reconcile against completed server facts on every retry, including after process loss. */
10759
+ async function stepSessionWork(dependencies, journeyId, context, item, connection, signal) {
10760
+ const remote = connection.sessions;
10761
+ const sessions = dependencies.artifacts.sessions;
10762
+ if (!remote || !sessions) throw new ApplicationError("INVALID_STATE");
10763
+ async function guard() {
10764
+ await connection.assertCurrent(signal);
10765
+ await context.active();
10766
+ signal.throwIfAborted();
10767
+ }
10768
+ await guard();
10769
+ if (!item.sessionAssembly && (item.status !== "uploading" || item.receipt || item.reservation)) return {
10770
+ ...item,
10771
+ receipt: null,
10772
+ reservation: null,
10773
+ target: null,
10774
+ previousReceipts: item.receipt ? [...item.previousReceipts, item.receipt] : item.previousReceipts,
10775
+ status: "uploading",
10776
+ error: null
10777
+ };
10778
+ if (item.sessionAssembly) {
10779
+ const assembly = await remote.assembly(item.workId, signal);
10780
+ if (!assembly) throw new ApplicationError("INVALID_STATE");
10781
+ if (assembly.revision < item.sessionAssembly.revision) return item;
10782
+ if (assembly.error) return {
10783
+ ...item,
10784
+ status: "blocked",
10785
+ error: "ASSESSMENT_INCOMPLETE"
10786
+ };
10787
+ const scanId = assembly.completedRevision === assembly.revision ? assembly.scanId : null;
10788
+ const state = scanId ? await remote.assessment(scanId, signal) : "accepted";
10789
+ await guard();
10790
+ const failed = state === "failed" || state === "partial";
10791
+ return {
10792
+ ...item,
10793
+ sessionAssembly: {
10794
+ revision: assembly.revision,
10795
+ scanId
10796
+ },
10797
+ status: state === "succeeded" ? "completed" : failed ? "blocked" : "assessing",
10798
+ error: failed ? "ASSESSMENT_INCOMPLETE" : null
10799
+ };
10800
+ }
10801
+ if (item.sessionMembership) {
10802
+ const intent = item.sessionMembership;
10803
+ const assembly = await remote.assembly(intent.workId, signal);
10804
+ const conflict = async () => {
10805
+ const current = await remote.inventory(signal);
10806
+ const revision = await remote.assembly(intent.workId, signal);
10807
+ await guard();
10808
+ return {
10809
+ ...item,
10810
+ status: "blocked",
10811
+ error: "SESSION_MEMBERSHIP_CONFLICT",
10812
+ membershipConflict: {
10813
+ revision: revision?.revision ?? 0,
10814
+ keys: current.works.find((work) => work.logicalWorkId === intent.workId)?.sessions.map((session) => session.key) ?? []
10815
+ }
10816
+ };
10817
+ };
10818
+ if ((assembly?.revision ?? 0) !== intent.expectedRevision) return conflict();
10819
+ await guard();
10820
+ try {
10821
+ const { workId, ...input } = intent;
10822
+ const result = await remote.membership(workId, input, signal);
10823
+ await guard();
10824
+ return {
10825
+ ...item,
10826
+ sessionAssembly: {
10827
+ revision: result.revision,
10828
+ scanId: null
10829
+ },
10830
+ status: "assessing",
10831
+ error: null
10832
+ };
10833
+ } catch (error) {
10834
+ if (error instanceof JourneyRemoteError && error.status === 409) return conflict();
10835
+ throw error;
10836
+ }
10837
+ }
10838
+ const assembly = await remote.assembly(item.workId, signal);
10839
+ const inventory = await remote.inventory(signal);
10840
+ const stored = new Map([...inventory.unassigned, ...inventory.works.flatMap((work) => work.sessions)].map((session) => [session.key, session]));
10841
+ const keys = [];
10842
+ for await (const session of sessions(journeyId, item.artifact, signal)) {
10843
+ const key = await sessionKeyFor(session);
10844
+ keys.push(key);
10845
+ if (stored.get(key)?.sha256 === session.sha256) continue;
10846
+ const input = {
10847
+ key,
10848
+ sha256: session.sha256,
10849
+ byteLength: session.byteLength,
10850
+ crc32: session.crc32,
10851
+ lastAt: session.lastAt,
10852
+ harness: session.harness
10853
+ };
10854
+ if (item.sessionUpload?.key !== key || item.sessionUpload.sha256 !== session.sha256) return {
10855
+ ...item,
10856
+ sessionUpload: {
10857
+ key,
10858
+ sha256: session.sha256
10859
+ }
10860
+ };
10861
+ await guard();
10862
+ try {
10863
+ await remote.complete({
10864
+ ...input,
10865
+ metadata: session.metadata
10866
+ }, signal);
10867
+ } catch (error) {
10868
+ if (!(error instanceof JourneyRemoteError) || error.status !== 409 || error.code !== "upload_incomplete") throw error;
10869
+ await guard();
10870
+ const reservation = await remote.reserve(input, signal);
10871
+ if (reservation.state === "awaiting_upload") {
10872
+ await guard();
10873
+ await session.upload(reservation, signal);
10874
+ await guard();
10875
+ await remote.complete({
10876
+ ...input,
10877
+ metadata: session.metadata
10878
+ }, signal);
10879
+ }
10880
+ }
10881
+ await guard();
10882
+ return {
10883
+ ...item,
10884
+ sessionUpload: null
10885
+ };
10886
+ }
10887
+ await guard();
10888
+ const current = inventory.works.find((work) => work.logicalWorkId === item.workId);
10889
+ if (current && current.access !== item.access) {
10890
+ const resolution = await connection.resolve(resolveInput(item, context.bindingId), signal);
10891
+ const [candidate] = resolution.candidates;
10892
+ if (resolution.status === "blocked" || candidate?.access === "removed") throw new ApplicationError("WORK_REMOVED");
10893
+ if (resolution.status !== "matched" || resolution.logicalWorkId !== item.workId || resolution.candidates.length !== 1 || !candidate || context.bindingId !== void 0 && candidate.bindingId !== context.bindingId) throw new ApplicationError("INVALID_STATE");
10894
+ const settings = await connection.preview(candidate.workId, item.access, signal);
10895
+ await guard();
10896
+ await connection.confirm(settings, signal);
10897
+ }
10898
+ await guard();
10899
+ return {
10900
+ ...item,
10901
+ sessionUpload: null,
10902
+ sessionMembership: {
10903
+ workId: item.workId,
10904
+ expectedRevision: assembly?.revision ?? 0,
10905
+ keys: [.../* @__PURE__ */ new Set([...current?.sessions.map((session) => session.key) ?? [], ...keys])],
10906
+ title: item.title,
10907
+ access: item.access
10908
+ }
10909
+ };
10910
+ }
10911
+ //#endregion
10912
+ //#region apps/cli/src/account/journey/state.ts
10913
+ function requireSend(snapshot) {
10914
+ if (!snapshot?.send) throw new ApplicationError("UNKNOWN_ITEM");
10915
+ return {
10916
+ ...snapshot,
10917
+ send: snapshot.send
10918
+ };
10919
+ }
10920
+ function matchesAction(snapshot, action) {
10921
+ return snapshot.journey.revision === action.expectedJourneyRevision && snapshot.send.revision === action.expectedSendRevision;
10922
+ }
10923
+ /** Preserve an in-flight whole-Work protocol across upgrades to the session sender. */
10924
+ function usesSessionSend(dependencies, send) {
10925
+ if (!dependencies.artifacts.sessions) return false;
10926
+ if (send.items.some((item) => item.sessionUpload !== void 0 || item.sessionMembership || item.sessionAssembly)) return true;
10927
+ return !send.items.some((item) => item.receipt || item.reservation || item.previousReceipts.length > 0);
10928
+ }
10929
+ async function saveSend(dependencies, snapshot, next, action) {
10930
+ return dependencies.storage.commitSend({
10931
+ journeyId: snapshot.journey.journeyId,
10932
+ expectedJourneyRevision: action?.expectedJourneyRevision ?? snapshot.journey.revision,
10933
+ expectedSendRevision: action?.expectedSendRevision ?? (snapshot.send?.id === next.id ? snapshot.send.revision : null),
10934
+ requestKey: action?.idempotencyKey ?? randomUUID(),
10935
+ requestHash: hash$1(action ?? next),
10936
+ state: journeySendSchema.parse({
10937
+ ...next,
10938
+ status: snapshot.send?.id === next.id && snapshot.send.status === "completed" ? "completed" : next.status,
10939
+ revision: (snapshot.send?.id === next.id ? snapshot.send.revision : -1) + 1
10940
+ }),
10941
+ at: dependencies.now()
10942
+ });
10943
+ }
10944
+ function aggregate(send) {
10945
+ if (send.status === "paused") return send;
10946
+ const statuses = send.items.map((item) => item.status);
10947
+ return {
10948
+ ...send,
10949
+ status: statuses.length === 0 ? "blocked" : statuses.every((status) => status === "completed") ? "completed" : statuses.some((status) => status === "target" || status === "settings") ? "review" : statuses.some((status) => status === "ready" || status === "uploading") ? "sending" : statuses.some((status) => status === "assessing") ? "waiting" : "blocked"
10950
+ };
10951
+ }
10952
+ /**
10953
+ * A completed Send that Passport has not finished with: a Work is still assessed or reapplied
10954
+ * (ADR 0233). A blocked Work waits on the person's Retry, not on Passport.
10955
+ */
10956
+ function awaitingPassport(send) {
10957
+ return send.status === "completed" && send.items.some((item) => !["completed", "blocked"].includes(item.status));
10958
+ }
10959
+ /** Explicit resume retries recoverable items and recaptures a pending sign-in or profile. */
10960
+ function resumeState(send) {
10961
+ if (send.status !== "completed" && send.items.length && send.items.every((item) => item.status === "completed") && send.items.some((item) => !item.sessionAssembly)) return {
10962
+ ...send,
10963
+ status: "sign-in",
10964
+ error: null
10965
+ };
10966
+ const profileRequired = ["paused", "sign-in"].includes(send.status) || send.subject === null || ["ACCOUNT_PROFILE_UNAVAILABLE", "ACCOUNT_CHANGED"].includes(send.error ?? "") || send.status === "blocked" && send.items.some((item) => [
10967
+ "ready",
10968
+ "uploading",
10969
+ "assessing",
10970
+ "target",
10971
+ "settings"
10972
+ ].includes(item.status));
10973
+ const next = aggregate({
10974
+ ...send,
10975
+ status: "sending",
10976
+ error: null,
10977
+ items: send.items.map((item) => item.status === "blocked" && [
10978
+ "OPERATION_FAILED",
10979
+ "UPLOAD_FAILED",
10980
+ "SEND_REMOTE_REFUSED",
10981
+ "SESSION_MOVE_ACCESS_REQUIRED",
10982
+ "IDENTITY_UNAVAILABLE",
10983
+ "ACCOUNT_SIGNED_OUT"
10984
+ ].includes(item.error ?? "") ? {
10985
+ ...item,
10986
+ status: item.receipt ? item.receipt.state === "awaiting_upload" ? "uploading" : "assessing" : "ready",
10987
+ error: null
10988
+ } : item)
10989
+ });
10990
+ return profileRequired && !["blocked", "completed"].includes(next.status) ? {
10991
+ ...next,
10992
+ status: "sign-in"
10993
+ } : next;
10994
+ }
10507
10995
  const confirmedSettings = (item) => item.settings !== null && item.settingsConfirmed;
10996
+ const started = (item) => Number(item.status === "uploading");
10997
+ /**
10998
+ * Works that share a workspace path or a git remote: once one completes, Passport offers it as
10999
+ * the other's candidate, so the other's target no longer resolves the same (ADR 0235).
11000
+ */
11001
+ function related(left, right) {
11002
+ return left.artifact.sources.some((one) => right.artifact.sources.some((other) => one.pathHash !== null && one.pathHash === other.pathHash || one.remoteHashes.some((hash) => other.remoteHashes.includes(hash))));
11003
+ }
10508
11004
  /**
10509
- * The Works one advance steps together. Upload work comes before assessment checks, smallest
10510
- * ZIP first. Completing
10511
- * an upload advances the owner's scope version, which every resolution and reservation carries,
10512
- * so one Work's resolve-to-complete steps never overlap another's: uploads step one Work at a
10513
- * time (ADR 0214). Assessment checks change nothing and step up to three together; a confirmed
10514
- * linked Settings change alters access versions, so it always steps alone.
11005
+ * The Works one advance steps together. Upload work comes before assessment checks: up to three
11006
+ * Works, started uploads first, then the largest ZIP, so the slowest Work starts earliest.
11007
+ * Completing an upload moves the owner's scope version, which Passport accepts while a Work's
11008
+ * own target resolves the same (ADR 0235). A Work related to one already picked waits for the
11009
+ * next batch. A reserved Work whose target would not survive that steps alone, and so does a
11010
+ * confirmed linked Settings change, which moves the access version every reservation carries,
11011
+ * once no upload has started. Assessment checks change nothing and step up to three together.
10515
11012
  */
10516
- function pickBatch(items, now = Date.now()) {
10517
- const uploading = items.find((item) => item.status === "uploading") ?? items.filter((item) => item.status === "ready").reduce((smallest, item) => !smallest || item.artifact.byteLength < smallest.artifact.byteLength ? item : smallest, void 0);
10518
- if (uploading) return [uploading];
11013
+ function pickBatch(items, now, limit) {
11014
+ const uploads = items.filter((item) => item.status === "uploading" || item.status === "ready").toSorted((left, right) => started(right) - started(left) || right.artifact.byteLength - left.artifact.byteLength);
11015
+ const alone = uploads.find((item) => item.status === "uploading" && item.reservation !== null && item.resolution !== null && !keepsTarget(item.resolution, item.reservation)) ?? (uploads[0] && confirmedSettings(uploads[0]) ? uploads[0] : void 0);
11016
+ if (alone) return [alone];
11017
+ if (uploads.length) return uploads.filter((item) => !confirmedSettings(item)).reduce((batch, item) => batch.length < limit && !batch.some((picked) => related(picked, item)) ? [...batch, item] : batch, []);
10519
11018
  const assessing = items.filter((item) => item.status === "assessing");
10520
11019
  const first = assessing[0];
10521
11020
  if (!first) return [];
@@ -10554,11 +11053,12 @@ async function prepareProfile(dependencies, snapshot, connection, signal) {
10554
11053
  async function captureProfile(connection, guard, signal, failure) {
10555
11054
  try {
10556
11055
  await guard();
10557
- await connection.syncProfile(signal);
11056
+ const { parallelCompletes } = await connection.syncProfile(signal);
10558
11057
  await guard();
10559
11058
  return {
10560
11059
  status: "sending",
10561
- error: null
11060
+ error: null,
11061
+ parallelCompletes: parallelCompletes ? true : void 0
10562
11062
  };
10563
11063
  } catch (error) {
10564
11064
  if (signal.aborted) throw error;
@@ -10583,6 +11083,8 @@ async function captureProfile(connection, guard, signal, failure) {
10583
11083
  }
10584
11084
  //#endregion
10585
11085
  //#region apps/cli/src/account/journey/advance.ts
11086
+ /** A Work Passport is done with: assessed, or stopped unless Resume retries it. */
11087
+ const settled = (item) => ["completed", "blocked"].includes(item.status);
10586
11088
  /** The stored Send still holds exactly this Work state, whatever else changed meanwhile. */
10587
11089
  function sameItem(items, item) {
10588
11090
  return JSON.stringify(items.find((value) => value.workId === item.workId)) === JSON.stringify(item);
@@ -10602,22 +11104,50 @@ async function advanceItems(host, signal) {
10602
11104
  status: "blocked",
10603
11105
  error: "ACCOUNT_CHANGED"
10604
11106
  });
11107
+ if (send.items.length && send.items.every((item) => item.status === "completed")) {
11108
+ const items = send.items.map((item) => {
11109
+ if (item.sessionAssembly) return item;
11110
+ try {
11111
+ if (item.receipt && acceptReceipt(item, {
11112
+ ...item.receipt,
11113
+ upload: null
11114
+ }).status === "completed") return item;
11115
+ } catch (error) {
11116
+ host.failure("LEGACY_SEND_RECOVERY_REQUIRED", error);
11117
+ }
11118
+ return {
11119
+ ...item,
11120
+ status: "blocked",
11121
+ error: "LEGACY_SEND_RECOVERY_REQUIRED"
11122
+ };
11123
+ });
11124
+ return host.save({
11125
+ ...send,
11126
+ items,
11127
+ status: items.every((item) => item.status === "completed") ? "completed" : "blocked",
11128
+ error: items.some((item) => item.status === "blocked") ? "LEGACY_SEND_RECOVERY_REQUIRED" : null
11129
+ });
11130
+ }
10605
11131
  if (send.subject === null || send.status === "sign-in") {
11132
+ const { parallelCompletes: captured, ...profile } = await host.profile(connection, signal);
11133
+ const parallelCompletes = send.subject === null ? captured : send.parallelCompletes && captured;
10606
11134
  const prepared = {
10607
11135
  ...send,
10608
11136
  subject: connection.subject,
10609
- ...await host.profile(connection, signal)
11137
+ ...profile
10610
11138
  };
11139
+ if (parallelCompletes) prepared.parallelCompletes = true;
11140
+ else delete prepared.parallelCompletes;
10611
11141
  return host.save(prepared.status === "sending" ? aggregate(prepared) : prepared);
10612
11142
  }
10613
- const batch = pickBatch(send.items);
10614
- if (host.finish && batch.every((item) => item.status === "assessing")) {
10615
- const finished = await host.finish(connection, signal);
10616
- if (finished !== null) return finished;
10617
- }
11143
+ const blocked = await host.preflight?.(connection, signal);
11144
+ if (blocked) return blocked;
11145
+ const batch = host.batch?.() ?? pickBatch(send.items, Date.now(), send.parallelCompletes ? 3 : 1);
10618
11146
  if (!batch.length) return host.applied();
11147
+ const busy = Math.max(0, ...batch.map((item) => item.busyRefusals ?? 0));
11148
+ if (busy) await wait(busyDelay(busy), signal);
10619
11149
  const checksumUploads = !send.items.some((item) => item.checksumUploadRefused);
10620
- const results = await Promise.allSettled(batch.map((item) => stepItem({
11150
+ const results = await Promise.allSettled(batch.map((item) => (host.step ?? stepItem)({
10621
11151
  ...host.context(item),
10622
11152
  checksumUploads
10623
11153
  }, item, connection, signal)));
@@ -10641,7 +11171,15 @@ async function advanceItems(host, signal) {
10641
11171
  if (progressed) next.set(item.workId, progressed);
10642
11172
  if (cause instanceof JourneyRemoteError && cause.status === 401 || isCode(cause, "ACCOUNT_SIGNED_OUT")) signIn = true;
10643
11173
  else if (isCode(cause, "IDENTITY_REJECTED")) changed = true;
10644
- else if (isInterrupted(cause) || isTransientRefusal(cause)) {
11174
+ else if (stagedRetry(cause, progressed ?? item)) {
11175
+ const staged = progressed ?? item;
11176
+ next.set(item.workId, {
11177
+ ...staged,
11178
+ busyRefusals: (staged.busyRefusals ?? 0) + 1
11179
+ });
11180
+ host.failure("SEND_SCOPE_BUSY", cause);
11181
+ error = "SEND_SCOPE_BUSY";
11182
+ } else if (isInterrupted(cause) || isTransientRefusal(cause)) {
10645
11183
  host.failure("SEND_INTERRUPTED", cause);
10646
11184
  error = "SEND_INTERRUPTED";
10647
11185
  } else {
@@ -10696,42 +11234,6 @@ function blockSend(host, error, signal) {
10696
11234
  error: code
10697
11235
  });
10698
11236
  }
10699
- /**
10700
- * The public Work IDs this Send keeps on the owner's Passport: every sent Work's receipt and
10701
- * every Work left out only because the same bundle and access were already sent to this
10702
- * account. Dropped and other omitted Works are not kept (ADR 0204).
10703
- */
10704
- function keepWorkIds(journey, send) {
10705
- const unchanged = send.omitted.flatMap((omitted) => {
10706
- const binding = journey.works.find((work) => work.id === omitted.workId)?.binding;
10707
- return omitted.reason === "sent" && binding && binding.subject === send.subject ? [binding.publicWorkId] : [];
10708
- });
10709
- return [.../* @__PURE__ */ new Set([...send.items.flatMap((item) => item.receipt?.workId ?? []), ...unchanged])];
10710
- }
10711
- /**
10712
- * Passport refused kept IDs it no longer has: a Work omitted as already sent whose Passport copy
10713
- * an earlier Send retired. Those Works are sent again (a retired Work no longer matches, so each
10714
- * becomes a new Work) and replace runs again once they are uploaded. Null when no refused ID is one.
10715
- */
10716
- async function requeueRetired(journey, send, error, signal, dependencies) {
10717
- if (error.code !== "unknown_keep_work_ids") return null;
10718
- const refused = new Set(error.details?.workIds ?? []);
10719
- const works = send.omitted.flatMap((omitted) => {
10720
- const work = journey.works.find((value) => value.id === omitted.workId);
10721
- return omitted.reason === "sent" && work?.binding?.subject === send.subject && refused.has(work.binding.publicWorkId) ? [work] : [];
10722
- });
10723
- if (!works.length) return null;
10724
- const items = [];
10725
- for (const work of works) items.push(await describeItem(journey, work, dependencies, signal));
10726
- const ids = new Set(works.map((work) => work.id));
10727
- return {
10728
- ...send,
10729
- status: "sending",
10730
- error: null,
10731
- items: [...send.items, ...items],
10732
- omitted: send.omitted.filter((omitted) => !ids.has(omitted.workId))
10733
- };
10734
- }
10735
11237
  /** A Send saved before ADR 0204 may wait on a target or Settings question that is never asked. */
10736
11238
  function withoutQuestions(send) {
10737
11239
  if (!send.items.some((item) => ["target", "settings"].includes(item.status))) return send;
@@ -10749,6 +11251,18 @@ function withoutQuestions(send) {
10749
11251
  };
10750
11252
  return send.status === "review" ? aggregate(next) : next;
10751
11253
  }
11254
+ /** Native completion follows acknowledged upload/membership, independently of assessment. */
11255
+ function completeAtUpload(send, sessions) {
11256
+ if (!send.confirmedAt || ![
11257
+ "sending",
11258
+ "waiting",
11259
+ "completed"
11260
+ ].includes(send.status) || !send.items.length || !send.items.every((item) => sessions ? !!item.sessionAssembly : ["assessing", "completed"].includes(item.status) && !!item.receipt && item.receipt.state !== "awaiting_upload")) return send;
11261
+ return {
11262
+ ...send,
11263
+ status: "completed"
11264
+ };
11265
+ }
10752
11266
  async function advanceSend(dependencies, address, signal, chain) {
10753
11267
  const snapshot = requireSend(await dependencies.storage.loadSend(address.journeyId, address.sendId));
10754
11268
  if (chain) chain.revision = snapshot.send.revision;
@@ -10756,18 +11270,29 @@ async function advanceSend(dependencies, address, signal, chain) {
10756
11270
  ...snapshot,
10757
11271
  status: "stale"
10758
11272
  };
10759
- if (snapshot.journey.phase === "paused" || !snapshot.send.confirmedAt || [
10760
- "paused",
10761
- "completed",
10762
- "prepared"
10763
- ].includes(snapshot.send.status)) return {
11273
+ const completed = snapshot.send.status === "completed";
11274
+ if (snapshot.journey.phase === "paused" || !snapshot.send.confirmedAt || ["paused", "prepared"].includes(snapshot.send.status) || completed && snapshot.send.items.every(settled)) return {
10764
11275
  ...snapshot,
10765
11276
  status: "applied"
10766
11277
  };
10767
- const save = (next) => saveSend(dependencies, snapshot, next);
11278
+ const save = (next) => !completed ? saveSend(dependencies, snapshot, completeAtUpload(next, usesSessionSend(dependencies, next))) : JSON.stringify(next.items) === JSON.stringify(snapshot.send.items) ? Promise.resolve({
11279
+ ...snapshot,
11280
+ status: "applied"
11281
+ }) : saveSend(dependencies, snapshot, {
11282
+ ...snapshot.send,
11283
+ items: next.items
11284
+ });
10768
11285
  try {
10769
11286
  assertScope(snapshot.journey, snapshot.send);
10770
11287
  } catch (error) {
11288
+ if (completed) return save({
11289
+ ...snapshot.send,
11290
+ items: snapshot.send.items.map((item) => settled(item) ? item : {
11291
+ ...item,
11292
+ status: "blocked",
11293
+ error: "SEND_SCOPE_CHANGED"
11294
+ })
11295
+ });
10771
11296
  return blockSend({
10772
11297
  send: snapshot.send,
10773
11298
  failure: dependencies.failure,
@@ -10780,14 +11305,39 @@ async function advanceSend(dependencies, address, signal, chain) {
10780
11305
  };
10781
11306
  const send = withoutQuestions(snapshot.send);
10782
11307
  if (send !== snapshot.send) return save(send);
11308
+ let independentItems = [];
10783
11309
  return advanceItems({
10784
11310
  send,
10785
11311
  failure: dependencies.failure,
11312
+ ...usesSessionSend(dependencies, send) ? {
11313
+ batch: () => {
11314
+ const limit = send.parallelCompletes ? 3 : 1;
11315
+ return independentItems.length ? pickBatch(independentItems, Date.now(), limit) : pickBatch(send.items, Date.now(), limit);
11316
+ },
11317
+ preflight: async (connection, preflightSignal) => {
11318
+ if (!connection.sessions) throw new ApplicationError("INVALID_STATE");
11319
+ const inventory = await connection.sessions.inventory(preflightSignal);
11320
+ if (!await sessionAccessBlocked(dependencies, snapshot.journey.journeyId, send.items, inventory, preflightSignal)) {
11321
+ independentItems = await sessionIndependentItems(dependencies, snapshot.journey.journeyId, send.items, inventory, preflightSignal);
11322
+ return null;
11323
+ }
11324
+ await connection.assertCurrent(preflightSignal);
11325
+ return save({
11326
+ ...send,
11327
+ status: "blocked",
11328
+ items: send.items.map((item) => item.sessionAssembly || item.status === "completed" ? item : {
11329
+ ...item,
11330
+ status: "blocked",
11331
+ error: "SESSION_MOVE_ACCESS_REQUIRED"
11332
+ })
11333
+ });
11334
+ },
11335
+ step: (context, item, connection, stepSignal) => stepSessionWork(dependencies, snapshot.journey.journeyId, context, item, connection, stepSignal)
11336
+ } : {},
10786
11337
  connect: chain?.connect ?? dependencies.auth.connect,
10787
11338
  profile: (connection, profileSignal) => prepareProfile(dependencies, snapshot, connection, profileSignal),
10788
11339
  context: (item) => {
10789
11340
  const binding = snapshot.journey.works.find((value) => value.id === item.workId)?.binding;
10790
- const retired = !item.receipt && binding?.sentBundleId === item.artifact.bundleId && binding.sentAccess === item.access;
10791
11341
  return {
10792
11342
  snapshot: true,
10793
11343
  ...chain ? { fresh: chain.fresh } : {},
@@ -10796,7 +11346,7 @@ async function advanceSend(dependencies, address, signal, chain) {
10796
11346
  if (current.send.id !== snapshot.send.id || !sameItem(current.send.items, item) || current.send.status === "paused" || current.journey.phase === "paused") throw new ApplicationError("REVISION_CONFLICT");
10797
11347
  assertScope(current.journey, current.send);
10798
11348
  },
10799
- bindingId: binding?.subject === send.subject && !retired ? binding.bundleRecordId : void 0,
11349
+ bindingId: binding?.subject === send.subject ? binding.bundleRecordId : void 0,
10800
11350
  upload: async (artifact, upload, uploadSignal) => {
10801
11351
  const started = performance.now();
10802
11352
  await dependencies.artifacts.upload(snapshot.journey.journeyId, artifact, upload, uploadSignal);
@@ -10804,36 +11354,6 @@ async function advanceSend(dependencies, address, signal, chain) {
10804
11354
  }
10805
11355
  };
10806
11356
  },
10807
- finish: async (connection, finishSignal) => {
10808
- if (send.replaced || !send.items.every((item) => ["assessing", "completed"].includes(item.status))) return null;
10809
- let retired;
10810
- try {
10811
- ({retiredWorkIds: retired} = await connection.replace(keepWorkIds(snapshot.journey, send), finishSignal));
10812
- } catch (error) {
10813
- if (error instanceof JourneyRemoteError && error.status === 401) return save({
10814
- ...send,
10815
- status: "sign-in",
10816
- error: null
10817
- });
10818
- if (!(error instanceof JourneyRemoteError) || error.status !== 400) throw error;
10819
- const requeued = await requeueRetired(snapshot.journey, send, error, finishSignal, dependencies);
10820
- if (requeued) return save(requeued);
10821
- dependencies.failure("SEND_REPLACE_REFUSED", error);
10822
- return save({
10823
- ...send,
10824
- status: "blocked",
10825
- error: "SEND_REPLACE_REFUSED"
10826
- });
10827
- }
10828
- return save(aggregate({
10829
- ...send,
10830
- error: null,
10831
- replaced: {
10832
- at: dependencies.now(),
10833
- retiredWorkCount: retired.length
10834
- }
10835
- }));
10836
- },
10837
11357
  save,
10838
11358
  applied: () => ({
10839
11359
  ...snapshot,
@@ -10845,6 +11365,28 @@ async function advanceSend(dependencies, address, signal, chain) {
10845
11365
  })
10846
11366
  }, signal);
10847
11367
  }
11368
+ /** The first wait after a bare refusal of staged bytes, doubled per refusal up to the cap. */
11369
+ const busyBaseMs = 2e3;
11370
+ /** Short, because a waiting advance holds the panel's single advance in flight. */
11371
+ const busyMaxMs = 16e3;
11372
+ function busyDelay(refusals) {
11373
+ return Math.min(busyBaseMs * 2 ** (refusals - 1), busyMaxMs);
11374
+ }
11375
+ /** Waits `ms`; Pause aborts the wait with the advance. */
11376
+ function wait(ms, signal) {
11377
+ return new Promise((resolve, reject) => {
11378
+ signal.throwIfAborted();
11379
+ const stop = () => {
11380
+ clearTimeout(timer);
11381
+ reject(signal.reason);
11382
+ };
11383
+ const timer = setTimeout(() => {
11384
+ signal.removeEventListener("abort", stop);
11385
+ resolve();
11386
+ }, ms);
11387
+ signal.addEventListener("abort", stop, { once: true });
11388
+ });
11389
+ }
10848
11390
  /**
10849
11391
  * A refusal Passport answers while it or Identity is briefly unavailable. It says nothing about
10850
11392
  * the Work, so the step is retried instead of blocking it (the 2026-11-01 recorded run blocked a
@@ -12363,12 +12905,12 @@ function createJourneyFiles(storage) {
12363
12905
  };
12364
12906
  }
12365
12907
  /**
12366
- * One advance carries the Work being uploaded through its phases with one verified connection,
12367
- * instead of one phase per panel round trip, and uploads a receipt it just resolved and reserved
12368
- * without reading it again (ADR 0229). Each phase is still fenced and saved on its own, so Pause, a
12369
- * revision change or a failure stops the chain at the next phase exactly as between advances.
12370
- * The advance returns once that Work leaves its upload phases, so the next Work starts from the
12371
- * caller's next advance as before.
12908
+ * One advance carries the Works being uploaded, up to three at once (ADR 0235), through their
12909
+ * phases with one verified connection, instead of one phase per panel round trip, and uploads a
12910
+ * receipt it just resolved and reserved without reading it again (ADR 0229). Each phase is still
12911
+ * fenced and saved on its own, so Pause, a revision change or a failure stops the chain at the
12912
+ * next phase exactly as between advances. The advance returns after three phases or once no
12913
+ * picked Work is mid-upload, so later Works continue from the caller's next advance.
12372
12914
  */
12373
12915
  async function advanceWork(dependencies, address, signal, phases = 3) {
12374
12916
  let connection;
@@ -12386,7 +12928,7 @@ async function advanceWork(dependencies, address, signal, phases = 3) {
12386
12928
  /** The phase saved progress and left its Work mid-upload, with nothing else to report. */
12387
12929
  function uploading(result, before) {
12388
12930
  const send = result.send;
12389
- return result.status === "applied" && send !== null && before !== void 0 && send.revision > before && send.status === "sending" && send.error === null && pickBatch(send.items)[0]?.status === "uploading";
12931
+ return result.status === "applied" && send !== null && before !== void 0 && send.revision > before && send.status === "sending" && send.error === null && pickBatch(send.items, Date.now(), send.parallelCompletes ? 3 : 1)[0]?.status === "uploading";
12390
12932
  }
12391
12933
  //#endregion
12392
12934
  //#region apps/cli/src/account/journey/send.ts
@@ -12418,7 +12960,42 @@ function createJourneySender(dependencies, phases = 3) {
12418
12960
  status: "stale"
12419
12961
  };
12420
12962
  const draft = await draftSend(snapshot.journey, dependencies, signal);
12421
- const reuse = snapshot.send?.scopeDigest === draft.scopeDigest && snapshot.send.subject === draft.subject;
12963
+ const recover = snapshot.send && [
12964
+ "blocked",
12965
+ "paused",
12966
+ "completed"
12967
+ ].includes(snapshot.send.status) && snapshot.send.items.some((item) => [
12968
+ "SESSION_MEMBERSHIP_CONFLICT",
12969
+ "LEGACY_SEND_RECOVERY_REQUIRED",
12970
+ "ASSESSMENT_INCOMPLETE",
12971
+ "SESSION_PUBLICATION_FENCE_REQUIRED"
12972
+ ].includes(item.error ?? ""));
12973
+ if (recover && snapshot.send) {
12974
+ const connection = await dependencies.auth.connect(signal);
12975
+ if (!connection) throw new ApplicationError("ACCOUNT_SIGNED_OUT");
12976
+ if (snapshot.send.subject !== null && snapshot.send.subject !== connection.subject) throw new ApplicationError("IDENTITY_REJECTED");
12977
+ const sessions = dependencies.artifacts.sessions;
12978
+ const remote = connection.sessions;
12979
+ if (sessions && remote) {
12980
+ const inventory = await remote.inventory(signal);
12981
+ for (const item of draft.items) {
12982
+ const current = inventory.works.find((work) => work.logicalWorkId === item.workId);
12983
+ const assembly = await remote.assembly(item.workId, signal);
12984
+ const selected = [];
12985
+ for await (const session of sessions(input.journeyId, item.artifact, signal)) selected.push(await sessionKeyFor(session));
12986
+ const currentKeys = current?.sessions.map((session) => session.key) ?? [];
12987
+ item.recoveryReview = {
12988
+ previousSendId: snapshot.send.id,
12989
+ revision: assembly?.revision ?? 0,
12990
+ currentAccess: current?.access ?? null,
12991
+ currentKeys,
12992
+ proposedKeys: [.../* @__PURE__ */ new Set([...currentKeys, ...selected])]
12993
+ };
12994
+ }
12995
+ await connection.assertCurrent(signal);
12996
+ }
12997
+ }
12998
+ const reuse = !recover && snapshot.send?.scopeDigest === draft.scopeDigest && snapshot.send.subject === draft.subject;
12422
12999
  const next = reuse && snapshot.send ? {
12423
13000
  ...snapshot.send,
12424
13001
  revision: snapshot.send.revision + 1
@@ -12476,6 +13053,7 @@ function createJourneySender(dependencies, phases = 3) {
12476
13053
  async pause(raw) {
12477
13054
  const input = sendActionSchema.parse(raw);
12478
13055
  const snapshot = await load(input);
13056
+ if (snapshot.send.status === "completed" || snapshot.send.replaced) throw new ApplicationError("INVALID_TRANSITION");
12479
13057
  const result = await saveSend(dependencies, snapshot, {
12480
13058
  ...snapshot.send,
12481
13059
  status: "paused"
@@ -12488,7 +13066,7 @@ function createJourneySender(dependencies, phases = 3) {
12488
13066
  const input = sendActionSchema.parse(raw);
12489
13067
  const snapshot = await load(input);
12490
13068
  if (!matchesAction(snapshot, input)) return saveSend(dependencies, snapshot, snapshot.send, input);
12491
- if (snapshot.send.status !== "sign-in" || snapshot.send.items.some((item) => item.receipt)) throw new ApplicationError("INVALID_TRANSITION");
13069
+ if (snapshot.send.status !== "sign-in" || snapshot.send.items.some((item) => item.receipt || item.previousReceipts.length > 0 || item.sessionUpload !== void 0 || item.sessionMembership || item.sessionAssembly)) throw new ApplicationError("INVALID_TRANSITION");
12492
13070
  const result = await saveSend(dependencies, snapshot, {
12493
13071
  ...snapshot.send,
12494
13072
  status: "prepared",
@@ -14745,4 +15323,4 @@ async function cancelOperation(runtime, id) {
14745
15323
  return readOperation(runtime, id);
14746
15324
  }
14747
15325
  //#endregion
14748
- export { createExecutionPort as _, readAgentPresence as a, executorRequestSchema as c, parseOptions as d, businessCommands as f, readStateDump as g, startupFailure as h, invoke as i, createRenderActivity as l, permissionDiagnostic as m, readOperation as n, createRuntime as o, longCommands as p, waitOperation as r, executorEventSchema as s, cancelOperation as t, helpText as u, startHost as v, traceStartup as y };
15326
+ export { readStateDump as _, readAgentPresence as a, traceStartup as b, executorRequestSchema as c, helpText as d, parseOptions as f, startupFailure as g, permissionDiagnostic as h, invoke as i, awaitingPassport as l, longCommands as m, readOperation as n, createRuntime as o, businessCommands as p, waitOperation as r, executorEventSchema as s, cancelOperation as t, createRenderActivity as u, createExecutionPort as v, startHost as y };