@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.
- package/dist/{agent-setup-B12VbvN_.mjs → agent-setup-CEPQfjBv.mjs} +2 -2
- package/dist/agent-setup-worker.mjs +5 -5
- package/dist/cli.mjs +10 -10
- package/dist/codex-title-worker.mjs +1 -1
- package/dist/configured-preview-worker.mjs +5 -5
- package/dist/configured-session-worker.mjs +5 -5
- package/dist/{frame-B3He21G8.mjs → frame-CRm99088.mjs} +1 -1
- package/dist/{heap-Blrat--4.mjs → heap-Bh9tySJp.mjs} +12 -7
- package/dist/inspection-worker.mjs +4 -4
- package/dist/{job-CBuIMTQ0.mjs → job-CsWKWcM4.mjs} +135 -36
- package/dist/{locate-D-ZaHpI0.mjs → locate-CP2t26j2.mjs} +449 -244
- package/dist/{model-DR9odhWP.mjs → model-ChHuVKoB.mjs} +1 -1
- package/dist/operation-executor.mjs +3 -3
- package/dist/preview-worker.mjs +1 -1
- package/dist/privacy-validator.mjs +1 -1
- package/dist/{query-BYDcNKy2.mjs → query-D28k3XOr.mjs} +6044 -5706
- package/dist/{run-DQ9xyKSt.mjs → run-CiJmV__5.mjs} +6 -6
- package/dist/{src-Do-qzuMn.mjs → src-B3il9x5F.mjs} +617 -545
- package/dist/{src-CKQlTA2Z.mjs → src-CWMrGXJK.mjs} +180 -30
- package/dist/{src-DeNJ8vOD.mjs → src-CmvEJj46.mjs} +1806 -1690
- package/dist/store-database-worker.mjs +1 -1
- package/dist/web/assets/index-XdWDOC5H.js +172 -0
- package/dist/web/index.html +1 -1
- package/package.json +1 -1
- package/skills/tacitt/INDEX.md +1 -1
- package/skills/tacitt/SKILL.md +5 -3
- package/skills/tacitt/references/INDEX.md +2 -2
- package/skills/tacitt/references/b-method.md +4 -2
- package/skills/tacitt/references/bootstrap.md +1 -1
- package/skills/tacitt/references/commands.md +23 -4
- package/skills/tacitt/references/journey.md +5 -3
- package/skills/tacitt/references/parallel-review.md +22 -9
- package/skills/tacitt/references/remote-workers.md +6 -2
- package/skills/tacitt/scripts/INDEX.md +5 -5
- package/skills/tacitt/scripts/answer.mjs +12 -1
- package/skills/tacitt/scripts/describe.mjs +3 -2
- package/skills/tacitt/scripts/journey-classification-tasks.mjs +2 -0
- package/skills/tacitt/scripts/journey-classification.mjs +1 -0
- package/skills/tacitt/scripts/journey-scan-timing.mjs +3 -2
- package/skills/tacitt/scripts/journey-state.d.mts +3 -0
- package/skills/tacitt/scripts/journey-state.mjs +8 -1
- package/skills/tacitt/scripts/journey.mjs +31 -1
- package/skills/tacitt/scripts/privacy-validator.mjs +160 -40
- package/skills/tacitt/scripts/review.d.mts +1 -0
- package/skills/tacitt/scripts/review.mjs +296 -35
- package/dist/web/assets/index-BgDdPL1c.js +0 -172
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { $ as
|
|
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/
|
|
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
|
-
*
|
|
1216
|
-
*
|
|
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
|
-
|
|
1219
|
-
|
|
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
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
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
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
return
|
|
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
|
|
1235
|
-
function
|
|
1236
|
-
return
|
|
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
|
-
*
|
|
1240
|
-
*
|
|
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
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
}
|
|
1250
|
-
|
|
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
|
-
|
|
1257
|
-
|
|
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
|
-
/**
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
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
|
-
/**
|
|
1265
|
-
function
|
|
1266
|
-
const
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
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
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
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
|
-
/**
|
|
1276
|
-
|
|
1277
|
-
|
|
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
|
-
|
|
1280
|
-
function
|
|
1281
|
-
const
|
|
1282
|
-
const
|
|
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
|
-
...
|
|
1285
|
-
|
|
1286
|
-
|
|
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
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
}
|
|
1300
|
-
|
|
1301
|
-
|
|
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
|
-
|
|
1304
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
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
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
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
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
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
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
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
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
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
|
-
/**
|
|
3314
|
-
function
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
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 (
|
|
3561
|
-
*
|
|
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 (
|
|
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 === "
|
|
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 >=
|
|
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
|
|
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:
|
|
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
|
|
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 $,
|
|
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 };
|