@unbrained/pm-cli 2026.8.11 → 2026.8.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (250) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/CHANGELOG.md +74 -21
  3. package/dist/cli/commander-usage.js +37 -11
  4. package/dist/cli/error-guidance.d.ts +2 -0
  5. package/dist/cli/error-guidance.js +82 -19
  6. package/dist/cli/main.js +35 -19
  7. package/dist/cli/register-annotations.js +56 -3
  8. package/dist/cli/register-assurance.js +28 -17
  9. package/dist/cli/register-list-query.js +10 -7
  10. package/dist/cli/register-operations.js +16 -6
  11. package/dist/cli-bundle/bundle-manifest.json +163 -163
  12. package/dist/cli-bundle/chunks/chunk-3ZMOWAFM.js +2 -0
  13. package/dist/cli-bundle/chunks/chunk-DEDU3DG7.js +196 -0
  14. package/dist/cli-bundle/chunks/chunk-DL5M5J3V.js +13 -0
  15. package/dist/cli-bundle/chunks/chunk-F7FJYGK3.js +8 -0
  16. package/dist/cli-bundle/chunks/chunk-HIUZVCMN.js +5 -0
  17. package/dist/cli-bundle/chunks/chunk-IMGIN7R6.js +2 -0
  18. package/dist/cli-bundle/chunks/{chunk-A2SJGHSI.js → chunk-Q47C67TQ.js} +2 -2
  19. package/dist/cli-bundle/chunks/{chunk-WMEZHCFI.js → chunk-QGTUPEU5.js} +62 -54
  20. package/dist/cli-bundle/chunks/chunk-TF3TPKPR.js +34 -0
  21. package/dist/cli-bundle/chunks/{chunk-ZU34C3LH.js → chunk-YLBAPY22.js} +50 -50
  22. package/dist/cli-bundle/chunks/register-list-query-4HIZ7EA3.js +10 -0
  23. package/dist/cli-bundle/chunks/register-mutation-W64FZ5HN.js +20 -0
  24. package/dist/cli-bundle/chunks/register-operations-C44ET7R5.js +2 -0
  25. package/dist/cli-bundle/chunks/{register-setup-NOCOOWQY.js → register-setup-634MFRXU.js} +2 -2
  26. package/dist/cli-bundle/focused-chunks/chunk-27N23W6M.js +31 -0
  27. package/dist/cli-bundle/focused-chunks/{chunk-4H5IA6AV.js → chunk-2JP7W7GO.js} +2 -2
  28. package/dist/cli-bundle/focused-chunks/chunk-2RI2FESN.js +4 -0
  29. package/dist/cli-bundle/focused-chunks/chunk-3BNVU6LC.js +8 -0
  30. package/dist/cli-bundle/focused-chunks/{chunk-2DL3MRTD.js → chunk-3DJSGWVB.js} +2 -2
  31. package/dist/cli-bundle/focused-chunks/chunk-6MLIRBLV.js +5 -0
  32. package/dist/cli-bundle/focused-chunks/chunk-6XYLENDV.js +155 -0
  33. package/dist/cli-bundle/focused-chunks/chunk-CP4K4WFS.js +2 -0
  34. package/dist/cli-bundle/focused-chunks/{chunk-TL5M2PAY.js → chunk-E3AVJ655.js} +2 -2
  35. package/dist/cli-bundle/focused-chunks/chunk-EBTNSFYE.js +2 -0
  36. package/dist/cli-bundle/focused-chunks/{chunk-J5EKAIVD.js → chunk-HC2OL2H2.js} +2 -2
  37. package/dist/cli-bundle/focused-chunks/{chunk-BKPIY34S.js → chunk-I75MANL7.js} +2 -2
  38. package/dist/cli-bundle/focused-chunks/chunk-IIS2FRKL.js +2 -0
  39. package/dist/cli-bundle/focused-chunks/chunk-NFHFZH2Z.js +2 -0
  40. package/dist/cli-bundle/focused-chunks/chunk-OGC4XSBC.js +17 -0
  41. package/dist/cli-bundle/focused-chunks/{chunk-UDAZWT25.js → chunk-OOYYKQWX.js} +4 -4
  42. package/dist/cli-bundle/focused-chunks/{chunk-23FTGO3W.js → chunk-PKPOZXLL.js} +2 -2
  43. package/dist/cli-bundle/focused-chunks/chunk-QDZUABWL.js +2 -0
  44. package/dist/cli-bundle/focused-chunks/chunk-QQX5TDFT.js +16 -0
  45. package/dist/cli-bundle/focused-chunks/{chunk-55BIB6DV.js → chunk-TEZIXMTH.js} +2 -2
  46. package/dist/cli-bundle/focused-chunks/chunk-WEH6NSL6.js +18 -0
  47. package/dist/cli-bundle/main.js +13 -13
  48. package/dist/cli-bundle/sdk-authoring.js +1 -1
  49. package/dist/cli-bundle/sdk-contracts.js +1 -1
  50. package/dist/cli-bundle/sdk-core.js +42 -41
  51. package/dist/cli-bundle/sdk-governance.js +1 -1
  52. package/dist/cli-bundle/sdk-graph.js +1 -1
  53. package/dist/cli-bundle/sdk-merge.js +1 -1
  54. package/dist/cli-bundle/sdk-query.js +1 -1
  55. package/dist/cli-bundle/sdk-runtime.js +1 -1
  56. package/dist/cli-bundle/sdk-testing.js +1 -1
  57. package/dist/cli-bundle/sdk.js +2 -2
  58. package/dist/core/checkpoint/checkpoint-gc.js +7 -12
  59. package/dist/core/extensions/activation-summary-markdown.js +5 -4
  60. package/dist/core/extensions/activation-summary.d.ts +2 -0
  61. package/dist/core/extensions/activation-summary.js +6 -2
  62. package/dist/core/extensions/capability-usage.d.ts +1 -1
  63. package/dist/core/extensions/capability-usage.js +4 -3
  64. package/dist/core/extensions/contribution-inventory.d.ts +1 -0
  65. package/dist/core/extensions/contribution-inventory.js +6 -2
  66. package/dist/core/extensions/extension-registries.js +3 -2
  67. package/dist/core/extensions/extension-types.d.ts +83 -1
  68. package/dist/core/extensions/extension-types.js +3 -2
  69. package/dist/core/extensions/loader.d.ts +2 -8
  70. package/dist/core/extensions/loader.js +53 -144
  71. package/dist/core/extensions/registration-values.d.ts +43 -0
  72. package/dist/core/extensions/registration-values.js +210 -0
  73. package/dist/core/extensions/reserved-host-flags.js +3 -2
  74. package/dist/core/extensions/runtime-registrations.d.ts +3 -1
  75. package/dist/core/extensions/runtime-registrations.js +12 -2
  76. package/dist/core/fs/fs-utils.d.ts +2 -0
  77. package/dist/core/fs/fs-utils.js +7 -3
  78. package/dist/core/history/drift-cache.js +4 -3
  79. package/dist/core/history/drift-scan.js +16 -17
  80. package/dist/core/history/history.d.ts +8 -0
  81. package/dist/core/history/history.js +57 -7
  82. package/dist/core/history/replay.d.ts +9 -1
  83. package/dist/core/history/replay.js +48 -13
  84. package/dist/core/lock/lock-gc.js +8 -13
  85. package/dist/core/lock/lock.js +5 -4
  86. package/dist/core/output/output.d.ts +2 -0
  87. package/dist/core/output/output.js +46 -14
  88. package/dist/core/search/vector-stores.js +6 -11
  89. package/dist/core/shared/command-types.d.ts +2 -0
  90. package/dist/core/shared/command-types.js +2 -2
  91. package/dist/core/shared/errors.d.ts +5 -0
  92. package/dist/core/shared/errors.js +2 -2
  93. package/dist/core/store/item-metadata-cache.d.ts +1 -1
  94. package/dist/core/store/item-metadata-cache.js +16 -9
  95. package/dist/core/telemetry/runtime.d.ts +6 -0
  96. package/dist/core/telemetry/runtime.js +101 -30
  97. package/dist/sdk/agent/command-recovery.d.ts +2 -0
  98. package/dist/sdk/agent/command-recovery.js +10 -2
  99. package/dist/sdk/annotations.js +4 -4
  100. package/dist/sdk/cli-bootstrap.d.ts +2 -0
  101. package/dist/sdk/cli-bootstrap.js +6 -2
  102. package/dist/sdk/cli-contracts/completeness.js +10 -3
  103. package/dist/sdk/cli-contracts/enum-contracts.d.ts +1 -1
  104. package/dist/sdk/cli-contracts/flag-contracts.js +9 -3
  105. package/dist/sdk/cli-contracts/registration-helpers.d.ts +20 -3
  106. package/dist/sdk/cli-contracts/registration-helpers.js +18 -12
  107. package/dist/sdk/cli-contracts/schemas/assurance-definition.d.ts +40 -6
  108. package/dist/sdk/cli-contracts/schemas/assurance-definition.js +27 -3
  109. package/dist/sdk/cli-contracts/tool-option-contracts.js +3 -2
  110. package/dist/sdk/cli-contracts/tool-parameter-tables.js +55 -5
  111. package/dist/sdk/cli-contracts/tool-schema.d.ts +2 -2
  112. package/dist/sdk/cli-contracts/tool-schema.js +22 -4
  113. package/dist/sdk/cli-program.js +3 -2
  114. package/dist/sdk/comments.js +4 -3
  115. package/dist/sdk/compile-cache.js +4 -3
  116. package/dist/sdk/completion.js +16 -7
  117. package/dist/sdk/compose.d.ts +3 -1
  118. package/dist/sdk/compose.js +16 -2
  119. package/dist/sdk/context-intent-contracts.js +45 -4
  120. package/dist/sdk/context-signal-store.js +4 -3
  121. package/dist/sdk/core.d.ts +1 -0
  122. package/dist/sdk/core.js +3 -2
  123. package/dist/sdk/extension/install-runtime.js +4 -4
  124. package/dist/sdk/extension/managed-state.js +4 -3
  125. package/dist/sdk/extension-contracts.d.ts +1 -1
  126. package/dist/sdk/flag-invocation-contracts.js +5 -6
  127. package/dist/sdk/generated/generated-error-code-catalog-part-1.d.ts +8 -0
  128. package/dist/sdk/generated/generated-error-code-catalog-part-1.js +1597 -0
  129. package/dist/sdk/generated/generated-error-code-catalog-part-2.d.ts +8 -0
  130. package/dist/sdk/generated/generated-error-code-catalog-part-2.js +1608 -0
  131. package/dist/sdk/generated-error-code-catalog.js +6 -3140
  132. package/dist/sdk/governance/assurance-action.d.ts +27 -3
  133. package/dist/sdk/governance/assurance-action.js +117 -31
  134. package/dist/sdk/governance/assurance-mutation-error.d.ts +14 -0
  135. package/dist/sdk/governance/assurance-mutation-error.js +58 -0
  136. package/dist/sdk/governance/assurance-presets.d.ts +54 -0
  137. package/dist/sdk/governance/assurance-presets.js +136 -0
  138. package/dist/sdk/governance/assurance-runtime.d.ts +4 -0
  139. package/dist/sdk/governance/assurance-runtime.js +118 -14
  140. package/dist/sdk/governance/assurance.d.ts +51 -6
  141. package/dist/sdk/governance/assurance.js +321 -67
  142. package/dist/sdk/governance/gc.js +5 -11
  143. package/dist/sdk/governance/health.d.ts +3 -1
  144. package/dist/sdk/governance/health.js +24 -7
  145. package/dist/sdk/governance/validate-item-reader.js +3 -3
  146. package/dist/sdk/governance/validate.d.ts +2 -0
  147. package/dist/sdk/governance/validate.js +15 -5
  148. package/dist/sdk/graph/durable-cache.d.ts +1 -1
  149. package/dist/sdk/graph/durable-cache.js +156 -29
  150. package/dist/sdk/graph/governance-contracts.d.ts +12 -0
  151. package/dist/sdk/graph/governance-contracts.js +27 -0
  152. package/dist/sdk/graph/governance.d.ts +44 -2
  153. package/dist/sdk/graph/governance.js +156 -12
  154. package/dist/sdk/guide.js +4 -4
  155. package/dist/sdk/index.d.ts +3 -2
  156. package/dist/sdk/index.js +5 -4
  157. package/dist/sdk/lifecycle/claim.js +10 -3
  158. package/dist/sdk/linked-artifacts.d.ts +2 -0
  159. package/dist/sdk/linked-artifacts.js +29 -4
  160. package/dist/sdk/merge/driver.d.ts +2 -2
  161. package/dist/sdk/merge/driver.js +3 -3
  162. package/dist/sdk/merge/install.js +5 -4
  163. package/dist/sdk/merge/receipts.d.ts +6 -4
  164. package/dist/sdk/merge/receipts.js +7 -5
  165. package/dist/sdk/merge/three-way.d.ts +2 -2
  166. package/dist/sdk/merge/three-way.js +3 -3
  167. package/dist/sdk/output-projection.d.ts +9 -1
  168. package/dist/sdk/output-projection.js +45 -9
  169. package/dist/sdk/query/activity.d.ts +49 -2
  170. package/dist/sdk/query/activity.js +185 -38
  171. package/dist/sdk/query/get.js +19 -2
  172. package/dist/sdk/read-output-contracts.d.ts +5 -1
  173. package/dist/sdk/read-output-contracts.js +17 -2
  174. package/dist/sdk/read-output-session.d.ts +2 -0
  175. package/dist/sdk/read-output-session.js +12 -5
  176. package/dist/sdk/relationship-history.js +5 -5
  177. package/dist/sdk/runtime-primitives.d.ts +3 -3
  178. package/dist/sdk/runtime-primitives.js +5 -5
  179. package/dist/sdk/runtime-stats-options.js +3 -2
  180. package/dist/sdk/runtime.js +5 -9
  181. package/dist/sdk/schema-migration.js +4 -4
  182. package/dist/sdk/stats.d.ts +36 -3
  183. package/dist/sdk/stats.js +55 -15
  184. package/dist/sdk/test/execution.d.ts +4 -0
  185. package/dist/sdk/test/execution.js +88 -23
  186. package/dist/sdk/workspace-memory.js +4 -4
  187. package/dist/sdk/workspace-snapshot.js +8 -14
  188. package/dist/sdk/workspace-transaction-gc.js +4 -9
  189. package/dist/sdk/workspace-transaction.js +4 -4
  190. package/dist/sdk/workspace.js +4 -3
  191. package/dist/types.d.ts +2 -0
  192. package/dist/types.js +2 -2
  193. package/docs/ASSURANCE.md +170 -14
  194. package/docs/COMMANDS.md +29 -8
  195. package/docs/CONFIGURATION.md +9 -0
  196. package/docs/DEPENDENCY_KIND_CONTRACT.md +10 -8
  197. package/docs/EXTENSIONS.md +11 -12
  198. package/docs/MERGE_SAFETY.md +3 -1
  199. package/docs/OUTPUT_PROJECTION_CONTRACTS.md +26 -13
  200. package/docs/README.md +2 -0
  201. package/docs/READ_OUTPUT_CONTRACTS.md +17 -2
  202. package/docs/RELATIONSHIP_GRAPH.md +15 -0
  203. package/docs/SDK.md +17 -3
  204. package/docs/SDK_AGENT_SESSION_CONTEXT.md +12 -1
  205. package/docs/SDK_CONTEXT_EVIDENCE_CONTRACTS.md +53 -0
  206. package/docs/SDK_CONTEXT_INTEGRITY.md +28 -1
  207. package/docs/TESTING.md +24 -15
  208. package/docs/TRUSTWORTHY_CONTEXT_EVIDENCE.md +106 -0
  209. package/docs/performance/sdk-entrypoint-import-costs.md +6 -2
  210. package/marketplace.json +2 -2
  211. package/package.json +6 -6
  212. package/packages/pm-beads/package.json +1 -1
  213. package/packages/pm-calendar/package.json +1 -1
  214. package/packages/pm-command-kit/package.json +1 -1
  215. package/packages/pm-digital-twin/package.json +1 -1
  216. package/packages/pm-governance-audit/package.json +1 -1
  217. package/packages/pm-guide-shell/package.json +1 -1
  218. package/packages/pm-kanban/package.json +1 -1
  219. package/packages/pm-lifecycle-hooks/package.json +1 -1
  220. package/packages/pm-linked-test-adapters/package.json +1 -1
  221. package/packages/pm-search-advanced/package.json +1 -1
  222. package/packages/pm-templates/package.json +1 -1
  223. package/packages/pm-todos/package.json +1 -1
  224. package/packages/pm-vcs/package.json +1 -1
  225. package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
  226. package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
  227. package/sdk/public-surface.json +384 -83
  228. package/dist/cli-bundle/chunks/chunk-2OOU2AFH.js +0 -8
  229. package/dist/cli-bundle/chunks/chunk-2RA2YTHJ.js +0 -33
  230. package/dist/cli-bundle/chunks/chunk-DSKYD4LG.js +0 -13
  231. package/dist/cli-bundle/chunks/chunk-HC3GM6YY.js +0 -194
  232. package/dist/cli-bundle/chunks/chunk-IUNRVDAI.js +0 -2
  233. package/dist/cli-bundle/chunks/chunk-W7CPZGSB.js +0 -2
  234. package/dist/cli-bundle/chunks/chunk-ZXOC6HG5.js +0 -5
  235. package/dist/cli-bundle/chunks/register-list-query-HAUXVUPY.js +0 -10
  236. package/dist/cli-bundle/chunks/register-mutation-7Y5U7ABT.js +0 -20
  237. package/dist/cli-bundle/chunks/register-operations-EQX3DLAZ.js +0 -2
  238. package/dist/cli-bundle/focused-chunks/chunk-3AYKXMYX.js +0 -2
  239. package/dist/cli-bundle/focused-chunks/chunk-4AHEC74N.js +0 -2
  240. package/dist/cli-bundle/focused-chunks/chunk-4SV3ZKG3.js +0 -16
  241. package/dist/cli-bundle/focused-chunks/chunk-6JW6Z2P5.js +0 -8
  242. package/dist/cli-bundle/focused-chunks/chunk-D4PIWE6D.js +0 -2
  243. package/dist/cli-bundle/focused-chunks/chunk-DPO7TEFX.js +0 -31
  244. package/dist/cli-bundle/focused-chunks/chunk-E3KCL7AY.js +0 -5
  245. package/dist/cli-bundle/focused-chunks/chunk-ERAAFLPS.js +0 -153
  246. package/dist/cli-bundle/focused-chunks/chunk-F6E4JZKG.js +0 -2
  247. package/dist/cli-bundle/focused-chunks/chunk-FPSYO5ZG.js +0 -18
  248. package/dist/cli-bundle/focused-chunks/chunk-KDH5OX3D.js +0 -4
  249. package/dist/cli-bundle/focused-chunks/chunk-OJ3LVVIS.js +0 -2
  250. package/dist/cli-bundle/focused-chunks/chunk-VABB4L5Y.js +0 -17
@@ -0,0 +1,210 @@
1
+ /**
2
+ * @module core/extensions/registration-values
3
+ *
4
+ * Preserves executable extension registrations while producing deterministic,
5
+ * serializable public inventory snapshots and validates assurance providers.
6
+ */
7
+
8
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="7316b9ef-38e1-5ee0-9107-da15ec7804d4")}catch(e){}}();
9
+ import { KNOWN_EXTENSION_SERVICE_NAMES, } from "./extension-types.js";
10
+ import { asRegistrationRecord, assertOptionalBooleanField, assertOptionalStringField, } from "./registration-validation.js";
11
+ import { normalizeCommandName } from "./extension-runtime-helpers.js";
12
+ /** Narrow a string to a renderer format accepted by the extension host. */
13
+ export function isOutputRendererFormat(value) {
14
+ return value === "toon" || value === "json";
15
+ }
16
+ /** Narrow a string to a service name accepted by the extension host. */
17
+ export function isExtensionServiceName(value) {
18
+ return KNOWN_EXTENSION_SERVICE_NAMES.includes(value);
19
+ }
20
+ /** Require a callable lifecycle hook before registry mutation. */
21
+ export function assertHookHandler(hookName, hook) {
22
+ if (typeof hook !== "function") {
23
+ throw new TypeError(`api.hooks.${hookName} requires a function handler`);
24
+ }
25
+ }
26
+ const EXTENSION_REGISTRATION_TRACE_SYMBOL = Symbol("extension_registration_trace");
27
+ /** Create a validation error carrying a hidden structured activation trace. */
28
+ export function createRegistrationValidationError(message, trace) {
29
+ const error = new TypeError(message);
30
+ Object.defineProperty(error, EXTENSION_REGISTRATION_TRACE_SYMBOL, {
31
+ value: trace,
32
+ enumerable: false,
33
+ configurable: false,
34
+ writable: false,
35
+ });
36
+ return error;
37
+ }
38
+ /** Recover a hidden structured activation trace from a validation error. */
39
+ export function extractRegistrationValidationTrace(error) {
40
+ return error instanceof Error
41
+ ? error[EXTENSION_REGISTRATION_TRACE_SYMBOL]
42
+ : undefined;
43
+ }
44
+ /** Recursively project an arbitrary registration value into serializable data. */
45
+ export function sanitizeRegistrationValue(value) {
46
+ if (value === null)
47
+ return null;
48
+ if (Array.isArray(value)) {
49
+ return value.map((entry) => sanitizeRegistrationValue(entry));
50
+ }
51
+ if (typeof value === "function")
52
+ return "[Function]";
53
+ if (typeof value === "bigint" || typeof value === "symbol") {
54
+ return value.toString();
55
+ }
56
+ if (typeof value === "object") {
57
+ const record = value;
58
+ const normalized = {};
59
+ for (const key of Object.keys(record).sort((left, right) => left.localeCompare(right))) {
60
+ normalized[key] = sanitizeRegistrationValue(record[key]);
61
+ }
62
+ return normalized;
63
+ }
64
+ return value;
65
+ }
66
+ /** Recursively clone registration containers while preserving function identities. */
67
+ export function cloneRuntimeRegistrationValue(value) {
68
+ if (Array.isArray(value)) {
69
+ return value.map((entry) => cloneRuntimeRegistrationValue(entry));
70
+ }
71
+ if (typeof value === "object" && value !== null) {
72
+ const record = value;
73
+ const cloned = {};
74
+ for (const key of Object.keys(record).sort((left, right) => left.localeCompare(right))) {
75
+ cloned[key] = cloneRuntimeRegistrationValue(record[key]);
76
+ }
77
+ return cloned;
78
+ }
79
+ return value;
80
+ }
81
+ /** Create a deterministic serializable registration snapshot with functions redacted. */
82
+ export function normalizeRegistrationRecord(name, value) {
83
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
84
+ throw new TypeError(`${name} requires an object definition`);
85
+ }
86
+ return sanitizeRegistrationValue(value);
87
+ }
88
+ /** Clone a registration record without stripping executable function values. */
89
+ export function normalizeRuntimeRegistrationRecord(name, value) {
90
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
91
+ throw new TypeError(`${name} requires an object definition`);
92
+ }
93
+ return cloneRuntimeRegistrationValue(value);
94
+ }
95
+ /** Implement the typed and untyped registration-array normalization overloads. */
96
+ export function normalizeRegistrationRecordList(name, value) {
97
+ if (!Array.isArray(value)) {
98
+ throw new TypeError(`${name} requires an array of object definitions`);
99
+ }
100
+ return value.map((entry) => normalizeRegistrationRecord(name, entry));
101
+ }
102
+ /** Require a trimmed non-empty string at an extension authoring boundary. */
103
+ export function assertNonEmptyRegistrationString(name, value) {
104
+ if (typeof value !== "string" || value.trim().length === 0) {
105
+ throw new TypeError(`${name} requires a non-empty string`);
106
+ }
107
+ return value.trim();
108
+ }
109
+ /** Require an executable handler at an extension authoring boundary. */
110
+ export function assertRegistrationFunction(name, value) {
111
+ if (typeof value !== "function") {
112
+ throw new TypeError(`${name} requires a function handler`);
113
+ }
114
+ }
115
+ /** Canonicalize a user-facing registration name for stable routing. */
116
+ export function normalizeRegistrationName(name) {
117
+ return name
118
+ .trim()
119
+ .toLowerCase()
120
+ .split(/\s+/)
121
+ .filter((part) => part.length > 0)
122
+ .join(" ");
123
+ }
124
+ /** Build the canonical command path synthesized for an importer or exporter. */
125
+ export function toRegistrationCommandPath(name, action) {
126
+ return normalizeCommandName(`${name} ${action}`);
127
+ }
128
+ /** Attach an executable definition as hidden immutable runtime-only state. */
129
+ export function attachRuntimeDefinition(entry, runtimeDefinition) {
130
+ Object.defineProperty(entry, "runtime_definition", {
131
+ value: runtimeDefinition,
132
+ enumerable: false,
133
+ writable: false,
134
+ configurable: false,
135
+ });
136
+ return entry;
137
+ }
138
+ const ASSURANCE_PROVIDER_COST_CLASSES = new Set(["low", "medium", "high"]);
139
+ const ASSURANCE_PROVIDER_VALUE_TYPES = new Set(["number", "string_set"]);
140
+ const ASSURANCE_PROVIDER_PARAMETER_TYPES = new Set([
141
+ "string",
142
+ "number",
143
+ "boolean",
144
+ ]);
145
+ function nonEmptyString(label, value) {
146
+ if (typeof value !== "string" || value.trim().length === 0) {
147
+ throw new TypeError(`${label} requires a non-empty string`);
148
+ }
149
+ return value.trim();
150
+ }
151
+ function validateAssuranceProviderKey(providerId, key, definition) {
152
+ const label = `registerAssuranceMeasurementProvider provider.keys.${key}`;
153
+ const record = asRegistrationRecord(label, definition);
154
+ if (!ASSURANCE_PROVIDER_VALUE_TYPES.has(String(record.value_type))) {
155
+ throw new TypeError(`${label}.value_type must be number or string_set`);
156
+ }
157
+ assertOptionalStringField(`${label}.description`, record.description);
158
+ if (record.parameters === undefined)
159
+ return;
160
+ const parameters = asRegistrationRecord(`${label}.parameters`, record.parameters);
161
+ for (const [parameter, rawDefinition] of Object.entries(parameters)) {
162
+ const parameterLabel = `${label}.parameters.${parameter}`;
163
+ nonEmptyString(`${parameterLabel}.name`, parameter);
164
+ const parameterDefinition = asRegistrationRecord(parameterLabel, rawDefinition);
165
+ if (!ASSURANCE_PROVIDER_PARAMETER_TYPES.has(String(parameterDefinition.type))) {
166
+ throw new TypeError(`${parameterLabel}.type must be string, number, or boolean`);
167
+ }
168
+ assertOptionalBooleanField(`${parameterLabel}.required`, parameterDefinition.required);
169
+ assertOptionalStringField(`${parameterLabel}.description`, parameterDefinition.description);
170
+ }
171
+ if (Object.keys(parameters).length > 50) {
172
+ throw new TypeError(`assurance provider ${providerId} key ${key} exceeds 50 parameters`);
173
+ }
174
+ }
175
+ /** Validate the closed provider schema before any activation registry mutation. */
176
+ export function validateAssuranceMeasurementProviderDefinition(provider) {
177
+ const record = asRegistrationRecord("registerAssuranceMeasurementProvider provider", provider);
178
+ const id = nonEmptyString("registerAssuranceMeasurementProvider provider.id", record.id);
179
+ if (!/^[a-z0-9][a-z0-9-]*$/u.test(id)) {
180
+ throw new TypeError("registerAssuranceMeasurementProvider provider.id must be a stable lowercase id");
181
+ }
182
+ if (!ASSURANCE_PROVIDER_COST_CLASSES.has(String(record.cost_class))) {
183
+ throw new TypeError("registerAssuranceMeasurementProvider provider.cost_class must be low, medium, or high");
184
+ }
185
+ if (typeof record.network !== "boolean") {
186
+ throw new TypeError("registerAssuranceMeasurementProvider provider.network must be boolean");
187
+ }
188
+ const keys = asRegistrationRecord("registerAssuranceMeasurementProvider provider.keys", record.keys);
189
+ if (Object.keys(keys).length === 0) {
190
+ throw new TypeError("registerAssuranceMeasurementProvider provider.keys requires at least one key");
191
+ }
192
+ for (const [key, definition] of Object.entries(keys)) {
193
+ if (!/^[a-z0-9][a-z0-9-]*$/u.test(key)) {
194
+ throw new TypeError(`registerAssuranceMeasurementProvider provider key ${key} must be a stable lowercase id`);
195
+ }
196
+ validateAssuranceProviderKey(id, key, definition);
197
+ }
198
+ if (record.timeout_ms !== undefined &&
199
+ (typeof record.timeout_ms !== "number" ||
200
+ !Number.isInteger(record.timeout_ms) ||
201
+ record.timeout_ms < 1 ||
202
+ record.timeout_ms > 300_000)) {
203
+ throw new TypeError("registerAssuranceMeasurementProvider provider.timeout_ms must be an integer from 1 through 300000");
204
+ }
205
+ if (typeof record.resolve !== "function") {
206
+ throw new TypeError("registerAssuranceMeasurementProvider provider.resolve requires a function handler");
207
+ }
208
+ }
209
+ //# sourceMappingURL=registration-values.js.map
210
+ //# debugId=7316b9ef-38e1-5ee0-9107-da15ec7804d4
@@ -5,7 +5,7 @@
5
5
  */
6
6
  /** Host-owned flags inherited by every subcommand, including extension commands. */
7
7
 
8
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="47bfb287-bdd9-5984-9db4-cb996845d98c")}catch(e){}}();
8
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="d4493313-8f07-585b-84c7-4141e1d0cba4")}catch(e){}}();
9
9
  export const RESERVED_EXTENSION_HOST_FLAGS = Object.freeze([
10
10
  { flag: "--json" },
11
11
  { flag: "--lean" },
@@ -15,6 +15,7 @@ export const RESERVED_EXTENSION_HOST_FLAGS = Object.freeze([
15
15
  { flag: "--output-budget", value_name: "tokens" },
16
16
  { flag: "--output-format", value_name: "toon|json" },
17
17
  { flag: "--output-session", value_name: "state" },
18
+ { flag: "--output-row-contract" },
18
19
  { flag: "--quiet" },
19
20
  { flag: "--no-changed-fields" },
20
21
  { flag: "--full-changed-fields" },
@@ -32,4 +33,4 @@ export const RESERVED_EXTENSION_HOST_FLAG_NAMES = new Set(RESERVED_EXTENSION_HOS
32
33
  ...(definition.aliases ?? []),
33
34
  ]));
34
35
  //# sourceMappingURL=reserved-host-flags.js.map
35
- //# debugId=47bfb287-bdd9-5984-9db4-cb996845d98c
36
+ //# debugId=d4493313-8f07-585b-84c7-4141e1d0cba4
@@ -3,7 +3,9 @@
3
3
  *
4
4
  * Implements extension runtime contracts and governance for Runtime Registrations.
5
5
  */
6
- import type { ExtensionRegistrationRegistry, RegisteredExtensionSchemaMigrationDefinition, RegisteredExtensionSearchProvider, RegisteredExtensionVectorStoreAdapter, SchemaFieldDefinition } from "./loader.js";
6
+ import type { ExtensionRegistrationRegistry, RegisteredExtensionSchemaMigrationDefinition, RegisteredExtensionSearchProvider, RegisteredExtensionVectorStoreAdapter, RegisteredExtensionAssuranceMeasurementProvider, SchemaFieldDefinition } from "./loader.js";
7
+ /** Resolve the last-activated assurance provider with the requested stable id. */
8
+ export declare function resolveRegisteredAssuranceMeasurementProvider(registrations: ExtensionRegistrationRegistry | null, providerId: string | undefined): RegisteredExtensionAssuranceMeasurementProvider | null;
7
9
  /** Implements collect registered item fields for the public runtime surface of this module. */
8
10
  export declare function collectRegisteredItemFields(registrations: ExtensionRegistrationRegistry | null): SchemaFieldDefinition[];
9
11
  /** Implements resolve registered search provider for the public runtime surface of this module. */
@@ -1,5 +1,5 @@
1
1
 
2
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="4507a449-1c20-5684-9ed8-e72c9bc694c1")}catch(e){}}();
2
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="ab276268-7a98-5f97-b3f5-300dfd1b5ed5")}catch(e){}}();
3
3
  function normalizeRegistrationName(value) {
4
4
  if (typeof value !== "string") {
5
5
  return null;
@@ -7,6 +7,16 @@ function normalizeRegistrationName(value) {
7
7
  const normalized = value.trim().toLowerCase();
8
8
  return normalized.length > 0 ? normalized : null;
9
9
  }
10
+ /** Resolve the last-activated assurance provider with the requested stable id. */
11
+ export function resolveRegisteredAssuranceMeasurementProvider(registrations, providerId) {
12
+ const normalizedId = normalizeRegistrationName(providerId);
13
+ if (!registrations || !normalizedId)
14
+ return null;
15
+ return ([...(registrations.assurance_providers ?? [])]
16
+ .reverse()
17
+ .find((registration) => normalizeRegistrationName(registration.definition.id) ===
18
+ normalizedId) ?? null);
19
+ }
10
20
  /** Implements collect registered item fields for the public runtime surface of this module. */
11
21
  export function collectRegisteredItemFields(registrations) {
12
22
  if (!registrations) {
@@ -55,4 +65,4 @@ export function getMigrationRuntimeDefinition(migration) {
55
65
  return migration.runtime_definition ?? migration.definition;
56
66
  }
57
67
  //# sourceMappingURL=runtime-registrations.js.map
58
- //# debugId=4507a449-1c20-5684-9ed8-e72c9bc694c1
68
+ //# debugId=ab276268-7a98-5f97-b3f5-300dfd1b5ed5
@@ -12,6 +12,8 @@ export declare function pathExists(targetPath: string): Promise<boolean>;
12
12
  * runtime fault instead of reaching the caller's own root validation.
13
13
  */
14
14
  export declare function isFileAbsentError(error: unknown): boolean;
15
+ /** Report an exact missing-entry race while preserving ENOTDIR as structural corruption. */
16
+ export declare function isFileMissingError(error: unknown): boolean;
15
17
  /** Implements read file if exists for the public runtime surface of this module. */
16
18
  export declare function readFileIfExists(targetPath: string): Promise<string | null>;
17
19
  /** Implements write file atomic for the public runtime surface of this module. */
@@ -4,7 +4,7 @@
4
4
  * Provides filesystem helpers for Fs Utils.
5
5
  */
6
6
 
7
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="5aef9dc6-948a-5c82-9e3c-2f85fef46beb")}catch(e){}}();
7
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="23fc5fcb-5c80-57ab-8d91-8d15ee35ab53")}catch(e){}}();
8
8
  import * as fs from "node:fs/promises";
9
9
  import path from "node:path";
10
10
  import crypto from "node:crypto";
@@ -33,7 +33,11 @@ export async function pathExists(targetPath) {
33
33
  * runtime fault instead of reaching the caller's own root validation.
34
34
  */
35
35
  export function isFileAbsentError(error) {
36
- return isErrno(error, "ENOENT") || isErrno(error, "ENOTDIR");
36
+ return isFileMissingError(error) || isErrno(error, "ENOTDIR");
37
+ }
38
+ /** Report an exact missing-entry race while preserving ENOTDIR as structural corruption. */
39
+ export function isFileMissingError(error) {
40
+ return isErrno(error, "ENOENT");
37
41
  }
38
42
  /** Implements read file if exists for the public runtime surface of this module. */
39
43
  export async function readFileIfExists(targetPath) {
@@ -152,4 +156,4 @@ function isErrno(error, code) {
152
156
  error.code === code);
153
157
  }
154
158
  //# sourceMappingURL=fs-utils.js.map
155
- //# debugId=5aef9dc6-948a-5c82-9e3c-2f85fef46beb
159
+ //# debugId=23fc5fcb-5c80-57ab-8d91-8d15ee35ab53
@@ -4,7 +4,8 @@
4
4
  * Invalidates derived history-chain verification state after committed writes.
5
5
  */
6
6
 
7
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="4db09d54-90c6-548b-baa8-c7ce419f70a2")}catch(e){}}();
7
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="5121b339-6594-58ec-b479-11cbf7dafd27")}catch(e){}}();
8
+ import { isFileMissingError } from "../fs/fs-utils.js";
8
9
  import fs from "node:fs/promises";
9
10
  import path from "node:path";
10
11
  import { writeStderr } from "../output/output.js";
@@ -49,7 +50,7 @@ export async function invalidateHistoryDriftCache(pmRoot) {
49
50
  }
50
51
  catch (error) {
51
52
  const errorCode = getFilesystemErrorCode(error);
52
- if (errorCode === "ENOENT") {
53
+ if (isFileMissingError(error)) {
53
54
  return;
54
55
  }
55
56
  const code = errorCode === undefined
@@ -69,4 +70,4 @@ export async function invalidateHistoryDriftCacheForPath(historyPath) {
69
70
  await invalidateHistoryDriftCache(path.dirname(historyDirectory));
70
71
  }
71
72
  //# sourceMappingURL=drift-cache.js.map
72
- //# debugId=4db09d54-90c6-548b-baa8-c7ce419f70a2
73
+ //# debugId=5121b339-6594-58ec-b479-11cbf7dafd27
@@ -1,14 +1,14 @@
1
1
 
2
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="90364096-b94c-591c-9024-e697fa9322a5")}catch(e){}}();
2
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="a6714da6-6e2b-5647-bfde-607a54d7e4cd")}catch(e){}}();
3
3
  import fs from "node:fs/promises";
4
4
  import { createHash } from "node:crypto";
5
5
  import path from "node:path";
6
6
  import { getHistoryPath } from "../store/paths.js";
7
- import { writeFileAtomic } from "../fs/fs-utils.js";
8
- import { hashDocument } from "./history.js";
9
- import { verifyHistoryChain } from "./replay.js";
7
+ import { isFileMissingError, writeFileAtomic } from "../fs/fs-utils.js";
8
+ import { CURRENT_HISTORY_ITEM_HASH_VERSION, hashDocumentForVersion, } from "./history.js";
9
+ import { verifyHistoryChainWithVersion } from "./replay.js";
10
10
  import { getWorkspaceHistoryPath, WORKSPACE_HISTORY_ID, } from "./workspace-history.js";
11
- const DRIFT_CACHE_VERSION = 3;
11
+ const DRIFT_CACHE_VERSION = 4;
12
12
  const DRIFT_CACHE_FILENAME = "history-drift-cache.json";
13
13
  function getDriftCachePath(pmRoot) {
14
14
  return path.join(pmRoot, "runtime", DRIFT_CACHE_FILENAME);
@@ -28,12 +28,6 @@ async function loadDriftCache(pmRoot) {
28
28
  return null;
29
29
  }
30
30
  }
31
- function isErrno(error, code) {
32
- return (typeof error === "object" &&
33
- error !== null &&
34
- "code" in error &&
35
- error.code === code);
36
- }
37
31
  async function scanWorkspaceHistory(pmRoot, previousEntries, nextEntries, verifyCacheHitByContent, accumulator) {
38
32
  const historyPath = getWorkspaceHistoryPath(pmRoot);
39
33
  let stat;
@@ -41,7 +35,7 @@ async function scanWorkspaceHistory(pmRoot, previousEntries, nextEntries, verify
41
35
  stat = await fs.stat(historyPath);
42
36
  }
43
37
  catch (error) {
44
- if (isErrno(error, "ENOENT"))
38
+ if (isFileMissingError(error))
45
39
  return false;
46
40
  accumulator.unreadableStreams.push(WORKSPACE_HISTORY_ID);
47
41
  return false;
@@ -69,6 +63,7 @@ async function scanWorkspaceHistory(pmRoot, previousEntries, nextEntries, verify
69
63
  content_hash: resolved.verification.contentHash,
70
64
  latest_after_hash: resolved.verification.latestAfterHash,
71
65
  chain_ok: resolved.verification.chainOk,
66
+ item_hash_version: resolved.verification.itemHashVersion,
72
67
  };
73
68
  return resolved.cacheDirty;
74
69
  }
@@ -106,10 +101,12 @@ async function verifyHistoryStream(historyPath) {
106
101
  return null;
107
102
  }
108
103
  /* c8 ignore stop */
104
+ const verification = verifyHistoryChainWithVersion(entries);
109
105
  return {
110
106
  latestAfterHash,
111
- chainOk: verifyHistoryChain(entries).ok,
107
+ chainOk: verification.ok,
112
108
  contentHash,
109
+ itemHashVersion: verification.item_hash_version ?? CURRENT_HISTORY_ITEM_HASH_VERSION,
113
110
  };
114
111
  }
115
112
  function cacheMetadataMatches(cached, stat) {
@@ -166,6 +163,7 @@ async function resolveStreamVerification(params) {
166
163
  latestAfterHash: params.cached.latest_after_hash,
167
164
  chainOk: params.cached.chain_ok,
168
165
  contentHash: currentContentHash,
166
+ itemHashVersion: params.cached.item_hash_version ?? CURRENT_HISTORY_ITEM_HASH_VERSION,
169
167
  },
170
168
  cacheDirty: false,
171
169
  };
@@ -206,7 +204,7 @@ export async function scanHistoryDrift(pmRoot, items, options = {}) {
206
204
  stat = await fs.stat(historyPath);
207
205
  }
208
206
  catch (error) {
209
- if (isErrno(error, "ENOENT")) {
207
+ if (isFileMissingError(error)) {
210
208
  accumulator.missingStreams.push(item.id);
211
209
  }
212
210
  else {
@@ -237,12 +235,13 @@ export async function scanHistoryDrift(pmRoot, items, options = {}) {
237
235
  content_hash: resolved.verification.contentHash,
238
236
  latest_after_hash: resolved.verification.latestAfterHash,
239
237
  chain_ok: resolved.verification.chainOk,
238
+ item_hash_version: resolved.verification.itemHashVersion,
240
239
  };
241
240
  const { body, ...itemMetadata } = item;
242
- const currentHash = hashDocument({
241
+ const currentHash = hashDocumentForVersion({
243
242
  metadata: itemMetadata,
244
243
  body,
245
- });
244
+ }, resolved.verification.itemHashVersion);
246
245
  if (currentHash !== resolved.verification.latestAfterHash) {
247
246
  accumulator.hashMismatches.push(item.id);
248
247
  }
@@ -271,4 +270,4 @@ export async function scanHistoryDrift(pmRoot, items, options = {}) {
271
270
  return { ...accumulator, driftedItems };
272
271
  }
273
272
  //# sourceMappingURL=drift-scan.js.map
274
- //# debugId=90364096-b94c-591c-9024-e697fa9322a5
273
+ //# debugId=a6714da6-6e2b-5647-bfde-607a54d7e4cd
@@ -1,9 +1,17 @@
1
1
  import { type AuthorSource } from "../shared/author.js";
2
2
  import type { HistoryEntry, ItemDocument } from "../../types/index.js";
3
+ /** Hash epoch that preserves linked-test insertion order in immutable history. */
4
+ export declare const CURRENT_HISTORY_ITEM_HASH_VERSION: 2;
5
+ /** Hash epochs understood by this runtime. */
6
+ export declare const SUPPORTED_HISTORY_ITEM_HASH_VERSIONS: readonly [1, 2];
7
+ /** Restricts item hash versions accepted by history replay. */
8
+ export type HistoryItemHashVersion = (typeof SUPPORTED_HISTORY_ITEM_HASH_VERSIONS)[number];
3
9
  declare function decodeJsonPointer(path: string): string[];
4
10
  declare function isDefinedPointerPath(document: unknown, path: string): boolean;
5
11
  /** Implements hash document for the public runtime surface of this module. */
6
12
  export declare function hashDocument(document: ItemDocument): string;
13
+ /** Hash an item with an explicit canonicalization epoch for replay compatibility. */
14
+ export declare function hashDocumentForVersion(document: ItemDocument, version: HistoryItemHashVersion): string;
7
15
  /** Implements hash empty document for the public runtime surface of this module. */
8
16
  export declare function hashEmptyDocument(): string;
9
17
  /** Implements create history entry for the public runtime surface of this module. */
@@ -4,7 +4,7 @@
4
4
  * Implements append-only history and replay behavior for History.
5
5
  */
6
6
 
7
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="ce2dace3-7c86-5edf-8bda-083a0b3e7f2b")}catch(e){}}();
7
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="2fc7f675-3500-5abb-8d5c-9ca9caa803b6")}catch(e){}}();
8
8
  import jsonPatch from "fast-json-patch";
9
9
  import { ITEM_METADATA_KEY_ORDER } from "../shared/constants.js";
10
10
  import { runActiveServiceOverride } from "../extensions/index.js";
@@ -20,6 +20,42 @@ const EMPTY_LEGACY_HASH_DOCUMENT = {
20
20
  front_matter: {},
21
21
  body: "",
22
22
  };
23
+ /** Hash epoch that preserves linked-test insertion order in immutable history. */
24
+ export const CURRENT_HISTORY_ITEM_HASH_VERSION = 2;
25
+ /** Hash epochs understood by this runtime. */
26
+ export const SUPPORTED_HISTORY_ITEM_HASH_VERSIONS = [1, 2];
27
+ function compareOptionalStrings(left, right) {
28
+ return (left ?? "").localeCompare(right ?? "");
29
+ }
30
+ function compareOptionalNumbers(left, right) {
31
+ return (left ?? 0) - (right ?? 0);
32
+ }
33
+ function compareJsonValues(left, right, fallback) {
34
+ // Preserve the original v1 comparator byte-for-byte: replacing JSON.stringify
35
+ // with stableStringify would invalidate immutable hashes from legacy streams.
36
+ return JSON.stringify(left ?? fallback).localeCompare(JSON.stringify(right ?? fallback));
37
+ }
38
+ function compareLegacyLinkedTests(left, right) {
39
+ const comparisons = [
40
+ left.scope.localeCompare(right.scope),
41
+ compareOptionalStrings(left.path, right.path),
42
+ compareOptionalStrings(left.command, right.command),
43
+ compareOptionalNumbers(left.timeout_seconds, right.timeout_seconds),
44
+ compareOptionalStrings(left.pm_context_mode, right.pm_context_mode),
45
+ Number(Boolean(left.shared_host_safe)) - Number(Boolean(right.shared_host_safe)),
46
+ compareJsonValues(left.env_clear, right.env_clear, []),
47
+ compareJsonValues(left.env_set, right.env_set, {}),
48
+ compareJsonValues(left.assert_stdout_contains, right.assert_stdout_contains, []),
49
+ compareJsonValues(left.assert_stdout_regex, right.assert_stdout_regex, []),
50
+ compareJsonValues(left.assert_stderr_contains, right.assert_stderr_contains, []),
51
+ compareJsonValues(left.assert_stderr_regex, right.assert_stderr_regex, []),
52
+ compareOptionalNumbers(left.assert_stdout_min_lines, right.assert_stdout_min_lines),
53
+ compareJsonValues(left.assert_json_field_equals, right.assert_json_field_equals, {}),
54
+ compareJsonValues(left.assert_json_field_gte, right.assert_json_field_gte, {}),
55
+ compareOptionalStrings(left.note, right.note),
56
+ ];
57
+ return comparisons.find((comparison) => comparison !== 0) ?? 0;
58
+ }
23
59
  function decodeJsonPointer(path) {
24
60
  if (!path || path === "/") {
25
61
  return [];
@@ -81,7 +117,7 @@ function normalizeHistoryPatchOps(beforeDocument, patch) {
81
117
  }
82
118
  return normalized;
83
119
  }
84
- function canonicalHashDocument(document) {
120
+ function canonicalHashDocument(document, version) {
85
121
  const hasMetadata = document.metadata && Object.keys(document.metadata).length > 0;
86
122
  if (!hasMetadata) {
87
123
  return {
@@ -90,7 +126,13 @@ function canonicalHashDocument(document) {
90
126
  };
91
127
  }
92
128
  const canonical = canonicalDocument(document);
93
- const orderedMetadata = orderObject(toItemRecord(canonical.metadata), ITEM_METADATA_KEY_ORDER);
129
+ const metadata = version === 1 && canonical.metadata.tests
130
+ ? {
131
+ ...canonical.metadata,
132
+ tests: [...canonical.metadata.tests].sort(compareLegacyLinkedTests),
133
+ }
134
+ : canonical.metadata;
135
+ const orderedMetadata = orderObject(toItemRecord(metadata), ITEM_METADATA_KEY_ORDER);
94
136
  return {
95
137
  front_matter: orderedMetadata,
96
138
  body: canonical.body,
@@ -113,7 +155,14 @@ function canonicalPatchDocument(document) {
113
155
  }
114
156
  /** Implements hash document for the public runtime surface of this module. */
115
157
  export function hashDocument(document) {
116
- return sha256Hex(stableStringify(canonicalHashDocument(document)));
158
+ return hashDocumentForVersion(document, CURRENT_HISTORY_ITEM_HASH_VERSION);
159
+ }
160
+ /** Hash an item with an explicit canonicalization epoch for replay compatibility. */
161
+ export function hashDocumentForVersion(document, version) {
162
+ if (!SUPPORTED_HISTORY_ITEM_HASH_VERSIONS.some((supportedVersion) => supportedVersion === version)) {
163
+ throw new TypeError(`unsupported_item_hash_version:${String(version)}`);
164
+ }
165
+ return sha256Hex(stableStringify(canonicalHashDocument(document, version)));
117
166
  }
118
167
  /** Implements hash empty document for the public runtime surface of this module. */
119
168
  export function hashEmptyDocument() {
@@ -121,8 +170,8 @@ export function hashEmptyDocument() {
121
170
  }
122
171
  /** Implements create history entry for the public runtime surface of this module. */
123
172
  export function createHistoryEntry(params) {
124
- const beforeHashCanonical = canonicalHashDocument(params.before);
125
- const afterHashCanonical = canonicalHashDocument(params.after);
173
+ const beforeHashCanonical = canonicalHashDocument(params.before, CURRENT_HISTORY_ITEM_HASH_VERSION);
174
+ const afterHashCanonical = canonicalHashDocument(params.after, CURRENT_HISTORY_ITEM_HASH_VERSION);
126
175
  const beforePatchCanonical = canonicalPatchDocument(params.before);
127
176
  const afterPatchCanonical = canonicalPatchDocument(params.after);
128
177
  const rawPatch = jsonPatch.compare(beforePatchCanonical, afterPatchCanonical);
@@ -159,6 +208,7 @@ export function createHistoryEntry(params) {
159
208
  patch,
160
209
  before_hash: sha256Hex(stableStringify(beforeHashCanonical)),
161
210
  after_hash: sha256Hex(stableStringify(afterHashCanonical)),
211
+ item_hash_version: CURRENT_HISTORY_ITEM_HASH_VERSION,
162
212
  message: params.message === undefined ? undefined : params.message,
163
213
  ...(context === undefined ? {} : { context }),
164
214
  };
@@ -242,4 +292,4 @@ export const _testOnly = {
242
292
  isDefinedPointerPath,
243
293
  };
244
294
  //# sourceMappingURL=history.js.map
245
- //# debugId=ce2dace3-7c86-5edf-8bda-083a0b3e7f2b
295
+ //# debugId=2fc7f675-3500-5abb-8d5c-9ca9caa803b6
@@ -1,4 +1,5 @@
1
1
  import { canonicalDocument } from "../item/item-format.js";
2
+ import { type HistoryItemHashVersion } from "./history.js";
2
3
  import type { HistoryEntry, HistoryPatchOp, ItemDocument } from "../../types/index.js";
3
4
  /**
4
5
  * Shared history replay/patch mechanics single-sourced for the history, restore,
@@ -19,7 +20,7 @@ export declare const EMPTY_REPLAY_DOCUMENT: ReplayDocument;
19
20
  /** Implements clone empty replay document for the public runtime surface of this module. */
20
21
  export declare function cloneEmptyReplayDocument(): ReplayDocument;
21
22
  /** Implements replay hash for the public runtime surface of this module. */
22
- export declare function replayHash(document: ReplayDocument): string;
23
+ export declare function replayHash(document: ReplayDocument, version?: HistoryItemHashVersion): string;
23
24
  /** Implements replay to item document for the public runtime surface of this module. */
24
25
  export declare function replayToItemDocument(document: ReplayDocument): ItemDocument;
25
26
  /** Converts a materialized replay document into a canonical item document. Use this when callers have already rejected the empty/deleted replay state and need restored metadata validated through the normal item-metadata rules. */
@@ -41,6 +42,13 @@ export type ReplayApplyResult = {
41
42
  /** Strictly apply a history patch (front_matter->metadata normalized) to a replay document. Returns a result envelope rather than throwing so each caller can format its own error contract. */
42
43
  export declare function tryApplyReplayPatch(current: ReplayDocument, patch: HistoryPatchOp[]): ReplayApplyResult;
43
44
  /** Deterministically verify a history chain: each entry's before_hash must equal the prior replayed after_hash, the patch must strictly apply, and the recorded after_hash must equal the replayed result. */
45
+ /** Verify a chain and report the explicit or auto-detected item hash epoch. */
46
+ export declare function verifyHistoryChainWithVersion(entries: HistoryEntry[]): {
47
+ ok: boolean;
48
+ errors: string[];
49
+ item_hash_version?: HistoryItemHashVersion;
50
+ };
51
+ /** Deterministically verify a history chain while preserving the legacy result shape. */
44
52
  export declare function verifyHistoryChain(entries: HistoryEntry[]): {
45
53
  ok: boolean;
46
54
  errors: string[];