@gitroll/tacitt-cli 0.6.11 → 0.6.13

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 (57) hide show
  1. package/README.md +4 -0
  2. package/bin/INDEX.md +6 -4
  3. package/bin/tacitt.cjs +1 -0
  4. package/dist/{agent-setup-DdfF3zch.mjs → agent-setup-Doy_AnPY.mjs} +2 -2
  5. package/dist/agent-setup-worker.mjs +5 -5
  6. package/dist/cli.mjs +32 -32
  7. package/dist/codex-title-worker.mjs +2 -2
  8. package/dist/configured-preview-worker.mjs +5 -5
  9. package/dist/configured-session-worker.mjs +23 -25
  10. package/dist/{frame-Bq2zRh-i.mjs → frame-BPbypzmt.mjs} +1 -1
  11. package/dist/{heap-CV8Eca8V.mjs → heap-DCS_WXIA.mjs} +264 -23
  12. package/dist/inspection-worker.mjs +4 -4
  13. package/dist/{job-BKsifhyv.mjs → job--zxwgaPy.mjs} +155 -41
  14. package/dist/{locate-Cx52Zqxv.mjs → locate-PLEyLYW-.mjs} +292 -236
  15. package/dist/{model-C1UXkHh9.mjs → model-CyXvsWLz.mjs} +1 -1
  16. package/dist/operation-executor.mjs +3 -3
  17. package/dist/preview-worker.mjs +1 -1
  18. package/dist/privacy-validator.mjs +28 -8
  19. package/dist/{query-AMZerUh8.mjs → query-BC7MIaWc.mjs} +1914 -759
  20. package/dist/{run-CaPktPhH.mjs → run-Dod7Vykp.mjs} +6 -6
  21. package/dist/session-process.mjs +20 -0
  22. package/dist/{src-DlVbGB32.mjs → src-CP-K2PBv.mjs} +2100 -1649
  23. package/dist/{src-D1iONla3.mjs → src-CttNuxOn.mjs} +925 -630
  24. package/dist/{src-wZx_v0Ih.mjs → src-dL6y2mcu.mjs} +180 -30
  25. package/dist/store-database-worker.mjs +1 -1
  26. package/dist/web/assets/index-BgDdPL1c.js +172 -0
  27. package/dist/web/index.html +2 -1
  28. package/package.json +2 -1
  29. package/skills/INDEX.md +6 -4
  30. package/skills/tacitt/INDEX.md +7 -6
  31. package/skills/tacitt/SKILL.md +7 -7
  32. package/skills/tacitt/references/INDEX.md +14 -11
  33. package/skills/tacitt/references/b-method.md +6 -4
  34. package/skills/tacitt/references/bootstrap.md +18 -4
  35. package/skills/tacitt/references/classification.md +1 -1
  36. package/skills/tacitt/references/commands.md +34 -10
  37. package/skills/tacitt/references/journey.md +7 -7
  38. package/skills/tacitt/references/parallel-review.md +31 -12
  39. package/skills/tacitt/references/remote-workers.md +6 -2
  40. package/skills/tacitt/references/send.md +7 -3
  41. package/skills/tacitt/scripts/INDEX.md +40 -37
  42. package/skills/tacitt/scripts/answer.mjs +31 -2
  43. package/skills/tacitt/scripts/describe.mjs +3 -2
  44. package/skills/tacitt/scripts/journey-card-consolidation.mjs +1 -1
  45. package/skills/tacitt/scripts/journey-classification-tasks.mjs +28 -8
  46. package/skills/tacitt/scripts/journey-classification.mjs +1 -0
  47. package/skills/tacitt/scripts/journey-panel-host.d.mts +2 -0
  48. package/skills/tacitt/scripts/journey-panel-host.mjs +25 -2
  49. package/skills/tacitt/scripts/journey-scan-timing.d.mts +4 -0
  50. package/skills/tacitt/scripts/journey-scan-timing.mjs +15 -2
  51. package/skills/tacitt/scripts/journey-state.mjs +11 -0
  52. package/skills/tacitt/scripts/journey-tasks.mjs +12 -5
  53. package/skills/tacitt/scripts/journey.mjs +11 -3
  54. package/skills/tacitt/scripts/privacy-validator.mjs +112 -40
  55. package/skills/tacitt/scripts/review.d.mts +1 -0
  56. package/skills/tacitt/scripts/review.mjs +680 -119
  57. package/dist/web/assets/index-CYl7eDUu.js +0 -172
@@ -1,6 +1,6 @@
1
- import { S as readPrivateReviewPayload, at as restrictToOwner, it as openPrivateFile, nt as exclusiveLock, ot as windowsOwnerOnly, x as writePrivateReviewPayload } from "./src-DlVbGB32.mjs";
2
- import { An as createDetectionSummary, Ft as emptyReviewCorrectionSchema, Ht as termTypeSchema, It as journeyTermSchema, Lt as reviewTermsInputSchema, Nt as privacyTermSchema, Pt as emptyReviewCorrectionResultSchema, Rt as reviewTermsResultSchema, S as validateBReview, a as reviewBatchChunks, b as bContextEvidence, bt as journeyWorkSuggestionSchema, c as reviewDigest, cn as reviewFamilies, d as validateReview, f as findingReferenceKey, g as reviewAlignmentSchema, gt as journeyGuidanceSchema, l as reviewSubmitSchema, lt as journeyIdSchema, mt as nativeJourneySchema, nn as ApplicationError, o as reviewBatchSchema, or as isStaticCategory, p as reviewAlignmentAudit, pn as fileIdentitySchema, sn as familySelected, sr as staticCategories, st as journeyDigestSchema, u as textDigest, v as validateReviewAlignment, y as semanticFindingSchema, yt as journeyDescribeSchema } from "./locate-Cx52Zqxv.mjs";
3
- import { $ as validateDetectionSummary, $t as journeyResourceSchema, At as obsoleteJourney, Bn as journeySearchResultSchema, Cn as mergeBRepair, Cr as preparationStartSchema, En as scanCanonicalResourceSchema, Er as validatePreparationFacts, Fr as applyCardAnswers, Ft as journeyGuidance, Gn as journeyEventSchema, Gr as staticSessionId, Gt as journeyContextQuerySchema, Hn as searchScopeDigest, Hr as mergePrivacyTerms, In as journeyInventoryReadSchema, It as journeyBatchReadSchema, J as validateInspectionFence, Jt as hideFinishSchema, Kt as journeyContextSchema, Ln as journeyInventorySchema, Lt as journeyReviewStorageQuerySchema, Nn as validateScanExclusion, Nt as attemptBudgetCodePoints, On as scanSessionResourceSchema, Ot as settleCorrections, Pt as validateAttemptAppend, Q as validateInspectionContribution, Qt as journeyResourceQuerySchema, Rn as journeyPreparationProgressSchema, Rt as applyClassificationProposal, Sn as bReviewDigest, Sr as preparationSettlementSchema, Tn as scanAliasResourceSchema, Tr as preparationUpdateSchema, U as validateInspectionBatch, Un as journeyCommitResultSchema, Ut as newJourneyWork, Vn as journeySearchSaveSchema, W as validateInspectionBatches, Wn as journeyCommitSchema, X as validateInspectionInputs, Xn as settleConfirmedHides, Xr as journeySendSchema, Xt as journeyPolicyRespondSchema, Y as validateInspectionHead, Yt as hidePublishSchema, Z as validateInspectionQuery, Zt as projectPreparationProgress, _r as workPrivacyDecisions, ai as isReadingComplete, an as reviewAttemptsResultSchema, bn as reviewRepairResourceSchema, br as preparationBeginSchema, c as reconstructBFields, ci as selectedWorks, cn as sessionReconciliationResourceSchema, cr as confirmedHideKey, d as resolveGroupSplit, dr as dictionaryHides, en as journeyIsolateSchema, et as inspectionInputs, fn as sessionReviewQuerySchema, g as semanticGroupKey, gi as stateSchema, gn as sessionUsefulnessResourceSchema, gr as workDecisionDigest, gt as manualUploadSchema, h as hideScopeDigest, hn as sessionUsefulnessInputSchema, hr as retireWorkDescription, in as reviewAttemptsQuerySchema, ir as bMaskedHits, kn as scanGitSeedsId, l as chooseCheapPlacement, li as withReadingProgress, mn as sessionReviewStatusSchema, mr as syncTermGroups, nn as sortingApplySchema, o as bRereadDigest, oi as isSelectedWork, pn as sessionReviewResourceSchema, pr as maskedHitsStale, pt as manualUploadBindingSchema, q as validateInspectionClaim, qn as confirmedHideDeltaSchema, qt as hideBeginSchema, rn as reviewAttemptResourceSchema, s as reconcileBSession, si as isSkippedWork, sn as sessionReconcileResultSchema, sr as confirmedHideGroupIds, t as scanBatchContextDigest, tn as journeyPlaceSchema, u as reviseGroupMembership, un as sessionRereadResourceSchema, ur as confirmedHideTerms, v as projectJourneyInventory, vn as reviewCandidateResourceSchema, vt as candidateSortAvailable, wr as preparationStateSchema, x as bReviewScope, xr as preparationPermissionSchema, y as preparationScope, zt as classificationCommitSchema } from "./src-D1iONla3.mjs";
1
+ import { C as writePrivateReviewPayload, ct as openPrivateFile, lt as restrictToOwner, ot as exclusiveLock, ut as windowsOwnerOnly, w as readPrivateReviewPayload } from "./src-CP-K2PBv.mjs";
2
+ import { Bt as reviewTermsResultSchema, C as validateBReview, Ft as privacyTermSchema, It as emptyReviewCorrectionResultSchema, Lt as emptyReviewCorrectionSchema, M as semanticFindingSchema, Mn as createDetectionSummary, Rt as journeyTermSchema, St as journeyWorkSuggestionSchema, Wt as termTypeSchema, b as validateReviewAlignment, c as reviewBatchSchema, cr as isStaticCategory, d as reviewSubmitSchema, dt as journeyIdSchema, f as textDigest, gt as nativeJourneySchema, h as reviewAlignmentAudit, hn as fileIdentitySchema, in as ApplicationError, ln as familySelected, lr as staticCategories, lt as journeyDigestSchema, m as findingReferenceKey, p as validateReview, s as reviewBatchChunks, u as reviewDigest, un as reviewFamilies, v as reviewAlignmentSchema, vt as journeyGuidanceSchema, x as bContextEvidence, xt as journeyDescribeSchema, zt as reviewTermsInputSchema } from "./locate-PLEyLYW-.mjs";
3
+ import { $ as validateDetectionSummary, $t as journeyPlaceSchema, At as validateAttemptAppend, Bn as journeySearchSaveSchema, Bt as newJourneyWork, Cn as scanAliasResourceSchema, Dn as scanGitSeedsId, En as scanSessionResourceSchema, Fn as journeyInventoryReadSchema, Ft as classificationCommitSchema, Gr as staticSessionId, Gt as hideFinishSchema, Hn as journeyCommitResultSchema, Hr as mergePrivacyTerms, Ht as journeyContextQuerySchema, In as journeyInventorySchema, Ir as obsoleteJourney, J as validateInspectionFence, Jt as sendOutputQuerySchema, Kn as confirmedHideDeltaSchema, Kt as hidePublishSchema, Ln as journeyPreparationProgressSchema, Lr as sendOwesPassport, Mi as stateSchema, Mt as journeyBatchReadSchema, Nn as projectPreparationProgress, Nt as journeyReviewStorageQuerySchema, Ot as settleCorrections, Pt as applyClassificationProposal, Q as validateInspectionContribution, Qt as journeyIsolateSchema, Rr as sendReachedPassport, Si as bMaskedHits, Sr as assertJourneyPrivacyReady, U as validateInspectionBatch, Un as journeyCommitSchema, Ut as journeyContextSchema, Vn as searchScopeDigest, W as validateInspectionBatches, Wn as journeyEventSchema, Wt as hideBeginSchema, X as validateInspectionInputs, Xr as journeySendSchema, Xt as journeyResourceQuerySchema, Y as validateInspectionHead, Yn as settleConfirmedHides, Yt as sendOutputSchema, Z as validateInspectionQuery, Zt as journeyResourceSchema, an as sessionReconcileResultSchema, bn as bReviewDigest, c as reconstructBFields, ci as isSkippedWork, cn as sessionRereadResourceSchema, cr as preparationSettlementSchema, d as resolveGroupSplit, di as withReadingProgress, dn as sessionReviewResourceSchema, dr as preparationUpdateSchema, en as sortingApplySchema, et as inspectionInputs, fi as confirmedHideGroupIds, fn as sessionReviewStatusSchema, fr as validatePreparationFacts, g as semanticGroupKey, gi as dictionaryHides, gn as reviewCandidateResourceSchema, gt as manualUploadSchema, h as hideScopeDigest, hi as confirmedHideTerms, jn as validateScanExclusion, jt as journeyGuidance, kt as attemptBudgetCodePoints, l as chooseCheapPlacement, li as selectedWorks, lr as preparationStartSchema, mn as sessionUsefulnessResourceSchema, nn as reviewAttemptsQuerySchema, nr as workDecisionDigest, o as bRereadDigest, oi as isReadingComplete, on as sessionReconciliationResourceSchema, or as preparationBeginSchema, pi as confirmedHideKey, pn as sessionUsefulnessInputSchema, pt as manualUploadBindingSchema, q as validateInspectionClaim, qt as journeyPolicyRespondSchema, ri as hasCompleteWorkDescription, rn as reviewAttemptsResultSchema, rr as workPrivacyDecisions, s as reconcileBSession, si as isSelectedWork, sr as preparationPermissionSchema, t as scanBatchContextDigest, ti as sendSessionUploadSchema, tn as reviewAttemptResourceSchema, tr as retireWorkDescription, u as reviseGroupMembership, un as sessionReviewQuerySchema, ur as preparationStateSchema, v as projectJourneyInventory, vi as maskedHitsStale, vn as reviewRepairResourceSchema, vt as candidateSortAvailable, wn as scanCanonicalResourceSchema, wr as reconcileReaderKeeps, x as bReviewScope, xn as mergeBRepair, xr as applyCardAnswers, y as preparationScope, yi as syncTermGroups, zn as journeySearchResultSchema, zt as isolatedWorkId } from "./src-CttNuxOn.mjs";
4
4
  import { constants } from "node:fs";
5
5
  import { z } from "zod";
6
6
  import { copyFile, link, lstat, mkdir, mkdtemp, open, readFile, realpath, rm, unlink } from "node:fs/promises";
@@ -346,9 +346,26 @@ function readJourney(db, journeyId) {
346
346
  if (typeof text !== "string") throw new ApplicationError("CORRUPTED_STATE");
347
347
  const state = JSON.parse(text);
348
348
  if (typeof state === "object" && state !== null && "formatVersion" in state && state.formatVersion !== 10) throw new StoreError("OBSOLETE_SCHEMA", "Stored native Journey state uses an obsolete candidate format and has been preserved.");
349
+ let value = reconcileReaderKeeps(nativeJourneySchema.parse(state));
350
+ if (value.outputVersion !== void 0 && value.outputVersion > 1) throw new StoreError("OBSOLETE_SCHEMA", "This journey requires a newer final writer.");
351
+ if (value.outputVersion !== 1 && value.phase !== "completed" && !obsoleteJourney(value)) value = {
352
+ ...value,
353
+ outputVersion: 1,
354
+ phase: value.phase === "ready" ? "review" : value.phase,
355
+ privacy: {
356
+ ...value.privacy,
357
+ packageApproval: null
358
+ },
359
+ works: value.works.map((work) => ({
360
+ ...work,
361
+ revision: work.revision + 1,
362
+ status: work.status === "ready" ? "review" : work.status,
363
+ artifact: null
364
+ }))
365
+ };
349
366
  return {
350
367
  text,
351
- value: nativeJourneySchema.parse(state)
368
+ value
352
369
  };
353
370
  });
354
371
  }
@@ -362,23 +379,29 @@ function insertCorrectionReport(db, resource) {
362
379
  }
363
380
  db.prepare("INSERT INTO journey_resources VALUES(?,?,?,?)").run(resource.journeyId, resource.kind, resource.id, json);
364
381
  }
365
- /**
366
- * A snapshot of an earlier release cannot render, so a new Journey may replace it, but never
367
- * one with a Send record or a sent Work: that Send stays for the person to finish.
368
- */
369
- /** A receipt or persisted session mutation intent means a Send may have reached Passport. */
382
+ /** Whether any Send of this Journey may have reached Passport (`sendReachedPassport`). */
370
383
  function reachedPassport(db, journeyId) {
371
384
  return db.prepare("SELECT json FROM journey_sends WHERE journey_id=?").all(journeyId).some((row) => {
372
385
  if (typeof row["json"] !== "string") throw new ApplicationError("CORRUPTED_STATE");
373
- return journeySendSchema.parse(JSON.parse(row["json"])).items.some((item) => item.receipt || item.previousReceipts.length || item.sessionUpload !== void 0 || item.sessionMembership || item.sessionAssembly);
386
+ return sendReachedPassport(journeySendSchema.parse(JSON.parse(row["json"])));
374
387
  });
375
388
  }
376
389
  /** An abandoned Journey gives way to a new one unless one of its Sends reached Passport. */
377
390
  function abandoned(db, current) {
378
391
  return current.abandonedAt !== void 0 && !reachedPassport(db, current.journeyId);
379
392
  }
393
+ /**
394
+ * A snapshot of an earlier release cannot render, so a new Journey replaces it unless its current
395
+ * Send still owes Passport something its next advance sends without the person
396
+ * (`sendOwesPassport`, ADR 0284, owner answer A): sent Works stay on Passport, and the new Journey
397
+ * reads every Work again. Replacement cancels its unfinished Sends.
398
+ */
380
399
  function replaceable(db, current) {
381
- return obsoleteJourney(current) && !current.works.some((work) => work.binding) && !db.prepare("SELECT 1 FROM journey_sends WHERE journey_id=? LIMIT 1").get(current.journeyId);
400
+ if (!obsoleteJourney(current)) return false;
401
+ const row = db.prepare("SELECT json FROM journey_sends JOIN journey_current_send ON journey_sends.id=journey_current_send.send_id WHERE journey_current_send.journey_id=?").get(current.journeyId);
402
+ if (!row) return true;
403
+ if (typeof row["json"] !== "string") throw new ApplicationError("CORRUPTED_STATE");
404
+ return !sendOwesPassport(current, journeySendSchema.parse(JSON.parse(row["json"])));
382
405
  }
383
406
  /** An addition counts only when one of its findings was assigned to a confirmed Hide category. */
384
407
  function joinedHideValues(db, state, delta) {
@@ -439,6 +462,7 @@ function commitJourney(db, input) {
439
462
  ...input,
440
463
  state: {
441
464
  ...input.state,
465
+ outputVersion: 1,
442
466
  privacy: {
443
467
  ...input.state.privacy,
444
468
  rememberedRules
@@ -453,6 +477,7 @@ function commitJourney(db, input) {
453
477
  state: current
454
478
  };
455
479
  }
480
+ if (input.expectedRevision === null && current && replaceable(db, current)) db.prepare("DELETE FROM journey_current_send WHERE journey_id=?").run(current.journeyId);
456
481
  if (input.state.journeyId !== input.event.journeyId) throw new ApplicationError("INVALID_ARGUMENTS");
457
482
  if (input.closedReader) {
458
483
  const hold = input.closedReader;
@@ -2578,6 +2603,7 @@ function selectTerms(db, query) {
2578
2603
  if (!source || source["review_ref_json"] === null) throw new ApplicationError("SCOPE_NOT_READY");
2579
2604
  }
2580
2605
  const work = requireActiveBatch(state, batch.header);
2606
+ if (work.access === "drop") throw new ApplicationError("REVISION_CONFLICT");
2581
2607
  if (work.reviewInterruptions?.some((hold) => hold.inputRevision === work.inputRevision && hold.batchIds.includes(unit.batchId)) || db.prepare("SELECT 1 FROM journey_resources WHERE journey_id=? AND kind='closed-reader' AND id=?").get(state.journeyId, unit.batchId + ":" + input.packId)) throw new ApplicationError("REVISION_CONFLICT");
2582
2608
  if (batch.header.digest !== unit.batchDigest) throw new ApplicationError("REVISION_CONFLICT");
2583
2609
  return batch;
@@ -2640,6 +2666,10 @@ function finishTerms(db, packet) {
2640
2666
  count: term.count - previous
2641
2667
  })) added++;
2642
2668
  }
2669
+ for (const term of packet.located) if (upsertJourneyTerm(db, state.journeyId, {
2670
+ ...term,
2671
+ count: 0
2672
+ })) added++;
2643
2673
  const groups = syncTermGroups(state, readTermCounts(db, state.journeyId), (key) => findingPreference(db, key));
2644
2674
  const reference = {
2645
2675
  kind: "term-pack",
@@ -3853,9 +3883,9 @@ function activeScope(db, input, starting) {
3853
3883
  if (state.pendingHideId) throw new ApplicationError("SCOPE_NOT_READY");
3854
3884
  const work = state.works.find((item) => item.id === input.workId);
3855
3885
  if (!work || input.inputRevision !== work.inputRevision || input.snapshotId !== state.snapshotId) throw new ApplicationError("REVISION_CONFLICT");
3856
- const plan = readInventoryPlan(db, state);
3886
+ const { plan, families } = readInventoryFamilies(db, state);
3857
3887
  if (!plan.approval.inventory.complete) throw new ApplicationError("CORRUPTED_STATE");
3858
- return preparationScope(plan.approval.inventory, work);
3888
+ return preparationScope(plan.approval.inventory, work, families);
3859
3889
  }
3860
3890
  function insert(db, record) {
3861
3891
  db.prepare("INSERT INTO journey_resources VALUES(?,?,?,?)").run(record.journeyId, record.kind, record.id, JSON.stringify(record));
@@ -3926,7 +3956,16 @@ function readJourneyPreparationProgress(db, input) {
3926
3956
  const state = readFencedJourney(db, input);
3927
3957
  if (!state.consent || !state.snapshotId) return null;
3928
3958
  const { starts, settlements } = readPreparationReceipts(db, state.journeyId);
3929
- return projectPreparationProgress(state, readInventoryPlan(db, state), readCaptures(db, state.journeyId), starts, settlements);
3959
+ const captures = db.prepare(`SELECT id, json_extract(json,'$.capture.partId') AS partId,
3960
+ json_extract(json,'$.capture.facts.byteLength') AS bytes FROM journey_resources
3961
+ WHERE journey_id=? AND kind='capture'`).all(state.journeyId).map(({ id, partId, bytes }) => {
3962
+ if (id !== partId || typeof partId !== "string" || typeof bytes !== "number" || !Number.isSafeInteger(bytes) || bytes < 0) throw new ApplicationError("CORRUPTED_STATE");
3963
+ return {
3964
+ partId,
3965
+ bytes
3966
+ };
3967
+ });
3968
+ return projectPreparationProgress(state, readInventoryFamilies(db, state).families, captures, starts, settlements);
3930
3969
  }
3931
3970
  //#endregion
3932
3971
  //#region packages/local-host/src/store/journey/hide.ts
@@ -4732,14 +4771,6 @@ function finishJourneyPlacement(db, packet) {
4732
4771
  //#endregion
4733
4772
  //#region packages/local-host/src/store/journey/isolation.ts
4734
4773
  const reason = " – couldn't prepare";
4735
- /** Stable blocked Work IDs per parent, so later failing roots join one instead of multiplying. */
4736
- function isolatedWorkId(parentId, ordinal) {
4737
- const bytes = createHash("sha256").update(`isolated-roots:${parentId}:${ordinal}`).digest().subarray(0, 16);
4738
- bytes[6] = (bytes[6] ?? 0) & 15 | 64;
4739
- bytes[8] = (bytes[8] ?? 0) & 63 | 128;
4740
- const hex = bytes.toString("hex");
4741
- return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
4742
- }
4743
4774
  /**
4744
4775
  * Moves identified failing roots out of a pending multi-root Work into its visible blocked
4745
4776
  * Work, carrying their public-safe codes; the parent returns to pending for the rest.
@@ -4783,8 +4814,15 @@ function isolateJourneyRoots(db, input) {
4783
4814
  })),
4784
4815
  blocks: /* @__PURE__ */ new Map([[targetId, [...codes]]])
4785
4816
  });
4817
+ const classified = !existing && parent.classificationRecommendation && hasCompleteWorkDescription(parent) && !parent.suggestion ? {
4818
+ description: parent.description,
4819
+ classificationRecommendation: parent.classificationRecommendation,
4820
+ reason: parent.classificationRecommendation.reason,
4821
+ projectType: parent.projectType?.origin === "model" ? parent.projectType : null
4822
+ } : {};
4786
4823
  const works = next.works.map((work) => work.id === targetId ? {
4787
4824
  ...work,
4825
+ ...classified,
4788
4826
  ...access
4789
4827
  } : work);
4790
4828
  return commitJourney(db, commit(input, {
@@ -4912,9 +4950,197 @@ function finishJourneyDescription(db, packet) {
4912
4950
  }
4913
4951
  //#endregion
4914
4952
  //#region packages/local-host/src/store/journey/send.ts
4953
+ /** Run the privacy fence against the store's current immutable row on every request.
4954
+ * Only the approved output and this Work's frozen upload identities cross the worker boundary. */
4955
+ function readSendOutput(db, input) {
4956
+ const { journeyId, workId, revision } = input;
4957
+ const state = readJourney(db, journeyId);
4958
+ if (!state?.consent) throw new ApplicationError("CONFIRMATION_REQUIRED");
4959
+ if (state.phase === "completed" && state.outputVersion !== 1) throw new ApplicationError("SCOPE_NOT_READY");
4960
+ assertJourneyPrivacyReady(state, true);
4961
+ if (state.phase === "paused") throw new ApplicationError("OPERATION_CANCELED");
4962
+ const work = state.works.find((item) => item.id === workId);
4963
+ if (!work?.artifact || work.revision !== revision || work.artifact.revision !== revision) throw new ApplicationError("REVISION_CONFLICT");
4964
+ const [output] = readJourneyResources(db, {
4965
+ journeyId,
4966
+ kind: "output",
4967
+ id: `${workId}:${revision}`
4968
+ });
4969
+ if (output?.kind !== "output" || output.workId !== workId || output.revision !== revision || output.primary.sha256 !== work.artifact.sha256) throw new ApplicationError("ARTIFACT_IDENTITY_MISMATCH");
4970
+ if (!input.sessions) return {
4971
+ output,
4972
+ identities: []
4973
+ };
4974
+ const [base] = readJourneyResources(db, {
4975
+ journeyId,
4976
+ kind: "base",
4977
+ id: `${workId}:${output.finalMaskLedger.inputRevision}`
4978
+ });
4979
+ if (base?.kind !== "base" || base.workId !== workId || base.inputRevision !== output.finalMaskLedger.inputRevision || base.primary.sha256 !== output.finalMaskLedger.basePrimarySha256) throw new ApplicationError("ARTIFACT_IDENTITY_MISMATCH");
4980
+ const plan = readInventoryPlan(db, state);
4981
+ const sessions = new Map(plan.approval.inventory.sessions.map((session) => [session.id, session]));
4982
+ const groups = /* @__PURE__ */ new Map();
4983
+ const identities = base.sessions.map((binding) => {
4984
+ const session = sessions.get(binding.applicationSessionId);
4985
+ const part = session?.parts[0];
4986
+ const lastAt = session?.lastInteractionAt ?? session?.createdAt;
4987
+ if (!session || !part || !lastAt || !Number.isFinite(Date.parse(lastAt))) throw new ApplicationError("ARTIFACT_IDENTITY_MISMATCH");
4988
+ const native = part.nativeEvidence?.identity;
4989
+ if (native?.sessionId?.trim() && native.role !== "unknown" && (native.role !== "worker" || native.agentId?.trim() && session.harness === "claude")) {
4990
+ const key = JSON.stringify([
4991
+ session.harness,
4992
+ native.sessionId,
4993
+ native.role === "worker" ? native.agentId : null
4994
+ ]);
4995
+ const group = groups.get(key) ?? [];
4996
+ group.push(binding.archiveSessionId);
4997
+ groups.set(key, group);
4998
+ }
4999
+ const nativeId = part.nativeEvidence?.identity?.sessionId ?? session.sourceSessionId;
5000
+ const path = part.harnessRelativePath ?? "";
5001
+ if (!path && !nativeId?.trim() || isAbsolute(path) || path.includes("\\") || path.split("/").some((segment) => segment === ".." || segment === ".")) throw new ApplicationError("ARTIFACT_IDENTITY_MISMATCH");
5002
+ return {
5003
+ archiveSessionId: binding.archiveSessionId,
5004
+ relativePath: path,
5005
+ lastAt: new Date(lastAt).toISOString()
5006
+ };
5007
+ });
5008
+ const duplicates = [...groups.values()].filter((group) => group.length > 1);
5009
+ if (!duplicates.length) return {
5010
+ output,
5011
+ identities
5012
+ };
5013
+ const duplicateIds = new Set(duplicates.flat());
5014
+ const owners = new Map(base.sessions.map((s) => [s.archiveSessionId, s.applicationSessionId]));
5015
+ const sources = base.sourceBindings.flatMap((binding) => {
5016
+ if (!duplicateIds.has(binding.sessionId) || binding.bundleId !== base.bundleId || owners.get(binding.sessionId) !== binding.applicationSessionId) return [];
5017
+ const part = sessions.get(binding.applicationSessionId)?.parts.find((p) => p.id === binding.sourcePartId);
5018
+ return part ? [{
5019
+ session: binding.sessionId,
5020
+ source: binding.bundleSourceId,
5021
+ part
5022
+ }] : [];
5023
+ });
5024
+ const outputs = state.works.filter(isSelectedWork).flatMap((w) => w.artifact ? [{
5025
+ workId: w.id,
5026
+ revision: w.revision,
5027
+ sha256: w.artifact.sha256,
5028
+ bundleId: w.artifact.bundleId
5029
+ }] : []);
5030
+ const approval = state.privacy.packageApproval;
5031
+ if (!approval) throw new ApplicationError("CONFIRMATION_REQUIRED");
5032
+ return {
5033
+ output,
5034
+ identities,
5035
+ extension: {
5036
+ groups: duplicates,
5037
+ sources,
5038
+ outputs,
5039
+ approval
5040
+ }
5041
+ };
5042
+ }
5043
+ const sendCurrentSchema = z.object({
5044
+ journeyId: journeyIdSchema,
5045
+ sendId: journeyIdSchema,
5046
+ revision: z.number().int().nonnegative()
5047
+ }).strict();
5048
+ function isCurrentSend(db, input) {
5049
+ return !!db.prepare(`SELECT 1 FROM journey_runs r JOIN journey_current_send s ON s.journey_id=r.id
5050
+ JOIN journey_current c ON c.journey_id=r.id AND c.singleton=1
5051
+ WHERE r.id=? AND r.revision=? AND s.send_id=?`).get(input.journeyId, input.revision, input.sendId);
5052
+ }
5053
+ const transferCountSchema = z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER);
5054
+ const sendTransferSchema = z.object({
5055
+ journeyId: journeyIdSchema,
5056
+ sendId: journeyIdSchema,
5057
+ workId: journeyIdSchema,
5058
+ expectedJourneyRevision: transferCountSchema,
5059
+ expectedSendRevision: transferCountSchema,
5060
+ storedSessions: transferCountSchema.optional(),
5061
+ sessionUpload: sendSessionUploadSchema,
5062
+ requestKey: journeyIdSchema,
5063
+ at: z.iso.datetime()
5064
+ }).strict();
5065
+ const sendTransferResultSchema = z.discriminatedUnion("status", [z.object({
5066
+ status: z.literal("applied"),
5067
+ revision: transferCountSchema,
5068
+ lastEvent: transferCountSchema,
5069
+ sendRevision: transferCountSchema
5070
+ }).strict(), z.object({ status: z.enum(["stale", "replayed"]) }).strict()]);
5071
+ /** Update only transfer fields in the Send document and stamp its Journey revision/event. */
5072
+ function checkpointJourneySendTransfer(db, input) {
5073
+ const requestHash = createHash("sha256").update(JSON.stringify(input)).digest("hex");
5074
+ const receipt = db.prepare("SELECT request_hash,journey_id FROM journey_receipts WHERE request_key=?").get(input.requestKey);
5075
+ if (receipt) {
5076
+ if (receipt["request_hash"] !== requestHash || receipt["journey_id"] !== input.journeyId) throw new ApplicationError("OPERATION_CONFLICT");
5077
+ return { status: "replayed" };
5078
+ }
5079
+ const row = db.prepare(`SELECT r.revision,r.state_json ->> '$.formatVersion' AS formatVersion,
5080
+ r.state_json ->> '$.phase' AS phase,
5081
+ json_type(r.state_json,'$.consent') AS consentType,
5082
+ r.state_json ->> '$.pendingHideId' AS pendingHideId,
5083
+ json_type(r.state_json,'$.abandonedAt') AS abandonedType,s.json
5084
+ FROM journey_runs r
5085
+ JOIN journey_current c ON c.singleton=1 AND c.journey_id=r.id
5086
+ JOIN journey_current_send cs ON cs.journey_id=r.id AND cs.send_id=?
5087
+ JOIN journey_sends s ON s.journey_id=r.id AND s.id=cs.send_id
5088
+ WHERE r.id=?`).get(input.sendId, input.journeyId);
5089
+ if (!row) return { status: "stale" };
5090
+ const revisionValue = row["revision"];
5091
+ if (typeof revisionValue !== "number" || !Number.isSafeInteger(revisionValue) || revisionValue < 0) throw new ApplicationError("CORRUPTED_STATE");
5092
+ if (revisionValue !== input.expectedJourneyRevision) return { status: "stale" };
5093
+ if (row["formatVersion"] !== 10) throw new StoreError("OBSOLETE_SCHEMA", "Stored native Journey state uses an obsolete candidate format and has been preserved.");
5094
+ if (row["consentType"] !== "object") throw new ApplicationError("CONFIRMATION_REQUIRED");
5095
+ if (row["phase"] !== "sending") throw new ApplicationError(row["phase"] === "paused" ? "OPERATION_CANCELED" : "INVALID_TRANSITION");
5096
+ if (row["pendingHideId"] !== null && row["pendingHideId"] !== void 0) throw new ApplicationError("REVISION_CONFLICT");
5097
+ if (row["abandonedType"] !== null && row["abandonedType"] !== void 0) throw new ApplicationError("INVALID_TRANSITION");
5098
+ if (typeof row["json"] !== "string") throw new ApplicationError("CORRUPTED_STATE");
5099
+ const send = journeySendSchema.parse(JSON.parse(row["json"]));
5100
+ if (send.revision !== input.expectedSendRevision) return { status: "stale" };
5101
+ if (!send.subject || !send.confirmedAt) throw new ApplicationError("CONFIRMATION_REQUIRED");
5102
+ if (send.status === "paused") throw new ApplicationError("OPERATION_CANCELED");
5103
+ if (!["sending", "waiting"].includes(send.status)) throw new ApplicationError("INVALID_TRANSITION");
5104
+ const index = send.items.findIndex((item) => item.workId === input.workId);
5105
+ if (index < 0) throw new ApplicationError("INVALID_SELECTION");
5106
+ const item = send.items[index];
5107
+ if (!item) throw new ApplicationError("CORRUPTED_STATE");
5108
+ if (item.status !== "uploading") throw new ApplicationError("INVALID_TRANSITION");
5109
+ const revision = revisionValue + 1;
5110
+ const sendRevision = send.revision + 1;
5111
+ const next = journeySendSchema.parse({
5112
+ ...send,
5113
+ revision: sendRevision,
5114
+ items: send.items.map((candidate, candidateIndex) => candidateIndex === index ? {
5115
+ ...candidate,
5116
+ sessionUpload: input.sessionUpload,
5117
+ ...input.storedSessions === void 0 ? {} : { storedSessions: input.storedSessions }
5118
+ } : candidate)
5119
+ });
5120
+ if (db.prepare("UPDATE journey_sends SET json=? WHERE journey_id=? AND id=?").run(JSON.stringify(next), input.journeyId, input.sendId).changes !== 1) throw new ApplicationError("REVISION_CONFLICT");
5121
+ const inserted = db.prepare("INSERT INTO journey_events(journey_id,event_json) VALUES(?,?)").run(input.journeyId, JSON.stringify({
5122
+ journeyId: input.journeyId,
5123
+ revision,
5124
+ kind: "send",
5125
+ source: "system",
5126
+ at: input.at
5127
+ }));
5128
+ const lastEvent = Number(inserted.lastInsertRowid);
5129
+ if (!Number.isSafeInteger(lastEvent) || lastEvent < 1) throw new ApplicationError("CORRUPTED_STATE");
5130
+ if (db.prepare(`UPDATE journey_runs SET revision=?,state_json=json_set(
5131
+ state_json,'$.revision',?,'$.lastEvent',?) WHERE id=? AND revision=?`).run(revision, revision, lastEvent, input.journeyId, input.expectedJourneyRevision).changes !== 1) throw new ApplicationError("REVISION_CONFLICT");
5132
+ db.prepare("INSERT INTO journey_receipts VALUES(?,?,?,?)").run(input.requestKey, requestHash, input.journeyId, revision);
5133
+ return {
5134
+ status: "applied",
5135
+ revision,
5136
+ lastEvent,
5137
+ sendRevision
5138
+ };
5139
+ }
4915
5140
  const sendLoadSchema = z.object({
4916
5141
  journeyId: journeyIdSchema,
4917
- sendId: journeyIdSchema.optional()
5142
+ sendId: journeyIdSchema.optional(),
5143
+ observedRevision: z.number().int().nonnegative().optional()
4918
5144
  }).strict();
4919
5145
  const sendSnapshotSchema = z.object({
4920
5146
  journey: nativeJourneySchema,
@@ -4935,6 +5161,7 @@ const sendCommitResultSchema = sendSnapshotSchema.extend({ status: z.enum([
4935
5161
  "stale"
4936
5162
  ]) });
4937
5163
  function loadJourneySend(db, input) {
5164
+ if (input.observedRevision !== void 0 && !input.sendId && db.prepare("SELECT 1 AS same FROM journey_runs WHERE id=? AND revision=?").get(input.journeyId, input.observedRevision)) return null;
4938
5165
  const journey = readJourney(db, input.journeyId);
4939
5166
  if (!journey) return null;
4940
5167
  const row = input.sendId ? db.prepare("SELECT json FROM journey_sends WHERE journey_id=? AND id=?").get(input.journeyId, input.sendId) : db.prepare("SELECT json FROM journey_sends JOIN journey_current_send ON journey_sends.id=journey_current_send.send_id WHERE journey_current_send.journey_id=?").get(input.journeyId);
@@ -4972,6 +5199,7 @@ function commitJourneySend(db, input) {
4972
5199
  ...input.state,
4973
5200
  revision
4974
5201
  });
5202
+ if (!prior && obsoleteJourney(current.journey)) throw new ApplicationError("JOURNEY_OBSOLETE");
4975
5203
  const skipped = (workId) => current.journey.works.some((work) => work.id === workId && isSkippedWork(work));
4976
5204
  const finished = send.status === "completed" && send.omitted.every((work) => [
4977
5205
  "drop",
@@ -7058,6 +7286,9 @@ const operations = {
7058
7286
  "journey.policy.respond": define(true, policyRespondSchema, respondJourneyPolicy, (value) => journeyCommitResultSchema.parse(value)),
7059
7287
  "journey.policy.batches": define(false, policyBatchesSchema, selectPolicyBatches, (value) => z.array(z.uuid()).parse(value)),
7060
7288
  "journey.send.load": define(false, sendLoadSchema, loadJourneySend, (value) => sendSnapshotSchema.nullable().parse(value)),
7289
+ "journey.send.current": define(false, sendCurrentSchema, isCurrentSend, (value) => z.boolean().parse(value)),
7290
+ "journey.send.transfer": define(true, sendTransferSchema, checkpointJourneySendTransfer, (value) => sendTransferResultSchema.parse(value)),
7291
+ "journey.send.output": define(false, sendOutputQuerySchema, readSendOutput, (value) => sendOutputSchema.parse(value)),
7061
7292
  "journey.send.commit": define(true, sendCommitSchema, commitJourneySend, (value) => sendCommitResultSchema.parse(value)),
7062
7293
  "manual.upload.load": define(false, manualUploadLoadSchema, loadManualUpload, (value) => manualUploadSchema.nullable().parse(value)),
7063
7294
  "manual.upload.commit": define(true, manualUploadCommitSchema, commitManualUpload, (value) => manualUploadResultSchema.parse(value)),
@@ -7091,6 +7322,16 @@ const operations = {
7091
7322
  ]),
7092
7323
  state: preparationStateSchema
7093
7324
  }).strict().parse(value)),
7325
+ "journey.receipt.revision": define(false, z.object({
7326
+ journeyId: z.uuid(),
7327
+ key: z.uuid(),
7328
+ hash: z.string().regex(/^[0-9a-f]{64}$/u)
7329
+ }).strict(), (db, { journeyId, key, hash }) => {
7330
+ const row = db.prepare("SELECT journey_id,request_hash,revision FROM journey_receipts WHERE request_key=?").get(key);
7331
+ if (!row) return null;
7332
+ if (row["journey_id"] !== journeyId || row["request_hash"] !== hash) throw new ApplicationError("OPERATION_CONFLICT");
7333
+ return z.number().int().nonnegative().parse(row["revision"]);
7334
+ }, (value) => z.number().int().nonnegative().nullable().parse(value)),
7094
7335
  "journey.current": define(false, z.object({ journeyId: z.uuid().optional() }), (db, input) => readJourney(db, input.journeyId), (value) => nativeJourneySchema.nullable().parse(value)),
7095
7336
  "journey.terms.reopen-empty": define(true, emptyReviewCorrectionSchema, reopenEmptyReview, (value) => emptyReviewCorrectionResultSchema.parse(value)),
7096
7337
  "journey.terms.select": define(false, termsQuerySchema, selectTerms, (value) => termsSelectionSchema.parse(value)),
@@ -1,7 +1,7 @@
1
- import { X as createInspectionProcessor } from "./src-DlVbGB32.mjs";
2
- import { Nn as createStaticProcessor } from "./locate-Cx52Zqxv.mjs";
3
- import { l as createNativeAdapters, u as createGitNameSeedProvider } from "./src-wZx_v0Ih.mjs";
4
- import { G as validateInspectionRequest } from "./src-D1iONla3.mjs";
1
+ import { et as createInspectionProcessor } from "./src-CP-K2PBv.mjs";
2
+ import { Fn as createStaticProcessor } from "./locate-PLEyLYW-.mjs";
3
+ import { l as createNativeAdapters, u as createGitNameSeedProvider } from "./src-dL6y2mcu.mjs";
4
+ import { G as validateInspectionRequest } from "./src-CttNuxOn.mjs";
5
5
  import "./messages-D3m3bhva.mjs";
6
6
  import { z } from "zod";
7
7
  import { parentPort, workerData } from "node:worker_threads";