@gitroll/tacitt-cli 0.4.0 → 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.
- package/dist/{agent-setup-CQRWVUWR.mjs → agent-setup-Bpsi-BCv.mjs} +2 -2
- package/dist/agent-setup-worker.mjs +5 -5
- package/dist/cli.mjs +81 -27
- package/dist/codex-title-worker.mjs +1 -1
- package/dist/configured-preview-worker.mjs +5 -5
- package/dist/configured-session-worker.mjs +5 -5
- package/dist/{frame-waS5FnN5.mjs → frame-IeDOLy1o.mjs} +1 -1
- package/dist/{heap-BaTNC9kf.mjs → heap-DiOlSa8T.mjs} +6 -6
- package/dist/inspection-worker.mjs +4 -4
- package/dist/{job-DrcpSL_9.mjs → job-BII94L2c.mjs} +3 -3
- package/dist/{locate-U3-nkird.mjs → locate-CBiw-CJK.mjs} +5 -0
- package/dist/{model-D7UnGeag.mjs → model-DGTsARtV.mjs} +1 -1
- package/dist/operation-executor.mjs +3 -3
- package/dist/preview-worker.mjs +1 -1
- package/dist/privacy-validator.mjs +1 -1
- package/dist/{query-ByD0Rp_o.mjs → query-CVqWNFYf.mjs} +886 -269
- package/dist/{run-3UIkr7GZ.mjs → run-BGlf-W0M.mjs} +4 -4
- package/dist/{src-DES7hBAP.mjs → src-37kIclGK.mjs} +170 -41
- package/dist/{src-DNn7SifS.mjs → src-CBTvOa44.mjs} +46 -3
- package/dist/{src-CmP73EcD.mjs → src-D4nrfC2S.mjs} +222 -19
- package/dist/store-database-worker.mjs +1 -1
- package/dist/web/assets/index-D3g_ATU-.js +69 -0
- package/dist/web/assets/{index-Clg6Pfv_.css → index-D5DEG5Ue.css} +1 -1
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/skills/tacitt/SKILL.md +3 -3
- package/skills/tacitt/references/INDEX.md +10 -10
- package/skills/tacitt/references/commands.md +2 -0
- package/skills/tacitt/references/journey.md +3 -3
- package/skills/tacitt/references/send.md +38 -12
- package/skills/tacitt/scripts/INDEX.md +36 -36
- package/skills/tacitt/scripts/journey-card-consolidation.mjs +2 -4
- package/skills/tacitt/scripts/journey-state.d.mts +30 -0
- package/skills/tacitt/scripts/journey-state.mjs +56 -3
- package/skills/tacitt/scripts/journey-tasks.d.mts +8 -0
- package/skills/tacitt/scripts/journey-tasks.mjs +4 -1
- package/skills/tacitt/scripts/privacy-validator.mjs +2 -0
- package/skills/tacitt/scripts/review.mjs +110 -9
- package/dist/web/assets/index-BkuUI3Eu.js +0 -67
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
7
|
-
import { t as createAgentSetupPort } from "./agent-setup-
|
|
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$
|
|
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$
|
|
176
|
-
partIndex: count$
|
|
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$
|
|
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$
|
|
567
|
-
const identifier$1 = count$
|
|
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$
|
|
712
|
-
queueMs: count$
|
|
713
|
-
activeMs: count$
|
|
714
|
-
phaseMs: count$
|
|
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$
|
|
722
|
-
queueMs: count$
|
|
723
|
-
activeMs: count$
|
|
724
|
-
phaseMs: count$
|
|
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$
|
|
730
|
-
dequeuedAccessId: count$
|
|
731
|
-
queueDepth: count$
|
|
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$
|
|
746
|
+
elapsedMs: count$4,
|
|
747
747
|
ownership: legState,
|
|
748
748
|
procedureState: legState,
|
|
749
|
-
pendingAccesses: count$
|
|
750
|
-
truncatedAccesses: count$
|
|
751
|
-
stoppedBeforeEnqueue: count$
|
|
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$
|
|
755
|
-
droppedSnapshots: count$
|
|
756
|
-
observerFailures: count$
|
|
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;
|
|
@@ -925,7 +925,7 @@ function createRouter(application, account, stopping, delivery, nativeInspection
|
|
|
925
925
|
})
|
|
926
926
|
}),
|
|
927
927
|
send: t.router({
|
|
928
|
-
get: t.procedure.input(sendAddress.partial({ sendId: true })).query(({ input }) => sending().get(input)),
|
|
928
|
+
get: t.procedure.input(sendAddress.partial({ sendId: true })).query(({ input, signal }) => readThroughContention(() => sending().get(input), signal)),
|
|
929
929
|
prepare: t.procedure.input(sendPrepareSchema).mutation(({ input, signal }) => sending().prepare(input, signal)),
|
|
930
930
|
start: t.procedure.input(sendStartSchema).mutation(({ input, signal }) => sending().start(input, signal)),
|
|
931
931
|
respond: t.procedure.input(sendChoiceSchema).mutation(({ input }) => sending().respond(input)),
|
|
@@ -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$
|
|
5081
|
+
const count$3 = z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER);
|
|
5082
5082
|
const scope = z.object({
|
|
5083
|
-
completed: count$
|
|
5084
|
-
total: count$
|
|
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$
|
|
5093
|
+
workspaceSequence: count$3,
|
|
5094
5094
|
workspaces: scope,
|
|
5095
5095
|
sessions: scope,
|
|
5096
5096
|
sourceBytes: z.object({
|
|
5097
|
-
processed: count$
|
|
5098
|
-
total: count$
|
|
5097
|
+
processed: count$3,
|
|
5098
|
+
total: count$3
|
|
5099
5099
|
}).strict().nullable(),
|
|
5100
|
-
archiveBytes: count$
|
|
5100
|
+
archiveBytes: count$3
|
|
5101
5101
|
}).strict()
|
|
5102
5102
|
}).strict();
|
|
5103
5103
|
/** Disposable progress only. The workflow claim serializes publishers and matching clears. */
|
|
@@ -6660,6 +6660,7 @@ async function prepareScanContext(storage, journeyId, work, selected, processing
|
|
|
6660
6660
|
}
|
|
6661
6661
|
//#endregion
|
|
6662
6662
|
//#region apps/cli/src/journey/prepare/scan-plan.ts
|
|
6663
|
+
let publication = Promise.resolve();
|
|
6663
6664
|
/**
|
|
6664
6665
|
* The second pass of a Work's Track A (ADR 0177): with every session's receipt saved, its
|
|
6665
6666
|
* authenticated fields files are read in the Work's reading order twice, once to find each
|
|
@@ -6667,6 +6668,18 @@ async function prepareScanContext(storage, journeyId, work, selected, processing
|
|
|
6667
6668
|
* Only the plan's outlines stay in memory between the reads, never the Work's text.
|
|
6668
6669
|
*/
|
|
6669
6670
|
async function publishWorkBatches(storage, plan, work, resources, signal) {
|
|
6671
|
+
const previous = publication;
|
|
6672
|
+
const turn = Promise.withResolvers();
|
|
6673
|
+
publication = turn.promise;
|
|
6674
|
+
await previous;
|
|
6675
|
+
try {
|
|
6676
|
+
signal.throwIfAborted();
|
|
6677
|
+
return await publishAdmittedWork(storage, plan, work, resources, signal);
|
|
6678
|
+
} finally {
|
|
6679
|
+
turn.resolve();
|
|
6680
|
+
}
|
|
6681
|
+
}
|
|
6682
|
+
async function publishAdmittedWork(storage, plan, work, resources, signal) {
|
|
6670
6683
|
const order = new Map(plan.approval.inventory.sessions.map((item, index) => [item.id, index]));
|
|
6671
6684
|
const workIndex = (resource) => {
|
|
6672
6685
|
const index = order.get(resource.input.sessionId);
|
|
@@ -8188,7 +8201,7 @@ async function draftSend(journey, dependencies, signal) {
|
|
|
8188
8201
|
const omitted = [];
|
|
8189
8202
|
for (const work of journey.works) {
|
|
8190
8203
|
signal.throwIfAborted();
|
|
8191
|
-
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;
|
|
8192
8205
|
if (reason !== null || work.access === "drop") {
|
|
8193
8206
|
omitted.push({
|
|
8194
8207
|
workId: work.id,
|
|
@@ -8766,18 +8779,18 @@ function createFrozenPathRouter(repositories, folderPaths) {
|
|
|
8766
8779
|
}
|
|
8767
8780
|
//#endregion
|
|
8768
8781
|
//#region apps/cli/src/journey/works/project-evidence.ts
|
|
8769
|
-
const count$
|
|
8782
|
+
const count$2 = z.number().int().nonnegative();
|
|
8770
8783
|
const resultSchema = z.object({
|
|
8771
8784
|
repositories: z.array(z.object({
|
|
8772
8785
|
id: z.string(),
|
|
8773
8786
|
path: z.string(),
|
|
8774
|
-
references: count$
|
|
8775
|
-
reads: count$
|
|
8776
|
-
changes: count$
|
|
8777
|
-
commands: count$
|
|
8787
|
+
references: count$2,
|
|
8788
|
+
reads: count$2,
|
|
8789
|
+
changes: count$2,
|
|
8790
|
+
commands: count$2
|
|
8778
8791
|
})),
|
|
8779
8792
|
incomplete: z.boolean(),
|
|
8780
|
-
ignored: count$
|
|
8793
|
+
ignored: count$2
|
|
8781
8794
|
});
|
|
8782
8795
|
const cacheSchema = z.object({
|
|
8783
8796
|
key: z.string(),
|
|
@@ -8992,7 +9005,7 @@ function createClassificationSessions(storage, processing, stateDir) {
|
|
|
8992
9005
|
}
|
|
8993
9006
|
//#endregion
|
|
8994
9007
|
//#region apps/cli/src/journey/works/projection.ts
|
|
8995
|
-
const digest = (bytes) => createHash("sha256").update(bytes).digest();
|
|
9008
|
+
const digest$1 = (bytes) => createHash("sha256").update(bytes).digest();
|
|
8996
9009
|
async function buildProjection(directory, key, source, role, signal) {
|
|
8997
9010
|
const temporary = await mkdtemp(join(directory, ".classification-"));
|
|
8998
9011
|
const target = join(directory, `classification-${key}`);
|
|
@@ -9025,7 +9038,7 @@ async function buildProjection(directory, key, source, role, signal) {
|
|
|
9025
9038
|
const entry = Buffer.alloc(48);
|
|
9026
9039
|
entry.writeBigUInt64LE(BigInt(offset), 0);
|
|
9027
9040
|
entry.writeBigUInt64LE(BigInt(bytes.length), 8);
|
|
9028
|
-
digest(bytes).copy(entry, 16);
|
|
9041
|
+
digest$1(bytes).copy(entry, 16);
|
|
9029
9042
|
await data.writeFile(bytes);
|
|
9030
9043
|
await index.writeFile(entry);
|
|
9031
9044
|
offset += bytes.length;
|
|
@@ -9076,7 +9089,7 @@ async function buildProjection(directory, key, source, role, signal) {
|
|
|
9076
9089
|
/** Persisted derived content, never a scan receipt. Only the first read replays the capture. */
|
|
9077
9090
|
async function readClassificationProjection(source, options, signal) {
|
|
9078
9091
|
signal.throwIfAborted();
|
|
9079
|
-
const key = digest(JSON.stringify([
|
|
9092
|
+
const key = digest$1(JSON.stringify([
|
|
9080
9093
|
1,
|
|
9081
9094
|
options.snapshotId,
|
|
9082
9095
|
source,
|
|
@@ -9103,7 +9116,7 @@ async function readClassificationProjection(source, options, signal) {
|
|
|
9103
9116
|
const offset = Number(entry.readBigUInt64LE(0)), length = Number(entry.readBigUInt64LE(8));
|
|
9104
9117
|
if (!Number.isSafeInteger(offset) || !Number.isSafeInteger(length) || length > 1048576) throw new ApplicationError("CORRUPTED_STATE");
|
|
9105
9118
|
const bytes = await readProjectionBytes(dataPath, offset, length, manifest.data);
|
|
9106
|
-
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");
|
|
9107
9120
|
const row = projectionRowSchema.parse(JSON.parse(bytes.toString("utf8")));
|
|
9108
9121
|
if (row.sessionId !== source.sessionId || row.sourcePartId !== source.capture.partId) throw new ApplicationError("CORRUPTED_STATE");
|
|
9109
9122
|
rows.push(row);
|
|
@@ -9771,6 +9784,82 @@ function protectedSourceText(field, hidden) {
|
|
|
9771
9784
|
return text;
|
|
9772
9785
|
}
|
|
9773
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
|
|
9774
9863
|
//#region apps/cli/src/journey/send-artifacts.ts
|
|
9775
9864
|
function createJourneySendArtifacts(storage) {
|
|
9776
9865
|
async function find(journeyId, workId, revision) {
|
|
@@ -9788,6 +9877,7 @@ function createJourneySendArtifacts(storage) {
|
|
|
9788
9877
|
return output;
|
|
9789
9878
|
}
|
|
9790
9879
|
return {
|
|
9880
|
+
sessions: (journeyId, artifact, signal) => sessionArtifacts(storage, find, journeyId, artifact, signal),
|
|
9791
9881
|
async describe(journey, work, signal) {
|
|
9792
9882
|
const output = await find(journey.journeyId, work.id, work.revision);
|
|
9793
9883
|
const info = await readConfiguredOutputInfo(output.primary, signal);
|
|
@@ -9871,69 +9961,61 @@ async function putPrimary(primary, byteLength, upload, signal) {
|
|
|
9871
9961
|
}
|
|
9872
9962
|
}
|
|
9873
9963
|
//#endregion
|
|
9874
|
-
//#region apps/cli/src/account/journey/
|
|
9875
|
-
|
|
9876
|
-
|
|
9877
|
-
|
|
9878
|
-
|
|
9879
|
-
|
|
9880
|
-
|
|
9881
|
-
|
|
9882
|
-
|
|
9883
|
-
|
|
9884
|
-
|
|
9885
|
-
|
|
9886
|
-
return
|
|
9887
|
-
|
|
9888
|
-
|
|
9889
|
-
|
|
9890
|
-
|
|
9891
|
-
|
|
9892
|
-
|
|
9893
|
-
|
|
9894
|
-
|
|
9895
|
-
|
|
9896
|
-
|
|
9897
|
-
|
|
9898
|
-
|
|
9899
|
-
|
|
9900
|
-
|
|
9901
|
-
|
|
9902
|
-
|
|
9903
|
-
|
|
9904
|
-
|
|
9905
|
-
|
|
9906
|
-
|
|
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;
|
|
9907
10004
|
}
|
|
9908
|
-
/**
|
|
9909
|
-
function
|
|
9910
|
-
const
|
|
9911
|
-
|
|
9912
|
-
|
|
9913
|
-
|
|
9914
|
-
|
|
9915
|
-
|
|
9916
|
-
|
|
9917
|
-
|
|
9918
|
-
...
|
|
9919
|
-
|
|
9920
|
-
|
|
9921
|
-
|
|
9922
|
-
"OPERATION_FAILED",
|
|
9923
|
-
"UPLOAD_FAILED",
|
|
9924
|
-
"SEND_REMOTE_REFUSED",
|
|
9925
|
-
"IDENTITY_UNAVAILABLE",
|
|
9926
|
-
"ACCOUNT_SIGNED_OUT"
|
|
9927
|
-
].includes(item.error ?? "") ? {
|
|
9928
|
-
...item,
|
|
9929
|
-
status: item.receipt ? item.receipt.state === "awaiting_upload" ? "uploading" : "assessing" : "ready",
|
|
9930
|
-
error: null
|
|
9931
|
-
} : item)
|
|
9932
|
-
});
|
|
9933
|
-
return profileRequired && !["blocked", "completed"].includes(next.status) ? {
|
|
9934
|
-
...next,
|
|
9935
|
-
status: "sign-in"
|
|
9936
|
-
} : 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;
|
|
9937
10019
|
}
|
|
9938
10020
|
//#endregion
|
|
9939
10021
|
//#region apps/cli/src/account/journey/receipt.ts
|
|
@@ -10057,14 +10139,154 @@ async function checkPublicAgentSources(candidates, requestFetch = fetch, signal)
|
|
|
10057
10139
|
return results.filter((item) => item !== null);
|
|
10058
10140
|
}
|
|
10059
10141
|
//#endregion
|
|
10060
|
-
//#region apps/cli/src/account/journey/
|
|
10061
|
-
const
|
|
10062
|
-
|
|
10063
|
-
|
|
10064
|
-
|
|
10065
|
-
|
|
10066
|
-
|
|
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)
|
|
10067
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
|
|
10068
10290
|
const publicProfileSchema = z.strictObject({
|
|
10069
10291
|
displayName: z.string().trim().min(1).max(300).nullable(),
|
|
10070
10292
|
avatarUrl: z.url().max(2048).refine((value) => {
|
|
@@ -10073,18 +10295,6 @@ const publicProfileSchema = z.strictObject({
|
|
|
10073
10295
|
return url.protocol === "https:" && !url.username && !url.password;
|
|
10074
10296
|
}).nullable()
|
|
10075
10297
|
});
|
|
10076
|
-
var JourneyRemoteError = class extends Error {
|
|
10077
|
-
status;
|
|
10078
|
-
code;
|
|
10079
|
-
details;
|
|
10080
|
-
constructor(status, code, details, options) {
|
|
10081
|
-
super(code, options);
|
|
10082
|
-
this.name = "JourneyRemoteError";
|
|
10083
|
-
this.status = status;
|
|
10084
|
-
this.code = code;
|
|
10085
|
-
this.details = details;
|
|
10086
|
-
}
|
|
10087
|
-
};
|
|
10088
10298
|
function apiBase(value) {
|
|
10089
10299
|
const url = new URL(value);
|
|
10090
10300
|
const local = [
|
|
@@ -10105,7 +10315,7 @@ const completeTimeoutMs = 6e5;
|
|
|
10105
10315
|
function createJourneyOwnerClient(options) {
|
|
10106
10316
|
const base = apiBase(options.apiUrl);
|
|
10107
10317
|
const requestFetch = options.fetch ?? fetch;
|
|
10108
|
-
async function
|
|
10318
|
+
async function requestWithHeaders(path, schema, signal, body, method = "POST", key, timeoutMs = requestTimeoutMs$2, checksumUpload = false, expectedStatus = 200) {
|
|
10109
10319
|
const token = await options.authorize();
|
|
10110
10320
|
signal.throwIfAborted();
|
|
10111
10321
|
const response = await requestFetch(new URL(path, base), {
|
|
@@ -10133,7 +10343,7 @@ function createJourneyOwnerClient(options) {
|
|
|
10133
10343
|
throw new JourneyRemoteError(response.status, "REMOTE_PROTOCOL", null);
|
|
10134
10344
|
}
|
|
10135
10345
|
const value = await readOwnerJson(response);
|
|
10136
|
-
if (response.status !==
|
|
10346
|
+
if (response.status !== expectedStatus) {
|
|
10137
10347
|
const refusal = refusalSchema.safeParse(value);
|
|
10138
10348
|
throw new JourneyRemoteError(response.status, refusal.success ? refusal.data.error : "REMOTE_PROTOCOL", refusal.success ? refusal.data : null);
|
|
10139
10349
|
}
|
|
@@ -10141,11 +10351,18 @@ function createJourneyOwnerClient(options) {
|
|
|
10141
10351
|
if (!parsed.success) throw new JourneyRemoteError(response.status, "REMOTE_PROTOCOL", null, { cause: parsed.error });
|
|
10142
10352
|
await options.authorize();
|
|
10143
10353
|
signal.throwIfAborted();
|
|
10144
|
-
return
|
|
10354
|
+
return {
|
|
10355
|
+
data: parsed.data,
|
|
10356
|
+
headers: response.headers
|
|
10357
|
+
};
|
|
10358
|
+
}
|
|
10359
|
+
async function request(...args) {
|
|
10360
|
+
return (await requestWithHeaders(...args)).data;
|
|
10145
10361
|
}
|
|
10146
10362
|
const receipt = (id) => sendReceiptSchema.refine((value) => value.receiptId === id);
|
|
10147
10363
|
return {
|
|
10148
10364
|
subject: options.subject,
|
|
10365
|
+
sessions: createSessionClient(request),
|
|
10149
10366
|
checkAgentSources(candidates, signal) {
|
|
10150
10367
|
return checkPublicAgentSources(candidates, requestFetch, signal);
|
|
10151
10368
|
},
|
|
@@ -10154,7 +10371,8 @@ function createJourneyOwnerClient(options) {
|
|
|
10154
10371
|
signal.throwIfAborted();
|
|
10155
10372
|
},
|
|
10156
10373
|
async syncProfile(signal) {
|
|
10157
|
-
await
|
|
10374
|
+
const { headers } = await requestWithHeaders("/me/profile", publicProfileSchema, signal);
|
|
10375
|
+
return { parallelCompletes: (headers.get("passport-capabilities") ?? "").split(",").some((token) => token.trim() === "parallel-completes") };
|
|
10158
10376
|
},
|
|
10159
10377
|
resolve(input, signal) {
|
|
10160
10378
|
return request("/me/work-bundles/resolve", sendResolutionSchema.refine((value) => value.logicalWorkId === input.logicalWorkId), signal, input);
|
|
@@ -10252,6 +10470,23 @@ async function applySettings(item, connection, signal, guard, auto = false) {
|
|
|
10252
10470
|
}
|
|
10253
10471
|
//#endregion
|
|
10254
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
|
+
}
|
|
10255
10490
|
function resolveInput(item, bindingId) {
|
|
10256
10491
|
return {
|
|
10257
10492
|
logicalWorkId: item.workId,
|
|
@@ -10386,6 +10621,7 @@ async function perform(context, item, connection, signal) {
|
|
|
10386
10621
|
return next;
|
|
10387
10622
|
}
|
|
10388
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);
|
|
10389
10625
|
const reserved = await connection.reserve(item.operationKey, item.reservation, signal, context.checksumUploads !== false);
|
|
10390
10626
|
const next = await observe(item, reserved, connection, signal, auto);
|
|
10391
10627
|
if (context.fresh?.has(item.workId) && reserved.upload && next.status === "uploading") context.fresh.set(item.workId, {
|
|
@@ -10414,24 +10650,24 @@ async function perform(context, item, connection, signal) {
|
|
|
10414
10650
|
return withoutUpload(item);
|
|
10415
10651
|
}
|
|
10416
10652
|
}
|
|
10417
|
-
|
|
10418
|
-
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);
|
|
10419
10654
|
return put(context, item, receipt.receiptId, receipt.upload, connection, signal, guard);
|
|
10420
10655
|
}
|
|
10421
10656
|
/** Uploads the bytes to the receipt's signed URL and completes it. */
|
|
10422
10657
|
async function put(context, item, receiptId, upload, connection, signal, guard) {
|
|
10423
10658
|
await guard();
|
|
10424
10659
|
await context.upload(item.artifact, upload, signal);
|
|
10660
|
+
const sent = withoutBusy(item);
|
|
10425
10661
|
await guard();
|
|
10426
10662
|
try {
|
|
10427
|
-
return observe(
|
|
10663
|
+
return observe(sent, await connection.complete(receiptId, signal), connection, signal, context.snapshot === true);
|
|
10428
10664
|
} catch (error) {
|
|
10429
10665
|
if (upload.headers !== void 0 && error instanceof JourneyRemoteError && error.code === "bundle_mismatch") return {
|
|
10430
10666
|
...renewed(withoutUpload(item), null),
|
|
10431
10667
|
checksumUploadRefused: true
|
|
10432
10668
|
};
|
|
10433
10669
|
if (error instanceof Object) uploaded.set(error, {
|
|
10434
|
-
...
|
|
10670
|
+
...sent,
|
|
10435
10671
|
uploadedReceiptId: receiptId
|
|
10436
10672
|
});
|
|
10437
10673
|
throw error;
|
|
@@ -10443,10 +10679,24 @@ function uploadedItem(error) {
|
|
|
10443
10679
|
return error instanceof Object ? uploaded.get(error) : void 0;
|
|
10444
10680
|
}
|
|
10445
10681
|
function withoutUpload(item) {
|
|
10446
|
-
if (item.uploadedReceiptId === void 0) return item;
|
|
10447
|
-
const { uploadedReceiptId: _, ...rest } = item;
|
|
10682
|
+
if (item.uploadedReceiptId === void 0 && item.busyRefusals === void 0) return item;
|
|
10683
|
+
const { uploadedReceiptId: _, busyRefusals: __, ...rest } = item;
|
|
10684
|
+
return rest;
|
|
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;
|
|
10448
10689
|
return rest;
|
|
10449
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
|
+
}
|
|
10450
10700
|
/** A definite stale refusal can renew the intent; unknown outcomes always retry the same key. */
|
|
10451
10701
|
async function stepItem(context, item, connection, signal) {
|
|
10452
10702
|
try {
|
|
@@ -10459,10 +10709,11 @@ async function stepItem(context, item, connection, signal) {
|
|
|
10459
10709
|
"target_conflict",
|
|
10460
10710
|
"work_removed"
|
|
10461
10711
|
].includes(error.code)) throw error;
|
|
10712
|
+
if (stagedRetry(error, uploadedItem(error) ?? item)) throw error;
|
|
10462
10713
|
if (item.receipt) {
|
|
10463
10714
|
const observed = await connection.status(item.receipt.receiptId, signal);
|
|
10464
10715
|
await connection.assertCurrent(signal);
|
|
10465
|
-
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));
|
|
10466
10717
|
}
|
|
10467
10718
|
const candidate = error.details?.resolution?.candidates[0];
|
|
10468
10719
|
if (error.code === "target_conflict" && item.target?.kind === "existing" && candidate?.bindingId === item.target.bindingId) return {
|
|
@@ -10475,6 +10726,17 @@ async function stepItem(context, item, connection, signal) {
|
|
|
10475
10726
|
status: "blocked",
|
|
10476
10727
|
error: "WORK_REMOVED"
|
|
10477
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
|
+
}
|
|
10478
10740
|
return renewed(item, "SETTINGS_CHANGED");
|
|
10479
10741
|
}
|
|
10480
10742
|
}
|
|
@@ -10491,18 +10753,268 @@ function renewed(item, error) {
|
|
|
10491
10753
|
error
|
|
10492
10754
|
};
|
|
10493
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
|
+
}
|
|
10494
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
|
+
}
|
|
10495
11004
|
/**
|
|
10496
|
-
* The Works one advance steps together. Upload work comes before assessment checks
|
|
10497
|
-
* ZIP
|
|
10498
|
-
* an upload
|
|
10499
|
-
*
|
|
10500
|
-
*
|
|
10501
|
-
* linked Settings change
|
|
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.
|
|
10502
11012
|
*/
|
|
10503
|
-
function pickBatch(items, now
|
|
10504
|
-
const
|
|
10505
|
-
|
|
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, []);
|
|
10506
11018
|
const assessing = items.filter((item) => item.status === "assessing");
|
|
10507
11019
|
const first = assessing[0];
|
|
10508
11020
|
if (!first) return [];
|
|
@@ -10541,11 +11053,12 @@ async function prepareProfile(dependencies, snapshot, connection, signal) {
|
|
|
10541
11053
|
async function captureProfile(connection, guard, signal, failure) {
|
|
10542
11054
|
try {
|
|
10543
11055
|
await guard();
|
|
10544
|
-
await connection.syncProfile(signal);
|
|
11056
|
+
const { parallelCompletes } = await connection.syncProfile(signal);
|
|
10545
11057
|
await guard();
|
|
10546
11058
|
return {
|
|
10547
11059
|
status: "sending",
|
|
10548
|
-
error: null
|
|
11060
|
+
error: null,
|
|
11061
|
+
parallelCompletes: parallelCompletes ? true : void 0
|
|
10549
11062
|
};
|
|
10550
11063
|
} catch (error) {
|
|
10551
11064
|
if (signal.aborted) throw error;
|
|
@@ -10570,6 +11083,8 @@ async function captureProfile(connection, guard, signal, failure) {
|
|
|
10570
11083
|
}
|
|
10571
11084
|
//#endregion
|
|
10572
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);
|
|
10573
11088
|
/** The stored Send still holds exactly this Work state, whatever else changed meanwhile. */
|
|
10574
11089
|
function sameItem(items, item) {
|
|
10575
11090
|
return JSON.stringify(items.find((value) => value.workId === item.workId)) === JSON.stringify(item);
|
|
@@ -10589,22 +11104,50 @@ async function advanceItems(host, signal) {
|
|
|
10589
11104
|
status: "blocked",
|
|
10590
11105
|
error: "ACCOUNT_CHANGED"
|
|
10591
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
|
+
}
|
|
10592
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;
|
|
10593
11134
|
const prepared = {
|
|
10594
11135
|
...send,
|
|
10595
11136
|
subject: connection.subject,
|
|
10596
|
-
...
|
|
11137
|
+
...profile
|
|
10597
11138
|
};
|
|
11139
|
+
if (parallelCompletes) prepared.parallelCompletes = true;
|
|
11140
|
+
else delete prepared.parallelCompletes;
|
|
10598
11141
|
return host.save(prepared.status === "sending" ? aggregate(prepared) : prepared);
|
|
10599
11142
|
}
|
|
10600
|
-
const
|
|
10601
|
-
if (
|
|
10602
|
-
|
|
10603
|
-
if (finished !== null) return finished;
|
|
10604
|
-
}
|
|
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);
|
|
10605
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);
|
|
10606
11149
|
const checksumUploads = !send.items.some((item) => item.checksumUploadRefused);
|
|
10607
|
-
const results = await Promise.allSettled(batch.map((item) => stepItem({
|
|
11150
|
+
const results = await Promise.allSettled(batch.map((item) => (host.step ?? stepItem)({
|
|
10608
11151
|
...host.context(item),
|
|
10609
11152
|
checksumUploads
|
|
10610
11153
|
}, item, connection, signal)));
|
|
@@ -10628,7 +11171,15 @@ async function advanceItems(host, signal) {
|
|
|
10628
11171
|
if (progressed) next.set(item.workId, progressed);
|
|
10629
11172
|
if (cause instanceof JourneyRemoteError && cause.status === 401 || isCode(cause, "ACCOUNT_SIGNED_OUT")) signIn = true;
|
|
10630
11173
|
else if (isCode(cause, "IDENTITY_REJECTED")) changed = true;
|
|
10631
|
-
else if (
|
|
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)) {
|
|
10632
11183
|
host.failure("SEND_INTERRUPTED", cause);
|
|
10633
11184
|
error = "SEND_INTERRUPTED";
|
|
10634
11185
|
} else {
|
|
@@ -10683,42 +11234,6 @@ function blockSend(host, error, signal) {
|
|
|
10683
11234
|
error: code
|
|
10684
11235
|
});
|
|
10685
11236
|
}
|
|
10686
|
-
/**
|
|
10687
|
-
* The public Work IDs this Send keeps on the owner's Passport: every sent Work's receipt and
|
|
10688
|
-
* every Work left out only because the same bundle and access were already sent to this
|
|
10689
|
-
* account. Dropped and other omitted Works are not kept (ADR 0204).
|
|
10690
|
-
*/
|
|
10691
|
-
function keepWorkIds(journey, send) {
|
|
10692
|
-
const unchanged = send.omitted.flatMap((omitted) => {
|
|
10693
|
-
const binding = journey.works.find((work) => work.id === omitted.workId)?.binding;
|
|
10694
|
-
return omitted.reason === "sent" && binding && binding.subject === send.subject ? [binding.publicWorkId] : [];
|
|
10695
|
-
});
|
|
10696
|
-
return [.../* @__PURE__ */ new Set([...send.items.flatMap((item) => item.receipt?.workId ?? []), ...unchanged])];
|
|
10697
|
-
}
|
|
10698
|
-
/**
|
|
10699
|
-
* Passport refused kept IDs it no longer has: a Work omitted as already sent whose Passport copy
|
|
10700
|
-
* an earlier Send retired. Those Works are sent again (a retired Work no longer matches, so each
|
|
10701
|
-
* becomes a new Work) and replace runs again once they are uploaded. Null when no refused ID is one.
|
|
10702
|
-
*/
|
|
10703
|
-
async function requeueRetired(journey, send, error, signal, dependencies) {
|
|
10704
|
-
if (error.code !== "unknown_keep_work_ids") return null;
|
|
10705
|
-
const refused = new Set(error.details?.workIds ?? []);
|
|
10706
|
-
const works = send.omitted.flatMap((omitted) => {
|
|
10707
|
-
const work = journey.works.find((value) => value.id === omitted.workId);
|
|
10708
|
-
return omitted.reason === "sent" && work?.binding?.subject === send.subject && refused.has(work.binding.publicWorkId) ? [work] : [];
|
|
10709
|
-
});
|
|
10710
|
-
if (!works.length) return null;
|
|
10711
|
-
const items = [];
|
|
10712
|
-
for (const work of works) items.push(await describeItem(journey, work, dependencies, signal));
|
|
10713
|
-
const ids = new Set(works.map((work) => work.id));
|
|
10714
|
-
return {
|
|
10715
|
-
...send,
|
|
10716
|
-
status: "sending",
|
|
10717
|
-
error: null,
|
|
10718
|
-
items: [...send.items, ...items],
|
|
10719
|
-
omitted: send.omitted.filter((omitted) => !ids.has(omitted.workId))
|
|
10720
|
-
};
|
|
10721
|
-
}
|
|
10722
11237
|
/** A Send saved before ADR 0204 may wait on a target or Settings question that is never asked. */
|
|
10723
11238
|
function withoutQuestions(send) {
|
|
10724
11239
|
if (!send.items.some((item) => ["target", "settings"].includes(item.status))) return send;
|
|
@@ -10736,6 +11251,18 @@ function withoutQuestions(send) {
|
|
|
10736
11251
|
};
|
|
10737
11252
|
return send.status === "review" ? aggregate(next) : next;
|
|
10738
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
|
+
}
|
|
10739
11266
|
async function advanceSend(dependencies, address, signal, chain) {
|
|
10740
11267
|
const snapshot = requireSend(await dependencies.storage.loadSend(address.journeyId, address.sendId));
|
|
10741
11268
|
if (chain) chain.revision = snapshot.send.revision;
|
|
@@ -10743,18 +11270,29 @@ async function advanceSend(dependencies, address, signal, chain) {
|
|
|
10743
11270
|
...snapshot,
|
|
10744
11271
|
status: "stale"
|
|
10745
11272
|
};
|
|
10746
|
-
|
|
10747
|
-
|
|
10748
|
-
"completed",
|
|
10749
|
-
"prepared"
|
|
10750
|
-
].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 {
|
|
10751
11275
|
...snapshot,
|
|
10752
11276
|
status: "applied"
|
|
10753
11277
|
};
|
|
10754
|
-
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
|
+
});
|
|
10755
11285
|
try {
|
|
10756
11286
|
assertScope(snapshot.journey, snapshot.send);
|
|
10757
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
|
+
});
|
|
10758
11296
|
return blockSend({
|
|
10759
11297
|
send: snapshot.send,
|
|
10760
11298
|
failure: dependencies.failure,
|
|
@@ -10767,14 +11305,39 @@ async function advanceSend(dependencies, address, signal, chain) {
|
|
|
10767
11305
|
};
|
|
10768
11306
|
const send = withoutQuestions(snapshot.send);
|
|
10769
11307
|
if (send !== snapshot.send) return save(send);
|
|
11308
|
+
let independentItems = [];
|
|
10770
11309
|
return advanceItems({
|
|
10771
11310
|
send,
|
|
10772
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
|
+
} : {},
|
|
10773
11337
|
connect: chain?.connect ?? dependencies.auth.connect,
|
|
10774
11338
|
profile: (connection, profileSignal) => prepareProfile(dependencies, snapshot, connection, profileSignal),
|
|
10775
11339
|
context: (item) => {
|
|
10776
11340
|
const binding = snapshot.journey.works.find((value) => value.id === item.workId)?.binding;
|
|
10777
|
-
const retired = !item.receipt && binding?.sentBundleId === item.artifact.bundleId && binding.sentAccess === item.access;
|
|
10778
11341
|
return {
|
|
10779
11342
|
snapshot: true,
|
|
10780
11343
|
...chain ? { fresh: chain.fresh } : {},
|
|
@@ -10783,7 +11346,7 @@ async function advanceSend(dependencies, address, signal, chain) {
|
|
|
10783
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");
|
|
10784
11347
|
assertScope(current.journey, current.send);
|
|
10785
11348
|
},
|
|
10786
|
-
bindingId: binding?.subject === send.subject
|
|
11349
|
+
bindingId: binding?.subject === send.subject ? binding.bundleRecordId : void 0,
|
|
10787
11350
|
upload: async (artifact, upload, uploadSignal) => {
|
|
10788
11351
|
const started = performance.now();
|
|
10789
11352
|
await dependencies.artifacts.upload(snapshot.journey.journeyId, artifact, upload, uploadSignal);
|
|
@@ -10791,36 +11354,6 @@ async function advanceSend(dependencies, address, signal, chain) {
|
|
|
10791
11354
|
}
|
|
10792
11355
|
};
|
|
10793
11356
|
},
|
|
10794
|
-
finish: async (connection, finishSignal) => {
|
|
10795
|
-
if (send.replaced || !send.items.every((item) => ["assessing", "completed"].includes(item.status))) return null;
|
|
10796
|
-
let retired;
|
|
10797
|
-
try {
|
|
10798
|
-
({retiredWorkIds: retired} = await connection.replace(keepWorkIds(snapshot.journey, send), finishSignal));
|
|
10799
|
-
} catch (error) {
|
|
10800
|
-
if (error instanceof JourneyRemoteError && error.status === 401) return save({
|
|
10801
|
-
...send,
|
|
10802
|
-
status: "sign-in",
|
|
10803
|
-
error: null
|
|
10804
|
-
});
|
|
10805
|
-
if (!(error instanceof JourneyRemoteError) || error.status !== 400) throw error;
|
|
10806
|
-
const requeued = await requeueRetired(snapshot.journey, send, error, finishSignal, dependencies);
|
|
10807
|
-
if (requeued) return save(requeued);
|
|
10808
|
-
dependencies.failure("SEND_REPLACE_REFUSED", error);
|
|
10809
|
-
return save({
|
|
10810
|
-
...send,
|
|
10811
|
-
status: "blocked",
|
|
10812
|
-
error: "SEND_REPLACE_REFUSED"
|
|
10813
|
-
});
|
|
10814
|
-
}
|
|
10815
|
-
return save(aggregate({
|
|
10816
|
-
...send,
|
|
10817
|
-
error: null,
|
|
10818
|
-
replaced: {
|
|
10819
|
-
at: dependencies.now(),
|
|
10820
|
-
retiredWorkCount: retired.length
|
|
10821
|
-
}
|
|
10822
|
-
}));
|
|
10823
|
-
},
|
|
10824
11357
|
save,
|
|
10825
11358
|
applied: () => ({
|
|
10826
11359
|
...snapshot,
|
|
@@ -10832,6 +11365,28 @@ async function advanceSend(dependencies, address, signal, chain) {
|
|
|
10832
11365
|
})
|
|
10833
11366
|
}, signal);
|
|
10834
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
|
+
}
|
|
10835
11390
|
/**
|
|
10836
11391
|
* A refusal Passport answers while it or Identity is briefly unavailable. It says nothing about
|
|
10837
11392
|
* the Work, so the step is retried instead of blocking it (the 2026-11-01 recorded run blocked a
|
|
@@ -12350,12 +12905,12 @@ function createJourneyFiles(storage) {
|
|
|
12350
12905
|
};
|
|
12351
12906
|
}
|
|
12352
12907
|
/**
|
|
12353
|
-
* One advance carries the
|
|
12354
|
-
* instead of one phase per panel round trip, and uploads a
|
|
12355
|
-
* without reading it again (ADR 0229). Each phase is still
|
|
12356
|
-
* revision change or a failure stops the chain at the
|
|
12357
|
-
*
|
|
12358
|
-
* caller's next advance
|
|
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.
|
|
12359
12914
|
*/
|
|
12360
12915
|
async function advanceWork(dependencies, address, signal, phases = 3) {
|
|
12361
12916
|
let connection;
|
|
@@ -12373,7 +12928,7 @@ async function advanceWork(dependencies, address, signal, phases = 3) {
|
|
|
12373
12928
|
/** The phase saved progress and left its Work mid-upload, with nothing else to report. */
|
|
12374
12929
|
function uploading(result, before) {
|
|
12375
12930
|
const send = result.send;
|
|
12376
|
-
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";
|
|
12377
12932
|
}
|
|
12378
12933
|
//#endregion
|
|
12379
12934
|
//#region apps/cli/src/account/journey/send.ts
|
|
@@ -12405,7 +12960,42 @@ function createJourneySender(dependencies, phases = 3) {
|
|
|
12405
12960
|
status: "stale"
|
|
12406
12961
|
};
|
|
12407
12962
|
const draft = await draftSend(snapshot.journey, dependencies, signal);
|
|
12408
|
-
const
|
|
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;
|
|
12409
12999
|
const next = reuse && snapshot.send ? {
|
|
12410
13000
|
...snapshot.send,
|
|
12411
13001
|
revision: snapshot.send.revision + 1
|
|
@@ -12428,6 +13018,7 @@ function createJourneySender(dependencies, phases = 3) {
|
|
|
12428
13018
|
if (!sameIds(input.confirmedWorkIds, snapshot.send.items.map((item) => item.workId)) || !sameIds(input.confirmedOmittedWorkIds, snapshot.send.omitted.map((item) => item.workId)) || input.scopeDigest !== snapshot.send.scopeDigest || snapshot.send.items.length === 0) throw new ApplicationError("CONFIRMATION_REQUIRED");
|
|
12429
13019
|
if (snapshot.send.confirmedAt !== null) return saveSend(dependencies, snapshot, snapshot.send, input);
|
|
12430
13020
|
const connection = await dependencies.auth.connect(signal);
|
|
13021
|
+
if (!connection && !dependencies.auth.configured) throw new ApplicationError("ACCOUNT_NOT_CONFIGURED");
|
|
12431
13022
|
if (connection && snapshot.send.subject !== null && snapshot.send.subject !== connection.subject) throw new ApplicationError("IDENTITY_REJECTED");
|
|
12432
13023
|
let profile = {
|
|
12433
13024
|
status: "sign-in",
|
|
@@ -12462,6 +13053,7 @@ function createJourneySender(dependencies, phases = 3) {
|
|
|
12462
13053
|
async pause(raw) {
|
|
12463
13054
|
const input = sendActionSchema.parse(raw);
|
|
12464
13055
|
const snapshot = await load(input);
|
|
13056
|
+
if (snapshot.send.status === "completed" || snapshot.send.replaced) throw new ApplicationError("INVALID_TRANSITION");
|
|
12465
13057
|
const result = await saveSend(dependencies, snapshot, {
|
|
12466
13058
|
...snapshot.send,
|
|
12467
13059
|
status: "paused"
|
|
@@ -12474,7 +13066,7 @@ function createJourneySender(dependencies, phases = 3) {
|
|
|
12474
13066
|
const input = sendActionSchema.parse(raw);
|
|
12475
13067
|
const snapshot = await load(input);
|
|
12476
13068
|
if (!matchesAction(snapshot, input)) return saveSend(dependencies, snapshot, snapshot.send, input);
|
|
12477
|
-
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");
|
|
12478
13070
|
const result = await saveSend(dependencies, snapshot, {
|
|
12479
13071
|
...snapshot.send,
|
|
12480
13072
|
status: "prepared",
|
|
@@ -12602,33 +13194,36 @@ function createJourneySendAuth(account, requestFetch) {
|
|
|
12602
13194
|
}
|
|
12603
13195
|
return response;
|
|
12604
13196
|
};
|
|
12605
|
-
return {
|
|
12606
|
-
|
|
12607
|
-
|
|
12608
|
-
|
|
12609
|
-
if (!verified) {
|
|
12610
|
-
await refuse(credential.accessToken);
|
|
12611
|
-
credential = await current();
|
|
13197
|
+
return {
|
|
13198
|
+
configured: account.configuration.clientId !== null,
|
|
13199
|
+
async connect(signal) {
|
|
13200
|
+
let credential = await current();
|
|
12612
13201
|
if (!credential) return null;
|
|
12613
|
-
verified = await account.identity.readIdentity(credential.accessToken, signal);
|
|
13202
|
+
let verified = await account.identity.readIdentity(credential.accessToken, signal);
|
|
12614
13203
|
if (!verified) {
|
|
12615
|
-
await
|
|
12616
|
-
|
|
13204
|
+
await refuse(credential.accessToken);
|
|
13205
|
+
credential = await current();
|
|
13206
|
+
if (!credential) return null;
|
|
13207
|
+
verified = await account.identity.readIdentity(credential.accessToken, signal);
|
|
13208
|
+
if (!verified) {
|
|
13209
|
+
await expire(credential.accessToken);
|
|
13210
|
+
return null;
|
|
13211
|
+
}
|
|
12617
13212
|
}
|
|
13213
|
+
if (verified.identityId !== credential.identityId) throw new ApplicationError("IDENTITY_REJECTED");
|
|
13214
|
+
return createJourneyOwnerClient({
|
|
13215
|
+
apiUrl: account.configuration.passportApiUrl,
|
|
13216
|
+
subject: verified.identityId,
|
|
13217
|
+
fetch: ownerFetch,
|
|
13218
|
+
async authorize() {
|
|
13219
|
+
const latest = await current();
|
|
13220
|
+
if (!latest) throw new ApplicationError("ACCOUNT_SIGNED_OUT");
|
|
13221
|
+
if (latest.identityId !== verified.identityId) throw new ApplicationError("IDENTITY_REJECTED");
|
|
13222
|
+
return latest.accessToken;
|
|
13223
|
+
}
|
|
13224
|
+
});
|
|
12618
13225
|
}
|
|
12619
|
-
|
|
12620
|
-
return createJourneyOwnerClient({
|
|
12621
|
-
apiUrl: account.configuration.passportApiUrl,
|
|
12622
|
-
subject: verified.identityId,
|
|
12623
|
-
fetch: ownerFetch,
|
|
12624
|
-
async authorize() {
|
|
12625
|
-
const latest = await current();
|
|
12626
|
-
if (!latest) throw new ApplicationError("ACCOUNT_SIGNED_OUT");
|
|
12627
|
-
if (latest.identityId !== verified.identityId) throw new ApplicationError("IDENTITY_REJECTED");
|
|
12628
|
-
return latest.accessToken;
|
|
12629
|
-
}
|
|
12630
|
-
});
|
|
12631
|
-
} };
|
|
13226
|
+
};
|
|
12632
13227
|
}
|
|
12633
13228
|
//#endregion
|
|
12634
13229
|
//#region apps/cli/src/operation/protocol.ts
|
|
@@ -12646,6 +13241,15 @@ const sourceExclusionReasonSchema = z.enum([
|
|
|
12646
13241
|
]);
|
|
12647
13242
|
const identifier = z.string().regex(/^[A-Za-z0-9_-]{1,128}$/);
|
|
12648
13243
|
const count = z.number().int().min(0).max(Number.MAX_SAFE_INTEGER);
|
|
13244
|
+
const driftCountsSchema = z.object({
|
|
13245
|
+
"claude-row": count.optional(),
|
|
13246
|
+
"claude-block": count.optional(),
|
|
13247
|
+
"codex-row": count.optional(),
|
|
13248
|
+
"codex-event": count.optional(),
|
|
13249
|
+
"codex-item": count.optional(),
|
|
13250
|
+
"codex-extension": count.optional(),
|
|
13251
|
+
launch: count.optional()
|
|
13252
|
+
});
|
|
12649
13253
|
const safeDiagnosticSchema = z.object({
|
|
12650
13254
|
code: z.string().regex(/^[A-Z][A-Z0-9_]{0,79}$/),
|
|
12651
13255
|
stage: z.string().regex(/^[a-z][a-z0-9.-]{0,63}$/).default("runtime"),
|
|
@@ -12655,6 +13259,7 @@ const safeDiagnosticSchema = z.object({
|
|
|
12655
13259
|
at: z.string().datetime().max(64).optional(),
|
|
12656
13260
|
reason: sourceExclusionReasonSchema.optional(),
|
|
12657
13261
|
sourceId: z.string().regex(/^src[0-9]{1,12}$/).optional(),
|
|
13262
|
+
driftCounts: driftCountsSchema.optional(),
|
|
12658
13263
|
counts: z.object({
|
|
12659
13264
|
workspaces: count,
|
|
12660
13265
|
sessions: count,
|
|
@@ -12722,6 +13327,7 @@ function projectSafeDiagnostic(value) {
|
|
|
12722
13327
|
operationId: Reflect.get(value, "operationId"),
|
|
12723
13328
|
at: Reflect.get(value, "at"),
|
|
12724
13329
|
counts: Reflect.get(value, "counts"),
|
|
13330
|
+
driftCounts: Reflect.get(value, "driftCounts"),
|
|
12725
13331
|
location: safeLocation,
|
|
12726
13332
|
reason: Reflect.get(value, "reason"),
|
|
12727
13333
|
sourceId: Reflect.get(value, "sourceId")
|
|
@@ -12736,11 +13342,22 @@ function createSafeProducerCallbacks(emit, record) {
|
|
|
12736
13342
|
code: diagnostic.code,
|
|
12737
13343
|
stage: diagnostic.stage,
|
|
12738
13344
|
...diagnostic.reason ? { reason: diagnostic.reason } : {},
|
|
12739
|
-
...diagnostic.sourceId ? { sourceId: diagnostic.sourceId } : {}
|
|
13345
|
+
...diagnostic.sourceId ? { sourceId: diagnostic.sourceId } : {},
|
|
13346
|
+
...diagnostic.driftCounts ? { driftCounts: diagnostic.driftCounts } : {}
|
|
12740
13347
|
});
|
|
12741
13348
|
emit(diagnostic);
|
|
12742
13349
|
}
|
|
12743
13350
|
function anomaly(stage, event) {
|
|
13351
|
+
if (event.code === "tolerated-harness-drift") {
|
|
13352
|
+
const counts = driftCountsSchema.safeParse(event.driftCounts);
|
|
13353
|
+
publish({
|
|
13354
|
+
code: "HARNESS_DRIFT_TOLERATED",
|
|
13355
|
+
...counts.success ? { driftCounts: counts.data } : {},
|
|
13356
|
+
stage,
|
|
13357
|
+
.../^src[0-9]{1,12}$/.test(event.sourceId) ? { sourceId: event.sourceId } : {}
|
|
13358
|
+
});
|
|
13359
|
+
return;
|
|
13360
|
+
}
|
|
12744
13361
|
const reason = sourceExclusionReasonSchema.safeParse(event.code);
|
|
12745
13362
|
if (!reason.success) return;
|
|
12746
13363
|
publish({
|
|
@@ -14706,4 +15323,4 @@ async function cancelOperation(runtime, id) {
|
|
|
14706
15323
|
return readOperation(runtime, id);
|
|
14707
15324
|
}
|
|
14708
15325
|
//#endregion
|
|
14709
|
-
export {
|
|
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 };
|