@gitroll/tacitt-cli 0.6.12 → 0.6.14

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 (46) hide show
  1. package/dist/{agent-setup-B12VbvN_.mjs → agent-setup-CEPQfjBv.mjs} +2 -2
  2. package/dist/agent-setup-worker.mjs +5 -5
  3. package/dist/cli.mjs +10 -10
  4. package/dist/codex-title-worker.mjs +1 -1
  5. package/dist/configured-preview-worker.mjs +5 -5
  6. package/dist/configured-session-worker.mjs +5 -5
  7. package/dist/{frame-B3He21G8.mjs → frame-CRm99088.mjs} +1 -1
  8. package/dist/{heap-Blrat--4.mjs → heap-Bh9tySJp.mjs} +12 -7
  9. package/dist/inspection-worker.mjs +4 -4
  10. package/dist/{job-CBuIMTQ0.mjs → job-CsWKWcM4.mjs} +135 -36
  11. package/dist/{locate-D-ZaHpI0.mjs → locate-CP2t26j2.mjs} +449 -244
  12. package/dist/{model-DR9odhWP.mjs → model-ChHuVKoB.mjs} +1 -1
  13. package/dist/operation-executor.mjs +3 -3
  14. package/dist/preview-worker.mjs +1 -1
  15. package/dist/privacy-validator.mjs +1 -1
  16. package/dist/{query-BYDcNKy2.mjs → query-D28k3XOr.mjs} +6044 -5706
  17. package/dist/{run-DQ9xyKSt.mjs → run-CiJmV__5.mjs} +6 -6
  18. package/dist/{src-Do-qzuMn.mjs → src-B3il9x5F.mjs} +617 -545
  19. package/dist/{src-CKQlTA2Z.mjs → src-CWMrGXJK.mjs} +180 -30
  20. package/dist/{src-DeNJ8vOD.mjs → src-CmvEJj46.mjs} +1806 -1690
  21. package/dist/store-database-worker.mjs +1 -1
  22. package/dist/web/assets/index-XdWDOC5H.js +172 -0
  23. package/dist/web/index.html +1 -1
  24. package/package.json +1 -1
  25. package/skills/tacitt/INDEX.md +1 -1
  26. package/skills/tacitt/SKILL.md +5 -3
  27. package/skills/tacitt/references/INDEX.md +2 -2
  28. package/skills/tacitt/references/b-method.md +4 -2
  29. package/skills/tacitt/references/bootstrap.md +1 -1
  30. package/skills/tacitt/references/commands.md +23 -4
  31. package/skills/tacitt/references/journey.md +5 -3
  32. package/skills/tacitt/references/parallel-review.md +22 -9
  33. package/skills/tacitt/references/remote-workers.md +6 -2
  34. package/skills/tacitt/scripts/INDEX.md +5 -5
  35. package/skills/tacitt/scripts/answer.mjs +12 -1
  36. package/skills/tacitt/scripts/describe.mjs +3 -2
  37. package/skills/tacitt/scripts/journey-classification-tasks.mjs +2 -0
  38. package/skills/tacitt/scripts/journey-classification.mjs +1 -0
  39. package/skills/tacitt/scripts/journey-scan-timing.mjs +3 -2
  40. package/skills/tacitt/scripts/journey-state.d.mts +3 -0
  41. package/skills/tacitt/scripts/journey-state.mjs +8 -1
  42. package/skills/tacitt/scripts/journey.mjs +31 -1
  43. package/skills/tacitt/scripts/privacy-validator.mjs +160 -40
  44. package/skills/tacitt/scripts/review.d.mts +1 -0
  45. package/skills/tacitt/scripts/review.mjs +296 -35
  46. package/dist/web/assets/index-BgDdPL1c.js +0 -172
@@ -1,5 +1,5 @@
1
- import { $ as bTermSchema, A as indexBRanges, At as packageConfirmActionSchema, B as unownedBObligations, Bn as cleanupImplementationVersion, Bt as termOverrideValueSchema, C as createBAnalysisUnits, Ct as hidePlanResourceSchema, D as bLiteralTerms, Dt as cardsSubmitSchema, E as bLiteralAdmitted, Et as cardsConfirmActionSchema, F as bUnitFragments, Fn as staticOptions, Ft as emptyReviewCorrectionSchema, G as codePointSlice, H as bEvidenceReader, Ht as termTypeSchema, I as bAdoptedObligations, Jt as journeyProfileAddressActionSchema, K as createCodePointIndex, Kt as sessionPeriodSchema, Lt as reviewTermsInputSchema, M as bDuplicateMirror, Mt as privacyTermKey, Nn as createStaticProcessor, O as bLiterals, On as currentDetectorVersion, P as bLegacyBindings, Pn as staticDefaults, Q as bSessionDecisionSchema, Qn as orderProfile, R as bIssue, Rt as reviewTermsResultSchema, S as validateBReview, St as hidePlanInputSchema, T as validateBUnit, Tt as cardAnswerActionSchema, U as bDigest, Ut as termTypes, V as bAnchorKey, Vn as entityDictionaryVersion, Vt as termTypeInfo, W as codePointLength, Wt as deselectOutsidePeriod, X as bRetainedGroupSchema, Y as bRepresentationSchema, Yt as journeyProfileLinksActionSchema, Z as bReviewSubmissionSchema, Zn as validateProfile, _ as reviewFindingsDigest, _t as journeyInstructionInputSchema, a as reviewBatchChunks, ar as detectStatic, at as journeyAccessSchema, c as reviewDigest, cn as reviewFamilies, ct as journeyExceptionSchema, d as validateReview, dn as observeSource, dt as journeyPreferenceScopeSchema, en as respondProfileLinks, er as validatePlan, et as bAnchorSchema, f as findingReferenceKey, fn as sourceSnapshotId, ft as journeyProjectTypeSchema, g as reviewAlignmentSchema, gn as sourcePartSchema, gt as journeyGuidanceSchema, h as reviewAlignmentInputSchema, hr as defaultDetectionLimits, ht as reviewInterruptionSchema, i as compileBFindings, in as safeCode, it as consentedWithoutAccount, jn as gapReasons, jt as privacyResolveSchema, k as uncoveredBObligations, kn as inspectionSemanticVersions, kt as emptyJourneyPrivacy, l as reviewSubmitSchema, ln as sourceGraph, lt as journeyIdSchema, m as reviewAlignmentAuditSchema, mn as inventorySchema, mr as scalarBefore, mt as nativeJourneySchema, nn as ApplicationError, nt as bIdSchema, o as reviewBatchSchema, on as familyNeedsReview, ot as journeyChoiceSchema, pn as fileIdentitySchema, pr as scalarAfter, pt as journeyWorkTypeSchema, q as subtractBRanges, qt as emptyJourneyProfile, rn as UnfinishedJourneyError, rt as bMethod, s as reviewChunkSchema, sn as familySelected, sr as staticCategories, st as journeyDigestSchema, tn as classificationTitleMaskSchema, tr as copyData, tt as bCountSchema, u as textDigest, un as sourceKey, ur as createEntityMatcher, ut as journeyKindSchema, v as validateReviewAlignment, vn as nativeSourceIdentitySchema, vt as delegatedDecisionSchema, w as validateBFields, wt as journeyConfigurationSchema, x as validateBClassification, xt as hideConfirmSchema, y as semanticFindingSchema, yt as journeyDescribeSchema, z as bOverlaps, zt as termAssessmentSchema } from "./locate-D-ZaHpI0.mjs";
2
- import { z } from "zod";
1
+ import { $ as createCodePointIndex, A as bLegacyBindings, B as bIssue, Bt as reviewTermsResultSchema, C as validateBReview, Ct as hideConfirmSchema, Dt as cardAnswerActionSchema, E as validateBUnit, Et as journeyConfigurationSchema, F as bLiterals, G as bReviewSubmissionSchema, Gt as termTypes, H as unownedBObligations, Ht as termOverrideValueSchema, I as uncoveredBObligations, In as createStaticProcessor, J as bAnchorKey, Jt as sessionPeriodSchema, K as bSessionDecisionSchema, Kt as deselectOutsidePeriod, L as indexBRanges, Ln as staticDefaults, Lt as emptyReviewCorrectionSchema, M as semanticFindingSchema, Mn as inspectionSemanticVersions, Mt as packageConfirmActionSchema, N as bLiteralAdmitted, Nt as privacyResolveSchema, O as bDuplicateMirror, Ot as cardsConfirmActionSchema, P as bLiteralTerms, Pn as gapReasons, Pt as privacyTermKey, Q as codePointSlice, R as bAdoptedObligations, Rn as staticOptions, S as validateBClassification, T as validateBFields, Tt as hidePlanResourceSchema, U as bRepresentationSchema, Un as cleanupImplementationVersion, Ut as termTypeInfo, V as bOverlaps, Vt as termAssessmentSchema, W as bRetainedGroupSchema, Wn as entityDictionaryVersion, Wt as termTypeSchema, X as bDigest, Xt as journeyProfileAddressActionSchema, Y as bEvidenceReader, Yt as emptyJourneyProfile, Z as codePointLength, Zt as journeyProfileLinksActionSchema, _ as reviewAlignmentInputSchema, _n as inventorySchema, _r as scalarBefore, _t as reviewInterruptionSchema, an as ApplicationError, at as bMethod, b as validateReviewAlignment, bt as delegatedDecisionSchema, c as reviewBatchSchema, cr as detectStatic, ct as journeyChoiceSchema, d as reviewSubmitSchema, dn as reviewFamilies, dt as journeyIdSchema, er as validateProfile, et as subtractBRanges, f as textDigest, fn as sourceGraph, ft as journeyKindSchema, g as reviewAlignmentAuditSchema, gn as fileIdentitySchema, gr as scalarAfter, gt as nativeJourneySchema, hn as sourceSnapshotId, ht as journeyWorkTypeSchema, in as classificationTitleMaskSchema, ir as copyData, it as bIdSchema, j as bUnitFragments, jn as currentDetectorVersion, jt as emptyJourneyPrivacy, kt as cardsSubmitSchema, l as reviewChunkSchema, ln as familyNeedsReview, lt as journeyDigestSchema, m as findingReferenceKey, mn as observeSource, mt as journeyProjectTypeSchema, nn as respondProfileLinks, nt as bAnchorSchema, o as compileBFindings, on as UnfinishedJourneyError, ot as consentedWithoutAccount, p as validateReview, pn as sourceKey, pr as createEntityMatcher, pt as journeyPreferenceScopeSchema, q as bTermSchema, rr as validatePlan, rt as bCountSchema, s as reviewBatchChunks, sn as safeCode, st as journeyAccessSchema, tr as orderProfile, u as reviewDigest, un as familySelected, ur as staticCategories, ut as journeyExceptionSchema, v as reviewAlignmentSchema, vr as defaultDetectionLimits, vt as journeyGuidanceSchema, w as createBAnalysisUnits, wt as hidePlanInputSchema, xn as nativeSourceIdentitySchema, xt as journeyDescribeSchema, y as reviewFindingsDigest, yn as sourcePartSchema, yt as journeyInstructionInputSchema, zt as reviewTermsInputSchema } from "./locate-CP2t26j2.mjs";
2
+ import { ZodError, z } from "zod";
3
3
  import { createHash, createHmac, createSecretKey, randomUUID } from "node:crypto";
4
4
  import { setTimeout as setTimeout$1 } from "node:timers/promises";
5
5
  //#region packages/application/src/processing/model.ts
@@ -1199,109 +1199,440 @@ function requireSelecting(run) {
1199
1199
  if (run.phase !== "selecting") throw new ApplicationError("INVALID_TRANSITION");
1200
1200
  }
1201
1201
  //#endregion
1202
- //#region packages/application/src/journey/placement/readiness.ts
1203
- function hasCompleteWorkDescription(work) {
1204
- return work.placement === "placed" && !!work.description.trim() && work.classificationRecommendation !== null;
1205
- }
1206
- /** Explain why a requested Work still has no usable output after rendering. */
1207
- function unfinishedPackagingCode(work) {
1208
- if (!work) return "REVISION_CONFLICT";
1209
- if (work.renderFailure?.revision === work.revision) return work.renderFailure.code;
1210
- if (!hasCompleteWorkDescription(work)) return "WORK_METADATA_INCOMPLETE";
1211
- if (!work.accessConfirmed) return "CONFIRMATION_REQUIRED";
1212
- return "PACKAGING_FAILED";
1213
- }
1202
+ //#region packages/processing/src/session/marker.ts
1214
1203
  /**
1215
- * Dropped Works are not AI-read; a Work emptied by repartition has nothing left to read, and
1216
- * roots classification has not grouped yet are read once they join a Work (E2).
1204
+ * One complete replacement marker as bindings and semantic markers write it. Group 1 names the
1205
+ * static method, or `semantic` for an AI-review span. Dependency-free so the browser can use it.
1217
1206
  */
1218
- function isSelectedWork(work) {
1219
- return work.access !== "drop" && work.rootSessionIds.length > 0 && work.placement !== "classifying";
1207
+ const replacementMarkerPattern = /\[sp_[a-z0-9]{8,48}_([a-z][a-z0-9_]{0,47})_[a-f0-9]{24,64}\]/gu;
1208
+ //#endregion
1209
+ //#region packages/application/src/journey/b/matches.ts
1210
+ function bDictionaryHits(fields, literals, signal = new AbortController().signal) {
1211
+ const matcher = createEntityMatcher([...new Set(literals.map((part) => part.term))].map((value) => ({
1212
+ kind: "custom-term",
1213
+ value
1214
+ })));
1215
+ const hits = [];
1216
+ for (const field of fields) {
1217
+ const index = createCodePointIndex(field.text);
1218
+ const base = field.start ?? 0;
1219
+ for (const match of matcher.find(field.text, signal, 1e5)) {
1220
+ if (hits.length >= 1e6) throw new ApplicationError("PROCESSING_LIMIT");
1221
+ const body = {
1222
+ fieldId: field.id,
1223
+ fieldHash: field.fieldHash,
1224
+ recordId: field.recordId,
1225
+ start: base + index.codePointOffset(match.range.start),
1226
+ end: base + index.codePointOffset(match.range.end),
1227
+ exactQuote: field.text.slice(match.range.start, match.range.end),
1228
+ term: match.entity.value
1229
+ };
1230
+ hits.push({
1231
+ ...body,
1232
+ digest: bDigest(JSON.stringify(body))
1233
+ });
1234
+ }
1235
+ }
1236
+ return [...new Map(hits.map((hit) => [hit.digest, hit])).values()];
1220
1237
  }
1221
- /** Classification published some Works and is still grouping the remaining roots (E2). */
1222
- function isClassifying(state) {
1223
- return state.works.some((work) => work.placement === "classifying");
1238
+ function unclassifiedBHits(hits, literals, negative) {
1239
+ const issues = [];
1240
+ const positiveFields = new Map([...Map.groupBy(literals, (part) => part.fieldId)].map(([id, parts]) => [id, indexBRanges(parts)]));
1241
+ const negativeFields = new Map([...Map.groupBy(negative, (part) => part.fieldId)].map(([id, parts]) => [id, indexBRanges(parts)]));
1242
+ for (const hit of hits) {
1243
+ const positive = positiveFields.get(hit.fieldId)?.relation(hit).covers ?? false;
1244
+ const negative = negativeFields.get(hit.fieldId)?.relation(hit);
1245
+ if (!positive && !negative?.covers || positive && negative?.overlaps) issues.push(bIssue(positive ? "CONFLICTING_PRIVACY_DECISIONS" : "UNCLASSIFIED_MATCH", hit));
1246
+ }
1247
+ return issues;
1224
1248
  }
1225
- /**
1226
- * A Work whose read failed: it is skipped for this Send without a decision (ADR 0236). It holds no
1227
- * reading, card, packaging or Send gate and is never sent; Retry or a move out of Drop reads it
1228
- * again. A pending Hide also blocks the Works it changes, but those wait for its recovery and are
1229
- * not skipped. The Skill helpers mirror this rule in `journey-state.mjs`.
1230
- */
1231
- function isSkippedWork(work) {
1232
- return work.status === "blocked" && !work.blockedCodes.includes("PRIVACY_CHANGE_PENDING") && currentReviewInterruptions(work).length === 0;
1249
+ const bSpacedWord = (value) => /[\p{L}\p{N}_]/u.test(value) && !/[\p{sc=Han}\p{sc=Hiragana}\p{sc=Katakana}\p{sc=Thai}\p{sc=Lao}\p{sc=Khmer}\p{sc=Myanmar}]/u.test(value);
1250
+ /** In decoded tool text an odd backslash run escapes its following n, r or t. */
1251
+ function afterEscapedSpace(text, start) {
1252
+ const letter = text[start - 1];
1253
+ if (letter !== "n" && letter !== "r" && letter !== "t") return false;
1254
+ let index = start - 2;
1255
+ while (index >= 0 && text[index] === "\\") index--;
1256
+ return (start - 2 - index) % 2 === 1;
1233
1257
  }
1234
- /** A selected Work that is not skipped (ADR 0236). */
1235
- function isIncludedWork(work) {
1236
- return isSelectedWork(work) && !isSkippedWork(work);
1258
+ /** A confirmed Hide value matches only where it does not run into a neighbouring word (ADR 0069). */
1259
+ function bWordBounded(text, start, end, toolResultEscapes = false) {
1260
+ return !(bSpacedWord(scalarBefore(text, start)) && bSpacedWord(scalarAfter(text, start)) && !(toolResultEscapes && afterEscapedSpace(text, start)) || bSpacedWord(scalarBefore(text, end)) && bSpacedWord(scalarAfter(text, end)));
1237
1261
  }
1238
1262
  /**
1239
- * ADR 0274: Public needs a recorded choice: the person's panel click or named conversation
1240
- * answer (origin `explicit`), or, under ADR 0275, the account proof that every root was already
1241
- * Public (origin and channel `account`). A Public Work stored before `accessChoice` existed is
1242
- * still pending. Mirrored by `phaseOf` in the Skill's `journey-state.mjs` and by the panel's
1243
- * `WorkAccess`.
1263
+ * Occurrences of a Work's explicit exceptions, matched like Hide everywhere: exact NFC values
1264
+ * over complete graphemes. A confirmed Hide value inside one stays visible there.
1244
1265
  */
1245
- function hasPublicChoice(work) {
1246
- if (work.access !== "public") return true;
1247
- if (!work.accessConfirmed || !work.accessChoice) return false;
1248
- return work.accessChoice.channel === "account" ? work.accessOrigin === "account" : work.accessOrigin === "explicit";
1249
- }
1250
- /** ADR 0277 slice 2 display projection. Send consent remains a separate gate. */
1251
- function publicSource(work) {
1252
- if (!isIncludedWork(work) || work.access !== "public" || work.accountAccessConflict) return null;
1253
- if (!hasPublicChoice(work)) return "suggestion";
1254
- return work.accessChoice?.channel === "account" ? "account" : "choice";
1266
+ function bExceptionRanges(values) {
1267
+ const matcher = createEntityMatcher([...new Set([...values].map((value) => value.normalize("NFC")))].map((value) => ({
1268
+ kind: "custom-term",
1269
+ value
1270
+ })));
1271
+ return (text, signal = new AbortController().signal) => matcher.find(text, signal, 1e5).filter(({ range, entity }) => text.slice(range.start, range.end).normalize("NFC") === entity.value).map(({ range }) => range);
1255
1272
  }
1256
- function selectedWorks(state) {
1257
- return state.works.filter(isSelectedWork);
1273
+ /** Whether a range lies inside one of `ranges`: a prefix maximum of ends, O(log n) per query. */
1274
+ function bContainment(ranges) {
1275
+ const sorted = ranges.toSorted((a, b) => a.start - b.start);
1276
+ const reach = [];
1277
+ for (const [index, range] of sorted.entries()) reach.push(Math.max(range.end, reach[index - 1] ?? range.end));
1278
+ return (range) => {
1279
+ let low = 0, high = sorted.length;
1280
+ while (low < high) {
1281
+ const middle = low + high >> 1;
1282
+ if ((sorted[middle]?.start ?? Infinity) <= range.start) low = middle + 1;
1283
+ else high = middle;
1284
+ }
1285
+ return low > 0 && (reach[low - 1] ?? -1) >= range.end;
1286
+ };
1258
1287
  }
1259
- /** Categories found only in Dropped Works are neither asked nor counted. */
1260
- function unselectedGroupIds(state) {
1261
- const selected = new Set(selectedWorks(state).flatMap((work) => work.groupIds));
1262
- return new Set(state.works.filter((work) => !isSelectedWork(work)).flatMap((work) => work.groupIds).filter((id) => !selected.has(id)));
1288
+ /**
1289
+ * Word-bounded, exact, case-sensitive NFC occurrences of the terms, in UTF-16 coordinates,
1290
+ * except where an explicit exception occurrence contains one.
1291
+ */
1292
+ function bTermMatcher(terms, exceptions = []) {
1293
+ const matcher = createEntityMatcher([...new Set(terms)].map((value) => ({
1294
+ kind: "custom-term",
1295
+ value
1296
+ })));
1297
+ const excepted = bExceptionRanges(exceptions);
1298
+ return (text, signal = new AbortController().signal) => {
1299
+ const hits = matcher.find(text, signal, 1e5).filter(({ range }) => bWordBounded(text, range.start, range.end));
1300
+ const kept = hits.length ? bContainment(excepted(text, signal)) : () => false;
1301
+ return hits.filter(({ range }) => !kept(range)).map(({ range, entity }) => ({
1302
+ ...range,
1303
+ term: entity.value
1304
+ }));
1305
+ };
1263
1306
  }
1264
- /** Every root without batches is covered by an empty-input or explicit exclusion proof. */
1265
- function hasCertifiedNoBatchReview(work, snapshotId) {
1266
- const proofs = [work.emptyReview, work.excludedReview].filter((proof) => proof !== void 0);
1267
- if (!proofs.length || proofs.some((proof) => proof.snapshotId !== snapshotId || proof.inputRevision !== work.inputRevision)) return false;
1268
- const roots = proofs.flatMap((proof) => proof.rootSessionIds);
1269
- return roots.length === work.rootSessionIds.length && new Set(roots).size === roots.length && work.rootSessionIds.every((id) => roots.includes(id));
1307
+ /** Dictionary hits that a confirmed Hide value masks at render, so they need no classification. */
1308
+ function bMaskedHits(fields, terms) {
1309
+ const byId = new Map(fields.map((field) => [field.id, {
1310
+ text: field.text,
1311
+ index: createCodePointIndex(field.text),
1312
+ base: field.start ?? 0
1313
+ }]));
1314
+ return (hit) => {
1315
+ const field = byId.get(hit.fieldId);
1316
+ return !!field && terms.has(hit.term) && bWordBounded(field.text, field.index.offset(hit.start - field.base), field.index.offset(hit.end - field.base));
1317
+ };
1270
1318
  }
1271
- /** A reused Work ID cannot carry an old input generation's failed review forward. */
1272
- function currentReviewInterruptions(work) {
1273
- return (work.reviewInterruptions ?? []).filter((item) => item.inputRevision === work.inputRevision);
1319
+ //#endregion
1320
+ //#region packages/application/src/journey/terms/groups.ts
1321
+ const keyPrefix = "type:";
1322
+ const termGroupKey = (type) => `${keyPrefix}${type}`;
1323
+ /** RFC 9562 UUID version 5 of `name` in the `namespace` UUID. */
1324
+ function uuidV5(namespace, name) {
1325
+ const bytes = createHash("sha1").update(Buffer.from(namespace.replaceAll("-", ""), "hex")).update(name, "utf8").digest().subarray(0, 16);
1326
+ bytes.writeUInt8(bytes.readUInt8(6) & 15 | 80, 6);
1327
+ bytes.writeUInt8(bytes.readUInt8(8) & 63 | 128, 8);
1328
+ const hex = bytes.toString("hex");
1329
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
1274
1330
  }
1275
- /** Reading completes over included Works once the person has confirmed the scan scope. */
1276
- function isReadingComplete(state) {
1277
- return state.scope.status === "confirmed" && state.works.length > 0 && !isClassifying(state) && state.works.filter(isIncludedWork).every((work) => currentReviewInterruptions(work).length === 0 && work.status !== "pending" && work.status !== "blocked" && (work.totalBatches > 0 || hasCertifiedNoBatchReview(work, state.snapshotId)) && work.completedBatches === work.totalBatches);
1331
+ /** Deterministic: every reader and writer of a journey names a type's group alike. */
1332
+ const termGroupId = (journeyId, type) => uuidV5(journeyId, type);
1333
+ /** The term type a group answers, or null for any other category. */
1334
+ function termGroupType(group) {
1335
+ if (!group.key.startsWith(keyPrefix)) return null;
1336
+ const parsed = termTypeSchema.safeParse(group.key.slice(5));
1337
+ return parsed.success ? parsed.data : null;
1278
1338
  }
1279
- /** Recompute completion after a selection or reading change without leaving a paused/send phase. */
1280
- function withReadingProgress(state) {
1281
- const readingComplete = isReadingComplete(state);
1282
- const phase = state.phase === "paused" || !state.snapshotId ? state.phase : readingComplete ? state.phase === "processing" ? "review" : state.phase : state.phase === "review" || state.phase === "ready" ? "processing" : state.phase;
1339
+ const typeGroup = (state, type) => state.groups.find((group) => termGroupType(group) === type);
1340
+ function newTypeGroup(state, type, preference) {
1341
+ const info = termTypeInfo[type];
1342
+ const base = {
1343
+ id: termGroupId(state.journeyId, type),
1344
+ kind: info.kind,
1345
+ key: termGroupKey(type),
1346
+ label: info.label,
1347
+ condition: info.condition,
1348
+ count: 0,
1349
+ preferenceScope: null,
1350
+ representative: null
1351
+ };
1352
+ if (!info.asked) return {
1353
+ ...base,
1354
+ belief: .97,
1355
+ choice: "hide",
1356
+ confirmed: true,
1357
+ origin: "policy"
1358
+ };
1359
+ if (state.questionsPaused) return {
1360
+ ...base,
1361
+ belief: .97,
1362
+ choice: "hide",
1363
+ confirmed: false,
1364
+ origin: "paused"
1365
+ };
1283
1366
  return {
1284
- ...state,
1285
- readingComplete,
1286
- phase
1367
+ ...base,
1368
+ belief: preference === null ? .5 : preference === "hide" ? .97 : .03,
1369
+ choice: preference,
1370
+ confirmed: false,
1371
+ origin: preference === null ? "model" : "preference"
1287
1372
  };
1288
1373
  }
1289
- //#endregion
1290
- //#region packages/application/src/journey/send/model.ts
1291
- const count$13 = z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER);
1292
- const access = z.enum(["public", "private"]);
1293
- const sendSourceSchema = z.object({
1294
- id: z.string().regex(/^w[0-9]{6,}$/u),
1295
- pathHash: journeyDigestSchema.nullable(),
1296
- remoteHashes: z.array(journeyDigestSchema),
1297
- primaryRemoteHash: journeyDigestSchema.nullable(),
1298
- challengeId: z.string().regex(/^[A-Za-z0-9][A-Za-z0-9_.-]{0,127}$/u).optional()
1299
- }).strict();
1300
- function agentSourcePathSegment(value) {
1301
- return /^[A-Za-z0-9.][A-Za-z0-9._-]*$/u.test(value) && value !== "." && value !== "..";
1374
+ /**
1375
+ * One group per type with at least one dictionary value, `count` its distinct values. Existing
1376
+ * groups keep their identity and answer; other groups are unchanged.
1377
+ */
1378
+ function syncTermGroups(state, counts, preference) {
1379
+ const groups = state.groups.map((group) => {
1380
+ const type = termGroupType(group);
1381
+ return type === null ? group : {
1382
+ ...group,
1383
+ count: counts.get(type) ?? group.count
1384
+ };
1385
+ });
1386
+ for (const type of termTypes) {
1387
+ const count = counts.get(type) ?? 0;
1388
+ if (count === 0 || groups.some((group) => termGroupType(group) === type)) continue;
1389
+ groups.push({
1390
+ ...newTypeGroup(state, type, preference(termGroupKey(type))),
1391
+ count
1392
+ });
1393
+ }
1394
+ return groups;
1302
1395
  }
1303
- /** Local candidates become public only after anonymous upload-time evidence checks. */
1304
- const agentSourceSchema = z.object({
1396
+ //#endregion
1397
+ //#region packages/application/src/journey/review/confirmed-hides.ts
1398
+ /** Kinds from lowest to highest mask priority. */
1399
+ const confirmedHideKinds = [
1400
+ "internal",
1401
+ "identity",
1402
+ "private",
1403
+ "credentials"
1404
+ ];
1405
+ const kinds = confirmedHideKinds;
1406
+ const rank$1 = (kind) => kinds.indexOf(kind);
1407
+ /**
1408
+ * ADR 0086: a type's values are hidden unless its group is answered Keep; before an answer the
1409
+ * default is Hide. Credentials and contact details are always hidden (D06).
1410
+ */
1411
+ function typeHides(state, type) {
1412
+ const group = typeGroup(state, type);
1413
+ return !termTypeInfo[type].asked || !group?.confirmed || group.choice !== "keep";
1414
+ }
1415
+ /** B review (superseded by ADR 0086): IDs of the categories confirmed as Hide. */
1416
+ const confirmedHideGroupIds = (state) => state.groups.filter((group) => group.confirmed && group.choice === "hide").map((group) => group.id);
1417
+ const overrideKey = (state) => (state.valueOverrides ?? []).map((item) => JSON.stringify([
1418
+ item.type,
1419
+ item.value,
1420
+ item.choice
1421
+ ])).toSorted();
1422
+ /**
1423
+ * Identifies the hidden set (ADR 0079, 0086). The dictionary only grows and a type's group
1424
+ * counts its distinct values, so each type's count and Hide answer, with the value overrides,
1425
+ * name exactly one hidden set.
1426
+ */
1427
+ function confirmedHideKey(state) {
1428
+ return createHash("sha256").update(JSON.stringify({
1429
+ types: termTypes.map((type) => [
1430
+ type,
1431
+ typeGroup(state, type)?.count ?? 0,
1432
+ typeHides(state, type)
1433
+ ]),
1434
+ overrides: overrideKey(state),
1435
+ terms: state.privacy.terms.filter((term) => term.disposition === "hidden").map((term) => [term.type, term.value]).toSorted((a, b) => JSON.stringify(a).localeCompare(JSON.stringify(b)))
1436
+ })).digest("hex");
1437
+ }
1438
+ /** Whether two states hold the same value overrides. */
1439
+ const sameValueOverrides = (left, right) => JSON.stringify(overrideKey(left)) === JSON.stringify(overrideKey(right));
1440
+ /**
1441
+ * A preparation's waived hits stay waived only while every category recorded for them is still a
1442
+ * confirmed Hide; otherwise render may no longer mask them, so the session needs reconciliation.
1443
+ */
1444
+ function maskedHitsStale(maskedHits, hiding) {
1445
+ const current = new Set(hiding);
1446
+ return !!maskedHits?.some((hit) => hit.groupIds.some((id) => !current.has(id)));
1447
+ }
1448
+ /**
1449
+ * ADR 0086 hidden set: values of types not answered Keep, minus Keep overrides, plus Hide
1450
+ * overrides, plus every value of an always-hidden type. A value of several types takes the
1451
+ * highest mask kind among the types hiding it.
1452
+ */
1453
+ function dictionaryHides(state, terms) {
1454
+ const overrides = state.valueOverrides ?? [];
1455
+ const kept = new Set(overrides.flatMap((item) => item.choice === "keep" ? [item.value] : []));
1456
+ const hidden = /* @__PURE__ */ new Map();
1457
+ const add = (value, type) => {
1458
+ const prior = hidden.get(value);
1459
+ const kind = termTypeInfo[type].kind;
1460
+ hidden.set(value, {
1461
+ kind: prior && rank$1(prior.kind) > rank$1(kind) ? prior.kind : kind,
1462
+ groupIds: [.../* @__PURE__ */ new Set([...prior?.groupIds ?? [], termGroupId(state.journeyId, type)])].sort()
1463
+ });
1464
+ };
1465
+ const all = [...terms];
1466
+ const hiddenValues = new Set(state.privacy.terms.filter((item) => item.disposition === "hidden").map((item) => item.value));
1467
+ for (const term of all) if (hiddenValues.has(term.value) && !kept.has(term.value)) add(term.value, term.type);
1468
+ for (const item of overrides) if (item.choice === "hide") add(item.value, item.type);
1469
+ for (const term of all) if (!termTypeInfo[term.type].asked) add(term.value, term.type);
1470
+ for (const value of state.rememberedHides ?? []) if (!hidden.has(value) && !kept.has(value)) add(value, "projects");
1471
+ return hidden;
1472
+ }
1473
+ /** The journey's current hidden set, applied to every selected Work (ADR 0086, D03). */
1474
+ async function journeyConfirmedHides(reader, state) {
1475
+ return dictionaryHides(state, [...await reader.journeyTerms({ journeyId: state.journeyId }), ...state.privacy.terms]);
1476
+ }
1477
+ function confirmedHideTerms(groups, records) {
1478
+ const confirmed = new Map(groups.filter((group) => group.confirmed && group.choice === "hide").map((group) => [group.id, group]));
1479
+ const fields = /* @__PURE__ */ new Map();
1480
+ for (const record of records) {
1481
+ const assigned = new Map(record.assignments.map((item) => [item.findingId, item.groupId]));
1482
+ const chunks = new Map(record.chunks.map((chunk) => [chunk.id, chunk]));
1483
+ for (const finding of record.findings) {
1484
+ const group = confirmed.get(assigned.get(finding.id) ?? "");
1485
+ const chunk = chunks.get(finding.chunkId);
1486
+ if (!group || !chunk || finding.class !== "gray") continue;
1487
+ const key = JSON.stringify([chunk.fieldId, chunk.fieldHash]);
1488
+ fields.set(key, [...fields.get(key) ?? [], {
1489
+ start: chunk.offset + finding.start,
1490
+ end: chunk.offset + finding.end,
1491
+ text: chunk.text.slice(finding.start, finding.end),
1492
+ split: [finding.start === 0, finding.end === chunk.text.length],
1493
+ group
1494
+ }]);
1495
+ }
1496
+ }
1497
+ const terms = /* @__PURE__ */ new Map();
1498
+ const add = (text, groupIds, kind) => {
1499
+ for (const term of bLiteralTerms(text)) {
1500
+ const prior = terms.get(term);
1501
+ terms.set(term, {
1502
+ kind: prior && rank$1(prior.kind) > rank$1(kind) ? prior.kind : kind,
1503
+ groupIds: [.../* @__PURE__ */ new Set([...prior?.groupIds ?? [], ...groupIds])].sort()
1504
+ });
1505
+ }
1506
+ };
1507
+ for (const spans of fields.values()) {
1508
+ let open;
1509
+ for (const span of spans.toSorted((a, b) => a.start - b.start || b.end - a.end)) {
1510
+ if (open && span.split[0] && span.start === open.end) {
1511
+ open.text += span.text;
1512
+ open.end = span.end;
1513
+ open.groupIds.push(span.group.id);
1514
+ } else {
1515
+ if (open) add(open.text, open.groupIds, open.kind);
1516
+ open = {
1517
+ end: span.end,
1518
+ text: span.text,
1519
+ groupIds: [span.group.id],
1520
+ kind: span.group.kind
1521
+ };
1522
+ }
1523
+ if (!span.split[1]) {
1524
+ add(open.text, open.groupIds, open.kind);
1525
+ open = void 0;
1526
+ }
1527
+ }
1528
+ if (open) add(open.text, open.groupIds, open.kind);
1529
+ }
1530
+ return terms;
1531
+ }
1532
+ //#endregion
1533
+ //#region packages/application/src/journey/placement/readiness.ts
1534
+ function hasCompleteWorkDescription(work) {
1535
+ return work.placement === "placed" && (work.accountWork === true || !!work.description.trim() && work.classificationRecommendation !== null);
1536
+ }
1537
+ /** Explain why a requested Work still has no usable output after rendering. */
1538
+ function unfinishedPackagingCode(work) {
1539
+ if (!work) return "REVISION_CONFLICT";
1540
+ if (work.renderFailure?.revision === work.revision) return work.renderFailure.code;
1541
+ if (!hasCompleteWorkDescription(work)) return "WORK_METADATA_INCOMPLETE";
1542
+ if (!work.accessConfirmed) return "CONFIRMATION_REQUIRED";
1543
+ return "PACKAGING_FAILED";
1544
+ }
1545
+ /**
1546
+ * Dropped Works are not AI-read; a Work emptied by repartition has nothing left to read, and
1547
+ * roots classification has not grouped yet are read once they join a Work (E2).
1548
+ */
1549
+ function isSelectedWork(work) {
1550
+ return work.access !== "drop" && work.rootSessionIds.length > 0 && work.placement !== "classifying";
1551
+ }
1552
+ /** Classification published some Works and is still grouping the remaining roots (E2). */
1553
+ function isClassifying(state) {
1554
+ return state.works.some((work) => work.placement === "classifying");
1555
+ }
1556
+ /**
1557
+ * A Work whose read failed: it is skipped for this Send without a decision (ADR 0236). It holds no
1558
+ * reading, card, packaging or Send gate and is never sent; Retry or a move out of Drop reads it
1559
+ * again. A pending Hide also blocks the Works it changes, but those wait for its recovery and are
1560
+ * not skipped. The Skill helpers mirror this rule in `journey-state.mjs`.
1561
+ */
1562
+ function isSkippedWork(work) {
1563
+ return work.status === "blocked" && !work.blockedCodes.includes("PRIVACY_CHANGE_PENDING") && currentReviewInterruptions(work).length === 0;
1564
+ }
1565
+ /** A selected Work that is not skipped (ADR 0236). */
1566
+ function isIncludedWork(work) {
1567
+ return isSelectedWork(work) && !isSkippedWork(work);
1568
+ }
1569
+ /**
1570
+ * ADR 0274: Public needs a recorded choice: the person's panel click or named conversation
1571
+ * answer (origin `explicit`), or, under ADR 0275, the account proof that every root was already
1572
+ * Public (origin and channel `account`). A Public Work stored before `accessChoice` existed is
1573
+ * still pending. Mirrored by `phaseOf` in the Skill's `journey-state.mjs` and by the panel's
1574
+ * `WorkAccess`.
1575
+ */
1576
+ function hasPublicChoice(work) {
1577
+ if (work.access !== "public") return true;
1578
+ if (!work.accessConfirmed || !work.accessChoice) return false;
1579
+ return work.accessChoice.channel === "account" ? work.accessOrigin === "account" : work.accessOrigin === "explicit";
1580
+ }
1581
+ /** ADR 0277 slice 2 display projection. Send consent remains a separate gate. */
1582
+ function publicSource(work) {
1583
+ if (!isIncludedWork(work) || work.access !== "public" || work.accountAccessConflict) return null;
1584
+ if (!hasPublicChoice(work)) return "suggestion";
1585
+ return work.accessChoice?.channel === "account" ? "account" : "choice";
1586
+ }
1587
+ function selectedWorks(state) {
1588
+ return state.works.filter(isSelectedWork);
1589
+ }
1590
+ /** Categories found only in Dropped Works are neither asked nor counted. */
1591
+ function unselectedGroupIds(state) {
1592
+ const selected = new Set(selectedWorks(state).flatMap((work) => work.groupIds));
1593
+ return new Set(state.works.filter((work) => !isSelectedWork(work)).flatMap((work) => work.groupIds).filter((id) => !selected.has(id)));
1594
+ }
1595
+ /** Every root without batches is covered by an empty-input or explicit exclusion proof. */
1596
+ function hasCertifiedNoBatchReview(work, snapshotId) {
1597
+ const proofs = [work.emptyReview, work.excludedReview].filter((proof) => proof !== void 0);
1598
+ if (!proofs.length || proofs.some((proof) => proof.snapshotId !== snapshotId || proof.inputRevision !== work.inputRevision)) return false;
1599
+ const roots = proofs.flatMap((proof) => proof.rootSessionIds);
1600
+ return roots.length === work.rootSessionIds.length && new Set(roots).size === roots.length && work.rootSessionIds.every((id) => roots.includes(id));
1601
+ }
1602
+ /** A reused Work ID cannot carry an old input generation's failed review forward. */
1603
+ function currentReviewInterruptions(work) {
1604
+ return (work.reviewInterruptions ?? []).filter((item) => item.inputRevision === work.inputRevision);
1605
+ }
1606
+ /** Reading completes over included Works once the person has confirmed the scan scope. */
1607
+ function isReadingComplete(state) {
1608
+ return state.scope.status === "confirmed" && state.works.length > 0 && !isClassifying(state) && state.works.filter(isIncludedWork).every((work) => currentReviewInterruptions(work).length === 0 && work.status !== "pending" && work.status !== "blocked" && (work.totalBatches > 0 || hasCertifiedNoBatchReview(work, state.snapshotId)) && work.completedBatches === work.totalBatches);
1609
+ }
1610
+ /** Recompute completion after a selection or reading change without leaving a paused/send phase. */
1611
+ function withReadingProgress(state) {
1612
+ const readingComplete = isReadingComplete(state);
1613
+ const phase = state.phase === "paused" || !state.snapshotId ? state.phase : readingComplete ? state.phase === "processing" ? "review" : state.phase : state.phase === "review" || state.phase === "ready" ? "processing" : state.phase;
1614
+ return {
1615
+ ...state,
1616
+ readingComplete,
1617
+ phase
1618
+ };
1619
+ }
1620
+ //#endregion
1621
+ //#region packages/application/src/journey/send/model.ts
1622
+ const count$13 = z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER);
1623
+ const access = z.enum(["public", "private"]);
1624
+ const sendSourceSchema = z.object({
1625
+ id: z.string().regex(/^w[0-9]{6,}$/u),
1626
+ pathHash: journeyDigestSchema.nullable(),
1627
+ remoteHashes: z.array(journeyDigestSchema),
1628
+ primaryRemoteHash: journeyDigestSchema.nullable(),
1629
+ challengeId: z.string().regex(/^[A-Za-z0-9][A-Za-z0-9_.-]{0,127}$/u).optional()
1630
+ }).strict();
1631
+ function agentSourcePathSegment(value) {
1632
+ return /^[A-Za-z0-9.][A-Za-z0-9._-]*$/u.test(value) && value !== "." && value !== "..";
1633
+ }
1634
+ /** Local candidates become public only after anonymous upload-time evidence checks. */
1635
+ const agentSourceSchema = z.object({
1305
1636
  kind: z.enum(["skill", "mcp"]),
1306
1637
  name: z.string().regex(/^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,199}$/u),
1307
1638
  source: z.discriminatedUnion("type", [z.object({
@@ -1678,8 +2009,10 @@ function currentTermRules(state) {
1678
2009
  if (!overrides.has(value)) overrides.set(value, item);
1679
2010
  }
1680
2011
  const categories = new Map(state.groups.filter((group) => group.origin === "preference" || group.confirmed && (group.origin === "explicit" || group.origin === "delegate")).map((group) => [group.key, group.choice]));
2012
+ const remembered = new Set(state.rememberedHides ?? []);
1681
2013
  function choice(term) {
1682
- return overrides.get(term.value.normalize("NFC"))?.choice ?? categories.get(`type:${term.type}`);
2014
+ const value = term.value.normalize("NFC");
2015
+ return overrides.get(value)?.choice ?? (remembered.has(value) ? "hide" : categories.get(`type:${term.type}`));
1683
2016
  }
1684
2017
  return {
1685
2018
  overrides,
@@ -2468,6 +2801,18 @@ function answeredKeys(state, optionId) {
2468
2801
  return card.answer.optionId === optionId ? card.termKeys.filter((key) => !excluded.has(key)) : [...excluded];
2469
2802
  }));
2470
2803
  }
2804
+ /**
2805
+ * Values the person hid with their own panel answer, still on that card and Hidden now; the next
2806
+ * journey hides them on arrival (issue #126). Never sent anywhere.
2807
+ */
2808
+ function rememberedHideValues(state) {
2809
+ const keys = new Set(state.privacy.cards.flatMap((card) => {
2810
+ if (card.answer?.optionId !== "hide" || card.answer.source !== "panel") return [];
2811
+ const excluded = new Set(card.answer.excludedTermKeys ?? []);
2812
+ return card.termKeys.filter((key) => !excluded.has(key));
2813
+ }));
2814
+ return [...new Set(state.privacy.terms.filter((term) => term.disposition === "hidden" && keys.has(privacyTermKey(term))).map((term) => term.value.normalize("NFC")))];
2815
+ }
2471
2816
  function answeredValues(state) {
2472
2817
  const keepKeys = answeredKeys(state, "keep");
2473
2818
  const hideKeys = answeredKeys(state, "hide");
@@ -2566,6 +2911,7 @@ function respondPrivacy(state, action, source) {
2566
2911
  rule: [option.rule, action.rule?.trim()].filter(Boolean).join("\n"),
2567
2912
  additionalRule: action.rule?.trim() ?? "",
2568
2913
  remember: action.remember,
2914
+ source,
2569
2915
  ...excluded.length ? { excludedTermKeys: excluded } : {}
2570
2916
  }
2571
2917
  });
@@ -2747,7 +3093,9 @@ const journeyClassificationReadSchema = z.object({
2747
3093
  journeyId: journeyIdSchema,
2748
3094
  workId: journeyIdSchema,
2749
3095
  rootSessionId: z.string().min(1).max(128).optional(),
2750
- cursor: classificationCursorSchema.optional()
3096
+ cursor: classificationCursorSchema.optional(),
3097
+ /** Classification-only prefix; omitted callers retain full capture verification. */
3098
+ limit: z.number().int().min(1).max(8).optional()
2751
3099
  }).strict();
2752
3100
  const journeyClassificationSubmitSchema = z.object({
2753
3101
  journeyId: journeyIdSchema,
@@ -2871,455 +3219,132 @@ const preparationUpdateSchema = z.object({
2871
3219
  phase: nativeJourneySchema.shape.phase,
2872
3220
  readingComplete: z.boolean(),
2873
3221
  readingStartedAt: nativeJourneySchema.shape.readingStartedAt,
2874
- clearPackageApproval: z.boolean()
2875
- }).strict();
2876
- function emptyPreparationFacts() {
2877
- return {
2878
- stage: "capture",
2879
- unsupported: {
2880
- known: 0,
2881
- evaluated: 0
2882
- },
2883
- resourceLimited: {
2884
- known: 0,
2885
- evaluated: 0
2886
- },
2887
- excluded: {
2888
- known: 0,
2889
- evaluated: 0
2890
- },
2891
- emptyReviewRoots: {
2892
- known: 0,
2893
- evaluated: 0
2894
- }
2895
- };
2896
- }
2897
- function validatePreparationFacts(start, facts) {
2898
- preparationFactsSchema.parse(facts);
2899
- if (facts.unsupported.evaluated > start.sourceParts || facts.emptyReviewRoots.evaluated > start.reviewRoots || facts.unsupported.known + facts.resourceLimited.known + facts.excluded.known > facts.unsupported.evaluated) throw new ApplicationError("CORRUPTED_STATE");
2900
- }
2901
- //#endregion
2902
- //#region packages/application/src/journey/correction/model.ts
2903
- /** Original field-relative code-point coordinates, end-exclusive, exactly as B anchors. */
2904
- const correctionAnchorSchema = z.object({
2905
- fieldId: bIdSchema,
2906
- recordId: bIdSchema,
2907
- fieldHash: journeyDigestSchema,
2908
- start: bCountSchema,
2909
- end: bCountSchema,
2910
- quote: z.string().min(1).max(4096)
2911
- }).strict();
2912
- const correctionReportSchema = z.object({
2913
- id: journeyIdSchema,
2914
- workId: journeyIdSchema,
2915
- inputRevision: z.number().int().min(1),
2916
- batchId: journeyIdSchema,
2917
- groupId: journeyIdSchema,
2918
- kind: journeyKindSchema,
2919
- reason: z.string().trim().min(1).max(1e3),
2920
- anchors: z.array(correctionAnchorSchema).min(1).max(256)
2921
- }).strict();
2922
- const reviewCorrectSchema = z.object({
2923
- journeyId: journeyIdSchema,
2924
- expectedRevision: z.number().int().nonnegative(),
2925
- idempotencyKey: journeyIdSchema,
2926
- report: correctionReportSchema
2927
- }).strict();
2928
- /** Immutable private report receipt; the only place where correction quotes are retained. */
2929
- const correctionReportResourceSchema = z.object({
2930
- journeyId: journeyIdSchema,
2931
- kind: z.literal("correction-report"),
2932
- id: journeyIdSchema,
2933
- requestHash: journeyDigestSchema,
2934
- at: z.iso.datetime(),
2935
- snapshotId: journeyDigestSchema,
2936
- batchDigest: journeyDigestSchema,
2937
- decisionDigest: journeyDigestSchema,
2938
- report: correctionReportSchema
2939
- }).strict();
2940
- const correctionAuditSchema = z.object({
2941
- reportId: journeyIdSchema,
2942
- action: z.enum([
2943
- "report",
2944
- "hide",
2945
- "dismiss"
2946
- ]),
2947
- actor: z.enum([
2948
- "observer",
2949
- "human",
2950
- "delegate"
2951
- ]),
2952
- authorityId: journeyIdSchema.nullable(),
2953
- decisionDigest: journeyDigestSchema
2954
- }).strict();
2955
- //#endregion
2956
- //#region packages/application/src/journey/correction/binding.ts
2957
- function workCorrections(state, workId) {
2958
- return (state.corrections ?? []).filter((item) => item.workId === workId);
2959
- }
2960
- /** Binds rendered output and generated metadata to every correction disposition of the Work. */
2961
- function workCorrectionBinding(state, workId) {
2962
- const corrections = workCorrections(state, workId);
2963
- return corrections.length ? corrections.map(({ id, groupId, inputRevision, batchId, status, disposition }) => ({
2964
- id,
2965
- groupId,
2966
- inputRevision,
2967
- batchId,
2968
- status,
2969
- choice: disposition?.choice ?? null,
2970
- decisionDigest: disposition?.decisionDigest ?? null
2971
- })) : null;
2972
- }
2973
- //#endregion
2974
- //#region packages/application/src/journey/placement/decisions.ts
2975
- function workPrivacyDecisions(state, work) {
2976
- return state.groups.filter((group) => work.groupIds.includes(group.id)).map(({ id, key, kind, label, condition, choice, confirmed }) => ({
2977
- id,
2978
- key,
2979
- kind,
2980
- label,
2981
- condition,
2982
- choice,
2983
- confirmed
2984
- })).sort((a, b) => a.id.localeCompare(b.id));
2985
- }
2986
- /** Corrections are additive under an unchanged Hide group, so they bind metadata review too. */
2987
- function workDecisionDigest(state, work) {
2988
- const decisions = workPrivacyDecisions(state, work);
2989
- const corrections = workCorrectionBinding(state, work.id);
2990
- return textDigest(JSON.stringify(corrections ? {
2991
- decisions,
2992
- corrections
2993
- } : decisions));
2994
- }
2995
- /** Retire generated text until it is reviewed; the processed project name keeps rows distinct. */
2996
- function retireWorkDescription(work) {
2997
- return {
2998
- ...work,
2999
- suggestion: null
3000
- };
3001
- }
3002
- //#endregion
3003
- //#region packages/application/src/journey/terms/groups.ts
3004
- const keyPrefix = "type:";
3005
- const termGroupKey = (type) => `${keyPrefix}${type}`;
3006
- /** RFC 9562 UUID version 5 of `name` in the `namespace` UUID. */
3007
- function uuidV5(namespace, name) {
3008
- const bytes = createHash("sha1").update(Buffer.from(namespace.replaceAll("-", ""), "hex")).update(name, "utf8").digest().subarray(0, 16);
3009
- bytes.writeUInt8(bytes.readUInt8(6) & 15 | 80, 6);
3010
- bytes.writeUInt8(bytes.readUInt8(8) & 63 | 128, 8);
3011
- const hex = bytes.toString("hex");
3012
- return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
3013
- }
3014
- /** Deterministic: every reader and writer of a journey names a type's group alike. */
3015
- const termGroupId = (journeyId, type) => uuidV5(journeyId, type);
3016
- /** The term type a group answers, or null for any other category. */
3017
- function termGroupType(group) {
3018
- if (!group.key.startsWith(keyPrefix)) return null;
3019
- const parsed = termTypeSchema.safeParse(group.key.slice(5));
3020
- return parsed.success ? parsed.data : null;
3021
- }
3022
- const typeGroup = (state, type) => state.groups.find((group) => termGroupType(group) === type);
3023
- function newTypeGroup(state, type, preference) {
3024
- const info = termTypeInfo[type];
3025
- const base = {
3026
- id: termGroupId(state.journeyId, type),
3027
- kind: info.kind,
3028
- key: termGroupKey(type),
3029
- label: info.label,
3030
- condition: info.condition,
3031
- count: 0,
3032
- preferenceScope: null,
3033
- representative: null
3034
- };
3035
- if (!info.asked) return {
3036
- ...base,
3037
- belief: .97,
3038
- choice: "hide",
3039
- confirmed: true,
3040
- origin: "policy"
3041
- };
3042
- if (state.questionsPaused) return {
3043
- ...base,
3044
- belief: .97,
3045
- choice: "hide",
3046
- confirmed: false,
3047
- origin: "paused"
3048
- };
3049
- return {
3050
- ...base,
3051
- belief: preference === null ? .5 : preference === "hide" ? .97 : .03,
3052
- choice: preference,
3053
- confirmed: false,
3054
- origin: preference === null ? "model" : "preference"
3055
- };
3056
- }
3057
- /**
3058
- * One group per type with at least one dictionary value, `count` its distinct values. Existing
3059
- * groups keep their identity and answer; other groups are unchanged.
3060
- */
3061
- function syncTermGroups(state, counts, preference) {
3062
- const groups = state.groups.map((group) => {
3063
- const type = termGroupType(group);
3064
- return type === null ? group : {
3065
- ...group,
3066
- count: counts.get(type) ?? group.count
3067
- };
3068
- });
3069
- for (const type of termTypes) {
3070
- const count = counts.get(type) ?? 0;
3071
- if (count === 0 || groups.some((group) => termGroupType(group) === type)) continue;
3072
- groups.push({
3073
- ...newTypeGroup(state, type, preference(termGroupKey(type))),
3074
- count
3075
- });
3076
- }
3077
- return groups;
3078
- }
3079
- //#endregion
3080
- //#region packages/application/src/journey/review/confirmed-hides.ts
3081
- /** Kinds from lowest to highest mask priority. */
3082
- const confirmedHideKinds = [
3083
- "internal",
3084
- "identity",
3085
- "private",
3086
- "credentials"
3087
- ];
3088
- const kinds = confirmedHideKinds;
3089
- const rank$1 = (kind) => kinds.indexOf(kind);
3090
- /**
3091
- * ADR 0086: a type's values are hidden unless its group is answered Keep; before an answer the
3092
- * default is Hide. Credentials and contact details are always hidden (D06).
3093
- */
3094
- function typeHides(state, type) {
3095
- const group = typeGroup(state, type);
3096
- return !termTypeInfo[type].asked || !group?.confirmed || group.choice !== "keep";
3097
- }
3098
- /** B review (superseded by ADR 0086): IDs of the categories confirmed as Hide. */
3099
- const confirmedHideGroupIds = (state) => state.groups.filter((group) => group.confirmed && group.choice === "hide").map((group) => group.id);
3100
- const overrideKey = (state) => (state.valueOverrides ?? []).map((item) => JSON.stringify([
3101
- item.type,
3102
- item.value,
3103
- item.choice
3104
- ])).toSorted();
3105
- /**
3106
- * Identifies the hidden set (ADR 0079, 0086). The dictionary only grows and a type's group
3107
- * counts its distinct values, so each type's count and Hide answer, with the value overrides,
3108
- * name exactly one hidden set.
3109
- */
3110
- function confirmedHideKey(state) {
3111
- return createHash("sha256").update(JSON.stringify({
3112
- types: termTypes.map((type) => [
3113
- type,
3114
- typeGroup(state, type)?.count ?? 0,
3115
- typeHides(state, type)
3116
- ]),
3117
- overrides: overrideKey(state),
3118
- terms: state.privacy.terms.filter((term) => term.disposition === "hidden").map((term) => [term.type, term.value]).toSorted((a, b) => JSON.stringify(a).localeCompare(JSON.stringify(b)))
3119
- })).digest("hex");
3120
- }
3121
- /** Whether two states hold the same value overrides. */
3122
- const sameValueOverrides = (left, right) => JSON.stringify(overrideKey(left)) === JSON.stringify(overrideKey(right));
3123
- /**
3124
- * A preparation's waived hits stay waived only while every category recorded for them is still a
3125
- * confirmed Hide; otherwise render may no longer mask them, so the session needs reconciliation.
3126
- */
3127
- function maskedHitsStale(maskedHits, hiding) {
3128
- const current = new Set(hiding);
3129
- return !!maskedHits?.some((hit) => hit.groupIds.some((id) => !current.has(id)));
3130
- }
3131
- /**
3132
- * ADR 0086 hidden set: values of types not answered Keep, minus Keep overrides, plus Hide
3133
- * overrides, plus every value of an always-hidden type. A value of several types takes the
3134
- * highest mask kind among the types hiding it.
3135
- */
3136
- function dictionaryHides(state, terms) {
3137
- const overrides = state.valueOverrides ?? [];
3138
- const kept = new Set(overrides.flatMap((item) => item.choice === "keep" ? [item.value] : []));
3139
- const hidden = /* @__PURE__ */ new Map();
3140
- const add = (value, type) => {
3141
- const prior = hidden.get(value);
3142
- const kind = termTypeInfo[type].kind;
3143
- hidden.set(value, {
3144
- kind: prior && rank$1(prior.kind) > rank$1(kind) ? prior.kind : kind,
3145
- groupIds: [.../* @__PURE__ */ new Set([...prior?.groupIds ?? [], termGroupId(state.journeyId, type)])].sort()
3146
- });
3147
- };
3148
- const all = [...terms];
3149
- const hiddenValues = new Set(state.privacy.terms.filter((item) => item.disposition === "hidden").map((item) => item.value));
3150
- for (const term of all) if (hiddenValues.has(term.value) && !kept.has(term.value)) add(term.value, term.type);
3151
- for (const item of overrides) if (item.choice === "hide") add(item.value, item.type);
3152
- for (const term of all) if (!termTypeInfo[term.type].asked) add(term.value, term.type);
3153
- return hidden;
3154
- }
3155
- /** The journey's current hidden set, applied to every selected Work (ADR 0086, D03). */
3156
- async function journeyConfirmedHides(reader, state) {
3157
- return dictionaryHides(state, [...await reader.journeyTerms({ journeyId: state.journeyId }), ...state.privacy.terms]);
3158
- }
3159
- function confirmedHideTerms(groups, records) {
3160
- const confirmed = new Map(groups.filter((group) => group.confirmed && group.choice === "hide").map((group) => [group.id, group]));
3161
- const fields = /* @__PURE__ */ new Map();
3162
- for (const record of records) {
3163
- const assigned = new Map(record.assignments.map((item) => [item.findingId, item.groupId]));
3164
- const chunks = new Map(record.chunks.map((chunk) => [chunk.id, chunk]));
3165
- for (const finding of record.findings) {
3166
- const group = confirmed.get(assigned.get(finding.id) ?? "");
3167
- const chunk = chunks.get(finding.chunkId);
3168
- if (!group || !chunk || finding.class !== "gray") continue;
3169
- const key = JSON.stringify([chunk.fieldId, chunk.fieldHash]);
3170
- fields.set(key, [...fields.get(key) ?? [], {
3171
- start: chunk.offset + finding.start,
3172
- end: chunk.offset + finding.end,
3173
- text: chunk.text.slice(finding.start, finding.end),
3174
- split: [finding.start === 0, finding.end === chunk.text.length],
3175
- group
3176
- }]);
3177
- }
3178
- }
3179
- const terms = /* @__PURE__ */ new Map();
3180
- const add = (text, groupIds, kind) => {
3181
- for (const term of bLiteralTerms(text)) {
3182
- const prior = terms.get(term);
3183
- terms.set(term, {
3184
- kind: prior && rank$1(prior.kind) > rank$1(kind) ? prior.kind : kind,
3185
- groupIds: [.../* @__PURE__ */ new Set([...prior?.groupIds ?? [], ...groupIds])].sort()
3186
- });
3222
+ clearPackageApproval: z.boolean()
3223
+ }).strict();
3224
+ function emptyPreparationFacts() {
3225
+ return {
3226
+ stage: "capture",
3227
+ unsupported: {
3228
+ known: 0,
3229
+ evaluated: 0
3230
+ },
3231
+ resourceLimited: {
3232
+ known: 0,
3233
+ evaluated: 0
3234
+ },
3235
+ excluded: {
3236
+ known: 0,
3237
+ evaluated: 0
3238
+ },
3239
+ emptyReviewRoots: {
3240
+ known: 0,
3241
+ evaluated: 0
3187
3242
  }
3188
3243
  };
3189
- for (const spans of fields.values()) {
3190
- let open;
3191
- for (const span of spans.toSorted((a, b) => a.start - b.start || b.end - a.end)) {
3192
- if (open && span.split[0] && span.start === open.end) {
3193
- open.text += span.text;
3194
- open.end = span.end;
3195
- open.groupIds.push(span.group.id);
3196
- } else {
3197
- if (open) add(open.text, open.groupIds, open.kind);
3198
- open = {
3199
- end: span.end,
3200
- text: span.text,
3201
- groupIds: [span.group.id],
3202
- kind: span.group.kind
3203
- };
3204
- }
3205
- if (!span.split[1]) {
3206
- add(open.text, open.groupIds, open.kind);
3207
- open = void 0;
3208
- }
3209
- }
3210
- if (open) add(open.text, open.groupIds, open.kind);
3211
- }
3212
- return terms;
3213
- }
3214
- //#endregion
3215
- //#region packages/application/src/journey/b/matches.ts
3216
- function bDictionaryHits(fields, literals, signal = new AbortController().signal) {
3217
- const matcher = createEntityMatcher([...new Set(literals.map((part) => part.term))].map((value) => ({
3218
- kind: "custom-term",
3219
- value
3220
- })));
3221
- const hits = [];
3222
- for (const field of fields) {
3223
- const index = createCodePointIndex(field.text);
3224
- const base = field.start ?? 0;
3225
- for (const match of matcher.find(field.text, signal, 1e5)) {
3226
- if (hits.length >= 1e6) throw new ApplicationError("PROCESSING_LIMIT");
3227
- const body = {
3228
- fieldId: field.id,
3229
- fieldHash: field.fieldHash,
3230
- recordId: field.recordId,
3231
- start: base + index.codePointOffset(match.range.start),
3232
- end: base + index.codePointOffset(match.range.end),
3233
- exactQuote: field.text.slice(match.range.start, match.range.end),
3234
- term: match.entity.value
3235
- };
3236
- hits.push({
3237
- ...body,
3238
- digest: bDigest(JSON.stringify(body))
3239
- });
3240
- }
3241
- }
3242
- return [...new Map(hits.map((hit) => [hit.digest, hit])).values()];
3243
- }
3244
- function unclassifiedBHits(hits, literals, negative) {
3245
- const issues = [];
3246
- const positiveFields = new Map([...Map.groupBy(literals, (part) => part.fieldId)].map(([id, parts]) => [id, indexBRanges(parts)]));
3247
- const negativeFields = new Map([...Map.groupBy(negative, (part) => part.fieldId)].map(([id, parts]) => [id, indexBRanges(parts)]));
3248
- for (const hit of hits) {
3249
- const positive = positiveFields.get(hit.fieldId)?.relation(hit).covers ?? false;
3250
- const negative = negativeFields.get(hit.fieldId)?.relation(hit);
3251
- if (!positive && !negative?.covers || positive && negative?.overlaps) issues.push(bIssue(positive ? "CONFLICTING_PRIVACY_DECISIONS" : "UNCLASSIFIED_MATCH", hit));
3252
- }
3253
- return issues;
3254
3244
  }
3255
- const bSpacedWord = (value) => /[\p{L}\p{N}_]/u.test(value) && !/[\p{sc=Han}\p{sc=Hiragana}\p{sc=Katakana}\p{sc=Thai}\p{sc=Lao}\p{sc=Khmer}\p{sc=Myanmar}]/u.test(value);
3256
- /** In decoded tool text an odd backslash run escapes its following n, r or t. */
3257
- function afterEscapedSpace(text, start) {
3258
- const letter = text[start - 1];
3259
- if (letter !== "n" && letter !== "r" && letter !== "t") return false;
3260
- let index = start - 2;
3261
- while (index >= 0 && text[index] === "\\") index--;
3262
- return (start - 2 - index) % 2 === 1;
3245
+ function validatePreparationFacts(start, facts) {
3246
+ preparationFactsSchema.parse(facts);
3247
+ if (facts.unsupported.evaluated > start.sourceParts || facts.emptyReviewRoots.evaluated > start.reviewRoots || facts.unsupported.known + facts.resourceLimited.known + facts.excluded.known > facts.unsupported.evaluated) throw new ApplicationError("CORRUPTED_STATE");
3263
3248
  }
3264
- /** A confirmed Hide value matches only where it does not run into a neighbouring word (ADR 0069). */
3265
- function bWordBounded(text, start, end, toolResultEscapes = false) {
3266
- return !(bSpacedWord(scalarBefore(text, start)) && bSpacedWord(scalarAfter(text, start)) && !(toolResultEscapes && afterEscapedSpace(text, start)) || bSpacedWord(scalarBefore(text, end)) && bSpacedWord(scalarAfter(text, end)));
3249
+ //#endregion
3250
+ //#region packages/application/src/journey/correction/model.ts
3251
+ /** Original field-relative code-point coordinates, end-exclusive, exactly as B anchors. */
3252
+ const correctionAnchorSchema = z.object({
3253
+ fieldId: bIdSchema,
3254
+ recordId: bIdSchema,
3255
+ fieldHash: journeyDigestSchema,
3256
+ start: bCountSchema,
3257
+ end: bCountSchema,
3258
+ quote: z.string().min(1).max(4096)
3259
+ }).strict();
3260
+ const correctionReportSchema = z.object({
3261
+ id: journeyIdSchema,
3262
+ workId: journeyIdSchema,
3263
+ inputRevision: z.number().int().min(1),
3264
+ batchId: journeyIdSchema,
3265
+ groupId: journeyIdSchema,
3266
+ kind: journeyKindSchema,
3267
+ reason: z.string().trim().min(1).max(1e3),
3268
+ anchors: z.array(correctionAnchorSchema).min(1).max(256)
3269
+ }).strict();
3270
+ const reviewCorrectSchema = z.object({
3271
+ journeyId: journeyIdSchema,
3272
+ expectedRevision: z.number().int().nonnegative(),
3273
+ idempotencyKey: journeyIdSchema,
3274
+ report: correctionReportSchema
3275
+ }).strict();
3276
+ /** Immutable private report receipt; the only place where correction quotes are retained. */
3277
+ const correctionReportResourceSchema = z.object({
3278
+ journeyId: journeyIdSchema,
3279
+ kind: z.literal("correction-report"),
3280
+ id: journeyIdSchema,
3281
+ requestHash: journeyDigestSchema,
3282
+ at: z.iso.datetime(),
3283
+ snapshotId: journeyDigestSchema,
3284
+ batchDigest: journeyDigestSchema,
3285
+ decisionDigest: journeyDigestSchema,
3286
+ report: correctionReportSchema
3287
+ }).strict();
3288
+ const correctionAuditSchema = z.object({
3289
+ reportId: journeyIdSchema,
3290
+ action: z.enum([
3291
+ "report",
3292
+ "hide",
3293
+ "dismiss"
3294
+ ]),
3295
+ actor: z.enum([
3296
+ "observer",
3297
+ "human",
3298
+ "delegate"
3299
+ ]),
3300
+ authorityId: journeyIdSchema.nullable(),
3301
+ decisionDigest: journeyDigestSchema
3302
+ }).strict();
3303
+ //#endregion
3304
+ //#region packages/application/src/journey/correction/binding.ts
3305
+ function workCorrections(state, workId) {
3306
+ return (state.corrections ?? []).filter((item) => item.workId === workId);
3267
3307
  }
3268
- /**
3269
- * Occurrences of a Work's explicit exceptions, matched like Hide everywhere: exact NFC values
3270
- * over complete graphemes. A confirmed Hide value inside one stays visible there.
3271
- */
3272
- function bExceptionRanges(values) {
3273
- const matcher = createEntityMatcher([...new Set([...values].map((value) => value.normalize("NFC")))].map((value) => ({
3274
- kind: "custom-term",
3275
- value
3276
- })));
3277
- return (text, signal = new AbortController().signal) => matcher.find(text, signal, 1e5).filter(({ range, entity }) => text.slice(range.start, range.end).normalize("NFC") === entity.value).map(({ range }) => range);
3308
+ /** Binds rendered output and generated metadata to every correction disposition of the Work. */
3309
+ function workCorrectionBinding(state, workId) {
3310
+ const corrections = workCorrections(state, workId);
3311
+ return corrections.length ? corrections.map(({ id, groupId, inputRevision, batchId, status, disposition }) => ({
3312
+ id,
3313
+ groupId,
3314
+ inputRevision,
3315
+ batchId,
3316
+ status,
3317
+ choice: disposition?.choice ?? null,
3318
+ decisionDigest: disposition?.decisionDigest ?? null
3319
+ })) : null;
3278
3320
  }
3279
- /** Whether a range lies inside one of `ranges`: a prefix maximum of ends, O(log n) per query. */
3280
- function bContainment(ranges) {
3281
- const sorted = ranges.toSorted((a, b) => a.start - b.start);
3282
- const reach = [];
3283
- for (const [index, range] of sorted.entries()) reach.push(Math.max(range.end, reach[index - 1] ?? range.end));
3284
- return (range) => {
3285
- let low = 0, high = sorted.length;
3286
- while (low < high) {
3287
- const middle = low + high >> 1;
3288
- if ((sorted[middle]?.start ?? Infinity) <= range.start) low = middle + 1;
3289
- else high = middle;
3290
- }
3291
- return low > 0 && (reach[low - 1] ?? -1) >= range.end;
3292
- };
3321
+ //#endregion
3322
+ //#region packages/application/src/journey/placement/decisions.ts
3323
+ function workPrivacyDecisions(state, work) {
3324
+ return state.groups.filter((group) => work.groupIds.includes(group.id)).map(({ id, key, kind, label, condition, choice, confirmed }) => ({
3325
+ id,
3326
+ key,
3327
+ kind,
3328
+ label,
3329
+ condition,
3330
+ choice,
3331
+ confirmed
3332
+ })).sort((a, b) => a.id.localeCompare(b.id));
3293
3333
  }
3294
- /**
3295
- * Word-bounded, exact, case-sensitive NFC occurrences of the terms, in UTF-16 coordinates,
3296
- * except where an explicit exception occurrence contains one.
3297
- */
3298
- function bTermMatcher(terms, exceptions = []) {
3299
- const matcher = createEntityMatcher([...new Set(terms)].map((value) => ({
3300
- kind: "custom-term",
3301
- value
3302
- })));
3303
- const excepted = bExceptionRanges(exceptions);
3304
- return (text, signal = new AbortController().signal) => {
3305
- const hits = matcher.find(text, signal, 1e5).filter(({ range }) => bWordBounded(text, range.start, range.end));
3306
- const kept = hits.length ? bContainment(excepted(text, signal)) : () => false;
3307
- return hits.filter(({ range }) => !kept(range)).map(({ range, entity }) => ({
3308
- ...range,
3309
- term: entity.value
3310
- }));
3311
- };
3334
+ /** Corrections are additive under an unchanged Hide group, so they bind metadata review too. */
3335
+ function workDecisionDigest(state, work) {
3336
+ const decisions = workPrivacyDecisions(state, work);
3337
+ const corrections = workCorrectionBinding(state, work.id);
3338
+ return textDigest(JSON.stringify(corrections ? {
3339
+ decisions,
3340
+ corrections
3341
+ } : decisions));
3312
3342
  }
3313
- /** Dictionary hits that a confirmed Hide value masks at render, so they need no classification. */
3314
- function bMaskedHits(fields, terms) {
3315
- const byId = new Map(fields.map((field) => [field.id, {
3316
- text: field.text,
3317
- index: createCodePointIndex(field.text),
3318
- base: field.start ?? 0
3319
- }]));
3320
- return (hit) => {
3321
- const field = byId.get(hit.fieldId);
3322
- return !!field && terms.has(hit.term) && bWordBounded(field.text, field.index.offset(hit.start - field.base), field.index.offset(hit.end - field.base));
3343
+ /** Retire generated text until it is reviewed; the processed project name keeps rows distinct. */
3344
+ function retireWorkDescription(work) {
3345
+ return {
3346
+ ...work,
3347
+ suggestion: null
3323
3348
  };
3324
3349
  }
3325
3350
  const segmenter = new Intl.Segmenter("und", { granularity: "grapheme" });
@@ -3557,8 +3582,9 @@ function memberChange(previous, next) {
3557
3582
  return counts.every((count) => count >= 0) ? "additions" : counts.every((count) => count <= 0) ? "removals" : "other";
3558
3583
  }
3559
3584
  /**
3560
- * Whether a Work's output may change with the delta (Work metadata is never masked, so a
3561
- * generated description never retires on its own). Without a delta that covers this exact change every Work is affected, as ADR 0069 first decided, except when
3585
+ * Whether a Work's output may change with the delta (generated descriptions remain unmasked
3586
+ * and never retire output on their own; titles are covered by the output index). Without a
3587
+ * delta that covers this exact change every Work is affected, as ADR 0069 first decided, except when
3562
3588
  * members were only removed: no value is then added, so nothing can be under-hidden, and an
3563
3589
  * output may keep masking a value that left its category until it is rendered again.
3564
3590
  */
@@ -3918,7 +3944,7 @@ function retryAccountRead(state, read, now) {
3918
3944
  /** Classification confirmation initializes visibility and scan membership for this journey. */
3919
3945
  function confirmScope(state, now, decision, accountAccess) {
3920
3946
  if (state.scope.status === "confirmed") return state;
3921
- if (state.classificationRevision === null || !state.works.length || state.works.some((work) => work.placement !== "placed" && work.placement !== "classifying" || work.placement === "placed" && !(work.status === "blocked" && work.access === "drop" && work.accessConfirmed) && (!work.description.trim() || !work.projectType))) throw new ApplicationError("SCOPE_NOT_READY");
3947
+ if (state.classificationRevision === null || !state.works.length || state.works.some((work) => work.placement !== "placed" && work.placement !== "classifying" || work.placement === "placed" && !work.accountWork && !(work.status === "blocked" && work.access === "drop" && work.accessConfirmed) && (!work.description.trim() || !work.projectType))) throw new ApplicationError("SCOPE_NOT_READY");
3922
3948
  return withReadingProgress({
3923
3949
  ...state,
3924
3950
  scope: {
@@ -4897,7 +4923,7 @@ const sessionReconcileResultSchema = z.object({
4897
4923
  //#endregion
4898
4924
  //#region packages/application/src/journey/review/attempt-model.ts
4899
4925
  /**
4900
- * At most twenty small-model analysis agents run at once (D12, raised on 2026-10-20). One agent receives at most
4926
+ * At most twenty-four small-model analysis agents run at once (raised on 2026-11-14). One agent receives at most
4901
4927
  * 240,000 code points and 48 units; attempts dispatched with the same `packId` share one agent.
4902
4928
  */
4903
4929
  const reviewAgentCodePoints = 24e4;
@@ -5587,9 +5613,9 @@ function sameAccessRecord(work, other) {
5587
5613
  /** The proposal's Works built from the given state's current Works (E2). */
5588
5614
  function classificationWorks(state, plan) {
5589
5615
  const late = plan.late;
5590
- const works = [];
5616
+ const works = state.works.filter((work) => work.accountWork);
5591
5617
  for (const item of plan.items) {
5592
- const existing = state.works.find((work) => work.placement !== "classifying" && work.rootSessionIds.length === item.rootSessionIds.length && work.rootSessionIds.every((root) => item.rootSessionIds.includes(root)));
5618
+ const existing = state.works.find((work) => work.placement !== "classifying" && !work.accountWork && work.rootSessionIds.length === item.rootSessionIds.length && work.rootSessionIds.every((root) => item.rootSessionIds.includes(root)));
5593
5619
  const owners = late ? state.works.filter((work) => work.placement === "placed" && work.rootSessionIds.some((root) => item.rootSessionIds.includes(root))) : [];
5594
5620
  if (owners.some((work) => !sameAccessRecord(work, owners[0]))) throw new ApplicationError("INVALID_SELECTION", "classification.late.several-works");
5595
5621
  const extended = owners.find((work) => work.rootSessionIds.every((root) => item.rootSessionIds.includes(root)));
@@ -5609,7 +5635,7 @@ function classificationWorks(state, plan) {
5609
5635
  works.push({
5610
5636
  ...original,
5611
5637
  projectName: item.projectName,
5612
- title: base.titleOrigin === "explicit" ? base.title : item.projectName,
5638
+ title: base.titleOrigin === "model" ? item.projectName : base.title,
5613
5639
  description: item.description,
5614
5640
  projectType: {
5615
5641
  type: item.projectType,
@@ -6100,6 +6126,7 @@ function respondToJourney(state, action, source, now, decision, preferenceScope,
6100
6126
  return accountRead && confirmed !== state ? withAccountRead(confirmed, accountRead, now) : confirmed;
6101
6127
  }
6102
6128
  if (action.kind === "account-read") {
6129
+ if (!state.snapshotId && state.accountWorks && state.accountWorks.status !== "read") return state;
6103
6130
  if (!accountRead) throw new ApplicationError("INVALID_TRANSITION");
6104
6131
  return retryAccountRead(state, accountRead, now);
6105
6132
  }
@@ -6259,7 +6286,7 @@ function answeredRule(card) {
6259
6286
  /** A dispatch without a pack ID is its own analysis agent. */
6260
6287
  const agentKey = (attemptId, packId) => packId === void 0 ? `attempt:${attemptId}` : `pack:${packId}`;
6261
6288
  /**
6262
- * Refuses a dispatch that would run more than twenty agents at once, or give one agent more than
6289
+ * Refuses a dispatch that would run more than twenty-four agents at once, or give one agent more than
6263
6290
  * 240,000 code points or 48 units. A full reservation (repair or reconciliation) fills an agent,
6264
6291
  * so it always runs alone.
6265
6292
  */
@@ -6278,7 +6305,7 @@ function assertAgentCapacity(open, dispatches, attemptId, packId, budgetCodePoin
6278
6305
  }
6279
6306
  const key = agentKey(attemptId, packId);
6280
6307
  const agent = agents.get(key);
6281
- if (agent === void 0 && agents.size >= 16 || (agent?.codePoints ?? 0) + budgetCodePoints > 24e4 || (agent?.units ?? 0) + 1 > 48) throw new ApplicationError("OPERATION_CONFLICT");
6308
+ if (agent === void 0 && agents.size >= 24 || (agent?.codePoints ?? 0) + budgetCodePoints > 24e4 || (agent?.units ?? 0) + 1 > 48) throw new ApplicationError("OPERATION_CONFLICT");
6282
6309
  }
6283
6310
  //#endregion
6284
6311
  //#region packages/application/src/journey/review/attempt.ts
@@ -6884,6 +6911,25 @@ async function sessionReviewStatus(storage, value) {
6884
6911
  }
6885
6912
  //#endregion
6886
6913
  //#region packages/application/src/journey/service.ts
6914
+ /** Display uses the same bounded literal/exception matcher without depending on artifact I/O. */
6915
+ function presentedTitle(text, terms, exceptions) {
6916
+ const markers = [...text.matchAll(replacementMarkerPattern)].map((match) => ({
6917
+ start: match.index,
6918
+ end: match.index + match[0].length
6919
+ }));
6920
+ const spans = [];
6921
+ for (const hit of bTermMatcher(terms, exceptions)(text).sort((left, right) => left.start - right.start || right.end - left.end)) {
6922
+ if (markers.some((marker) => marker.start < hit.end && marker.end > hit.start)) continue;
6923
+ const previous = spans.at(-1);
6924
+ if (previous && hit.start < previous.end) previous.end = Math.max(previous.end, hit.end);
6925
+ else spans.push({
6926
+ start: hit.start,
6927
+ end: hit.end
6928
+ });
6929
+ }
6930
+ for (const span of spans.toReversed()) text = text.slice(0, span.start) + "[hidden]" + text.slice(span.end);
6931
+ return text;
6932
+ }
6887
6933
  function digest$1(value) {
6888
6934
  return createHash("sha256").update(JSON.stringify(value)).digest("hex");
6889
6935
  }
@@ -6892,8 +6938,9 @@ function digest$1(value) {
6892
6938
  * quotes stay out. The agent reads override values from `journey.get` and the guidance.
6893
6939
  */
6894
6940
  function journeyView(state, now) {
6895
- const { exceptions, instructions, corrections, valueOverrides, consent, accountRead, ...safe } = state;
6941
+ const { exceptions, instructions, corrections, valueOverrides, consent, accountRead, accountWorks, rememberedHides: _remembered, ...safe } = state;
6896
6942
  const terms = activePrivacyTerms(state);
6943
+ const hidden = dictionaryHides(state, state.privacy.terms);
6897
6944
  return {
6898
6945
  ...safe,
6899
6946
  consent: consent && {
@@ -6901,6 +6948,21 @@ function journeyView(state, now) {
6901
6948
  allowedAt: consent.allowedAt,
6902
6949
  source: consent.source
6903
6950
  },
6951
+ ...accountWorks ? { accountWorks: accountWorks.status === "read" ? {
6952
+ status: accountWorks.status,
6953
+ at: accountWorks.at,
6954
+ leftOut: accountWorks.leftOut.length,
6955
+ heldBack: accountWorks.heldBack.length,
6956
+ ...accountWorks.nothingToSend ? { nothingToSend: true } : {}
6957
+ } : {
6958
+ status: accountWorks.status,
6959
+ at: accountWorks.at
6960
+ } } : {},
6961
+ accountReadRecovery: !state.snapshotId && !consentedWithoutAccount(state) && accountWorks && accountWorks.status !== "read" ? {
6962
+ code: accountWorks.status === "unavailable" ? "ACCOUNT_WORKS_UNAVAILABLE" : "ACCOUNT_SIGNED_OUT",
6963
+ action: { kind: "account-read" },
6964
+ choices: ["retry"]
6965
+ } : null,
6904
6966
  ...accountRead ? { accountRead: {
6905
6967
  status: accountRead.status,
6906
6968
  at: accountRead.at
@@ -6914,15 +6976,17 @@ function journeyView(state, now) {
6914
6976
  const localTitle = work.suggestion || work.explicitTitleId ? work.title : work.classificationMetadata?.title ?? work.title;
6915
6977
  const imagesOmitted = hasCurrentStaticReview(state, work) ? work.staticReview?.imagesOmitted ?? 0 : 0;
6916
6978
  const source = publicSource(work);
6979
+ const title = work.titleOrigin === "explicit" || work.titleOrigin === "account" ? localTitle : presentedTitle(localTitle, hidden.keys(), state.exceptions.filter((item) => item.workId === work.id).map((item) => item.value));
6917
6980
  if (!work.classificationMetadata) return {
6918
6981
  ...work,
6982
+ title,
6919
6983
  localTitle,
6920
6984
  imagesOmitted,
6921
6985
  publicSource: source
6922
6986
  };
6923
6987
  const metadata = work.classificationMetadata;
6924
6988
  const displayed = {
6925
- title: work.suggestion || work.explicitTitleId ? work.title : metadata.title,
6989
+ title,
6926
6990
  projectName: metadata.projectName,
6927
6991
  description: work.suggestion ? work.description : metadata.description,
6928
6992
  reason: work.suggestion ? work.reason : metadata.reason
@@ -7169,7 +7233,7 @@ function createNativeJourney(storage, now = () => (/* @__PURE__ */ new Date()).t
7169
7233
  if (current && current.phase !== "completed" && !current.abandonedAt && !obsoleteJourney(current)) return resume(current);
7170
7234
  const carried = current && !current.abandonedAt && obsoleteJourney(current) ? (current.instructions ?? []).filter((item) => item.actor === "human") : [];
7171
7235
  const state = {
7172
- formatVersion: 10,
7236
+ formatVersion: 11,
7173
7237
  classificationRevision: null,
7174
7238
  privacy: emptyJourneyPrivacy(),
7175
7239
  delegation: null,
@@ -7319,7 +7383,7 @@ function createNativeJourney(storage, now = () => (/* @__PURE__ */ new Date()).t
7319
7383
  if (!current) throw new ApplicationError("UNKNOWN_ITEM");
7320
7384
  const expectedRevision = rebase ? current.revision : input.expectedRevision;
7321
7385
  const scopeRead = input.action.kind === "scope-confirm" && current.scope.status !== "confirmed" && source === "conversation" && !input.decision;
7322
- const accountRead = current.revision === expectedRevision && (scopeRead || input.action.kind === "account-read") ? await readAccount(current) : void 0;
7386
+ const accountRead = current.revision === expectedRevision && (scopeRead || input.action.kind === "account-read" && current.snapshotId !== null) ? await readAccount(current) : void 0;
7323
7387
  const account = current.revision === expectedRevision && (input.action.kind === "consent" && input.action.allow && !current.consent || scopeRead) ? await local?.accountState?.() : void 0;
7324
7388
  const stop = scopeRead && accountReadStop(current, accountRead);
7325
7389
  if (stop && accountRead) {
@@ -13756,7 +13820,8 @@ function reconcileBSession(fields, units, reviews, signal = new AbortController(
13756
13820
  validateBFields(fields);
13757
13821
  const first = units[0];
13758
13822
  if (!first) throw new ApplicationError("INCOMPLETE_REVIEW");
13759
- const expected = createBAnalysisUnits({
13823
+ const rebuild = (lineContext) => createBAnalysisUnits({
13824
+ lineContext,
13760
13825
  sessionId: first.sessionId,
13761
13826
  snapshotId: first.snapshotId,
13762
13827
  inputRevision: first.inputRevision,
@@ -13766,6 +13831,13 @@ function reconcileBSession(fields, units, reviews, signal = new AbortController(
13766
13831
  cores: units.toSorted((left, right) => left.ordinal - right.ordinal).map((unit) => unit.coreCodePoints)
13767
13832
  });
13768
13833
  const byId = new Map(units.map((unit) => [unit.id, unit]));
13834
+ let current;
13835
+ try {
13836
+ current = rebuild(true);
13837
+ } catch (error) {
13838
+ if (!(error instanceof ZodError) && !(error instanceof ApplicationError && error.code === "INCOMPLETE_REVIEW")) throw error;
13839
+ }
13840
+ const expected = current?.every((unit) => byId.get(unit.id)?.digest === unit.digest) ? current : rebuild(false);
13769
13841
  if (byId.size !== units.length || expected.length !== units.length || expected.some((unit) => byId.get(unit.id)?.digest !== unit.digest)) throw new ApplicationError("INCOMPLETE_REVIEW");
13770
13842
  const seen = /* @__PURE__ */ new Set();
13771
13843
  const validated = reviews.map((review) => {
@@ -14297,4 +14369,4 @@ function equivalenceDuplicates(batch) {
14297
14369
  return bindings.sort();
14298
14370
  }
14299
14371
  //#endregion
14300
- export { validateDetectionSummary as $, maximumHideIndexTokens as $n, nativeAdapterVersions as $r, journeyPlaceSchema as $t, renewBeforeMs as A, stateSchema as Ai, scanExclusionResourceSchema as An, journeyRenderInputSchema as Ar, validateAttemptAppend as At, createApplication as B, journeySearchSaveSchema as Bn, nativeRespondSchema as Br, newJourneyWork as Bt, rebindReviewOverlay as C, withReadingProgress as Ci, scanAliasResourceSchema as Cn, preparationStateSchema as Cr, sendActionSchema as Ct, staticallyHiddenFindings as D, selectionSchema as Di, scanGitSeedsId as Dn, journeyFindingImpactSchema as Dr, sendStartSchema as Dt, guardedFindingText as E, selectionBatchSchema as Ei, scanSessionResourceSchema as En, journeyClassificationSessionsSchema as Er, sendRequestSchema as Et, readAccountAvatarUrl as F, defaultScopedSettings as Fi, journeyInventoryReadSchema as Fn, assertJourneyPrivacyReady as Fr, classificationCommitSchema as Ft, validateInspectionRequest as G, confirmedHideAdditions as Gn, currentJourneyAdapters as Gr, hideFinishSchema as Gt, inspectionStartSchema as H, journeyCommitResultSchema as Hn, retryReviewActionSchema as Hr, journeyContextQuerySchema as Ht, storedCredentialSchema as I, freezeConfiguration as Ii, journeyInventorySchema as In, privacyDecisionDigest as Ir, classificationPartition as It, validateInspectionFence as J, renderedHideValue as Jn, obsoleteJourney as Jr, sendOutputQuerySchema as Jt, inspectionQuerySchema as K, confirmedHideDeltaSchema as Kn, hostDrives as Kr, hidePublishSchema as Kt, sourceIdentityEvidence as L, selectFrozenConfiguration as Li, journeyPreparationProgressSchema as Ln, reconcileReaderKeeps as Lr, classificationWorks as Lt, deviceAuthorizationSchema as M, recommendParallelism as Mi, resolveSourceSelection as Mn, displayedScopeDigest as Mr, journeyBatchReadSchema as Mt, deviceProfileScopeSchema as N, approvedTreatmentSchema as Ni, projectPreparationProgress as Nn, requestedScope as Nr, journeyReviewStorageQuerySchema as Nt, createAccount as O, gitMetadataBudgetBytes as Oi, scanGitSeedsResource as On, journeyFindingsSchema as Or, settleCorrections as Ot, profileUrlMatchesIdentity as P, effectiveTreatmentSchema as Pi, journeyInventoryQuerySchema as Pn, applyCardAnswers as Pr, applyClassificationProposal as Pt, validateInspectionContribution as Q, hideKeyId as Qn, metadataProcessingVersion as Qr, journeyIsolateSchema as Qt, withSourceEvidence as R, staticCatalogRef as Ri, applyClassification as Rn, nativeEventsSchema as Rr, retireScanGeneration as Rt, rebindReviewAlignment as S, unfinishedPackagingCode as Si, planBRepair as Sn, preparationStartSchema as Sr, replaceSensitiveTermsSchema as St, reviewBatchReuse as T, packageCommandSchema as Ti, scanPreparationDigest as Tn, validatePreparationFacts as Tr, sendPrepareSchema as Tt, validateInspectionBatch as U, journeyCommitSchema as Un, advanceStepsSend as Ur, journeyContextSchema as Ut, inspectionCancelSchema as V, searchScopeDigest as Vn, nativeStartSchema as Vr, reviewCompileSchema as Vt, validateInspectionBatches as W, journeyEventSchema as Wn, awaitingPassport as Wr, hideBeginSchema as Wt, validateInspectionInputs as X, termHideAdditions as Xn, sendReachedPassport as Xr, journeyResourceQuerySchema as Xt, validateInspectionHead as Y, settleConfirmedHides as Yn, sendOwesPassport as Yr, sendOutputSchema as Yt, validateInspectionQuery as Z, addHideIndexTokens as Zn, sendsOnNextAdvance as Zr, journeyResourceSchema as Zt, changeJourney as _, isIncludedWork as _i, reviewRepairInputSchema as _n, workCorrectionBinding as _r, manualUploadStartSchema as _t, bCrossSegmentReviewSchema as a, agentSourcePathSegment as ai, sessionReconcileResultSchema as an, bWordBounded as ar, appendSensitiveTerms as at, isGenericPublicTerm as b, isSkippedWork as bi, bReviewDigest as bn, preparationPermissionSchema as br, maximumTermsFileBytes as bt, reconstructBFields as c, journeySendSchema as ci, sessionRereadResourceSchema as cn, confirmedHideKinds as cr, maximumAgentSetupSessions as ct, resolveGroupSplit as d, sendReplaceResultSchema as di, sessionReviewResourceSchema as dn, journeyConfirmedHides as dr, validateFailureLocation as dt, mergePrivacyTerms as ei, sortingApplySchema as en, packHideIndex as er, inspectionInputs as et, selectedValueMembers as f, sendResolutionSchema as fi, sessionReviewStatusSchema as fn, maskedHitsStale as fr, manualUploadActionSchema as ft, semanticGroupKey as g, isClassifying as gi, reviewCandidateResourceSchema as gn, workPrivacyDecisions as gr, manualUploadSchema as gt, hideScopeDigest as h, hasCompleteWorkDescription as hi, reviewCandidateInputSchema as hn, workDecisionDigest as hr, manualUploadPrepareSchema as ht, validateBCrossSegmentReview as i, staticSessionResourceSchema as ii, sessionReconcileInputSchema as in, bTermMatcher as ir, reviewInventory as it, accountProfileSchema as j, sourceChangePolicy as ji, validateScanExclusion as jn, journeyRenderSchema as jr, journeyGuidance as jt, renew as k, producerVersion as ki, verifiedScanGitSeeds as kn, journeyPreviewSchema as kr, attemptBudgetCodePoints as kt, chooseCheapPlacement as l, sendArtifactSchema as li, sessionReviewInputSchema as ln, confirmedHideTerms as lr, artifactFailureDetailSchema as lt, activeJourneyTerms as m, sendSettingsSchema as mi, sessionUsefulnessResourceSchema as mn, retireWorkDescription as mr, manualUploadChoiceSchema as mt, scanBatchEquivalenceDigest as n, staticReviewDigest as ni, reviewAttemptsQuerySchema as nn, bExceptionRanges as nr, settingsQuerySchema as nt, bRereadDigest as o, agentSourceSchema as oi, sessionReconciliationResourceSchema as on, confirmedHideGroupIds as or, classifyAgentSetup as ot, splitSelectorValues as p, sendSessionUploadSchema as pi, sessionUsefulnessInputSchema as pn, syncTermGroups as pr, manualUploadBindingSchema as pt, validateInspectionClaim as q, readConfirmedHideChange as qn, itemAtPassport as qr, journeyPolicyRespondSchema as qt, locateTermEvidence as r, staticSessionId as ri, reviewAttemptsResultSchema as rn, bMaskedHits as rr, readWorkspaceOutcomes as rt, reconcileBSession as s, agentSourcesSchema as si, sessionRereadInputSchema as sn, confirmedHideKey as sr, defaultAgentSetupInclusion as st, scanBatchContextDigest as t, hasCurrentStaticReview as ti, reviewAttemptResourceSchema as tn, bContainment as tr, settingsBatchSchema as tt, reviseGroupMembership as u, sendReceiptSchema as ui, sessionReviewQuerySchema as un, dictionaryHides as ur, resolveFailureLocation as ut, projectJourneyInventory as v, isReadingComplete as vi, reviewRepairResourceSchema as vn, emptyPreparationFacts as vr, candidateSortAvailable as vt, rebindReviewSubmit as w, commandSchema as wi, scanCanonicalResourceSchema as wn, preparationUpdateSchema as wr, sendChoiceSchema as wt, bReviewScope as x, selectedWorks as xi, mergeBRepair as xn, preparationSettlementSchema as xr, normalizeSensitiveTerms as xt, preparationScope as y, isSelectedWork as yi, reviewTransportBytes as yn, preparationBeginSchema as yr, emptyTermsRevision as yt, agentSetupKey as z, journeySearchResultSchema as zn, nativeGetSchema as zr, isolatedWorkId as zt };
14372
+ export { validateDetectionSummary as $, hideKeyId as $n, sendReceiptSchema as $r, journeyPlaceSchema as $t, renewBeforeMs as A, selectionSchema as Ai, scanExclusionResourceSchema as An, nativeStartSchema as Ar, validateAttemptAppend as At, createApplication as B, selectFrozenConfiguration as Bi, journeySearchResultSchema as Bn, sendsOnNextAdvance as Br, newJourneyWork as Bt, rebindReviewOverlay as C, bMaskedHits as Ci, scanAliasResourceSchema as Cn, assertJourneyPrivacyReady as Cr, sendActionSchema as Ct, staticallyHiddenFindings as D, commandSchema as Di, scanGitSeedsId as Dn, nativeEventsSchema as Dr, sendStartSchema as Dt, guardedFindingText as E, replacementMarkerPattern as Ei, scanSessionResourceSchema as En, rememberedHideValues as Er, sendRequestSchema as Et, readAccountAvatarUrl as F, recommendParallelism as Fi, journeyInventoryReadSchema as Fn, hostDrives as Fr, classificationCommitSchema as Ft, validateInspectionRequest as G, journeyEventSchema as Gn, staticReviewDigest as Gr, hideFinishSchema as Gt, inspectionStartSchema as H, searchScopeDigest as Hn, nativeAdapterVersions as Hr, journeyContextQuerySchema as Ht, storedCredentialSchema as I, approvedTreatmentSchema as Ii, journeyInventorySchema as In, itemAtPassport as Ir, classificationPartition as It, validateInspectionFence as J, readConfirmedHideChange as Jn, agentSourcePathSegment as Jr, sendOutputQuerySchema as Jt, inspectionQuerySchema as K, confirmedHideAdditions as Kn, staticSessionId as Kr, hidePublishSchema as Kt, sourceIdentityEvidence as L, effectiveTreatmentSchema as Li, journeyPreparationProgressSchema as Ln, obsoleteJourney as Lr, classificationWorks as Lt, deviceAuthorizationSchema as M, producerVersion as Mi, resolveSourceSelection as Mn, advanceStepsSend as Mr, journeyBatchReadSchema as Mt, deviceProfileScopeSchema as N, stateSchema as Ni, projectPreparationProgress as Nn, awaitingPassport as Nr, journeyReviewStorageQuerySchema as Nt, createAccount as O, packageCommandSchema as Oi, scanGitSeedsResource as On, nativeGetSchema as Or, settleCorrections as Ot, profileUrlMatchesIdentity as P, sourceChangePolicy as Pi, journeyInventoryQuerySchema as Pn, currentJourneyAdapters as Pr, applyClassificationProposal as Pt, validateInspectionContribution as Q, addHideIndexTokens as Qn, sendArtifactSchema as Qr, journeyIsolateSchema as Qt, withSourceEvidence as R, defaultScopedSettings as Ri, accountReadStop as Rn, sendOwesPassport as Rr, retireScanGeneration as Rt, rebindReviewAlignment as S, bExceptionRanges as Si, planBRepair as Sn, applyCardAnswers as Sr, replaceSensitiveTermsSchema as St, reviewBatchReuse as T, bWordBounded as Ti, scanPreparationDigest as Tn, reconcileReaderKeeps as Tr, sendPrepareSchema as Tt, validateInspectionBatch as U, journeyCommitResultSchema as Un, mergePrivacyTerms as Ur, journeyContextSchema as Ut, inspectionCancelSchema as V, staticCatalogRef as Vi, journeySearchSaveSchema as Vn, metadataProcessingVersion as Vr, reviewCompileSchema as Vt, validateInspectionBatches as W, journeyCommitSchema as Wn, hasCurrentStaticReview as Wr, hideBeginSchema as Wt, validateInspectionInputs as X, settleConfirmedHides as Xn, agentSourcesSchema as Xr, journeyResourceQuerySchema as Xt, validateInspectionHead as Y, renderedHideValue as Yn, agentSourceSchema as Yr, sendOutputSchema as Yt, validateInspectionQuery as Z, termHideAdditions as Zn, journeySendSchema as Zr, journeyResourceSchema as Zt, changeJourney as _, dictionaryHides as _i, reviewRepairInputSchema as _n, journeyPreviewSchema as _r, manualUploadStartSchema as _t, bCrossSegmentReviewSchema as a, isClassifying as ai, sessionReconcileResultSchema as an, workCorrectionBinding as ar, appendSensitiveTerms as at, isGenericPublicTerm as b, syncTermGroups as bi, bReviewDigest as bn, displayedScopeDigest as br, maximumTermsFileBytes as bt, reconstructBFields as c, isSelectedWork as ci, sessionRereadResourceSchema as cn, preparationPermissionSchema as cr, maximumAgentSetupSessions as ct, resolveGroupSplit as d, unfinishedPackagingCode as di, sessionReviewResourceSchema as dn, preparationStateSchema as dr, validateFailureLocation as dt, sendReplaceResultSchema as ei, sortingApplySchema as en, maximumHideIndexTokens as er, inspectionInputs as et, selectedValueMembers as f, withReadingProgress as fi, sessionReviewStatusSchema as fn, preparationUpdateSchema as fr, manualUploadActionSchema as ft, semanticGroupKey as g, confirmedHideTerms as gi, reviewCandidateResourceSchema as gn, journeyFindingsSchema as gr, manualUploadSchema as gt, hideScopeDigest as h, confirmedHideKinds as hi, reviewCandidateInputSchema as hn, journeyFindingImpactSchema as hr, manualUploadPrepareSchema as ht, validateBCrossSegmentReview as i, hasCompleteWorkDescription as ii, sessionReconcileInputSchema as in, workPrivacyDecisions as ir, reviewInventory as it, accountProfileSchema as j, gitMetadataBudgetBytes as ji, validateScanExclusion as jn, retryReviewActionSchema as jr, journeyGuidance as jt, renew as k, selectionBatchSchema as ki, verifiedScanGitSeeds as kn, nativeRespondSchema as kr, attemptBudgetCodePoints as kt, chooseCheapPlacement as l, isSkippedWork as li, sessionReviewInputSchema as ln, preparationSettlementSchema as lr, artifactFailureDetailSchema as lt, activeJourneyTerms as m, confirmedHideKey as mi, sessionUsefulnessResourceSchema as mn, journeyClassificationSessionsSchema as mr, manualUploadChoiceSchema as mt, scanBatchEquivalenceDigest as n, sendSessionUploadSchema as ni, reviewAttemptsQuerySchema as nn, retireWorkDescription as nr, settingsQuerySchema as nt, bRereadDigest as o, isIncludedWork as oi, sessionReconciliationResourceSchema as on, emptyPreparationFacts as or, classifyAgentSetup as ot, splitSelectorValues as p, confirmedHideGroupIds as pi, sessionUsefulnessInputSchema as pn, validatePreparationFacts as pr, manualUploadBindingSchema as pt, validateInspectionClaim as q, confirmedHideDeltaSchema as qn, staticSessionResourceSchema as qr, journeyPolicyRespondSchema as qt, locateTermEvidence as r, sendSettingsSchema as ri, reviewAttemptsResultSchema as rn, workDecisionDigest as rr, readWorkspaceOutcomes as rt, reconcileBSession as s, isReadingComplete as si, sessionRereadInputSchema as sn, preparationBeginSchema as sr, defaultAgentSetupInclusion as st, scanBatchContextDigest as t, sendResolutionSchema as ti, reviewAttemptResourceSchema as tn, packHideIndex as tr, settingsBatchSchema as tt, reviseGroupMembership as u, selectedWorks as ui, sessionReviewQuerySchema as un, preparationStartSchema as ur, resolveFailureLocation as ut, projectJourneyInventory as v, journeyConfirmedHides as vi, reviewRepairResourceSchema as vn, journeyRenderInputSchema as vr, candidateSortAvailable as vt, rebindReviewSubmit as w, bTermMatcher as wi, scanCanonicalResourceSchema as wn, privacyDecisionDigest as wr, sendChoiceSchema as wt, bReviewScope as x, bContainment as xi, mergeBRepair as xn, requestedScope as xr, normalizeSensitiveTerms as xt, preparationScope as y, maskedHitsStale as yi, reviewTransportBytes as yn, journeyRenderSchema as yr, emptyTermsRevision as yt, agentSetupKey as z, freezeConfiguration as zi, applyClassification as zn, sendReachedPassport as zr, isolatedWorkId as zt };