@gitroll/tacitt-cli 0.6.0 → 0.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/README.md +4 -4
  2. package/bin/tacitt.cjs +1 -1
  3. package/dist/agent-setup-worker.mjs +1 -1
  4. package/dist/cli.mjs +5 -5
  5. package/dist/configured-preview-worker.mjs +1 -1
  6. package/dist/configured-session-worker.mjs +2 -2
  7. package/dist/{heap-DY5n4EVC.mjs → heap-BR8WjR25.mjs} +1 -1
  8. package/dist/inspection-worker.mjs +1 -1
  9. package/dist/{job-BxUVFocm.mjs → job-CYsjL4AT.mjs} +1 -1
  10. package/dist/operation-executor.mjs +1 -1
  11. package/dist/{query-Cm5lumS4.mjs → query-CQ6Tr5zC.mjs} +264 -96
  12. package/dist/{src-BMSLVcgs.mjs → src-JoT3L94j.mjs} +1 -1
  13. package/dist/store-database-worker.mjs +1 -1
  14. package/dist/web/assets/{index-CXm08nyc.js → index-C9LcK9ls.js} +105 -5
  15. package/dist/web/assets/index-CYJBq1px.css +1 -0
  16. package/dist/web/index.html +2 -2
  17. package/package.json +1 -1
  18. package/skills/tacitt/SKILL.md +4 -4
  19. package/skills/tacitt/references/b-method.md +1 -1
  20. package/skills/tacitt/references/bootstrap.md +1 -1
  21. package/skills/tacitt/references/classification.md +2 -2
  22. package/skills/tacitt/references/commands.md +3 -3
  23. package/skills/tacitt/references/journey.md +2 -2
  24. package/skills/tacitt/references/parallel-review.md +1 -1
  25. package/skills/tacitt/references/remote-workers.md +1 -1
  26. package/skills/tacitt/references/send.md +6 -6
  27. package/skills/tacitt/scripts/journey-classification-metadata.mjs +1 -1
  28. package/skills/tacitt/scripts/journey-classification-tasks.mjs +1 -1
  29. package/skills/tacitt/scripts/journey.mjs +1 -1
  30. package/skills/tacitt/scripts/review.mjs +2 -2
  31. package/dist/web/assets/index-Bbck2aOB.css +0 -1
@@ -1,9 +1,9 @@
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-BMSLVcgs.mjs";
1
+ import { $ as createCleanup, B as parallelismLimits, C as readOriginalSearchFields, E as checkReusableBundle, F as SessionWorkerError, G as processJourneyMetadataFields, H as privateJourneyDirectory, I as sessionWorkerFailure, K as staticMaskCategory, L as privateCollectionDirectory, M as readCollectionIndex, N as parseCollectionWorkspaceSummary, O as captureLogicalJourneySources, P as createSharedSessionPool, Q as removeNamedTemporarySpools, R as defaultParallelism, T as createCollectionArtifacts, U as readLogicalJourneyFinalMasks, W as readConfiguredOutputInfo, Z as createTemporary, _ as processJourneyMetadata, a as scanReviewFields, at as windowsOwnerOnly, c as readSessionScanRecords, g as readCapturedOriginalRows, i as buildWorkScanBatches, it as restrictToOwner, j as runConfiguredSessionJob, k as capturedJourneyScope, l as scanPreparedSession, m as openConfiguredUpload, n as verifyPreparedSessionSources, nt as makePrivateDirectory, o as createWorkPieces, ot as isWindowsAclFailure, p as configuredSessionUploads, q as readWorkspacePair, r as prepareSessionScans, rt as openPrivateFile, s as readSessionScanFields, st as windowsAclCodes, t as preparedScanConfiguration, v as readStaticSessionMasks, w as readConfiguredSearchFields, y as staticSessionJobs, z as gitSeedsProvider } from "./src-JoT3L94j.mjs";
2
2
  import { $t as safeField, Bt as sessionPeriodSchema, Dn as createStaticProcessor, F as bClassificationKey, Fn as entityDictionaryVersion, Ft as termTypeInfo, Gt as ownerProfileSchema, H as codePointLength, Ht as journeyProfileAddressActionSchema, K as utf16Offset, Kt as passportAddressSchema, N as bUnitFragments, Ot as privacyTermKey, Qt as safeCode, Sn as currentDetectorVersion, T as bLiteralTerms, Un as frameRecords, Ut as journeyProfileLinksActionSchema, V as bDigest, Wt as mergeOwnerProfile, Xt as ApplicationError, Zt as UnfinishedJourneyError, an as observeSource, ar as admitCustomTerm, b as validateBReview, c as textDigest, h as reviewFindingsDigest, ht as journeyDescribeSchema, ir as createEntityMatcher, jt as reviewTermsInputSchema, l as validateReview, n as compileBFindings, nn as reviewFamilies, or as admitGitName, qt as profileHasChoices, r as reviewBatchChunks, s as reviewSubmitSchema, t as locateTerms, tn as familySelected, z as bAnchorKey, zt as filterInventoryByPeriod } from "./locate-JYtFmKu-.mjs";
3
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-CGFwLUd6.mjs";
4
4
  import { $ as settingsQuerySchema, $r as journeyRenderSchema, 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, Hr as isIncludedWork, Ht as journeyContextQuerySchema, Ir as sendReceiptSchema, Jn as reviewCandidateResourceSchema, 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 journeyRenderInputSchema, 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 isReadingComplete, Vn as maskedHitsStale, Vr as isClassifying, Vt as reviewCompileSchema, Wn as workDecisionDigest, Wr as isSelectedWork, Xn as reviewRepairResourceSchema, Xr as journeyFindingsSchema, Xt as journeyPolicyRespondSchema, Yn as reviewRepairInputSchema, Yr as journeyFindingImpactSchema, Yt as searchScopeDigest, Zr as journeyPreviewSchema, _t as maximumTermsFileBytes, a as bCrossSegmentReviewSchema, ai as producerVersion, ar as scanGitSeedsId, b as bReviewScope, bn as confirmedHideAdditions, br as journeyClassificationSessionsSchema, bt as sendActionSchema, c as reconstructBFields, ci as recommendParallelism, cr as scanExclusionResourceSchema, ct as validateFailureLocation, d as selectedValueMembers, dn as sessionReviewInputSchema, dr as journeyInventoryQuerySchema, dt as manualUploadChoiceSchema, ei as commandSchema, 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, ir as scanSessionResourceSchema, j as profileUrlMatchesIdentity, k as deviceAuthorizationSchema, ln as sessionRereadInputSchema, lt as manualUploadActionSchema, m as hideScopeDigest, mt as manualUploadStartSchema, n as scanBatchEquivalenceDigest, ni as selectionBatchSchema, nt as appendSensitiveTerms, o as bRereadDigest, oi as stateSchema, on as sessionReconcileInputSchema, or as scanGitSeedsResource, ot as artifactFailureDetailSchema, p as activeJourneyTerms, pt as manualUploadSchema, qn as reviewCandidateInputSchema, qr as withReadingProgress, qt as journeySearchResultSchema, r as locateTermEvidence, ri as selectionSchema, rn as reviewAttemptResourceSchema, rr as scanPreparationDigest, s as reconcileBSession, sr as verifiedScanGitSeeds, st as resolveFailureLocation, t as scanBatchContextDigest, ti as packageCommandSchema, 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--Ln5GHdz.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-DY5n4EVC.mjs";
6
- import { C as journeyDirectory, S as saveRemoteState, _ as staticBaseProduction, a as writeJourneyOutput, b as isRemoteImport, 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, w as collectRemoteWorkers, x as readRemoteState, y as workApproval } from "./job-BxUVFocm.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-BR8WjR25.mjs";
6
+ import { C as journeyDirectory, S as saveRemoteState, _ as staticBaseProduction, a as writeJourneyOutput, b as isRemoteImport, 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, w as collectRemoteWorkers, x as readRemoteState, y as workApproval } from "./job-CYsjL4AT.mjs";
7
7
  import { t as createAgentSetupPort } from "./agent-setup-Dv_mQ38P.mjs";
8
8
  import { t as workerMessageSchema } from "./messages-D3m3bhva.mjs";
9
9
  import { constants, write } from "node:fs";
@@ -13,7 +13,8 @@ import { execFile, spawn } from "node:child_process";
13
13
  import { basename, dirname, isAbsolute, join, posix, relative, resolve, sep } from "node:path";
14
14
  import { createInterface } from "node:readline";
15
15
  import { Writable } from "node:stream";
16
- import { parseArgs, promisify, types } from "node:util";
16
+ import { isDeepStrictEqual, parseArgs, promisify, types } from "node:util";
17
+ import { pathToFileURL } from "node:url";
17
18
  import { createHash, randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
18
19
  import { setTimeout as setTimeout$1 } from "node:timers/promises";
19
20
  import { fastify } from "fastify";
@@ -1231,7 +1232,7 @@ function closeConnection(root, db, onFatal, primary) {
1231
1232
  //#region packages/local-host/src/coordination/files.ts
1232
1233
  const coordinationApplicationId = 1397769036;
1233
1234
  function invalid() {
1234
- throw new StoreError("STATE_LAYOUT_INVALID", "Coordination files are incomplete or changed. Stop all SessPilot instances and use a fresh private --state-dir. Reset does not replace coordination files.");
1235
+ throw new StoreError("STATE_LAYOUT_INVALID", "Coordination files are incomplete or changed. Close every running Tacitt CLI and use a fresh private --state-dir. Reset does not replace coordination files.");
1235
1236
  }
1236
1237
  function initialize(root, path, onFatal) {
1237
1238
  const db = new DatabaseSync(path, { timeout: 0 });
@@ -1348,7 +1349,7 @@ async function tryAcquireCoordination(stateDir, domain, onFatal, onPhase) {
1348
1349
  observe("coordination.begin");
1349
1350
  db.exec("BEGIN IMMEDIATE");
1350
1351
  observe("coordination.header");
1351
- if (db.prepare("PRAGMA application_id").get()?.["application_id"] !== 1397769036 || db.prepare("PRAGMA journal_mode").get()?.["journal_mode"] !== "delete" || db.prepare("SELECT name FROM sqlite_schema").all().length !== 0) throw new StoreError("STATE_LAYOUT_INVALID", "The coordination database is not a recognized header-only file. Stop all SessPilot instances and use a fresh private --state-dir.");
1352
+ if (db.prepare("PRAGMA application_id").get()?.["application_id"] !== 1397769036 || db.prepare("PRAGMA journal_mode").get()?.["journal_mode"] !== "delete" || db.prepare("SELECT name FROM sqlite_schema").all().length !== 0) throw new StoreError("STATE_LAYOUT_INVALID", "The coordination database is not a recognized header-only file. Close every running Tacitt CLI and use a fresh private --state-dir.");
1352
1353
  } catch (error) {
1353
1354
  observe("coordination.close");
1354
1355
  closeConnection(file.root, db, onFatal, { error });
@@ -5440,8 +5441,8 @@ const longCommands = /* @__PURE__ */ new Set([
5440
5441
  //#endregion
5441
5442
  //#region apps/cli/src/account/defaults.ts
5442
5443
  /**
5443
- * Where the shipped CLI signs in. `--identity-url`, `--identity-client-id`, `--passport-api-url`
5444
- * and the matching `SESS_PILOT_*` variables override these for development targets.
5444
+ * Where the shipped CLI signs in. `--identity-url`, `--identity-client-id`, `--profile-api-url`
5445
+ * (or the older `--passport-api-url`) and the matching `SESS_PILOT_*` variables override these for development targets.
5445
5446
  *
5446
5447
  * The client ID is the public value Identity issues when the `sess-pilot` App declaration is
5447
5448
  * applied to the production target (`APP_SESS_PILOT_OIDC_CLI_CLIENT_ID`). Release builds inject
@@ -5512,6 +5513,7 @@ function parseOptions(args, source = sourceCheckout) {
5512
5513
  until: { type: "string" },
5513
5514
  "identity-url": { type: "string" },
5514
5515
  "identity-client-id": { type: "string" },
5516
+ "profile-api-url": { type: "string" },
5515
5517
  "passport-api-url": { type: "string" },
5516
5518
  help: {
5517
5519
  type: "boolean",
@@ -5571,7 +5573,7 @@ function parseOptions(args, source = sourceCheckout) {
5571
5573
  sessionPeriod: period.data,
5572
5574
  identityUrl: readOrigin(parsed.values["identity-url"] ?? process.env["SESS_PILOT_IDENTITY_URL"] ?? accountDefaults.identityUrl),
5573
5575
  identityClientId: identityClientId === "" ? null : identityClientId,
5574
- passportApiUrl: readOrigin(parsed.values["passport-api-url"] ?? process.env["SESS_PILOT_PASSPORT_API_URL"] ?? accountDefaults.passportApiUrl),
5576
+ passportApiUrl: readOrigin(parsed.values["profile-api-url"] ?? parsed.values["passport-api-url"] ?? process.env["SESS_PILOT_PROFILE_API_URL"] ?? process.env["SESS_PILOT_PASSPORT_API_URL"] ?? accountDefaults.passportApiUrl),
5575
5577
  stateDir: resolve(parsed.values["state-dir"] ?? defaultState),
5576
5578
  bundleDir: parsed.values["bundle-dir"] === void 0 ? void 0 : resolve(parsed.values["bundle-dir"]),
5577
5579
  claudeHome: resolve(parsed.values["claude-home"] ?? process.env["CLAUDE_CONFIG_DIR"] ?? join(home, ".claude")),
@@ -5607,11 +5609,11 @@ Configured previews use current cleanup/redaction settings, including metadata.
5607
5609
  Original-mode previews use a separate basic guard; sampling never limits export.
5608
5610
  Workspace ZIPs and collection indexes use the saved bundle library directory.
5609
5611
  --bundle-dir overrides that location for this CLI run only.
5610
- Native Skill Send uploads only the confirmed primary artifacts to the configured Passport API.
5612
+ Native Skill Send uploads only the confirmed primary artifacts to the configured Tacitt profile API.
5611
5613
  status prints safe workspace outcomes from the latest saved packaging operation.
5612
5614
  Private packaging error details stay in <state-dir>/diagnostics/ (up to 4 MiB).
5613
5615
  Sign in from the Web UI to link this tool to your GitRoll Identity account; the CLI contacts
5614
- only the Identity and Skill Passport origins below, only when you ask, and keeps the issued
5616
+ only the Identity and profile API origins below, only when you ask, and keeps the issued
5615
5617
  token in <state-dir>/account/ (owner-only).
5616
5618
 
5617
5619
  Options:
@@ -5632,7 +5634,7 @@ Options:
5632
5634
  (DATE: YYYY-MM-DD local day, inclusive, or an ISO timestamp)
5633
5635
  --identity-url URL Identity origin for sign-in (default: ${accountDefaults.identityUrl})
5634
5636
  --identity-client-id ID Public device client ID registered for this tool
5635
- --passport-api-url URL Skill Passport API origin the token is checked against
5637
+ --profile-api-url URL Profile API origin the token is checked against
5636
5638
  --output PATH Write a sensitive state dump to a new private file
5637
5639
  --dev Serve the repository UI through Vite (source checkout only)
5638
5640
  --help Show this help
@@ -10274,6 +10276,27 @@ async function* sessionArtifacts(storage, find, journeyId, artifact, signal) {
10274
10276
  }
10275
10277
  };
10276
10278
  }
10279
+ /** Own one live upload iterator; advancing it releases the preceding session ZIP. */
10280
+ async function prepareSessionArtifacts(storage, find, journeyId, artifact, signal) {
10281
+ const source = sessionArtifacts(storage, find, journeyId, artifact, signal);
10282
+ let closed = false;
10283
+ async function* iterate() {
10284
+ try {
10285
+ yield* source;
10286
+ } finally {
10287
+ closed = true;
10288
+ }
10289
+ }
10290
+ const items = iterate();
10291
+ return {
10292
+ items,
10293
+ async close() {
10294
+ if (closed) return;
10295
+ closed = true;
10296
+ await items.return(void 0);
10297
+ }
10298
+ };
10299
+ }
10277
10300
  //#endregion
10278
10301
  //#region apps/cli/src/journey/send-artifacts.ts
10279
10302
  function createJourneySendArtifacts(storage) {
@@ -10292,6 +10315,7 @@ function createJourneySendArtifacts(storage) {
10292
10315
  return output;
10293
10316
  }
10294
10317
  return {
10318
+ prepareSessions: (journeyId, artifact, signal) => prepareSessionArtifacts(storage, find, journeyId, artifact, signal),
10295
10319
  sessions: (journeyId, artifact, signal) => sessionArtifacts(storage, find, journeyId, artifact, signal),
10296
10320
  async describe(journey, work, signal) {
10297
10321
  const output = await find(journey.journeyId, work.id, work.revision);
@@ -10376,6 +10400,74 @@ async function putPrimary(primary, byteLength, upload, signal) {
10376
10400
  }
10377
10401
  }
10378
10402
  //#endregion
10403
+ //#region apps/cli/src/account/journey/session-preparation.ts
10404
+ /** Phase-local facts retain no bytes; only upload steps acquire live iterators. */
10405
+ function createSessionPreparation(dependencies) {
10406
+ const prepare = dependencies.artifacts.prepareSessions;
10407
+ const prepared = [];
10408
+ const facts = /* @__PURE__ */ new Map();
10409
+ const sessions = async function* (journeyId, artifact, signal) {
10410
+ if (!prepare) {
10411
+ const original = dependencies.artifacts.sessions;
10412
+ if (original) yield* original(journeyId, artifact, signal);
10413
+ return;
10414
+ }
10415
+ const result = await prepare(journeyId, artifact, signal);
10416
+ prepared.push(result);
10417
+ for await (const item of result.items) {
10418
+ signal.throwIfAborted();
10419
+ yield item;
10420
+ }
10421
+ };
10422
+ const sessionFacts = async function* (journeyId, artifact, signal) {
10423
+ const key = JSON.stringify([
10424
+ journeyId,
10425
+ artifact.workId,
10426
+ artifact.revision,
10427
+ artifact.sha256
10428
+ ]);
10429
+ let pending = facts.get(key);
10430
+ if (!pending) {
10431
+ pending = (async () => {
10432
+ const original = dependencies.artifacts.sessionFacts ?? dependencies.artifacts.sessions;
10433
+ const items = [];
10434
+ if (original) for await (const item of original(journeyId, artifact, signal)) {
10435
+ const { nativeSessionId, relativePath, harness, lastAt, metadata, sha256, byteLength, crc32 } = item;
10436
+ items.push({
10437
+ nativeSessionId,
10438
+ relativePath,
10439
+ harness,
10440
+ lastAt,
10441
+ metadata,
10442
+ sha256,
10443
+ byteLength,
10444
+ crc32
10445
+ });
10446
+ }
10447
+ return items;
10448
+ })();
10449
+ facts.set(key, pending);
10450
+ }
10451
+ for (const item of await pending) {
10452
+ signal.throwIfAborted();
10453
+ yield item;
10454
+ }
10455
+ };
10456
+ return {
10457
+ dependencies: {
10458
+ ...dependencies,
10459
+ artifacts: {
10460
+ ...dependencies.artifacts,
10461
+ sessions,
10462
+ sessionFacts
10463
+ }
10464
+ },
10465
+ async close() {
10466
+ await createCleanup().run(...prepared.map((result) => () => result.close()));
10467
+ }
10468
+ };
10469
+ }
10470
+ //#endregion
10379
10471
  //#region apps/cli/src/account/journey/session-key.ts
10380
10472
  /** Engine native identity, or harness-root-relative path identity when native identity is absent. */
10381
10473
  async function sessionKeyFor(input) {
@@ -10394,12 +10486,25 @@ async function sessionKeyFor(input) {
10394
10486
  //#region apps/cli/src/account/journey/session-access.ts
10395
10487
  /** Check the entire pending Send before admitting any session mutation. */
10396
10488
  async function sessionAccessBlocked(dependencies, journeyId, items, inventory, signal) {
10397
- const sessions = dependencies.artifacts.sessions;
10489
+ const sessions = dependencies.artifacts.sessionFacts ?? dependencies.artifacts.sessions;
10398
10490
  if (!sessions) return false;
10399
10491
  const requestedAccess = /* @__PURE__ */ new Map();
10400
10492
  const requestedDigests = /* @__PURE__ */ new Map();
10401
10493
  for (const item of items) {
10402
- if (item.sessionAssembly || item.status === "completed") continue;
10494
+ if (item.status === "blocked" || item.sessionAssembly || !item.sessionMembership) continue;
10495
+ const destination = inventory.works.find((work) => work.logicalWorkId === item.workId);
10496
+ if (destination?.access === "removed") throw new ApplicationError("WORK_REMOVED");
10497
+ for (const key of item.sessionMembership.keys) {
10498
+ const access = requestedAccess.get(key);
10499
+ if (access !== void 0 && access !== item.access) return true;
10500
+ requestedAccess.set(key, item.access);
10501
+ const source = inventory.works.find((work) => work.sessions.some((entry) => entry.key === key));
10502
+ if (source && source.logicalWorkId !== item.workId && source.access !== item.access) return true;
10503
+ if (destination && destination.access !== item.access && !destination.sessions.some((entry) => entry.key === key)) return true;
10504
+ }
10505
+ }
10506
+ for (const item of items) {
10507
+ if (item.sessionAssembly || item.sessionMembership || ["completed", "blocked"].includes(item.status)) continue;
10403
10508
  const destination = inventory.works.find((work) => work.logicalWorkId === item.workId);
10404
10509
  if (destination?.access === "removed") throw new ApplicationError("WORK_REMOVED");
10405
10510
  for await (const session of sessions(journeyId, item.artifact, signal)) {
@@ -10419,14 +10524,14 @@ async function sessionAccessBlocked(dependencies, journeyId, items, inventory, s
10419
10524
  }
10420
10525
  /** Keep overlapping session ownership serial through membership, including retained members. */
10421
10526
  async function sessionIndependentItems(dependencies, journeyId, items, inventory, signal) {
10422
- const sessions = dependencies.artifacts.sessions;
10527
+ const sessions = dependencies.artifacts.sessionFacts ?? dependencies.artifacts.sessions;
10423
10528
  if (!sessions) return [];
10424
10529
  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);
10425
10530
  const claimed = /* @__PURE__ */ new Set();
10426
10531
  const independent = [];
10427
10532
  for (const item of pending) {
10428
10533
  const keys = /* @__PURE__ */ new Set([...inventory.works.find((work) => work.logicalWorkId === item.workId)?.sessions.map((session) => session.key) ?? [], ...item.sessionMembership?.keys ?? []]);
10429
- for await (const session of sessions(journeyId, item.artifact, signal)) keys.add(await sessionKeyFor(session));
10534
+ if (!item.sessionMembership && !item.sessionAssembly) for await (const session of sessions(journeyId, item.artifact, signal)) keys.add(await sessionKeyFor(session));
10430
10535
  if (![...keys].some((key) => claimed.has(key))) independent.push(item);
10431
10536
  for (const key of keys) claimed.add(key);
10432
10537
  }
@@ -11187,7 +11292,7 @@ function renewed(item, error) {
11187
11292
  //#endregion
11188
11293
  //#region apps/cli/src/account/journey/session-step.ts
11189
11294
  /** Reconcile against completed server facts on every retry, including after process loss. */
11190
- async function stepSessionWork(dependencies, journeyId, context, item, connection, signal) {
11295
+ async function stepSessionWork(dependencies, journeyId, context, item, connection, signal, checkpoint) {
11191
11296
  const remote = connection.sessions;
11192
11297
  const sessions = dependencies.artifacts.sessions;
11193
11298
  if (!remote || !sessions) throw new ApplicationError("INVALID_STATE");
@@ -11266,7 +11371,6 @@ async function stepSessionWork(dependencies, journeyId, context, item, connectio
11266
11371
  throw error;
11267
11372
  }
11268
11373
  }
11269
- const assembly = await remote.assembly(item.workId, signal);
11270
11374
  const inventory = await remote.inventory(signal);
11271
11375
  const stored = new Map([...inventory.unassigned, ...inventory.works.flatMap((work) => work.sessions)].map((session) => [session.key, session]));
11272
11376
  const keys = [];
@@ -11282,13 +11386,17 @@ async function stepSessionWork(dependencies, journeyId, context, item, connectio
11282
11386
  lastAt: session.lastAt,
11283
11387
  harness: session.harness
11284
11388
  };
11285
- if (item.sessionUpload?.key !== key || item.sessionUpload.sha256 !== session.sha256) return {
11286
- ...item,
11287
- sessionUpload: {
11288
- key,
11289
- sha256: session.sha256
11290
- }
11291
- };
11389
+ if (item.sessionUpload?.key !== key || item.sessionUpload.sha256 !== session.sha256) {
11390
+ await guard();
11391
+ item = {
11392
+ ...item,
11393
+ sessionUpload: {
11394
+ key,
11395
+ sha256: session.sha256
11396
+ }
11397
+ };
11398
+ await checkpoint(item);
11399
+ }
11292
11400
  await guard();
11293
11401
  try {
11294
11402
  await remote.complete({
@@ -11310,13 +11418,14 @@ async function stepSessionWork(dependencies, journeyId, context, item, connectio
11310
11418
  }
11311
11419
  }
11312
11420
  await guard();
11313
- return {
11421
+ item = {
11314
11422
  ...item,
11315
11423
  sessionUpload: null
11316
11424
  };
11317
11425
  }
11318
11426
  await guard();
11319
- const current = inventory.works.find((work) => work.logicalWorkId === item.workId);
11427
+ const assembly = await remote.assembly(item.workId, signal);
11428
+ const current = (await remote.inventory(signal)).works.find((work) => work.logicalWorkId === item.workId);
11320
11429
  if (current && current.access !== item.access) {
11321
11430
  const resolution = await connection.resolve(resolveInput(item, context.bindingId), signal);
11322
11431
  const [candidate] = resolution.candidates;
@@ -11557,9 +11666,12 @@ async function captureProfile(connection, guard, signal, failure) {
11557
11666
  //#region apps/cli/src/account/journey/advance.ts
11558
11667
  /** A Work Passport is done with: assessed, or stopped unless Resume retries it. */
11559
11668
  const settled = (item) => ["completed", "blocked"].includes(item.status);
11669
+ /** Stored state equality: key order and explicit undefined fields do not count. */
11670
+ const sameJson = (left, right) => isDeepStrictEqual(JSON.parse(JSON.stringify(left)), JSON.parse(JSON.stringify(right)));
11560
11671
  /** The stored Send still holds exactly this Work state, whatever else changed meanwhile. */
11561
11672
  function sameItem(items, item) {
11562
- return JSON.stringify(items.find((value) => value.workId === item.workId)) === JSON.stringify(item);
11673
+ const current = items.find((value) => value.workId === item.workId);
11674
+ return current !== void 0 && sameJson(current, item);
11563
11675
  }
11564
11676
  /** Shared sign-in, account and per-item rules for native Send and Manual upload. */
11565
11677
  async function advanceItems(host, signal) {
@@ -11678,7 +11790,7 @@ async function advanceItems(host, signal) {
11678
11790
  status: "sign-in",
11679
11791
  error: null
11680
11792
  });
11681
- if (JSON.stringify(items) === JSON.stringify(send.items) && send.error === error) return host.applied();
11793
+ if (sameJson(items, send.items) && send.error === error) return host.applied();
11682
11794
  return host.save(aggregate({
11683
11795
  ...send,
11684
11796
  error,
@@ -11736,7 +11848,7 @@ function completeAtUpload(send, sessions) {
11736
11848
  };
11737
11849
  }
11738
11850
  async function advanceSend(dependencies, address, signal, chain) {
11739
- const snapshot = requireSend(await dependencies.storage.loadSend(address.journeyId, address.sendId));
11851
+ let snapshot = requireSend(await dependencies.storage.loadSend(address.journeyId, address.sendId));
11740
11852
  if (chain) chain.revision = snapshot.send.revision;
11741
11853
  if ((await dependencies.storage.loadSend(address.journeyId))?.send?.id !== snapshot.send.id) return {
11742
11854
  ...snapshot,
@@ -11747,13 +11859,36 @@ async function advanceSend(dependencies, address, signal, chain) {
11747
11859
  ...snapshot,
11748
11860
  status: "applied"
11749
11861
  };
11750
- const save = (next) => !completed ? saveSend(dependencies, snapshot, completeAtUpload(next, usesSessionSend(dependencies, next))) : JSON.stringify(next.items) === JSON.stringify(snapshot.send.items) ? Promise.resolve({
11751
- ...snapshot,
11752
- status: "applied"
11753
- }) : saveSend(dependencies, snapshot, {
11754
- ...snapshot.send,
11755
- items: next.items
11756
- });
11862
+ const checkpoints = /* @__PURE__ */ new Map();
11863
+ const fulfilled = /* @__PURE__ */ new Set();
11864
+ let saving = Promise.resolve();
11865
+ async function active(item) {
11866
+ const current = requireSend(await dependencies.storage.loadSend(address.journeyId));
11867
+ if (current.send.id !== snapshot.send.id || !sameItem(current.send.items, checkpoints.get(item.workId) ?? item) || current.send.status === "paused" || current.journey.phase === "paused") throw new ApplicationError("REVISION_CONFLICT");
11868
+ assertScope(current.journey, current.send);
11869
+ return current;
11870
+ }
11871
+ const save = (proposed) => {
11872
+ const next = {
11873
+ ...proposed,
11874
+ items: proposed.items.map((item) => {
11875
+ const checkpoint = checkpoints.get(item.workId);
11876
+ return checkpoint && !fulfilled.has(item.workId) ? {
11877
+ ...checkpoint,
11878
+ ...item,
11879
+ sessionUpload: checkpoint.sessionUpload
11880
+ } : item;
11881
+ })
11882
+ };
11883
+ return !completed ? saveSend(dependencies, snapshot, completeAtUpload(next, usesSessionSend(dependencies, next))) : sameJson(next.items, snapshot.send.items) ? Promise.resolve({
11884
+ ...snapshot,
11885
+ status: "applied"
11886
+ }) : saveSend(dependencies, snapshot, {
11887
+ ...snapshot.send,
11888
+ items: next.items
11889
+ });
11890
+ };
11891
+ const send = withoutQuestions(snapshot.send);
11757
11892
  try {
11758
11893
  assertScope(snapshot.journey, snapshot.send);
11759
11894
  } catch (error) {
@@ -11775,67 +11910,94 @@ async function advanceSend(dependencies, address, signal, chain) {
11775
11910
  ...snapshot,
11776
11911
  status: "applied"
11777
11912
  };
11778
- const send = withoutQuestions(snapshot.send);
11779
11913
  if (send !== snapshot.send) return save(send);
11780
11914
  let independentItems = [];
11781
- return advanceItems({
11782
- send,
11783
- failure: dependencies.failure,
11784
- ...usesSessionSend(dependencies, send) ? {
11785
- batch: () => {
11786
- const limit = send.parallelCompletes ? 3 : 1;
11787
- return independentItems.length ? pickBatch(independentItems, Date.now(), limit) : pickBatch(send.items, Date.now(), limit);
11788
- },
11789
- preflight: async (connection, preflightSignal) => {
11790
- if (!connection.sessions) throw new ApplicationError("INVALID_STATE");
11791
- const inventory = await connection.sessions.inventory(preflightSignal);
11792
- if (!await sessionAccessBlocked(dependencies, snapshot.journey.journeyId, send.items, inventory, preflightSignal)) {
11793
- independentItems = await sessionIndependentItems(dependencies, snapshot.journey.journeyId, send.items, inventory, preflightSignal);
11794
- return null;
11795
- }
11796
- await connection.assertCurrent(preflightSignal);
11797
- return save({
11798
- ...send,
11799
- status: "blocked",
11800
- items: send.items.map((item) => item.sessionAssembly || item.status === "completed" ? item : {
11801
- ...item,
11915
+ const preparation = createSessionPreparation(dependencies);
11916
+ const cleanup = createCleanup();
11917
+ try {
11918
+ return await advanceItems({
11919
+ send,
11920
+ failure: dependencies.failure,
11921
+ ...usesSessionSend(dependencies, send) ? {
11922
+ batch: () => {
11923
+ const limit = send.parallelCompletes ? 3 : 1;
11924
+ return independentItems.length ? pickBatch(independentItems, Date.now(), limit) : pickBatch(send.items, Date.now(), limit);
11925
+ },
11926
+ preflight: async (connection, preflightSignal) => {
11927
+ if (!connection.sessions) throw new ApplicationError("INVALID_STATE");
11928
+ if (!send.items.some((item) => item.status === "uploading" && !item.sessionAssembly && !item.receipt && !item.reservation)) return null;
11929
+ const inventory = await connection.sessions.inventory(preflightSignal);
11930
+ if (!await sessionAccessBlocked(preparation.dependencies, snapshot.journey.journeyId, send.items, inventory, preflightSignal)) {
11931
+ independentItems = await sessionIndependentItems(preparation.dependencies, snapshot.journey.journeyId, send.items, inventory, preflightSignal);
11932
+ return null;
11933
+ }
11934
+ await connection.assertCurrent(preflightSignal);
11935
+ return save({
11936
+ ...send,
11802
11937
  status: "blocked",
11803
- error: "SESSION_MOVE_ACCESS_REQUIRED"
11804
- })
11805
- });
11806
- },
11807
- step: (context, item, connection, stepSignal) => stepSessionWork(dependencies, snapshot.journey.journeyId, context, item, connection, stepSignal)
11808
- } : {},
11809
- connect: chain?.connect ?? dependencies.auth.connect,
11810
- profile: (connection, profileSignal) => prepareProfile(dependencies, snapshot, connection, profileSignal),
11811
- context: (item) => {
11812
- const binding = snapshot.journey.works.find((value) => value.id === item.workId)?.binding;
11813
- return {
11814
- snapshot: true,
11815
- ...chain ? { fresh: chain.fresh } : {},
11816
- active: async () => {
11817
- const current = requireSend(await dependencies.storage.loadSend(snapshot.journey.journeyId));
11818
- 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");
11819
- assertScope(current.journey, current.send);
11938
+ items: send.items.map((item) => item.sessionAssembly || item.status === "completed" ? item : {
11939
+ ...item,
11940
+ status: "blocked",
11941
+ error: "SESSION_MOVE_ACCESS_REQUIRED"
11942
+ })
11943
+ });
11820
11944
  },
11821
- bindingId: binding?.subject === send.subject ? binding.bundleRecordId : void 0,
11822
- upload: async (artifact, upload, uploadSignal) => {
11823
- const started = performance.now();
11824
- await dependencies.artifacts.upload(snapshot.journey.journeyId, artifact, upload, uploadSignal);
11825
- dependencies.uploaded?.(artifact.workId, artifact.byteLength, Math.round(performance.now() - started));
11826
- }
11827
- };
11828
- },
11829
- save,
11830
- applied: () => ({
11831
- ...snapshot,
11832
- status: "applied"
11833
- }),
11834
- stale: async () => ({
11835
- ...requireSend(await dependencies.storage.loadSend(address.journeyId, address.sendId)),
11836
- status: "stale"
11837
- })
11838
- }, signal);
11945
+ step: (context, item, connection, stepSignal) => stepSessionWork(preparation.dependencies, snapshot.journey.journeyId, context, item, connection, stepSignal, (next) => {
11946
+ const pending = saving.then(async () => {
11947
+ const current = await active(item);
11948
+ if (current.send.revision !== snapshot.send.revision || current.journey.revision !== snapshot.journey.revision) throw new ApplicationError("REVISION_CONFLICT");
11949
+ stepSignal.throwIfAborted();
11950
+ const result = await saveSend(dependencies, current, {
11951
+ ...current.send,
11952
+ items: current.send.items.map((value) => value.workId === next.workId ? next : value)
11953
+ });
11954
+ if (result.status === "stale") throw new ApplicationError("REVISION_CONFLICT");
11955
+ snapshot = requireSend(result);
11956
+ const saved = snapshot.send.items.find((value) => value.workId === next.workId);
11957
+ if (!saved) throw new ApplicationError("INVALID_STATE");
11958
+ checkpoints.set(next.workId, saved);
11959
+ });
11960
+ saving = pending.catch(() => void 0);
11961
+ return pending;
11962
+ }).then((result) => {
11963
+ fulfilled.add(item.workId);
11964
+ return result;
11965
+ })
11966
+ } : {},
11967
+ connect: chain?.connect ?? dependencies.auth.connect,
11968
+ profile: (connection, profileSignal) => prepareProfile(dependencies, snapshot, connection, profileSignal),
11969
+ context: (item) => {
11970
+ const binding = snapshot.journey.works.find((value) => value.id === item.workId)?.binding;
11971
+ return {
11972
+ snapshot: true,
11973
+ ...chain ? { fresh: chain.fresh } : {},
11974
+ active: async () => {
11975
+ await active(item);
11976
+ },
11977
+ bindingId: binding?.subject === send.subject ? binding.bundleRecordId : void 0,
11978
+ upload: async (artifact, upload, uploadSignal) => {
11979
+ const started = performance.now();
11980
+ await dependencies.artifacts.upload(snapshot.journey.journeyId, artifact, upload, uploadSignal);
11981
+ dependencies.uploaded?.(artifact.workId, artifact.byteLength, Math.round(performance.now() - started));
11982
+ }
11983
+ };
11984
+ },
11985
+ save,
11986
+ applied: () => ({
11987
+ ...snapshot,
11988
+ status: "applied"
11989
+ }),
11990
+ stale: async () => ({
11991
+ ...requireSend(await dependencies.storage.loadSend(address.journeyId, address.sendId)),
11992
+ status: "stale"
11993
+ })
11994
+ }, signal);
11995
+ } catch (error) {
11996
+ cleanup.failed(error);
11997
+ throw error;
11998
+ } finally {
11999
+ await cleanup.run(() => preparation.close());
12000
+ }
11839
12001
  }
11840
12002
  /** The first wait after a bare refusal of staged bytes, doubled per refusal up to the cap. */
11841
12003
  const busyBaseMs = 2e3;
@@ -15209,7 +15371,13 @@ async function createRuntime(options, callbacks = {}) {
15209
15371
  let bundles;
15210
15372
  if (import.meta.url.endsWith(".ts")) {
15211
15373
  traceStartup("worker-build-start");
15212
- bundles = await (await import(new URL("../../../scripts/workers/build.ts", import.meta.url).href)).buildWorkerBundles();
15374
+ const shared = process.env["SESS_PILOT_TEST_WORKER_BUNDLES"];
15375
+ if (shared) bundles = {
15376
+ directory: shared,
15377
+ url: (name) => pathToFileURL(join(shared, `${name}.mjs`)),
15378
+ dispose: async () => {}
15379
+ };
15380
+ else bundles = await (await import(new URL("../../../scripts/workers/build.ts", import.meta.url).href)).buildWorkerBundles();
15213
15381
  traceStartup("worker-build-end");
15214
15382
  }
15215
15383
  try {
@@ -15240,7 +15408,7 @@ async function compose(options, callbacks, bundles) {
15240
15408
  event: "STATE_SCHEMA_BACKED_UP",
15241
15409
  schema,
15242
15410
  backup: path,
15243
- detail: "State from an older SessPilot release was kept at this backup path and fresh state was started. Earlier journey progress and approvals start over; source logs, completed ZIP files and private sensitive terms are unchanged. Delete the backup once it is no longer needed."
15411
+ detail: "State from an older Tacitt CLI version was kept at this backup path and fresh state was started. Earlier journey progress and approvals start over; source logs, completed ZIP files and private sensitive terms are unchanged. Delete the backup once it is no longer needed."
15244
15412
  })}\n`);
15245
15413
  },
15246
15414
  ...process.env["SESS_PILOT_TEST_STARTUP_TRACE"] === "1" ? { onStartupPhase: traceStartup } : {}
@@ -15524,7 +15524,7 @@ async function* configuredSessionUploads(input, identities, signal) {
15524
15524
  bufferedWrite: false,
15525
15525
  dataDescriptor: true,
15526
15526
  unixMode: 33152,
15527
- lastModDate: /* @__PURE__ */ new Date("1980-01-01T00:00:00.000Z"),
15527
+ lastModDate: new Date(1980, 0, 1),
15528
15528
  extendedTimestamp: false
15529
15529
  });
15530
15530
  for (const [index, part] of session.parts.entries()) {
@@ -1,4 +1,4 @@
1
- import { i as createExecutor, t as assertStoreHeap } from "./heap-DY5n4EVC.mjs";
1
+ import { i as createExecutor, t as assertStoreHeap } from "./heap-BR8WjR25.mjs";
2
2
  import { parentPort } from "node:worker_threads";
3
3
  //#region packages/local-host/src/store/worker/entry.ts
4
4
  if (!parentPort) throw new Error("STORE_WORKER_REQUIRED");