dsh-completion-guard 0.4.0 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,30 +4,6 @@ import { dirname, join, resolve, sep } from "node:path";
4
4
  import { existsSync, readFileSync, renameSync, writeFileSync } from "node:fs";
5
5
  import { fileURLToPath } from "node:url";
6
6
 
7
- //#region src/domain/types.ts
8
- function createProjection() {
9
- return {
10
- enabled: false,
11
- epoch: 0,
12
- contractRevision: 0,
13
- items: /* @__PURE__ */ new Map(),
14
- evidence: /* @__PURE__ */ new Map(),
15
- checkpoints: [],
16
- boundaries: [],
17
- externalOperations: /* @__PURE__ */ new Map(),
18
- sessionRefDigest: "11".repeat(32),
19
- hostLockDigest: "22".repeat(32),
20
- hostStatus: "supported",
21
- integrityViolations: [],
22
- lastObservedSourceSeq: -1,
23
- lastGuardEventSeq: -1,
24
- continuationAttempts: /* @__PURE__ */ new Map(),
25
- persistenceCorrectionAttempts: /* @__PURE__ */ new Map(),
26
- integrity: "valid"
27
- };
28
- }
29
-
30
- //#endregion
31
7
  //#region src/domain/canonicalize.ts
32
8
  function normalizeClause(text) {
33
9
  return text.trim().replace(/\s+/g, " ");
@@ -862,10 +838,10 @@ function extractArtifactPaths(text) {
862
838
  }
863
839
  return [...found];
864
840
  }
865
- function segmentClauses(text) {
841
+ function segmentClauses(text, captureVersion = "v042") {
866
842
  const normalized = normalizeClause(text);
867
843
  if (!normalized) return [];
868
- const parts = normalized.split(/(?<=[。!?;])|(?<=[.!?])(?=\s|$)|(?<=(?:^|[\s。!?;.!?,,;:]))(?=(?:(?:do not|don't|never)(?![A-Za-z0-9_./@\\-])|禁止|不要|不得))/i).map((part) => part.trim()).filter(Boolean);
844
+ const parts = normalized.split(/(?<=[。!?;])|(?<=[.!?])(?=\s|$)|(?<=(?:^|[\s。!?;.!?,,;:]))(?=(?:(?:do not|don't|never)(?![A-Za-z0-9_./@\\-])|禁止|不要|不得))/i).flatMap((part) => captureVersion === "v041" || classifyClause(part.trim()).kind === "prohibition" ? [part] : part.split(/(?<!一)(?:并且?|以及|同时)(?=(?:验证(?!结果|全部通过|通过|成功)|确认(?!结果|全部通过|通过|成功|完成)|确保(?!结果|全部通过|通过|成功)|检查(?!结果)|更新|记录|安装|应用|重启|提交|推送|发布|在.{0,40}记录))|\s+and\s+(?=(?:verify|confirm|install|apply|restart|commit|push|publish|record)\b)/i)).map((part) => part.trim()).filter(Boolean);
869
845
  const segments = [];
870
846
  for (const part of parts) {
871
847
  const { kind, body } = classifyClause(part);
@@ -881,9 +857,10 @@ function segmentClauses(text) {
881
857
  * Build a GuardItem from an already-classified clause body and a resolved
882
858
  * verification subject/surface.
883
859
  */
884
- function captureItem(kind, body, sourceMessageId, id, revision, subject, surface, method, operation) {
860
+ function captureItem(kind, body, sourceMessageId, id, revision, subject, surface, method, operation, captureVersion = "v042") {
885
861
  const sanitized = sanitizeClauseText(body);
886
- const semanticAction = semanticActionFromText(sanitized);
862
+ const unsupportedVisual = captureVersion === "v042" && /\bGUI\b|界面|视觉|截图|颜色|布局|视觉效果/i.test(sanitized);
863
+ const semanticAction = unsupportedVisual ? "generic_run" : semanticActionFromText(sanitized);
887
864
  const capturedTarget = captureRequestedTarget(semanticAction, sanitized, subject, surface);
888
865
  const effectiveOperation = semanticAction === "verify" ? "verify" : operation;
889
866
  const item = {
@@ -900,7 +877,7 @@ function captureItem(kind, body, sourceMessageId, id, revision, subject, surface
900
877
  subject
901
878
  } : {
902
879
  enforced: true,
903
- surface,
880
+ surface: unsupportedVisual ? "ui" : surface,
904
881
  subject,
905
882
  method,
906
883
  operation: effectiveOperation
@@ -941,6 +918,214 @@ function captureClause(text, sourceMessageId, id, revision, scope = {}) {
941
918
  return captureItem(kind, body, sourceMessageId, id, revision, path$1 || scope.cwd || "scope", surface, extractMethod(body), extractOperation(body));
942
919
  }
943
920
 
921
+ //#endregion
922
+ //#region src/domain/rebind.ts
923
+ function preservesIdentity(old, clarified) {
924
+ const keys = Object.entries(old.requestedTarget ?? {}).filter(([key]) => key !== "scope");
925
+ const unwrap = (value) => JSON.stringify(value && typeof value === "object" && "v" in value ? value.v : value);
926
+ return keys.every(([key, value]) => unwrap(value) === unwrap(clarified.requestedTarget?.[key])) && (!old.verification.method || old.verification.method === clarified.verification.method) && (old.verification.surface !== "artifact" || old.verification.subject === clarified.verification.subject);
927
+ }
928
+ /** Exact source partition is deliberately conservative: a proposal cannot
929
+ * invent authority or silently discard a difficult acceptance clause. */
930
+ function proposeRebind(p, args) {
931
+ const item = p.items.get(args.item_id ?? "");
932
+ const clauses = args.clauses;
933
+ const clarificationItemIds = args.clarification_item_ids ?? [];
934
+ if (!item || item.status !== "pending" || item.kind === "prohibition" || !item.authority || item.authority === "legacy_authority_unclassified" || !Array.isArray(clauses) || clauses.length < 1 || clauses.length > 8 || clauses.some((s) => typeof s !== "string" || !s.trim() || s.length > 2048) || clauses.join("") !== item.normalizedText || clarificationItemIds.length !== 0 && clarificationItemIds.length !== clauses.length || new Set(clarificationItemIds.filter(Boolean)).size !== clarificationItemIds.filter(Boolean).length) return void 0;
935
+ for (const [index, id] of clarificationItemIds.entries()) {
936
+ if (!id) continue;
937
+ const clarified = p.items.get(id);
938
+ if (!clarified || clarified.id === item.id || clarified.status !== "pending" || clarified.reboundFrom || clarified.revision <= item.revision || clarified.sourceMessageId === item.sourceMessageId || clarified.authority !== "root_instruction" || clarified.legacyFlags?.length || clarified.kind !== item.kind || !clarified.normalizedText.includes(clauses[index].trim()) || !preservesIdentity(item, clarified) || /GUI|界面|视觉|截图|颜色|效果|布局/i.test(clauses[index]) && clarified.semanticAction !== "generic_run") return void 0;
939
+ }
940
+ const candidates = clauses.map((clause, index) => {
941
+ const root = p.items.get(clarificationItemIds[index] ?? "");
942
+ const captured = root ?? captureItem(item.kind, clause, item.sourceMessageId, "candidate", item.revision, item.verification.subject ?? "scope", item.verification.surface === "artifact" ? "artifact" : "scope", item.verification.method, item.verification.operation);
943
+ return {
944
+ sourceText: clause,
945
+ action: root || captured.semanticAction === item.semanticAction ? captured.semanticAction : "generic_run",
946
+ requestedTarget: captured.requestedTarget,
947
+ acceptance: root ? root.verification : item.verification,
948
+ sourceMessageId: captured.sourceMessageId,
949
+ rootItemId: root?.id ?? null,
950
+ rootRevision: root?.revision ?? null
951
+ };
952
+ });
953
+ const body = {
954
+ session: p.sessionRefDigest,
955
+ epoch: p.epoch,
956
+ contractRevision: p.contractRevision,
957
+ itemId: item.id,
958
+ itemRevision: item.revision,
959
+ sourceMessageId: item.sourceMessageId,
960
+ originalText: item.normalizedText,
961
+ clauses,
962
+ clarificationItemIds,
963
+ candidates
964
+ };
965
+ if (Buffer.byteLength(JSON.stringify(body), "utf8") > 8192) return void 0;
966
+ const digest$1 = sha256(JSON.stringify(body));
967
+ const normalized = JSON.parse(JSON.stringify(body));
968
+ return {
969
+ id: `RB-${digest$1.slice(0, 24)}`,
970
+ digest: digest$1,
971
+ ...normalized,
972
+ status: "pending"
973
+ };
974
+ }
975
+ function rebindResponse(p, args) {
976
+ if (!p.enabled || p.integrity !== "valid") return {
977
+ status: "unknown",
978
+ reason_code: "guard_unavailable"
979
+ };
980
+ if (Object.keys(args).some((key) => ![
981
+ "operation",
982
+ "item_id",
983
+ "proposal_id",
984
+ "clauses",
985
+ "clarification_item_ids"
986
+ ].includes(key))) return {
987
+ status: "rejected",
988
+ reason_code: "invalid_rebind_parameters"
989
+ };
990
+ if (args.operation === "propose") {
991
+ const candidate = proposeRebind(p, args);
992
+ if (!candidate) return {
993
+ status: "rejected",
994
+ reason_code: "source_partition_required",
995
+ next_step: "Supply 1-8 exact consecutive clauses covering the original text, including unsupported work; the proposal must fit 8 KiB. Clarification that changes meaning requires a new root-user instruction."
996
+ };
997
+ return {
998
+ status: "proposed",
999
+ proposal: p.rebindProposals.get(candidate.id) ?? candidate,
1000
+ next_step: `Root user must reply exactly: 确认重绑定 ${candidate.id}. This changes the contract only and grants no execution permission.`
1001
+ };
1002
+ }
1003
+ const proposal = p.rebindProposals.get(args.proposal_id ?? "");
1004
+ if (!proposal) return {
1005
+ status: "rejected",
1006
+ reason_code: "proposal_not_found"
1007
+ };
1008
+ if (args.operation === "withdraw") return proposal.status === "confirmed" ? {
1009
+ status: "rejected",
1010
+ reason_code: "proposal_already_applied"
1011
+ } : {
1012
+ status: "withdrawn",
1013
+ proposal_id: proposal.id,
1014
+ digest: proposal.digest
1015
+ };
1016
+ if (args.operation !== "query") return {
1017
+ status: "rejected",
1018
+ reason_code: "invalid_rebind_operation"
1019
+ };
1020
+ return proposal.status === "pending" && (proposal.contractRevision !== p.contractRevision || proposal.epoch !== p.epoch || proposal.session !== p.sessionRefDigest) ? {
1021
+ status: "stale",
1022
+ reason_code: "proposal_contract_changed",
1023
+ proposal: {
1024
+ ...proposal,
1025
+ status: "stale"
1026
+ },
1027
+ next_step: "Propose again against the current contract."
1028
+ } : {
1029
+ status: proposal.status,
1030
+ proposal
1031
+ };
1032
+ }
1033
+ function replayRebindResult(p, args, recorded) {
1034
+ const expected = rebindResponse(p, args);
1035
+ if (JSON.stringify(expected) !== JSON.stringify(recorded)) return;
1036
+ if (args.operation === "propose") {
1037
+ const candidate = proposeRebind(p, args);
1038
+ if (candidate && !p.rebindProposals.has(candidate.id)) p.rebindProposals.set(candidate.id, candidate);
1039
+ } else if (args.operation === "withdraw") {
1040
+ const proposal = p.rebindProposals.get(args.proposal_id ?? "");
1041
+ if (proposal?.status === "pending") proposal.status = "withdrawn";
1042
+ }
1043
+ }
1044
+ /** Invoked only for a canonical root user message, never tool or plugin text.
1045
+ * The single durable confirmation event is the atomic transaction commit. */
1046
+ function confirmRebind(p, text, eventId, durable) {
1047
+ const match = /^确认重绑定 (RB-[a-f0-9]{24})$/.exec(text.trim());
1048
+ if (!match) return false;
1049
+ const proposal = p.rebindProposals.get(match[1]);
1050
+ if (!proposal || !durable) return true;
1051
+ if (proposal.status !== "pending") return true;
1052
+ const old = p.items.get(proposal.itemId);
1053
+ if (!old || proposal.session !== p.sessionRefDigest || proposal.epoch !== p.epoch || old.status !== "pending" || old.revision !== proposal.itemRevision || p.contractRevision !== proposal.contractRevision || proposeRebind(p, {
1054
+ operation: "propose",
1055
+ item_id: old.id,
1056
+ clauses: proposal.clauses,
1057
+ clarification_item_ids: proposal.clarificationItemIds
1058
+ })?.digest !== proposal.digest) {
1059
+ proposal.status = "stale";
1060
+ return true;
1061
+ }
1062
+ const revision = p.contractRevision + 1;
1063
+ const replacements = proposal.clauses.map((clause, index) => {
1064
+ const clarified = p.items.get(proposal.clarificationItemIds[index] ?? "");
1065
+ if (clarified) return {
1066
+ ...clarified,
1067
+ reboundFrom: {
1068
+ itemId: old.id,
1069
+ proposalId: proposal.id,
1070
+ confirmationEvent: eventId
1071
+ }
1072
+ };
1073
+ const captured = captureItem(old.kind, clause, old.sourceMessageId, `${old.kind[0].toUpperCase()}:${proposal.id}:${index + 1}`, revision, old.verification.subject ?? "scope", old.verification.surface === "artifact" ? "artifact" : "scope", old.verification.method ?? extractMethod(clause), old.verification.operation ?? extractOperation(clause));
1074
+ if (captured.semanticAction !== old.semanticAction) captured.semanticAction = "generic_run";
1075
+ return {
1076
+ ...captured,
1077
+ authority: old.authority,
1078
+ reboundFrom: {
1079
+ itemId: old.id,
1080
+ proposalId: proposal.id,
1081
+ confirmationEvent: eventId
1082
+ },
1083
+ verification: {
1084
+ ...captured.verification,
1085
+ ...old.verification
1086
+ }
1087
+ };
1088
+ });
1089
+ if (replacements.some((item) => p.items.has(item.id) && !proposal.clarificationItemIds.includes(item.id))) {
1090
+ proposal.status = "stale";
1091
+ return true;
1092
+ }
1093
+ for (const item of replacements) p.items.set(item.id, item);
1094
+ old.status = "superseded";
1095
+ old.supersededByItems = replacements.map((item) => item.id);
1096
+ old.supersededBy = replacements[0].id;
1097
+ p.contractRevision = revision;
1098
+ proposal.status = "confirmed";
1099
+ proposal.confirmationEvent = eventId;
1100
+ proposal.replacementIds = old.supersededByItems;
1101
+ return true;
1102
+ }
1103
+
1104
+ //#endregion
1105
+ //#region src/domain/types.ts
1106
+ function createProjection() {
1107
+ return {
1108
+ enabled: false,
1109
+ epoch: 0,
1110
+ contractRevision: 0,
1111
+ rebindProposals: /* @__PURE__ */ new Map(),
1112
+ items: /* @__PURE__ */ new Map(),
1113
+ evidence: /* @__PURE__ */ new Map(),
1114
+ checkpoints: [],
1115
+ boundaries: [],
1116
+ externalOperations: /* @__PURE__ */ new Map(),
1117
+ sessionRefDigest: "11".repeat(32),
1118
+ hostLockDigest: "22".repeat(32),
1119
+ hostStatus: "supported",
1120
+ integrityViolations: [],
1121
+ lastObservedSourceSeq: -1,
1122
+ lastGuardEventSeq: -1,
1123
+ continuationAttempts: /* @__PURE__ */ new Map(),
1124
+ persistenceCorrectionAttempts: /* @__PURE__ */ new Map(),
1125
+ integrity: "valid"
1126
+ };
1127
+ }
1128
+
944
1129
  //#endregion
945
1130
  //#region src/domain/contract-digest.ts
946
1131
  function stable$3(value) {
@@ -1825,14 +2010,55 @@ function bindingSatisfies(projection, item, evidenceIds) {
1825
2010
  }
1826
2011
  }
1827
2012
 
2013
+ //#endregion
2014
+ //#region src/domain/diagnostics.ts
2015
+ function itemDiagnosis(p, item) {
2016
+ if (item.kind === "prohibition") return {
2017
+ certifiable: false,
2018
+ reason_code: "prohibition_active",
2019
+ next_step: "Keep this constraint enforced; it is not a completion evidence obligation."
2020
+ };
2021
+ const action = item.semanticAction ?? "generic_run";
2022
+ const reason = item.status === "passed" ? "certified" : action === "generic_run" ? "generic_run_non_certifiable" : item.legacyFlags?.length || item.targetCaptureStatus === "clarification_required" ? "target_clarification_required" : p.hostStatus !== "supported" ? "host_unavailable" : ACTION_MANIFEST.actions[action].evidenceProducer !== "supported" ? "adapter_unavailable" : "missing_evidence";
2023
+ return {
2024
+ certifiable: reason === "missing_evidence" || reason === "certified",
2025
+ reason_code: reason,
2026
+ next_step: reason === "certified" ? "No further binding needed." : reason === "generic_run_non_certifiable" || reason === "target_clarification_required" ? "Use context_guard_rebind to propose explicit clauses for root-user confirmation; preserve unsupported work pending. Rebinding grants no execution permission." : reason === "missing_evidence" ? "Collect matching durable evidence, then call context_guard_checkpoint with bindings." : "Restore the audited host/adapter capability before certification; keep pending work visible at a qualified safe boundary."
2027
+ };
2028
+ }
2029
+ const NATIVE_ADAPTERS = new Set([
2030
+ "dsh.bash.v1",
2031
+ "dsh.pwsh.v1",
2032
+ "dsh.shell.v1",
2033
+ "dsh.read.v1",
2034
+ "dsh.write.v1",
2035
+ "dsh.edit.v1",
2036
+ "dsh.web.v1"
2037
+ ]);
2038
+ function evidenceAvailabilityReason(evidence) {
2039
+ if (evidence.parseStatus !== "supported") return evidence.reasonCode ?? evidence.parseStatus ?? "adapter_unavailable";
2040
+ if (!evidence.adapterId || !evidence.adapterVersion || (SUPPORTED_EVIDENCE_ADAPTERS[evidence.adapterId] ?? (NATIVE_ADAPTERS.has(evidence.adapterId) ? "1.0.0" : void 0)) !== evidence.adapterVersion) return "adapter_unavailable";
2041
+ if (evidence.outcome !== "success") return "evidence_outcome_not_success";
2042
+ if (!evidence.semanticAction || evidence.semanticAction === "generic_run") return "generic_run_non_certifiable";
2043
+ }
2044
+ /** Shared display filter; certification remains the full domain check. */
2045
+ function relevantEvidence(p, item, evidence) {
2046
+ const action = item.semanticAction;
2047
+ if (!action || action === "generic_run" || !actionCompatible(action, evidence.semanticAction ?? "generic_run") || evidence.epoch !== p.epoch || evidenceAvailabilityReason(evidence) !== void 0) return false;
2048
+ if (item.reboundFrom) {
2049
+ const source = /^m(\d+)(?::|$)/.exec(item.sourceMessageId);
2050
+ if (!source || evidence.toolResultSeq < Number(source[1])) return false;
2051
+ }
2052
+ if (isStatefulAction(action)) return requestedTargetMatchesResolved(action, item.requestedTarget, evidence.resolvedTarget);
2053
+ const value = (entry) => JSON.stringify(entry && typeof entry === "object" && "v" in entry ? entry.v : entry);
2054
+ return !!item.requestedTarget && Object.entries(item.requestedTarget).every(([key, entry]) => evidence.resolvedTarget && value(entry) === value(evidence.resolvedTarget[key]));
2055
+ }
2056
+
1828
2057
  //#endregion
1829
2058
  //#region src/domain/recovery.ts
1830
2059
  const DEFAULT_RECOVERY_CHAR_BUDGET = 4e3;
1831
- const MAX_RECOVERY_ITEMS = 8;
1832
- const MAX_RECOVERY_EVIDENCE = 20;
1833
- const MORE_ITEMS_RULE = (remaining) => `…(${remaining} more open items; the full list is in the checkpoint tool response)`;
1834
- const MORE_EVIDENCE_RULE = (remaining) => `…(${remaining} more evidence rows)`;
1835
- const COMPLETION_RULE = "Obtain a Context Guard checkpoint from matching durable evidence before claiming completion.";
2060
+ const MIN_RECOVERY_CHAR_BUDGET = 512;
2061
+ const COMPLETION_RULE = "Obtain a Context Guard checkpoint from matching durable evidence before claiming completion. A qualified safe end preserves pending work; it is not completion.";
1836
2062
  /**
1837
2063
  * An actionable one-line hint for how an open item's verification contract can
1838
2064
  * be closed. It never weakens the contract; it only names the missing facet so
@@ -1841,6 +2067,7 @@ const COMPLETION_RULE = "Obtain a Context Guard checkpoint from matching durable
1841
2067
  * evidence already cover.
1842
2068
  */
1843
2069
  function closingHint(projection, item, evidenceIds) {
2070
+ if (item.semanticAction === "generic_run") return itemDiagnosis(projection, item).next_step;
1844
2071
  const verification = item.verification;
1845
2072
  const parts = [];
1846
2073
  if (evidenceIds?.length) {
@@ -1868,84 +2095,57 @@ function openItems$1(projection) {
1868
2095
  * injected once instead of looping (v0.2.1).
1869
2096
  */
1870
2097
  function recoveryDigest(packet, projection) {
2098
+ const items = openItems$1(projection);
2099
+ const evidence = [...projection.evidence.values()].filter((row) => items.some((item) => relevantEvidence(projection, item, row)));
1871
2100
  return sha256(JSON.stringify({
1872
2101
  packet,
1873
2102
  revision: projection.contractRevision,
1874
- epoch: projection.epoch
2103
+ epoch: projection.epoch,
2104
+ host: projection.hostLockDigest,
2105
+ evidence
1875
2106
  }));
1876
2107
  }
1877
2108
  function renderRecoveryPacket(projection, options = {}) {
1878
2109
  const budget = options.charBudget ?? DEFAULT_RECOVERY_CHAR_BUDGET;
1879
- const lines = [];
1880
- let used = 0;
1881
- const push = (line) => {
1882
- if (used + line.length + 1 > budget) return false;
1883
- lines.push(line);
1884
- used += line.length + 1;
2110
+ if (!Number.isSafeInteger(budget) || budget < MIN_RECOVERY_CHAR_BUDGET) throw new RangeError("recovery charBudget must be an integer >= 512");
2111
+ const clip = (text, size) => text.length <= size ? text : text.slice(0, size - 1) + "…";
2112
+ const items = openItems$1(projection).sort((a, b) => Number(b.kind === "prohibition") - Number(a.kind === "prohibition") || b.revision - a.revision || a.id.localeCompare(b.id));
2113
+ const rejected$1 = options.rejectedBindings ?? (projection.lastCheckpointRejectionRevision === projection.contractRevision ? projection.lastCheckpointRejections : []) ?? [];
2114
+ const compact = budget < 1e3;
2115
+ const lines = [`Context Guard: ${items.length} pending; revision ${projection.contractRevision}.`, compact ? "Checkpoint required before completion. Qualified safe end preserves pending work; it is not completion." : COMPLETION_RULE];
2116
+ const pointer = "Details/omissions: context_guard_checkpoint (item_ids, evidence_scope=history, cursor).";
2117
+ const evidence = [...projection.evidence.values()].filter((e) => items.some((item) => relevantEvidence(projection, item, e))).sort((a, b) => b.toolResultSeq - a.toolResultSeq || a.id.localeCompare(b.id));
2118
+ const footer = (count$1, refusals$1, shown$1) => `${items.length - count$1} items folded; ${rejected$1.length - refusals$1} rejections folded; ${evidence.length - shown$1} relevant evidence rows folded. Full ledger remains enforced.`;
2119
+ let remaining = budget - lines.join("\n").length - 87 - footer(0, 0, 0).length - 3;
2120
+ const add = (line, cap) => {
2121
+ if (remaining < 30) return false;
2122
+ const text = clip(line, Math.min(cap, remaining));
2123
+ lines.push(text);
2124
+ remaining -= text.length + 1;
1885
2125
  return true;
1886
2126
  };
1887
- const items = openItems$1(projection);
1888
- const listedIds = /* @__PURE__ */ new Set();
1889
- const pushItems = (list, render) => {
1890
- let count = 0;
1891
- for (const item of list) {
1892
- if (count >= MAX_RECOVERY_ITEMS) {
1893
- push(MORE_ITEMS_RULE(list.length - count));
1894
- return true;
1895
- }
1896
- if (!push(render(item))) return false;
1897
- listedIds.add(item.id);
1898
- count += 1;
1899
- }
1900
- return true;
2127
+ const constraints = items.filter((item) => item.kind === "prohibition");
2128
+ const work = items.filter((item) => item.kind !== "prohibition");
2129
+ let count = 0, refusals = 0, shown = 0;
2130
+ const constraint = (item) => {
2131
+ if (add(`DO NOT [${clip(item.id, 20)}] ${clip(item.normalizedText, compact ? 18 : 100)}`, compact ? 45 : 140)) count++;
1901
2132
  };
1902
- const requirementItems = items.filter((item) => item.kind === "requirement");
1903
- const compact = budget < 512;
1904
- const itemDiagnostic = (item) => {
1905
- if (compact) return `[${item.id}] ${item.normalizedText}`;
1906
- const action = item.semanticAction ?? "generic_run";
1907
- const spec = ACTION_MANIFEST.actions[action];
1908
- return `[${item.id}] ${item.normalizedText} action=${action} requested=${JSON.stringify(item.requestedTarget ?? {})} predicate=${spec.predicateId}@1 params=inline(resolved:${spec.resolvedTargetKeys.join(",") || "-"};observed:${spec.observedStateKeys.join(",") || "-"})`;
2133
+ const requirement = (item) => {
2134
+ const diagnosis = itemDiagnosis(projection, item);
2135
+ const remedy = diagnosis.reason_code === "generic_run_non_certifiable" || diagnosis.reason_code === "target_clarification_required" ? "context_guard_rebind; root confirmation required" : diagnosis.reason_code === "host_unavailable" || diagnosis.reason_code === "adapter_unavailable" ? "Restore audited host/adapter capability" : "Collect matching evidence; checkpoint";
2136
+ if (add(`[${clip(item.id, 20)}] ${diagnosis.reason_code}; ${compact ? remedy : diagnosis.next_step}; ${clip(item.normalizedText, 70)}`, compact ? 110 : 310)) count++;
1909
2137
  };
1910
- if (!pushItems(requirementItems, itemDiagnostic)) return finalize();
1911
- if (!pushItems(items.filter((item) => item.kind === "prohibition"), (item) => `[${item.id}] DO NOT ${item.normalizedText}`)) return finalize();
1912
- if (!pushItems(items.filter((item) => item.kind === "acceptance"), (item) => `VERIFY ${itemDiagnostic(item)}`)) return finalize();
1913
- const citableEvidence = [...projection.evidence.values()].filter((evidence) => evidence.epoch === projection.epoch && evidence.outcome === "success").sort((a, b) => a.id < b.id ? -1 : 1);
1914
- let evidenceCount = 0;
1915
- for (const evidence of citableEvidence) {
1916
- if (evidenceCount >= MAX_RECOVERY_EVIDENCE) {
1917
- push(MORE_EVIDENCE_RULE(citableEvidence.length - evidenceCount));
1918
- break;
1919
- }
1920
- if (!push([
1921
- `evidence ${evidence.id}`,
1922
- `tool=${evidence.toolName}`,
1923
- `action=${evidence.semanticAction ?? "generic_run"}`,
1924
- `role=${evidence.evidenceRole ?? "effect"}`,
1925
- `resolved=${JSON.stringify(evidence.resolvedTarget ?? {})}`,
1926
- `observed=${JSON.stringify(evidence.observedState ?? {})}`,
1927
- `adapter=${evidence.adapterId ?? "-"}@${evidence.adapterVersion ?? "-"}`,
1928
- `ops=${(evidence.operations ?? []).map((entry) => entry.op).join(",") || "-"}`,
1929
- `executables=${(evidence.executables ?? []).join(",") || "-"}`,
1930
- `parse=${evidence.parseStatus ?? "adapter_unavailable"}`
1931
- ].join(" "))) return finalize();
1932
- evidenceCount += 1;
1933
- }
1934
- for (const item of [...projection.items.values()].filter((item$1) => item$1.status === "superseded")) if (item.supersededBy && !push(`[${item.id} -> ${item.supersededBy}]`)) return finalize();
1935
- for (const binding of options.rejectedBindings ?? []) {
1936
- const offending = binding.offendingEvidenceIds?.length ? ` offending=${binding.offendingEvidenceIds.join(",")}` : "";
1937
- const reason = binding.reasonCode ? binding.reasonCode : binding.reason;
1938
- if (!push(`rejected ${binding.itemId}: ${reason}${offending}`)) return finalize();
1939
- }
1940
- for (const item of items) {
1941
- if (item.kind === "prohibition" || !listedIds.has(item.id)) continue;
1942
- push(`closing hint [${item.id}]: ${closingHint(projection, item)}`);
1943
- }
1944
- push(COMPLETION_RULE);
1945
- return finalize();
1946
- function finalize() {
1947
- return lines.join("\n");
2138
+ if (constraints[0]) constraint(constraints[0]);
2139
+ if (work[0]) requirement(work[0]);
2140
+ if (!compact) {
2141
+ for (const item of work.slice(1, 4)) requirement(item);
2142
+ for (const item of constraints.slice(1, 4)) constraint(item);
2143
+ for (const binding of rejected$1.slice(0, 4)) if (add(`rejected ${clip(binding.itemId, 30)}: ${clip(binding.reasonCode ?? binding.reason, 120)}`, 170)) refusals++;
2144
+ for (const item of work.slice(0, 4)) if (itemDiagnosis(projection, item).certifiable) add(`closing hint [${clip(item.id, 20)}]: ${closingHint(projection, item)}`, 240);
2145
+ for (const row of evidence.slice(0, 4)) if (add(`evidence ${clip(row.id, 40)} action=${row.semanticAction} role=${row.evidenceRole ?? "effect"}`, 140)) shown++;
1948
2146
  }
2147
+ lines.push(footer(count, refusals, shown), pointer);
2148
+ return lines.join("\n");
1949
2149
  }
1950
2150
 
1951
2151
  //#endregion
@@ -1996,6 +2196,16 @@ function evidenceProblem(projection, item, binding) {
1996
2196
  reasonCode: "evidence_missing",
1997
2197
  offendingEvidenceIds: missing
1998
2198
  };
2199
+ if (item.reboundFrom) {
2200
+ const sourceSeq = /^m(\d+)(?::|$)/.exec(item.sourceMessageId);
2201
+ const tooEarly = binding.evidenceIds.filter((id) => !sourceSeq || projection.evidence.get(id).toolResultSeq < Number(sourceSeq[1]));
2202
+ if (tooEarly.length) return {
2203
+ itemId: item.id,
2204
+ reason: "evidence predates the authoritative root clause used by this replacement",
2205
+ reasonCode: "rebind_evidence_predates_source",
2206
+ offendingEvidenceIds: tooEarly
2207
+ };
2208
+ }
1999
2209
  const wrongEpoch = binding.evidenceIds.filter((id) => projection.evidence.get(id)?.epoch !== projection.epoch);
2000
2210
  if (wrongEpoch.length) return {
2001
2211
  itemId: item.id,
@@ -3109,14 +3319,194 @@ const ALPHA3_HOST_PACKAGES = [
3109
3319
  integrity
3110
3320
  }));
3111
3321
 
3322
+ //#endregion
3323
+ //#region src/domain/rc1-host.ts
3324
+ /** Exact 34-row rc.1 runtime/web graph from the 2026-09-03 native macOS audit. */
3325
+ const RC1_HOST_PACKAGES = [
3326
+ [
3327
+ "@deepseek-ai/cordis",
3328
+ "4.0.2",
3329
+ "sha512-asOnXP1TzFSFQlHb1iegDZp0z/8WD1c7YNrwJR/Tx2bzNuMXfcekE/I67Iv6SQXeLB4csxqCngzQKANP7gdw0g=="
3330
+ ],
3331
+ [
3332
+ "@deepseek-ai/dsh-agent",
3333
+ "0.1.2-rc.1",
3334
+ "sha512-lfaqN34vUCWvbn1kJVHrhfJ6Dvt1HDHCm33ZCpmKkl07/5q6FxWqVv6rOdVX5QnM/9xz/uYiN0nQwUtBg4+Skg=="
3335
+ ],
3336
+ [
3337
+ "@deepseek-ai/dsh-commands",
3338
+ "0.1.2-rc.1",
3339
+ "sha512-uBh4JTX7pOkFhmbWjXjVLnOQOawyccC7+DazbHrLRN6/wy4OgTRH+DaW0+ibLq+XRAO19OvBTtuoh3x/KkJx1Q=="
3340
+ ],
3341
+ [
3342
+ "@deepseek-ai/dsh-goal",
3343
+ "0.1.2-rc.1",
3344
+ "sha512-djzY1oNZV5RwnOFXmDeWFbyswInV9RVWAD0qryxMznQtEDF31PUJ8BQfqs9tVrTV36V3a0neYrZP0DDvRP8ZCA=="
3345
+ ],
3346
+ [
3347
+ "@deepseek-ai/dsh-llm",
3348
+ "0.1.2-rc.1",
3349
+ "sha512-7VYsha5AXsVLnsAwYJffWXz9bwUbElw8i5N8tlTSdai9Bupk3sMbsotzPf8ZbsuGAxQYErahMwQwgGEu4qZO6g=="
3350
+ ],
3351
+ [
3352
+ "@deepseek-ai/dsh-session",
3353
+ "0.1.2-rc.1",
3354
+ "sha512-jRGNPTbQcvIx1F2MVmmkoiHLgpC3Btqo1wkl/3JDLlOWRVWKOuaC+5fQiMrFPOYfno6YiX/c2UvmA0td8qB92w=="
3355
+ ],
3356
+ [
3357
+ "@deepseek-ai/dsh-tools",
3358
+ "0.1.2-rc.1",
3359
+ "sha512-W9kUio00s7WbM8kEwniyd4hfb3CeUxVczsjXbOcKtakfiTywNLeyRWkpx2fvwRAH2rBfg3qCgczVfS0DOw1csg=="
3360
+ ],
3361
+ [
3362
+ "@deepseek-ai/dsh-tool-goal",
3363
+ "0.1.2-rc.1",
3364
+ "sha512-ooHKN6Eqy3owNS/oCDO7mR+UalEE4AxJMXou29waahIdSQsFAlk4pPApvhrwg1lpchF5CKwBPOtsVmjq2HfBKQ=="
3365
+ ],
3366
+ [
3367
+ "@deepseek-ai/dsh-agent-loop",
3368
+ "0.1.2-rc.1",
3369
+ "sha512-4h16Gn/5oXLTFeorehdlKe5xmDKscR/eRsUu3cs6clKgaTrn6YvnWNB0XkYEg+AXiXbsyfl5MW5Bs4576t7vZw=="
3370
+ ],
3371
+ [
3372
+ "@deepseek-ai/dsh-tool-bash",
3373
+ "0.1.2-rc.1",
3374
+ "sha512-xKc4oXVDBwM/PaicpjGdWEaJ1N14B7KPmOzdpQ7ynE4gKUnvGU/eTg18EHamdyDOidh5ox0fNUnxk0rQjVo2+Q=="
3375
+ ],
3376
+ [
3377
+ "@deepseek-ai/dsh-tool-pwsh",
3378
+ "0.1.2-rc.1",
3379
+ "sha512-LaImOCdizIGkQnxzzkoaqzRWGZLsuDuqqF2adgJ3zeG0nDwan4Sz+1wYz4YSp0Gg6mJ79CYacsbYmGhYYyKY/g=="
3380
+ ],
3381
+ [
3382
+ "@deepseek-ai/dsh-shell",
3383
+ "0.1.2-rc.1",
3384
+ "sha512-uFrSY0nNKzh5orGl2B0B4RfK7wTdIlwhPQc7aLA44jyjioLlqhF7PUek/NWtye2scwp3YHOw158XeOQUD8qTFg=="
3385
+ ],
3386
+ [
3387
+ "@deepseek-ai/dsh-subprocess-local",
3388
+ "0.1.2-rc.1",
3389
+ "sha512-Spc/IXWvjEteGysifGr6JvCokcH8T88z0mdxIGcu9SFdgDyY8HKR+h5yq73+rbo4RYzT7+TBE43TIZy5LfmzgQ=="
3390
+ ],
3391
+ [
3392
+ "@deepseek-ai/dsh-bash-sandbox",
3393
+ "0.1.2-rc.1",
3394
+ "sha512-dF9PfBWus80Juj3VjUmndbGw1/6cT1BY7BLFuXUi7SDMV1fA4iwA2c7HuvezYFGvpE/8QRZ+c5ZiRfxbWGYktw=="
3395
+ ],
3396
+ [
3397
+ "@deepseek-ai/dsh-pwsh-sandbox",
3398
+ "0.1.2-rc.1",
3399
+ "sha512-QjJzrM/tJDkgvtVzYz2bmCxBzdSUo/YwI/rPBJeTwLbWBuehyhX6BueCwU6Ja1Bsdm9crz2D7rVc/DU/xrFttw=="
3400
+ ],
3401
+ [
3402
+ "@deepseek-ai/dsh-shell-env",
3403
+ "0.1.2-rc.1",
3404
+ "sha512-o1VqxyHp1OrMB8aHnzYAPwuU4giUErCxBg0yr035r6f3Le36viS0sUHsnjcAsMdbaQJf/XvzbaokYDVrdVHE4A=="
3405
+ ],
3406
+ [
3407
+ "@deepseek-ai/dsh",
3408
+ "0.1.2-rc.1",
3409
+ "sha512-RPq48TzxvwpdT9/7W1tbhZDBMmeK+bxDrX9cqQC27Wx/LqtgJF8PSa3b3xriU8oxtvhwYmk21w2cej3uMQrnVA=="
3410
+ ],
3411
+ [
3412
+ "@deepseek-ai/dsh-host-plugin-inventory",
3413
+ "0.1.2-rc.1",
3414
+ "sha512-MyLA5XncFdfk9btv29FZSg+ojFOOsHEiPkGWAQiRw+EG/2HlVUEm/9KqPxh2jxB5eFge9GahTg2e7x3Veziadw=="
3415
+ ],
3416
+ [
3417
+ "@deepseek-ai/dsh-host-webserver",
3418
+ "0.1.2-rc.1",
3419
+ "sha512-QVcaf4qnIa1t215Y8TRiRhqkEz4Lu5/F+KqvWT+4sHOiyWmZr8g1JndqlwP6MBnMQEPmp0I/EKYJ7PWMV32gkA=="
3420
+ ],
3421
+ [
3422
+ "@deepseek-ai/dsh-web-app",
3423
+ "0.1.2-rc.1",
3424
+ "sha512-QGh+XWRgrsVktKD3YHw+cY/knwBESq+2TtyOGNc+V7GU3LG9qL6EPB0M8pIDjxF1jLls+M71LIVbevuFx3oZ6Q=="
3425
+ ],
3426
+ [
3427
+ "@deepseek-ai/dsh-jobs",
3428
+ "0.1.2-rc.1",
3429
+ "sha512-VuNPXosjgRbEg0tp+GXsdAqkicwSk5Ynl2AezUQheJgpmw5cvWy3rLEg79B686xGc72tIA6pCW6eBbZGnKOmIg=="
3430
+ ],
3431
+ [
3432
+ "@deepseek-ai/dsh-jobs-local",
3433
+ "0.1.2-rc.1",
3434
+ "sha512-bfNV6IJRG7vPWg+Rp3siRCA0BVK8rB1aqn0BG2nPKzCIA2coKqeJ8uPbEm+GuyW/naMR4uCOrrCVLyrwBuMwzg=="
3435
+ ],
3436
+ [
3437
+ "@deepseek-ai/dsh-tool-jobs",
3438
+ "0.1.2-rc.1",
3439
+ "sha512-ya7E6zToAdJ+GvNeZFPx8jNx0CfI52tlp0NCGzXHeB0S74haGWCV0iFe1nyoDJg7SDiN1xc1d2nV9klOqWtd5g=="
3440
+ ],
3441
+ [
3442
+ "@deepseek-ai/dsh-tool-fs",
3443
+ "0.1.2-rc.1",
3444
+ "sha512-9a1lcPGD4Z3p7OLTMkkCdwN0w7Gl96Jlypq6qopUA0WMkKai83VfeLZvramPnTJo6ezpuPXcLFWGm/2UDSlbcA=="
3445
+ ],
3446
+ [
3447
+ "@deepseek-ai/dsh-fs",
3448
+ "0.1.2-rc.1",
3449
+ "sha512-BSIB2j8WvATQ1mf7wUIpHPftDwbzz246qp6aUpLGSzBWGmLBdX3O1oDhyNV1YhgPmDQlwovac2odcAb62tuDdw=="
3450
+ ],
3451
+ [
3452
+ "@deepseek-ai/dsh-fs-local",
3453
+ "0.1.2-rc.1",
3454
+ "sha512-tFHHKtD11tIk3FpkWF0vBKdX05zKbwqXQW9LhreA5hErCKEsulUsQLamf+kF1D8fuKqa8Yb+yWQIfYS93cy39w=="
3455
+ ],
3456
+ [
3457
+ "@deepseek-ai/dsh-fs-sandbox",
3458
+ "0.1.2-rc.1",
3459
+ "sha512-nnZnsOYLWrN2AnoB0qvQBLhh2VdU4A39AqOgWwsxnT3JrScGpvBcBMCt4fpLP9l5VjOY1qhIbLf6xd5p93C/6Q=="
3460
+ ],
3461
+ [
3462
+ "@deepseek-ai/dsh-fs-observation-policy",
3463
+ "0.1.2-rc.1",
3464
+ "sha512-Uw3ErcPwQZUvXylLSf+CDjzfAza5ZVNOMkG4hIJjvgG4WBg3tqoCOY2OQk4O1DGTie4EzL4mHpAe5auwoV4JTw=="
3465
+ ],
3466
+ [
3467
+ "@deepseek-ai/dsh-sandbox",
3468
+ "0.1.2-rc.1",
3469
+ "sha512-nTO350NlVo9cvKzbeILcPIRIk9ijidrv29snRjTOx7kP5aO/BI2b4KxsRCYoXg8WGAMbIkGbsmZKUD4qz7hyXQ=="
3470
+ ],
3471
+ [
3472
+ "@deepseek-ai/dsh-sandbox-policy",
3473
+ "0.1.2-rc.1",
3474
+ "sha512-nLARL84X6K4DCUJsqRWINg3+1EVxsw70hbP+Yl92W/PsquQVs/UJQc55gnhYGmIMf6eJ0vQv39fziNSnAxdx5A=="
3475
+ ],
3476
+ [
3477
+ "@deepseek-ai/dsh-user-approval",
3478
+ "0.1.2-rc.1",
3479
+ "sha512-oYkLE4a/TwqVNi299pUf/QP1Yku6KScdUCTUyYbjaRBcN+/pXPpHikl2aoE2thH9D0uT/Kj6T2kz+wDDcFd9Xg=="
3480
+ ],
3481
+ [
3482
+ "@deepseek-ai/dsh-attachment",
3483
+ "0.1.2-rc.1",
3484
+ "sha512-QISKUjEITusLvAkqPLRn70xDUt+GjjY41pISjtPKYcZ4EGmayzhgZ3VJNYEcC+rbaF44hs2o/0VhOwHA7lyxjw=="
3485
+ ],
3486
+ [
3487
+ "@deepseek-ai/dsh-system-prompt",
3488
+ "0.1.2-rc.1",
3489
+ "sha512-7W93PZKIk4CHvjZGgLIxrrEKpk+6t8nQXje1vKR5vG73dfXH0t1MJ5WWH/fngadYc7c6KzM84ihQ66tJqJqyNQ=="
3490
+ ],
3491
+ [
3492
+ "dshmarket",
3493
+ "1.41.0",
3494
+ "sha512-uGhNo85g7i/+kVuFybPMjCOD1nn2ssATDSktm9Zd851qgRv39Gz8l+smpDtpBcfhlobAD3CG6l9caYkLHu4BFw=="
3495
+ ]
3496
+ ].map(([name, version, integrity]) => ({
3497
+ name,
3498
+ version,
3499
+ integrity
3500
+ }));
3501
+
3112
3502
  //#endregion
3113
3503
  //#region src/domain/host-lock.ts
3114
3504
  /**
3115
- * Capability expectations shared by every audited host cohort. The rc.2 ->
3116
- * alpha.2 source audit found no Guard-consumed contract change (session event
3117
- * vocabulary, flush contract, Goal disarm, `update_goal` gating, tool
3118
- * definition, renderer terminal markers), so the rows are identical; a future
3119
- * cohort that changes any of them must declare its own rows instead.
3505
+ * Capability expectations shared by every audited host cohort. The rc.1 audit
3506
+ * found one host API change: Session event reads moved from `events` to
3507
+ * `snapshotEvents()`. Guard adapts that API locally while the event vocabulary,
3508
+ * flush contract, Goal disarm, `update_goal` gating, tool definition, and
3509
+ * renderer terminal markers remain compatible.
3120
3510
  */
3121
3511
  const AUDITED_CAPABILITY_ROWS = [
3122
3512
  {
@@ -3388,7 +3778,12 @@ const ALPHA2_DSHMARKET_139_HOST_PACKAGES = ALPHA2_HOST_PACKAGES.map((row) => row
3388
3778
  * exact package graph extracted from native macOS and Windows DSH
3389
3779
  * `0.1.2-alpha.2` / dshmarket `1.38.1` runtimes. The alpha.2+dshmarket-1.39.0
3390
3780
  * cohort carries the exact upgraded-Windows graph. The alpha.3 cohort carries
3391
- * the graph audited in the 2026-09-01 annex. Graphs that mix cohorts, lack
3781
+ * the graph audited in the 2026-09-01 annex. The rc.1 cohort carries the exact
3782
+ * runtime plus dshmarket 1.41.0 graph audited natively on macOS, then confirmed
3783
+ * on Windows: the 2026-09-04 native Windows rc.1 runtime graph (dshmarket
3784
+ * 1.41.0) was extracted from the runtime lockfile and verified row-for-row
3785
+ * identical (name, version, registry integrity) to the posix extraction before
3786
+ * this cohort was widened. Graphs that mix cohorts, lack
3392
3787
  * rows, duplicate rows, or use identities outside every registered cohort
3393
3788
  * fail closed.
3394
3789
  */
@@ -3567,7 +3962,8 @@ const HOST_COHORTS = [
3567
3962
  ]),
3568
3963
  defineCohort("dsh-0.1.2-alpha.2", ["0.1.2-alpha.2"], ["posix", "windows"], ALPHA2_HOST_PACKAGES),
3569
3964
  defineCohort("dsh-0.1.2-alpha.2-dshmarket-1.39.0", ["0.1.2-alpha.2"], ["posix", "windows"], ALPHA2_DSHMARKET_139_HOST_PACKAGES),
3570
- defineCohort("dsh-0.1.2-alpha.3", ["0.1.2-alpha.3"], ["posix", "windows"], ALPHA3_HOST_PACKAGES)
3965
+ defineCohort("dsh-0.1.2-alpha.3", ["0.1.2-alpha.3"], ["posix", "windows"], ALPHA3_HOST_PACKAGES),
3966
+ defineCohort("dsh-0.1.2-rc.1", ["0.1.2-rc.1"], ["posix", "windows"], RC1_HOST_PACKAGES)
3571
3967
  ];
3572
3968
  /**
3573
3969
  * rc.2 audited package identities (first registry cohort). The audited
@@ -5154,13 +5550,14 @@ function supersedeItem(items, oldId, replacement) {
5154
5550
 
5155
5551
  //#endregion
5156
5552
  //#region src/domain/derive.ts
5553
+ const CAPTURE_V042_NOTICE = "Context Guard capture boundary: v0.4.2";
5157
5554
  const PROTOCOL_V3_NOTICE = "Context Guard protocol boundary: v3.0.0";
5158
- function isProtocolBoundaryNotice(event) {
5555
+ function isProtocolBoundaryNotice(event, notice = PROTOCOL_V3_NOTICE) {
5159
5556
  if (event.type !== "user/message") return false;
5160
5557
  const data = asRecord(event.data);
5161
5558
  const source = asRecord(data?.source);
5162
5559
  if (source?.kind !== "plugin" || source.plugin !== "context-guard" || source.form !== "notice") return false;
5163
- return extractTextContent(data?.content ?? []) === PROTOCOL_V3_NOTICE;
5560
+ return extractTextContent(data?.content ?? []) === notice;
5164
5561
  }
5165
5562
  function parseArguments(raw) {
5166
5563
  if (!raw) return {};
@@ -5268,16 +5665,16 @@ function resolveArtifact(path$1, scope) {
5268
5665
  * item, so evidence for one file cannot close a message that also covers other
5269
5666
  * files or embeds prohibitions.
5270
5667
  */
5271
- function insertItems(projection, text, sourceMessageId, scope, authority = "root_instruction", legacy = false, legacyAuthorityProven = false) {
5668
+ function insertItems(projection, text, sourceMessageId, scope, authority = "root_instruction", legacy = false, legacyAuthorityProven = false, captureVersion = "v042") {
5272
5669
  const before = new Set(projection.items.keys());
5273
- for (const segment of segmentClauses(text)) {
5670
+ for (const segment of segmentClauses(text, captureVersion)) {
5274
5671
  if (classifyUserInteraction(segment.body) === "conversational") continue;
5275
5672
  if (segment.kind === "requirement" && segment.paths.length === 0 && isInstructionFraming(segment.body)) continue;
5276
5673
  if (segment.kind === "prohibition" || segment.paths.length === 0) {
5277
- insert(projection, segment.kind, segment.body, sourceMessageId, scope.cwd || "scope", "scope");
5674
+ insert(projection, segment.kind, segment.body, sourceMessageId, scope.cwd || "scope", "scope", captureVersion);
5278
5675
  continue;
5279
5676
  }
5280
- for (const path$1 of segment.paths) insert(projection, segment.kind, segment.body, sourceMessageId, resolveArtifact(path$1, scope), "artifact");
5677
+ for (const path$1 of segment.paths) insert(projection, segment.kind, segment.body, sourceMessageId, resolveArtifact(path$1, scope), "artifact", captureVersion);
5281
5678
  }
5282
5679
  for (const [id, item] of projection.items) {
5283
5680
  if (before.has(id)) continue;
@@ -5292,10 +5689,10 @@ function insertItems(projection, text, sourceMessageId, scope, authority = "root
5292
5689
  else item.authority = authority;
5293
5690
  }
5294
5691
  }
5295
- function insert(projection, kind, body, sourceMessageId, subject, surface) {
5692
+ function insert(projection, kind, body, sourceMessageId, subject, surface, captureVersion) {
5296
5693
  const revision = projection.contractRevision + 1;
5297
5694
  const id = nextId(projection.items, kind);
5298
- const item = captureItem(kind, body, sourceMessageId, id, revision, subject, surface, extractMethod(body), extractOperation(body));
5695
+ const item = captureItem(kind, body, sourceMessageId, id, revision, subject, surface, extractMethod(body), extractOperation(body), captureVersion);
5299
5696
  const duplicate = [...projection.items.values()].find((existing) => existing.kind === kind && existing.status === "pending" && existing.textSha256 === item.textSha256 && existing.verification.subject === subject);
5300
5697
  if (duplicate) supersedeItem(projection.items, duplicate.id, item);
5301
5698
  else projection.items.set(id, item);
@@ -5322,9 +5719,11 @@ function deriveProjection(sourceEvents, config, scope, durableConfirmed, hostLoc
5322
5719
  let enablementTransitioned = false;
5323
5720
  let lastCompactionSeq = -1;
5324
5721
  const pendingCalls = /* @__PURE__ */ new Map();
5325
- const protocolBoundarySeq = sourceEvents.find(isProtocolBoundaryNotice)?.seq;
5722
+ const protocolBoundarySeq = sourceEvents.find((event) => isProtocolBoundaryNotice(event))?.seq;
5723
+ const captureBoundarySeq = sourceEvents.find((event) => isProtocolBoundaryNotice(event, CAPTURE_V042_NOTICE))?.seq;
5326
5724
  const priorRootMessages = [];
5327
5725
  for (const event of sourceEvents) {
5726
+ projection.enabled = enabled;
5328
5727
  projection.lastObservedSourceSeq = Math.max(projection.lastObservedSourceSeq, event.seq);
5329
5728
  switch (event.type) {
5330
5729
  case "command/run": {
@@ -5354,18 +5753,19 @@ function deriveProjection(sourceEvents, config, scope, durableConfirmed, hostLoc
5354
5753
  lastCompactionSeq = event.seq;
5355
5754
  break;
5356
5755
  case "user/message": {
5357
- if (isProtocolBoundaryNotice(event)) break;
5756
+ if (isProtocolBoundaryNotice(event) || isProtocolBoundaryNotice(event, CAPTURE_V042_NOTICE)) break;
5358
5757
  if (!enabled) break;
5359
5758
  const data = asRecord(event.data);
5360
5759
  if (asRecord(data?.source)?.kind !== "user") break;
5361
5760
  const text = extractTextContent(data?.content ?? []);
5362
5761
  if (!text.trim()) break;
5762
+ if (!scope.sessionHeader?.parentSession && !scope.sessionHeader?.delegationDepth && scope.sessionHeader?.origin !== "subagent" && confirmRebind(projection, text, `m${event.seq}`, durableConfirmed)) break;
5363
5763
  if (isInformationalMessage(text)) break;
5364
5764
  if (classifyUserInteraction(text) === "conversational") break;
5365
5765
  const blocks = segmentAuthorityBlocks(text, priorRootMessages);
5366
5766
  for (const block$1 of blocks) {
5367
5767
  if (!block$1.capture) continue;
5368
- insertItems(projection, block$1.text, `m${event.seq}:${block$1.blockId}`, scope, block$1.authority === "root_adoption" ? "root_adoption" : "root_instruction", protocolBoundarySeq !== void 0 && event.seq < protocolBoundarySeq, block$1.kind === "instruction" || block$1.authority === "root_adoption");
5768
+ insertItems(projection, block$1.text, `m${event.seq}:${block$1.blockId}`, scope, block$1.authority === "root_adoption" ? "root_adoption" : "root_instruction", protocolBoundarySeq !== void 0 && event.seq < protocolBoundarySeq, block$1.kind === "instruction" || block$1.authority === "root_adoption", captureBoundarySeq !== void 0 && event.seq < captureBoundarySeq || captureBoundarySeq === void 0 && protocolBoundarySeq !== void 0 ? "v041" : "v042");
5369
5769
  }
5370
5770
  priorRootMessages.push(text);
5371
5771
  if (priorRootMessages.length > 16) priorRootMessages.shift();
@@ -5468,9 +5868,19 @@ function deriveProjection(sourceEvents, config, scope, durableConfirmed, hostLoc
5468
5868
  if (!call) break;
5469
5869
  pendingCalls.delete(callId);
5470
5870
  const textContent = extractTextContent((isDispatch ? data?.content : void 0) ?? message?.content ?? []);
5871
+ if (call.name === "context_guard_rebind") {
5872
+ if (!call.rootCallId && !data?.error && durableConfirmed) replayRebindResult(projection, parseArguments(call.arguments), parseArguments(textContent));
5873
+ break;
5874
+ }
5471
5875
  if (call.name === "context_guard_checkpoint") {
5472
5876
  const recorded = parseArguments(textContent);
5473
- if (recorded.status !== "certified") break;
5877
+ if (recorded.status !== "certified") {
5878
+ if ((call.bindings?.length ?? 0) > 0) {
5879
+ projection.lastCheckpointRejections = certifyCheckpoint(projection, call.bindings ?? [], "diagnostic", false).rejectedBindings;
5880
+ projection.lastCheckpointRejectionRevision = projection.contractRevision;
5881
+ }
5882
+ break;
5883
+ }
5474
5884
  if (!asRecord(recorded.certificate)) {
5475
5885
  for (const binding of call.bindings ?? []) {
5476
5886
  const item = projection.items.get(binding.itemId);
@@ -5970,6 +6380,24 @@ async function executeRevalidatedGitEffect(resolved, manifest, target, currentSt
5970
6380
  return { status: "executed" };
5971
6381
  }
5972
6382
 
6383
+ //#endregion
6384
+ //#region src/domain/session-events.ts
6385
+ /**
6386
+ * Read a stable snapshot from both legacy DSH Sessions and the rc.1 Session
6387
+ * API. rc.1 replaced the public `events` getter with `snapshotEvents()`; the
6388
+ * structural adapter keeps older audited cohorts working without widening the
6389
+ * accepted event contract.
6390
+ */
6391
+ function snapshotSessionEvents(session) {
6392
+ if (!session || typeof session !== "object") return [];
6393
+ const source = session;
6394
+ if (typeof source.snapshotEvents === "function") {
6395
+ const events = source.snapshotEvents.call(session);
6396
+ return Array.isArray(events) ? events : [];
6397
+ }
6398
+ return Array.isArray(source.events) ? source.events : [];
6399
+ }
6400
+
5973
6401
  //#endregion
5974
6402
  //#region src/domain/host-resolver.ts
5975
6403
  /** Names audited in any registered cohort; rows outside the union are unknown. */
@@ -6549,4 +6977,4 @@ function proofEvidenceConstraints(evidence, obligation) {
6549
6977
  }
6550
6978
 
6551
6979
  //#endregion
6552
- export { GOAL_HOST_PACKAGES as $, COMMAND_SURFACE_MANIFEST as $t, decideTurnStopping as A, classifyClause as At, isDeterministicCheck as B, CERTIFICATE_VERSION as Bt, executeRevalidatedGitEffect as C, bindingSatisfies as Ct, verifiedLinearCommitReadback as D, currentContractDigest as Dt, revalidateGitPrestate as E, isVerifyingCapability as Et, deriveProjection as F, segmentClauses as Ft, parseShellCommand as G, actionCompatible as Gt, canonicalArgvFromCommand as H, STATEFUL_ACTIONS as Ht, supersedeItem as I, canonicalRegistryBase as It, ALPHA2_DSHMARKET_139_HOST_PACKAGES as J, requestedTargetMatchesResolved as Jt, goalCompletionDenial as K, isStatefulAction as Kt, evidenceFromPersistedToolResult as L, npmEscapedPackageName as Lt, latestAssistantText as M, extractMethod as Mt, observeAssistantOutcome as N, extractOperation as Nt, classifyCompletionClaim as O, captureClause as Ot, PROTOCOL_V3_NOTICE as P, isInformationalMessage as Pt, EXPECTED_HOST_PACKAGES as Q, validateActionTarget as Qt, extractTextContent as R, ACTION_MANIFEST as Rt, createGitPrestateEnvelope as S, renderRecoveryPacket as St, parseGitCommandManifest as T, evidenceMatchesItem as Tt, isRunExecutable as U, STOP_PROTOCOL_VERSION as Ut, withDurability as V, SEMANTIC_ACTIONS as Vt, parsePwshCommand as W, SUPPORTED_EVIDENCE_ADAPTERS as Wt, BASE_HOST_PACKAGES as X, semanticActionFromText as Xt, ALPHA2_HOST_PACKAGES as Y, semanticActionFromCommand as Yt, DEFAULT_HOST_LOCK as Z, validateActionManifest as Zt, resolveInstalledHostLock as _, certifyCheckpoint as _t, createProofManifest as a, sanitizeUrl as an, evaluateHostCapability as at, commitIndexSnapshotDigest as b, openItems$1 as bt, sessionQuery as c, selectHostCohort as ct, hostLockContextFromComposedDump as d, segmentAuthorityBlocks as dt, validateManifest as en, HOST_CAPABILITY_PACKAGE_GROUPS as et, hostLockRowsFromComposedDump as f, classifyUserInteraction as ft, resolveActiveProfileHostLock as g, qualifyBoundary as gt, packageRowsFromPnpmLock as h, isCurrentAcceptedBoundary as ht, canonicalProjection as i, sanitizeClauseText as in, evaluateExternalWaitCapability as it, isWholeTaskCompletionClaim as j, extractArtifactPaths as jt, decideTurnBoundary as k, captureItem as kt, validateProofManifest as l, ALPHA3_HOST_PACKAGES as lt, packageRowsFromActiveGraph as m, effectuateBoundary as mt, PROOF_PROTOCOL_VERSION as n, digestStrings as nn, bindExecutableIdentity as nt, proofDigest as o, sha256 as on, evaluateHostLock as ot, injectActiveProfileHostLock as p, availableBoundaryQualifications as pt, hasCurrentCertificate as q, requestedTargetAuthorizesMutation as qt, bindProofToProjection as r, normalizeClause as rn, bindLiveGoalCapability as rt, proofEvidenceConstraints as s, createProjection as sn, evaluateToolSurfaceCapability as st, PROOF_KINDS as t, canonicalizePath as tn, HOST_COHORTS as tt, HostProfileError as u, authorityCaptureCounts as ut, verifyComposedHostLockDump as v, DEFAULT_RECOVERY_CHAR_BUDGET as vt, gitCommandMatchesTarget as w, evidenceCoverage as wt, commitTreeSnapshotDigest as x, recoveryDigest as xt, GIT_COMMAND_MANIFEST_IDS as y, closingHint as yt, extractToolSubject as z, ACTION_MANIFEST_VERSION as zt };
6980
+ export { DEFAULT_HOST_LOCK as $, SUPPORTED_EVIDENCE_ADAPTERS as $t, decideTurnBoundary as A, bindingSatisfies as At, extractTextContent as B, extractArtifactPaths as Bt, createGitPrestateEnvelope as C, closingHint as Ct, revalidateGitPrestate as D, evidenceAvailabilityReason as Dt, parseGitCommandManifest as E, renderRecoveryPacket as Et, CAPTURE_V042_NOTICE as F, createProjection as Ft, isRunExecutable as G, canonicalRegistryBase as Gt, isDeterministicCheck as H, extractOperation as Ht, PROTOCOL_V3_NOTICE as I, rebindResponse as It, goalCompletionDenial as J, ACTION_MANIFEST_VERSION as Jt, parsePwshCommand as K, npmEscapedPackageName as Kt, deriveProjection as L, captureClause as Lt, isWholeTaskCompletionClaim as M, evidenceMatchesItem as Mt, latestAssistantText as N, isVerifyingCapability as Nt, verifiedLinearCommitReadback as O, itemDiagnosis as Ot, observeAssistantOutcome as P, currentContractDigest as Pt, BASE_HOST_PACKAGES as Q, STOP_PROTOCOL_VERSION as Qt, supersedeItem as R, captureItem as Rt, commitTreeSnapshotDigest as S, MIN_RECOVERY_CHAR_BUDGET as St, gitCommandMatchesTarget as T, recoveryDigest as Tt, withDurability as U, isInformationalMessage as Ut, extractToolSubject as V, extractMethod as Vt, canonicalArgvFromCommand as W, segmentClauses as Wt, ALPHA2_DSHMARKET_139_HOST_PACKAGES as X, SEMANTIC_ACTIONS as Xt, hasCurrentCertificate as Y, CERTIFICATE_VERSION as Yt, ALPHA2_HOST_PACKAGES as Z, STATEFUL_ACTIONS as Zt, resolveInstalledHostLock as _, effectuateBoundary as _t, createProofManifest as a, semanticActionFromText as an, bindLiveGoalCapability as at, GIT_COMMAND_MANIFEST_IDS as b, certifyCheckpoint as bt, sessionQuery as c, COMMAND_SURFACE_MANIFEST as cn, evaluateHostLock as ct, hostLockContextFromComposedDump as d, digestStrings as dn, RC1_HOST_PACKAGES as dt, actionCompatible as en, EXPECTED_HOST_PACKAGES as et, hostLockRowsFromComposedDump as f, normalizeClause as fn, ALPHA3_HOST_PACKAGES as ft, resolveActiveProfileHostLock as g, availableBoundaryQualifications as gt, packageRowsFromPnpmLock as h, sha256 as hn, classifyUserInteraction as ht, canonicalProjection as i, semanticActionFromCommand as in, bindExecutableIdentity as it, decideTurnStopping as j, evidenceCoverage as jt, classifyCompletionClaim as k, relevantEvidence as kt, validateProofManifest as l, validateManifest as ln, evaluateToolSurfaceCapability as lt, packageRowsFromActiveGraph as m, sanitizeUrl as mn, segmentAuthorityBlocks as mt, PROOF_PROTOCOL_VERSION as n, requestedTargetAuthorizesMutation as nn, HOST_CAPABILITY_PACKAGE_GROUPS as nt, proofDigest as o, validateActionManifest as on, evaluateExternalWaitCapability as ot, injectActiveProfileHostLock as p, sanitizeClauseText as pn, authorityCaptureCounts as pt, parseShellCommand as q, ACTION_MANIFEST as qt, bindProofToProjection as r, requestedTargetMatchesResolved as rn, HOST_COHORTS as rt, proofEvidenceConstraints as s, validateActionTarget as sn, evaluateHostCapability as st, PROOF_KINDS as t, isStatefulAction as tn, GOAL_HOST_PACKAGES as tt, HostProfileError as u, canonicalizePath as un, selectHostCohort as ut, verifyComposedHostLockDump as v, isCurrentAcceptedBoundary as vt, executeRevalidatedGitEffect as w, openItems$1 as wt, commitIndexSnapshotDigest as x, DEFAULT_RECOVERY_CHAR_BUDGET as xt, snapshotSessionEvents as y, qualifyBoundary as yt, evidenceFromPersistedToolResult as z, classifyClause as zt };