@unbrained/pm-cli 2026.10.17 → 2026.10.19

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 (256) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/CHANGELOG.md +35 -0
  3. package/dist/cli/help-content.js +2 -2
  4. package/dist/cli/register-list-query.js +2 -2
  5. package/dist/cli/register-mutation.js +2 -2
  6. package/dist/cli/register-setup.js +1 -1
  7. package/dist/cli-bundle/bundle-manifest.json +1 -1
  8. package/dist/cli-bundle/chunks/chunk-4CXQSRSP.js +126 -0
  9. package/dist/cli-bundle/chunks/{chunk-EA2CG7UM.js → chunk-4ICKT4GH.js} +2 -2
  10. package/dist/cli-bundle/chunks/{chunk-W3KZ22DW.js → chunk-4MJ6WBMA.js} +2 -2
  11. package/dist/cli-bundle/chunks/chunk-4XFVAXA4.js +2 -0
  12. package/dist/cli-bundle/chunks/{chunk-6ZDKKOKM.js → chunk-5GZSX5HU.js} +72 -66
  13. package/dist/cli-bundle/chunks/{chunk-3QKI3XNX.js → chunk-6DS5CAN2.js} +2 -2
  14. package/dist/cli-bundle/chunks/{chunk-B5QVGCZX.js → chunk-D3EDNAN7.js} +2 -2
  15. package/dist/cli-bundle/chunks/chunk-DMFM5R4G.js +3 -0
  16. package/dist/cli-bundle/chunks/chunk-H3XYP24A.js +70 -0
  17. package/dist/cli-bundle/chunks/chunk-LBEMB3BW.js +6 -0
  18. package/dist/cli-bundle/chunks/chunk-NX7Q4LGN.js +8 -0
  19. package/dist/cli-bundle/chunks/chunk-O2ZRRLAP.js +8 -0
  20. package/dist/cli-bundle/chunks/chunk-OEJ3G7XT.js +20 -0
  21. package/dist/cli-bundle/chunks/{chunk-O4YBC3OM.js → chunk-QBNEZII5.js} +30 -30
  22. package/dist/cli-bundle/chunks/chunk-QG4LMU7A.js +112 -0
  23. package/dist/cli-bundle/chunks/chunk-QKAAZWGC.js +3 -0
  24. package/dist/cli-bundle/chunks/chunk-RUKSEZE7.js +2 -0
  25. package/dist/cli-bundle/chunks/chunk-TK3G3GAP.js +2 -0
  26. package/dist/cli-bundle/chunks/chunk-VS565KBX.js +2 -0
  27. package/dist/cli-bundle/chunks/{chunk-JS3B2M4D.js → chunk-XH7C3A5N.js} +2 -2
  28. package/dist/cli-bundle/chunks/indexed-receipt-blobs-BVSHK4WQ.js +4 -0
  29. package/dist/cli-bundle/chunks/{register-list-query-RU3QZSAQ.js → register-list-query-WQUVDJIH.js} +7 -7
  30. package/dist/cli-bundle/chunks/register-mutation-IWIBQIO3.js +20 -0
  31. package/dist/cli-bundle/chunks/register-operations-SUWOZNDU.js +2 -0
  32. package/dist/cli-bundle/chunks/register-setup-QGM5YJW7.js +2 -0
  33. package/dist/cli-bundle/chunks/settlement-hook-UNFMCKKQ.js +7 -0
  34. package/dist/cli-bundle/chunks/stage-7DELI4Z5.js +2 -0
  35. package/dist/cli-bundle/focused-chunks/{chunk-LDKX4KYJ.js → chunk-2IEWB6MB.js} +2 -2
  36. package/dist/cli-bundle/focused-chunks/chunk-3TEIACFU.js +3 -0
  37. package/dist/cli-bundle/focused-chunks/chunk-5YTOYBTZ.js +65 -0
  38. package/dist/cli-bundle/focused-chunks/chunk-AK2CI2GI.js +2 -0
  39. package/dist/cli-bundle/focused-chunks/chunk-BJMPRB7A.js +91 -0
  40. package/dist/cli-bundle/focused-chunks/chunk-ENZMATOY.js +20 -0
  41. package/dist/cli-bundle/focused-chunks/chunk-EOQ6W25I.js +8 -0
  42. package/dist/cli-bundle/focused-chunks/{chunk-FTUZ3JF2.js → chunk-FEN7WOOP.js} +2 -2
  43. package/dist/cli-bundle/focused-chunks/chunk-FOA7XKWP.js +2 -0
  44. package/dist/cli-bundle/focused-chunks/chunk-GWTV45SB.js +2 -0
  45. package/dist/cli-bundle/focused-chunks/{chunk-SIGIGZCG.js → chunk-H6BVQ63U.js} +2 -2
  46. package/dist/cli-bundle/focused-chunks/{chunk-RBLSY6ML.js → chunk-HYMYAGSY.js} +2 -2
  47. package/dist/cli-bundle/focused-chunks/chunk-JBJJW2WL.js +6 -0
  48. package/dist/cli-bundle/focused-chunks/chunk-JCLC6HKT.js +8 -0
  49. package/dist/cli-bundle/focused-chunks/{chunk-2IOHHNRT.js → chunk-JDBO7SMR.js} +2 -2
  50. package/dist/cli-bundle/focused-chunks/{chunk-UNTQE37U.js → chunk-KLJN44VY.js} +2 -2
  51. package/dist/cli-bundle/focused-chunks/chunk-L4PL3GZR.js +2 -0
  52. package/dist/cli-bundle/focused-chunks/chunk-OV2WVLUR.js +2 -0
  53. package/dist/cli-bundle/focused-chunks/chunk-PXDR77TO.js +78 -0
  54. package/dist/cli-bundle/focused-chunks/chunk-RLRMNFLF.js +3 -0
  55. package/dist/cli-bundle/focused-chunks/{chunk-E33QA7PK.js → chunk-SKYLOPU2.js} +2 -2
  56. package/dist/cli-bundle/focused-chunks/chunk-VVRXF4JO.js +2 -0
  57. package/dist/cli-bundle/focused-chunks/chunk-WZTEFFQI.js +3 -0
  58. package/dist/cli-bundle/focused-chunks/chunk-Y54ONBEX.js +25 -0
  59. package/dist/cli-bundle/focused-chunks/indexed-receipt-blobs-QW2VWWJE.js +4 -0
  60. package/dist/cli-bundle/focused-chunks/settlement-hook-X4FNHTOD.js +7 -0
  61. package/dist/cli-bundle/focused-chunks/stage-MZSVQYO4.js +2 -0
  62. package/dist/cli-bundle/main.js +3 -3
  63. package/dist/cli-bundle/sdk-authoring.js +1 -1
  64. package/dist/cli-bundle/sdk-contracts.js +1 -1
  65. package/dist/cli-bundle/sdk-core.js +29 -23
  66. package/dist/cli-bundle/sdk-governance.js +1 -1
  67. package/dist/cli-bundle/sdk-graph.js +1 -1
  68. package/dist/cli-bundle/sdk-merge.js +66 -52
  69. package/dist/cli-bundle/sdk-query.js +1 -1
  70. package/dist/cli-bundle/sdk-runtime.js +1 -1
  71. package/dist/cli-bundle/sdk-testing.js +1 -1
  72. package/dist/cli-bundle/sdk.js +2 -2
  73. package/dist/core/history/event-index.d.ts +8 -1
  74. package/dist/core/history/event-index.js +19 -18
  75. package/dist/core/item/item-format.js +1 -1
  76. package/dist/core/lock/lock.d.ts +8 -1
  77. package/dist/core/lock/lock.js +3 -3
  78. package/dist/core/output/output.js +2 -2
  79. package/dist/core/store/paths.js +1 -1
  80. package/dist/core/telemetry/policy.d.ts +2 -0
  81. package/dist/core/telemetry/policy.js +1 -1
  82. package/dist/core/telemetry/runtime.d.ts +5 -3
  83. package/dist/core/telemetry/runtime.js +3 -3
  84. package/dist/sdk/cli-contracts/bootstrap-command-scanner.js +1 -1
  85. package/dist/sdk/cli-contracts/flag-contracts.js +1 -1
  86. package/dist/sdk/cli-contracts/flag-lexicon-contracts.js +1 -1
  87. package/dist/sdk/cli-contracts/runtime-contracts.d.ts +1 -0
  88. package/dist/sdk/cli-contracts/runtime-contracts.js +1 -1
  89. package/dist/sdk/cli-contracts/tool-parameter-tables.js +1 -1
  90. package/dist/sdk/cli-contracts/tool-schema.js +1 -1
  91. package/dist/sdk/extension/install-sources.js +3 -3
  92. package/dist/sdk/extension/shared.js +1 -1
  93. package/dist/sdk/generated/generated-error-code-catalog-part-1.js +1 -1
  94. package/dist/sdk/generated/generated-error-code-catalog-part-2.js +1 -1
  95. package/dist/sdk/governance/health/projection.d.ts +37 -0
  96. package/dist/sdk/governance/health/projection.js +2 -0
  97. package/dist/sdk/governance/health.d.ts +7 -18
  98. package/dist/sdk/governance/health.js +2 -2
  99. package/dist/sdk/history-repair.js +1 -1
  100. package/dist/sdk/merge/driver.js +7 -1
  101. package/dist/sdk/merge/indexed-receipt-blobs.d.ts +2 -0
  102. package/dist/sdk/merge/indexed-receipt-blobs.js +4 -0
  103. package/dist/sdk/merge/install.d.ts +8 -0
  104. package/dist/sdk/merge/install.js +3 -3
  105. package/dist/sdk/merge/receipts.d.ts +6 -0
  106. package/dist/sdk/merge/receipts.js +2 -2
  107. package/dist/sdk/merge/reconcile.d.ts +6 -0
  108. package/dist/sdk/merge/reconcile.js +1 -1
  109. package/dist/sdk/merge/settlement-hook.d.ts +12 -0
  110. package/dist/sdk/merge/settlement-hook.js +7 -0
  111. package/dist/sdk/merge/stage.d.ts +13 -0
  112. package/dist/sdk/merge/stage.js +2 -0
  113. package/dist/sdk/mutation-events.js +1 -1
  114. package/dist/sdk/public-surface.json +1 -1
  115. package/dist/sdk/query/list.d.ts +2 -0
  116. package/dist/sdk/query/list.js +1 -1
  117. package/dist/sdk/runtime-extended-actions.js +1 -1
  118. package/dist/types.d.ts +2 -2
  119. package/dist/types.js +1 -1
  120. package/docs/COMMANDS.md +39 -0
  121. package/docs/CONFIGURATION.md +36 -1
  122. package/docs/DEVELOPMENT_DEPENDENCY_SECURITY.md +22 -3
  123. package/docs/MERGE_SAFETY.md +88 -5
  124. package/docs/OUTPUT_PROJECTION_CONTRACTS.md +10 -0
  125. package/docs/RELEASING.md +8 -1
  126. package/docs/SDK.md +26 -0
  127. package/docs/SDK_CONTEXT_COORDINATION.md +71 -7
  128. package/docs/SDK_STORAGE_LIFECYCLE.md +24 -2
  129. package/docs/TESTING.md +15 -0
  130. package/docs/examples/ci/github-actions-pm-extension-gate.yml +1 -1
  131. package/docs/examples/ci/gitlab-ci-pm-extension-gate.yml +1 -1
  132. package/docs/examples/ci/jenkins-pm-extension-gate.Jenkinsfile +1 -1
  133. package/docs/generated/FLAG_LEXICON_BUDGETS.md +2 -2
  134. package/docs/generated/REFUSAL_CLOSURE_CENSUS.md +6 -3
  135. package/marketplace.json +1 -1
  136. package/node_modules/@opentelemetry/core/package.json +2 -1
  137. package/node_modules/@opentelemetry/instrumentation/package.json +2 -1
  138. package/node_modules/@opentelemetry/resources/package.json +2 -1
  139. package/node_modules/@opentelemetry/sdk-trace/package.json +2 -1
  140. package/node_modules/@opentelemetry/sdk-trace-base/package.json +2 -1
  141. package/node_modules/@sentry/core/build/cjs/client.js +5 -4
  142. package/node_modules/@sentry/core/build/cjs/tracing/ai/gen-ai-attributes.js +0 -4
  143. package/node_modules/@sentry/core/build/cjs/tracing/ai/utils.js +33 -23
  144. package/node_modules/@sentry/core/build/cjs/tracing/anthropic-ai/index.js +7 -6
  145. package/node_modules/@sentry/core/build/cjs/tracing/anthropic-ai/streaming.js +3 -1
  146. package/node_modules/@sentry/core/build/cjs/tracing/google-genai/index.js +13 -14
  147. package/node_modules/@sentry/core/build/cjs/tracing/google-genai/streaming.js +6 -1
  148. package/node_modules/@sentry/core/build/cjs/tracing/langchain/utils.js +71 -21
  149. package/node_modules/@sentry/core/build/cjs/tracing/langgraph/utils.js +23 -33
  150. package/node_modules/@sentry/core/build/cjs/tracing/openai/streaming.js +6 -0
  151. package/node_modules/@sentry/core/build/cjs/tracing/openai/utils.js +11 -0
  152. package/node_modules/@sentry/core/build/cjs/tracing/workers-ai/utils.js +1 -1
  153. package/node_modules/@sentry/core/build/cjs/utils/eventUtils.js +4 -0
  154. package/node_modules/@sentry/core/build/cjs/utils/prepareEvent.js +2 -2
  155. package/node_modules/@sentry/core/build/cjs/utils/version.js +1 -1
  156. package/node_modules/@sentry/core/build/esm/client.js +6 -5
  157. package/node_modules/@sentry/core/build/esm/package.json +1 -1
  158. package/node_modules/@sentry/core/build/esm/tracing/ai/gen-ai-attributes.js +1 -3
  159. package/node_modules/@sentry/core/build/esm/tracing/ai/utils.js +34 -24
  160. package/node_modules/@sentry/core/build/esm/tracing/anthropic-ai/index.js +8 -7
  161. package/node_modules/@sentry/core/build/esm/tracing/anthropic-ai/streaming.js +3 -1
  162. package/node_modules/@sentry/core/build/esm/tracing/google-genai/index.js +15 -16
  163. package/node_modules/@sentry/core/build/esm/tracing/google-genai/streaming.js +6 -1
  164. package/node_modules/@sentry/core/build/esm/tracing/langchain/utils.js +73 -24
  165. package/node_modules/@sentry/core/build/esm/tracing/langgraph/utils.js +24 -34
  166. package/node_modules/@sentry/core/build/esm/tracing/openai/streaming.js +6 -0
  167. package/node_modules/@sentry/core/build/esm/tracing/openai/utils.js +11 -0
  168. package/node_modules/@sentry/core/build/esm/tracing/workers-ai/utils.js +2 -2
  169. package/node_modules/@sentry/core/build/esm/utils/eventUtils.js +4 -1
  170. package/node_modules/@sentry/core/build/esm/utils/prepareEvent.js +2 -2
  171. package/node_modules/@sentry/core/build/esm/utils/version.js +1 -1
  172. package/node_modules/@sentry/core/build/types/tracing/ai/utils.d.ts +3 -7
  173. package/node_modules/@sentry/core/build/types/tracing/anthropic-ai/types.d.ts +2 -2
  174. package/node_modules/@sentry/core/build/types/tracing/langchain/types.d.ts +15 -0
  175. package/node_modules/@sentry/core/build/types/tracing/langchain/utils.d.ts +1 -0
  176. package/node_modules/@sentry/core/build/types/tracing/langgraph/utils.d.ts +2 -0
  177. package/node_modules/@sentry/core/build/types/tracing/openai/types.d.ts +2 -0
  178. package/node_modules/@sentry/core/build/types/utils/eventUtils.d.ts +2 -1
  179. package/node_modules/@sentry/core/build/types-ts3.8/tracing/ai/utils.d.ts +3 -7
  180. package/node_modules/@sentry/core/build/types-ts3.8/tracing/anthropic-ai/types.d.ts +2 -2
  181. package/node_modules/@sentry/core/build/types-ts3.8/tracing/langchain/types.d.ts +15 -0
  182. package/node_modules/@sentry/core/build/types-ts3.8/tracing/langchain/utils.d.ts +1 -0
  183. package/node_modules/@sentry/core/build/types-ts3.8/tracing/langgraph/utils.d.ts +2 -0
  184. package/node_modules/@sentry/core/build/types-ts3.8/tracing/openai/types.d.ts +2 -0
  185. package/node_modules/@sentry/core/build/types-ts3.8/utils/eventUtils.d.ts +2 -1
  186. package/node_modules/@sentry/core/package.json +1 -1
  187. package/node_modules/@sentry/node/build/esm/package.json +1 -1
  188. package/node_modules/@sentry/node/package.json +5 -5
  189. package/node_modules/@sentry/node-core/build/cjs/integrations/anr/index.js +1 -1
  190. package/node_modules/@sentry/node-core/build/cjs/integrations/local-variables/local-variables-async.js +1 -1
  191. package/node_modules/@sentry/node-core/build/esm/integrations/anr/index.js +1 -1
  192. package/node_modules/@sentry/node-core/build/esm/integrations/anr/worker-script.js +2 -2
  193. package/node_modules/@sentry/node-core/build/esm/integrations/local-variables/local-variables-async.js +1 -1
  194. package/node_modules/@sentry/node-core/build/esm/integrations/local-variables/worker-script.js +1 -1
  195. package/node_modules/@sentry/node-core/build/esm/package.json +1 -1
  196. package/node_modules/@sentry/node-core/package.json +10 -4
  197. package/node_modules/@sentry/opentelemetry/build/esm/package.json +1 -1
  198. package/node_modules/@sentry/opentelemetry/package.json +5 -2
  199. package/node_modules/@sentry/server-utils/build/THIRD-PARTY-LICENSES.txt +55 -55
  200. package/node_modules/@sentry/server-utils/build/esm/package.json +1 -1
  201. package/node_modules/@sentry/server-utils/package.json +2 -2
  202. package/node_modules/@toon-format/toon/README.md +10 -41
  203. package/node_modules/@toon-format/toon/dist/index.d.mts +12 -36
  204. package/node_modules/@toon-format/toon/dist/index.mjs +134 -202
  205. package/node_modules/@toon-format/toon/package.json +2 -2
  206. package/node_modules/debug/package.json +3 -0
  207. package/node_modules/fdir/package.json +3 -0
  208. package/node_modules/has-flag/index.d.ts +39 -0
  209. package/node_modules/has-flag/index.js +8 -0
  210. package/node_modules/has-flag/license +9 -0
  211. package/node_modules/has-flag/package.json +46 -0
  212. package/node_modules/has-flag/readme.md +89 -0
  213. package/node_modules/supports-color/browser.js +5 -0
  214. package/node_modules/supports-color/index.js +135 -0
  215. package/node_modules/supports-color/license +9 -0
  216. package/node_modules/supports-color/package.json +53 -0
  217. package/node_modules/supports-color/readme.md +76 -0
  218. package/package.json +4 -4
  219. package/packages/pm-beads/package.json +1 -1
  220. package/packages/pm-calendar/package.json +1 -1
  221. package/packages/pm-command-kit/package.json +1 -1
  222. package/packages/pm-digital-twin/package.json +1 -1
  223. package/packages/pm-governance-audit/package.json +1 -1
  224. package/packages/pm-guide-shell/package.json +1 -1
  225. package/packages/pm-kanban/package.json +1 -1
  226. package/packages/pm-lifecycle-hooks/package.json +1 -1
  227. package/packages/pm-linked-test-adapters/package.json +1 -1
  228. package/packages/pm-search-advanced/package.json +1 -1
  229. package/packages/pm-templates/package.json +1 -1
  230. package/packages/pm-todos/package.json +1 -1
  231. package/packages/pm-vcs/package.json +1 -1
  232. package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
  233. package/plugins/pm-claude/package.json +1 -1
  234. package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
  235. package/plugins/pm-codex/package.json +1 -1
  236. package/plugins/pm-codex/plugin.json +1 -1
  237. package/runtime-dependencies.json +1 -1
  238. package/dist/cli-bundle/chunks/chunk-4PNPDT2N.js +0 -236
  239. package/dist/cli-bundle/chunks/chunk-HY7FQ7QX.js +0 -2
  240. package/dist/cli-bundle/chunks/chunk-J4LPQCET.js +0 -8
  241. package/dist/cli-bundle/chunks/chunk-OZCSXX4D.js +0 -80
  242. package/dist/cli-bundle/chunks/chunk-VMCSTFSO.js +0 -2
  243. package/dist/cli-bundle/chunks/chunk-X2ONXFBP.js +0 -3
  244. package/dist/cli-bundle/chunks/chunk-X53HM753.js +0 -20
  245. package/dist/cli-bundle/chunks/register-mutation-EQYMT32A.js +0 -20
  246. package/dist/cli-bundle/chunks/register-operations-VXBGE4XI.js +0 -2
  247. package/dist/cli-bundle/chunks/register-setup-UUDEGFRO.js +0 -2
  248. package/dist/cli-bundle/focused-chunks/chunk-5JEFDEL3.js +0 -166
  249. package/dist/cli-bundle/focused-chunks/chunk-AH6NX6GM.js +0 -3
  250. package/dist/cli-bundle/focused-chunks/chunk-JXR6UKFM.js +0 -3
  251. package/dist/cli-bundle/focused-chunks/chunk-MBQF6HT6.js +0 -2
  252. package/dist/cli-bundle/focused-chunks/chunk-NZO7GOXD.js +0 -20
  253. package/dist/cli-bundle/focused-chunks/chunk-PYC7BF6Z.js +0 -8
  254. package/dist/cli-bundle/focused-chunks/chunk-QOSVJDOO.js +0 -75
  255. package/dist/cli-bundle/focused-chunks/chunk-RHTH5ILM.js +0 -2
  256. package/dist/cli-bundle/focused-chunks/chunk-TRFVAWF2.js +0 -26
@@ -1,8 +1,8 @@
1
- (function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="98a35fdb-bff9-5556-9c1d-1c345cb39171")}catch{}})();import{assertInitializedTracker}from"../environment/tracker-preflight.js";import{isFileMissingError,readFileIfExists,writeFileAtomic}from"../../core/fs/fs-utils.js";import{access,readFile,realpath}from"node:fs/promises";import{constants}from"node:fs";import{execFile}from"node:child_process";import path from"node:path";import{promisify}from"node:util";import{pathExists}from"../../core/fs/fs-utils.js";import{resolveItemTypeRegistry}from"../../core/item/type-registry.js";import{acquireLock}from"../../core/lock/lock.js";import{resolvePmPackageRootFromModule}from"../../core/packages/root.js";import{EXIT_CODE}from"../../core/shared/constants.js";import{PmCliError}from"../../core/shared/errors.js";import{resolveAuthor}from"../../core/shared/author.js";import{nowIso}from"../../core/shared/time.js";import{resolvePmRoot}from"../../core/store/paths.js";import{readSettings,resolveGovernanceKnobs}from"../../core/store/settings.js";import{isPathOutsideRoot}from"../workspace.js";import{resolveSourceContextWritePolicy}from"../environment/source-context.js";import{gitWorkspaceEnvironment,resolveMergeDriverConfigScope,writeMergeDriverGitConfig}from"./worktree-config.js";import{discoverProjectRuntimeVersionPins}from"../environment/project-runtime-compatibility.js";const execFileAsync=promisify(execFile),PM_GITATTRIBUTES_START="# pm-cli:merge-drivers:start",PM_GITATTRIBUTES_END="# pm-cli:merge-drivers:end",PM_GITATTRIBUTES_V2_START="# pm-cli:merge-drivers:v2:start",PM_GITATTRIBUTES_V2_END="# pm-cli:merge-drivers:v2:end",MERGE_FENCE_LOCK_ID="merge-fence",MERGE_ATTRIBUTES_LOCK_ID="merge-attributes";function quoteMergeDriverArgument(value){return`'${value.replaceAll("'",`'"'"'`)}'`}async function resolveMatchingBunLauncher(candidate,version){const resolvedCandidate=await realpath(candidate);if(/(?:^|[\\/])bun-node-[^\\/]+[\\/]bun(?:\.exe)?$/iu.test(resolvedCandidate))return;const{stdout}=await execFileAsync(candidate,["--version"],{encoding:"utf8",windowsHide:!0,timeout:1e4});return stdout.trim()===version?resolvedCandidate:void 0}async function resolveMergeDriverCliCommand(){const packageRoot=resolvePmPackageRootFromModule(import.meta.url,["../../.."]);let executable=process.execPath,bunLauncherFound=!1;const runtimeIdentity=await realpath(process.execPath),bunVersion=process.versions.bun,pathCandidates=(process.env.PATH??"").split(path.delimiter).filter(directory=>path.isAbsolute(directory)).map(directory=>path.join(directory,bunVersion===void 0?path.basename(process.execPath):process.platform==="win32"?"bun.exe":"bun")),candidates=bunVersion===void 0?pathCandidates:[process.env.npm_execpath??"",...pathCandidates,runtimeIdentity].filter(candidate=>path.isAbsolute(candidate)&&/^(?:bun|bun\.exe)$/iu.test(path.basename(candidate)));for(const candidate of candidates)try{if(await access(candidate,constants.X_OK),bunVersion!==void 0){const launcher=await resolveMatchingBunLauncher(candidate,bunVersion);if(launcher!==void 0){executable=launcher,bunLauncherFound=!0;break}continue}if(candidate!==runtimeIdentity&&await realpath(candidate)===runtimeIdentity){executable=candidate;break}}catch{}if(bunVersion!==void 0&&!bunLauncherFound)throw new PmCliError("Cannot install Bun merge drivers without a durable executable matching the current Bun version.",EXIT_CODE.DEPENDENCY_FAILED,{code:"merge_bun_launcher_unavailable",why:"The current Bun process may be a temporary bunx launcher that Git cannot reuse after this command exits.",nextSteps:["Run with a durable version-matched Bun executable, expose one on an absolute PATH entry, or invoke pm through a Bun runner that publishes an absolute npm_execpath.","Retry pm merge install, then check pm health --strict-exit before merging branches."]});return`${quoteMergeDriverArgument(executable)} ${quoteMergeDriverArgument(path.join(packageRoot,"dist","cli.js"))}`}const MERGE_DRIVER_DEFINITIONS=[{key:"pm-item-toon",name:"pm field-aware TOON item document merge",artifact:"item",itemPath:"%P"},{key:"pm-item-markdown",name:"pm field-aware JSON-markdown item document merge",artifact:"item",itemPath:"%P"},{key:"pm-history",name:"pm append-only history stream merge",artifact:"history",itemPath:void 0},{key:"pm-relationship",name:"pm append-only relationship event store merge",artifact:"relationship",itemPath:void 0},{key:"pm-json",name:"pm key-level JSON config merge",artifact:"json",itemPath:void 0}];function resolveMergeInstallContext(options){const explicitPath=options.explicitPath?.trim(),sourceWorkspaceRoot=options.environment.PM_SOURCE_WORKSPACE_ROOT?.trim(),sourcePmRoot=options.environment.PM_SOURCE_PM_PATH?.trim(),sourcePolicy=resolveSourceContextWritePolicy(options.environment),sourceCoordinatesAvailable=!!(sourceWorkspaceRoot&&sourcePmRoot),sourceContextSelected=explicitPath===void 0&&sourceCoordinatesAvailable&&(options.dryRun||sourcePolicy.source_writes_allowed);if(explicitPath!==void 0){const pmRoot=resolvePmRoot(options.cwd,explicitPath);return{pm_root:pmRoot,workspace_start:pmRoot,source_coordinates_available:sourceCoordinatesAvailable,source_context_selected:!1,source_policy:sourcePolicy}}return sourceContextSelected?{pm_root:path.resolve(sourcePmRoot),workspace_start:path.resolve(sourceWorkspaceRoot),source_coordinates_available:!0,source_context_selected:!0,source_policy:sourcePolicy}:{pm_root:sourceCoordinatesAvailable?resolvePmRoot(options.cwd,".agents/pm"):resolvePmRoot(options.cwd),workspace_start:options.cwd,source_coordinates_available:sourceCoordinatesAvailable,source_context_selected:!1,source_policy:sourcePolicy}}async function findGitWorkspaceRoot(cwd){try{const{stdout}=await execFileAsync("git",["rev-parse","--show-toplevel"],{cwd,env:gitWorkspaceEnvironment(),encoding:"utf8",windowsHide:!0,timeout:1e4});return await realpath(stdout.trim())}catch{return null}}async function resolveGitWorkspaceRoot(cwd){const workspaceRoot=await findGitWorkspaceRoot(cwd);if(workspaceRoot===null)throw new PmCliError("pm merge install requires a git repository (merge drivers are git configuration).",EXIT_CODE.USAGE);return workspaceRoot}function toPosixRelative(fromPath,toPath){return path.relative(fromPath,toPath).replaceAll("\\","/")}function quoteGitAttributePattern(value){return`"${value.replaceAll("\\","\\\\").replaceAll('"','\\"')}"`}function readSingleQuotedArgument(command,offset){if(command[offset]!=="'")return null;let value="";for(let index=offset+1;index<command.length;index+=1){if(command.startsWith(`'"'"'`,index)){value+="'",index+=4;continue}if(command[index]==="'")return{value,nextOffset:index+1};value+=command[index]}return null}async function isMergeDriverWorkspaceCompatible(packageRoot,workspaceRoot,version){if(discoverProjectRuntimeVersionPins(workspaceRoot).filter(pin=>pin.constraint==="exact").some(pin=>pin.version!==version))return!1;const driverWorkspace=await findGitWorkspaceRoot(packageRoot);if(driverWorkspace!==null&&driverWorkspace!==await realpath(workspaceRoot)){const commonDirectories=await Promise.all([driverWorkspace,workspaceRoot].map(async cwd=>{const result=await execFileAsync("git",["rev-parse","--path-format=absolute","--git-common-dir"],{cwd,env:gitWorkspaceEnvironment(),timeout:1e4});return realpath(result.stdout.trim())}));if(commonDirectories[0]===commonDirectories[1])return!1}return!0}async function isPortableMergeDriverCommand(configured,expectedSuffix,workspaceRoot){const nodeArgument=readSingleQuotedArgument(configured,0);if(nodeArgument===null||configured[nodeArgument.nextOffset]!==" ")return!1;const cliArgument=readSingleQuotedArgument(configured,nodeArgument.nextOffset+1);if(cliArgument===null||configured.slice(cliArgument.nextOffset)!==expectedSuffix||!/^(?:node|bun)(?:\.exe)?$/iu.test(path.basename(nodeArgument.value))||path.basename(cliArgument.value)!=="cli.js"||path.basename(path.dirname(cliArgument.value))!=="dist"||!await pathExists(nodeArgument.value)||!await pathExists(cliArgument.value))return!1;try{const packageRoot=path.dirname(path.dirname(cliArgument.value)),manifest=JSON.parse(await readFile(path.join(packageRoot,"package.json"),"utf8")),pmBin=typeof manifest.bin=="string"?manifest.bin:manifest.bin?.pm;return await isMergeDriverWorkspaceCompatible(packageRoot,workspaceRoot,manifest.version)?manifest.name==="@unbrained/pm-cli"&&pmBin==="dist/cli.js":!1}catch{return!1}}function buildMergeAttributePatterns(trackerRelativeRoot,typeFolders){const prefix=trackerRelativeRoot.length>0?`${trackerRelativeRoot}/`:"",patterns=[`${quoteGitAttributePattern(`${prefix}**/*.toon`)} merge=pm-item-toon`,`${quoteGitAttributePattern(`${prefix}**/*.md`)} merge=pm-item-markdown`];for(const folder of typeFolders)patterns.push(`${quoteGitAttributePattern(`${prefix}${folder}/*.toon`)} merge=pm-item-toon`),patterns.push(`${quoteGitAttributePattern(`${prefix}${folder}/*.md`)} merge=pm-item-markdown`);return patterns.push(`${quoteGitAttributePattern(`${prefix}**/*.jsonl`)} merge=pm-relationship`),patterns.push(`${quoteGitAttributePattern(`${prefix}history/*.jsonl`)} merge=pm-history`),patterns.push(`${quoteGitAttributePattern(`${prefix}settings.json`)} merge=pm-json`),patterns.push(`${quoteGitAttributePattern(`${prefix}**/*.json`)} merge=pm-json`),patterns.push(`${quoteGitAttributePattern(`${prefix}extensions/**`)} merge=text`),patterns.push(`${quoteGitAttributePattern(`${prefix}extensions/.managed-extensions.json`)} merge=pm-json`),patterns}function managedMergeBlocks(content){const blocks=[];let cursor=0;for(;cursor<content.length;){const legacyStart=content.indexOf(PM_GITATTRIBUTES_START,cursor),versionedStart=content.indexOf(PM_GITATTRIBUTES_V2_START,cursor),start=legacyStart===-1?versionedStart:versionedStart===-1?legacyStart:Math.min(legacyStart,versionedStart);if(start===-1)break;const versioned=start===versionedStart,startMarker=versioned?PM_GITATTRIBUTES_V2_START:PM_GITATTRIBUTES_START,endMarker=versioned?PM_GITATTRIBUTES_V2_END:PM_GITATTRIBUTES_END,closing=content.indexOf(endMarker,start+startMarker.length),end=closing===-1?content.length:closing+endMarker.length;blocks.push({start,end,closed:closing!==-1,lines:content.slice(start+startMarker.length,closing===-1?end:closing).split(`
1
+ (function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="da37bda1-1520-5db0-9dad-bb1acc556de8")}catch{}})();import{assertInitializedTracker}from"../environment/tracker-preflight.js";import{isFileMissingError,readFileIfExists,writeFileAtomic}from"../../core/fs/fs-utils.js";import{access,readFile,realpath}from"node:fs/promises";import{constants}from"node:fs";import{execFile}from"node:child_process";import path from"node:path";import{promisify}from"node:util";import{pathExists}from"../../core/fs/fs-utils.js";import{resolveItemTypeRegistry}from"../../core/item/type-registry.js";import{acquireLock}from"../../core/lock/lock.js";import{resolvePmPackageRootFromModule}from"../../core/packages/root.js";import{EXIT_CODE}from"../../core/shared/constants.js";import{PmCliError}from"../../core/shared/errors.js";import{resolveAuthor}from"../../core/shared/author.js";import{nowIso}from"../../core/shared/time.js";import{resolvePmRoot}from"../../core/store/paths.js";import{readSettings,resolveGovernanceKnobs}from"../../core/store/settings.js";import{isPathOutsideRoot}from"../workspace.js";import{resolveSourceContextWritePolicy}from"../environment/source-context.js";import{gitWorkspaceEnvironment,resolveMergeDriverConfigScope,writeMergeDriverGitConfig}from"./worktree-config.js";import{discoverProjectRuntimeVersionPins}from"../environment/project-runtime-compatibility.js";const execFileAsync=promisify(execFile),PM_GITATTRIBUTES_START="# pm-cli:merge-drivers:start",PM_GITATTRIBUTES_END="# pm-cli:merge-drivers:end",PM_GITATTRIBUTES_V2_START="# pm-cli:merge-drivers:v2:start",PM_GITATTRIBUTES_V2_END="# pm-cli:merge-drivers:v2:end",MERGE_FENCE_LOCK_ID="merge-fence",MERGE_ATTRIBUTES_LOCK_ID="merge-attributes";function quoteMergeDriverArgument(value){return`'${value.replaceAll("'",`'"'"'`)}'`}async function resolveMatchingBunLauncher(candidate,version){const resolvedCandidate=await realpath(candidate);if(/(?:^|[\\/])bun-node-[^\\/]+[\\/]bun(?:\.exe)?$/iu.test(resolvedCandidate))return;const{stdout}=await execFileAsync(candidate,["--version"],{encoding:"utf8",windowsHide:!0,timeout:1e4});return stdout.trim()===version?resolvedCandidate:void 0}async function resolveMergeDriverCliCommand(){const packageRoot=resolvePmPackageRootFromModule(import.meta.url,["../../.."]);let executable=process.execPath,bunLauncherFound=!1;const runtimeIdentity=await realpath(process.execPath),bunVersion=process.versions.bun,pathCandidates=(process.env.PATH??"").split(path.delimiter).filter(directory=>path.isAbsolute(directory)).map(directory=>path.join(directory,bunVersion===void 0?path.basename(process.execPath):process.platform==="win32"?"bun.exe":"bun")),candidates=bunVersion===void 0?pathCandidates:[process.env.npm_execpath??"",...pathCandidates,runtimeIdentity].filter(candidate=>path.isAbsolute(candidate)&&/^(?:bun|bun\.exe)$/iu.test(path.basename(candidate)));for(const candidate of candidates)try{if(await access(candidate,constants.X_OK),bunVersion!==void 0){const launcher=await resolveMatchingBunLauncher(candidate,bunVersion);if(launcher!==void 0){executable=launcher,bunLauncherFound=!0;break}continue}if(candidate!==runtimeIdentity&&await realpath(candidate)===runtimeIdentity){executable=candidate;break}}catch{}if(bunVersion!==void 0&&!bunLauncherFound)throw new PmCliError("Cannot install Bun merge drivers without a durable executable matching the current Bun version.",EXIT_CODE.DEPENDENCY_FAILED,{code:"merge_bun_launcher_unavailable",why:"The current Bun process may be a temporary bunx launcher that Git cannot reuse after this command exits.",nextSteps:["Run with a durable version-matched Bun executable, expose one on an absolute PATH entry, or invoke pm through a Bun runner that publishes an absolute npm_execpath.","Retry pm merge install, then check pm health --strict-exit before merging branches."]});return`${quoteMergeDriverArgument(executable)} ${quoteMergeDriverArgument(path.join(packageRoot,"dist","cli.js"))}`}const MERGE_DRIVER_DEFINITIONS=[{key:"pm-item-toon",name:"pm field-aware TOON item document merge",artifact:"item",itemPath:"%P"},{key:"pm-item-markdown",name:"pm field-aware JSON-markdown item document merge",artifact:"item",itemPath:"%P"},{key:"pm-history",name:"pm append-only history stream merge",artifact:"history",itemPath:void 0},{key:"pm-relationship",name:"pm append-only relationship event store merge",artifact:"relationship",itemPath:void 0},{key:"pm-json",name:"pm key-level JSON config merge",artifact:"json",itemPath:void 0}];function resolveMergeInstallContext(options){const explicitPath=options.explicitPath?.trim(),sourceWorkspaceRoot=options.environment.PM_SOURCE_WORKSPACE_ROOT?.trim(),sourcePmRoot=options.environment.PM_SOURCE_PM_PATH?.trim(),sourcePolicy=resolveSourceContextWritePolicy(options.environment),sourceCoordinatesAvailable=!!(sourceWorkspaceRoot&&sourcePmRoot),sourceContextSelected=explicitPath===void 0&&sourceCoordinatesAvailable&&(options.dryRun||sourcePolicy.source_writes_allowed);if(explicitPath!==void 0){const pmRoot=resolvePmRoot(options.cwd,explicitPath);return{pm_root:pmRoot,workspace_start:pmRoot,source_coordinates_available:sourceCoordinatesAvailable,source_context_selected:!1,source_policy:sourcePolicy}}return sourceContextSelected?{pm_root:path.resolve(sourcePmRoot),workspace_start:path.resolve(sourceWorkspaceRoot),source_coordinates_available:!0,source_context_selected:!0,source_policy:sourcePolicy}:{pm_root:sourceCoordinatesAvailable?resolvePmRoot(options.cwd,".agents/pm"):resolvePmRoot(options.cwd),workspace_start:options.cwd,source_coordinates_available:sourceCoordinatesAvailable,source_context_selected:!1,source_policy:sourcePolicy}}async function findGitWorkspaceRoot(cwd){try{const{stdout}=await execFileAsync("git",["rev-parse","--show-toplevel"],{cwd,env:gitWorkspaceEnvironment(),encoding:"utf8",windowsHide:!0,timeout:1e4});return await realpath(stdout.trim())}catch{return null}}async function resolveGitWorkspaceRoot(cwd){const workspaceRoot=await findGitWorkspaceRoot(cwd);if(workspaceRoot===null)throw new PmCliError("pm merge install requires a git repository (merge drivers are git configuration).",EXIT_CODE.USAGE);return workspaceRoot}function toPosixRelative(fromPath,toPath){return path.relative(fromPath,toPath).replaceAll("\\","/")}function quoteGitAttributePattern(value){return`"${value.replaceAll("\\","\\\\").replaceAll('"','\\"')}"`}function readSingleQuotedArgument(command,offset){if(command[offset]!=="'")return null;let value="";for(let index=offset+1;index<command.length;index+=1){if(command.startsWith(`'"'"'`,index)){value+="'",index+=4;continue}if(command[index]==="'")return{value,nextOffset:index+1};value+=command[index]}return null}async function isMergeDriverWorkspaceCompatible(packageRoot,workspaceRoot,version){if(discoverProjectRuntimeVersionPins(workspaceRoot).filter(pin=>pin.constraint==="exact").some(pin=>pin.version!==version))return!1;const driverWorkspace=await findGitWorkspaceRoot(packageRoot);if(driverWorkspace!==null&&driverWorkspace!==await realpath(workspaceRoot)){const commonDirectories=await Promise.all([driverWorkspace,workspaceRoot].map(async cwd=>{const result=await execFileAsync("git",["rev-parse","--path-format=absolute","--git-common-dir"],{cwd,env:gitWorkspaceEnvironment(),timeout:1e4});return realpath(result.stdout.trim())}));if(commonDirectories[0]===commonDirectories[1])return!1}return!0}async function isPortableMergeDriverCommand(configured,expectedSuffix,workspaceRoot){const nodeArgument=readSingleQuotedArgument(configured,0);if(nodeArgument===null||configured[nodeArgument.nextOffset]!==" ")return!1;const cliArgument=readSingleQuotedArgument(configured,nodeArgument.nextOffset+1);if(cliArgument===null||configured.slice(cliArgument.nextOffset)!==expectedSuffix||!/^(?:node|bun)(?:\.exe)?$/iu.test(path.basename(nodeArgument.value))||path.basename(cliArgument.value)!=="cli.js"||path.basename(path.dirname(cliArgument.value))!=="dist"||!await pathExists(nodeArgument.value)||!await pathExists(cliArgument.value))return!1;try{const packageRoot=path.dirname(path.dirname(cliArgument.value)),manifest=JSON.parse(await readFile(path.join(packageRoot,"package.json"),"utf8")),pmBin=typeof manifest.bin=="string"?manifest.bin:manifest.bin?.pm;return await isMergeDriverWorkspaceCompatible(packageRoot,workspaceRoot,manifest.version)?manifest.name==="@unbrained/pm-cli"&&pmBin==="dist/cli.js":!1}catch{return!1}}function buildMergeAttributePatterns(trackerRelativeRoot,typeFolders){const prefix=trackerRelativeRoot.length>0?`${trackerRelativeRoot}/`:"",patterns=[`${quoteGitAttributePattern(`${prefix}**/*.toon`)} merge=pm-item-toon`,`${quoteGitAttributePattern(`${prefix}**/*.md`)} merge=pm-item-markdown`];for(const folder of typeFolders)patterns.push(`${quoteGitAttributePattern(`${prefix}${folder}/*.toon`)} merge=pm-item-toon`),patterns.push(`${quoteGitAttributePattern(`${prefix}${folder}/*.md`)} merge=pm-item-markdown`);return patterns.push(`${quoteGitAttributePattern(`${prefix}**/*.jsonl`)} merge=pm-relationship`),patterns.push(`${quoteGitAttributePattern(`${prefix}history/*.jsonl`)} merge=pm-history`),patterns.push(`${quoteGitAttributePattern(`${prefix}settings.json`)} merge=pm-json`),patterns.push(`${quoteGitAttributePattern(`${prefix}**/*.json`)} merge=pm-json`),patterns.push(`${quoteGitAttributePattern(`${prefix}extensions/**`)} merge=text`),patterns.push(`${quoteGitAttributePattern(`${prefix}extensions/.managed-extensions.json`)} merge=pm-json`),patterns}function managedMergeBlocks(content){const blocks=[];let cursor=0;for(;cursor<content.length;){const legacyStart=content.indexOf(PM_GITATTRIBUTES_START,cursor),versionedStart=content.indexOf(PM_GITATTRIBUTES_V2_START,cursor),start=legacyStart===-1?versionedStart:versionedStart===-1?legacyStart:Math.min(legacyStart,versionedStart);if(start===-1)break;const versioned=start===versionedStart,startMarker=versioned?PM_GITATTRIBUTES_V2_START:PM_GITATTRIBUTES_START,endMarker=versioned?PM_GITATTRIBUTES_V2_END:PM_GITATTRIBUTES_END,closing=content.indexOf(endMarker,start+startMarker.length),end=closing===-1?content.length:closing+endMarker.length;blocks.push({start,end,closed:closing!==-1,lines:content.slice(start+startMarker.length,closing===-1?end:closing).split(`
2
2
  `).map(line=>line.trim()).filter(Boolean)}),cursor=end}return blocks}function blockBelongsToTracker(lines,patterns,trackerRelativeRoot){const ownedPatterns=new Set(patterns),prefix=quoteGitAttributePattern(trackerRelativeRoot.length>0?`${trackerRelativeRoot}/`:"").slice(0,-1);return lines.some(line=>ownedPatterns.has(line)||line.startsWith(prefix)&&/^[^/]+\/\*\.(?:toon|md)" merge=pm-item-(?:toon|markdown)$/u.test(line.slice(prefix.length)))}async function reconcileGitattributesBlock(workspaceRoot,trackerRelativeRoot,patterns,dryRun){const gitattributesPath=path.join(workspaceRoot,".gitattributes"),gitDirectory=dryRun?null:(await execFileAsync("git",["rev-parse","--absolute-git-dir"],{cwd:workspaceRoot,env:gitWorkspaceEnvironment(),encoding:"utf8",windowsHide:!0,timeout:1e4})).stdout.trim(),releaseLock=gitDirectory===null?null:await acquireLock(gitDirectory,MERGE_ATTRIBUTES_LOCK_ID,120,"merge-fence",!1,!0,5e3);try{const current=await readFileIfExists(gitattributesPath)??"",block=[PM_GITATTRIBUTES_V2_START,...patterns,PM_GITATTRIBUTES_V2_END].join(`
3
3
  `),blocks=managedMergeBlocks(current),owned=blocks.filter(candidate=>blockBelongsToTracker(candidate.lines,patterns,trackerRelativeRoot));owned.length===0&&trackerRelativeRoot===".agents/pm"&&blocks.length===1&&blocks[0].lines.every(line=>!line.includes(".agents/pm/"))&&owned.push(blocks[0]);let next;if(owned.length>0){const first=owned[0];next=current;for(const duplicate of owned.slice(1).reverse())next=`${next.slice(0,duplicate.start)}${next.slice(duplicate.end)}`;next=`${next.slice(0,first.start)}${block}${next.slice(first.end)}`}else{const prefix=current.trimEnd();next=`${prefix.length>0?`${prefix}
4
4
 
5
5
  `:""}${block}
6
- `}return next===current?{path:gitattributesPath,changed:!1}:(dryRun||await writeFileAtomic(gitattributesPath,next),{path:gitattributesPath,changed:!0})}finally{await releaseLock?.()}}function resolveProjectMergeTypeFolders(settings){const registry=resolveItemTypeRegistry(settings,null);return[...new Set(Object.values(registry.type_to_folder))].sort((left,right)=>left.localeCompare(right))}async function auditMergeAttributeFence(pmRoot,typeFolders){const resolvedRoot=path.resolve(pmRoot);let fenceDirectory=null,fenceContent="",directory=resolvedRoot;for(let depth=0;depth<8;depth+=1){const candidate=path.join(directory,".gitattributes");try{const raw=await readFile(candidate,"utf8");if(raw.includes(PM_GITATTRIBUTES_V2_START)||raw.includes(PM_GITATTRIBUTES_START)){fenceDirectory=directory,fenceContent=raw;break}}catch{}const parent=path.dirname(directory);if(parent===directory)break;directory=parent}if(fenceDirectory===null)return{status:"not_installed",missing_patterns:[],stale_patterns:[],schema_scope:"project"};const fencePath=path.join(fenceDirectory,".gitattributes"),relativeRoot=toPosixRelative(fenceDirectory,resolvedRoot),blocks=managedMergeBlocks(fenceContent),expected=buildMergeAttributePatterns(relativeRoot,typeFolders),owned=blocks.filter(block=>blockBelongsToTracker(block.lines,expected,relativeRoot)),committed=owned.flatMap(block=>block.closed?block.lines:[]),committedSet=new Set(committed),expectedSet=new Set(expected),missing=expected.filter(line=>!committedSet.has(line)),stale=committed.filter(line=>!expectedSet.has(line));return{status:owned.length!==1||!owned[0].closed||missing.length>0||stale.length>0?"drift":"ok",path:fencePath,missing_patterns:missing,stale_patterns:stale,schema_scope:"project"}}async function auditMergeDriverConfiguration(workspaceRoot){const missingKeys=[],driftedKeys=[];for(const definition of MERGE_DRIVER_DEFINITIONS){const key=`merge.${definition.key}.driver`,expectedSuffix=` merge driver ${definition.artifact} "%O" "%A" "%B"${definition.itemPath===void 0?"":` --item-path ${definition.itemPath}`}`;try{const{stdout}=await execFileAsync("git",["config","--get",key],{cwd:workspaceRoot,env:gitWorkspaceEnvironment(),encoding:"utf8",windowsHide:!0,timeout:1e4}),configured=stdout.trim();await isPortableMergeDriverCommand(configured,expectedSuffix,workspaceRoot)||driftedKeys.push(key)}catch{missingKeys.push(key)}}return{status:missingKeys.length>0?"missing":driftedKeys.length>0?"drift":"ok",missing_keys:missingKeys,drifted_keys:driftedKeys}}async function refreshMergeAttributeFenceIfInstalled(pmRoot){let workspaceRoot;try{workspaceRoot=await resolveGitWorkspaceRoot(path.resolve(pmRoot))}catch{return{status:"no_git"}}const trackerRelativeRoot=toPosixRelative(workspaceRoot,path.resolve(pmRoot)),lockSettings=await readSettings(pmRoot),owner=resolveAuthor(void 0,lockSettings.author_default),releaseLock=await acquireLock(pmRoot,MERGE_FENCE_LOCK_ID,lockSettings.locks.ttl_seconds,owner,!1,resolveGovernanceKnobs(lockSettings).force_required_for_stale_lock,lockSettings.locks.wait_ms);try{const gitattributesPath=path.join(workspaceRoot,".gitattributes");let current;try{current=await readFile(gitattributesPath,"utf8")}catch(error){if(error instanceof Error&&"code"in error&&isFileMissingError(error))return{status:"not_installed",path:gitattributesPath};throw error}if(!current.includes(PM_GITATTRIBUTES_V2_START)&&!current.includes(PM_GITATTRIBUTES_START))return{status:"not_installed",path:gitattributesPath};const settings=await readSettings(pmRoot),typeFolders=resolveProjectMergeTypeFolders(settings),outcome=await reconcileGitattributesBlock(workspaceRoot,trackerRelativeRoot,buildMergeAttributePatterns(trackerRelativeRoot,typeFolders),!1);return{status:outcome.changed?"refreshed":"unchanged",path:outcome.path}}finally{await releaseLock()}}async function runMergeInstall(options,global2){const context=resolveMergeInstallContext({cwd:process.cwd(),explicitPath:global2.path,dryRun:options.dryRun===!0,environment:process.env}),pmRoot=context.pm_root;await assertInitializedTracker(pmRoot);const workspaceRoot=await resolveGitWorkspaceRoot(context.workspace_start);return installMergeFence({pmRoot,workspaceRoot,dryRun:options.dryRun===!0})}async function installMergeDriverConfiguration(workspaceRoot,entries){const commonDirectory=(await execFileAsync("git",["rev-parse","--path-format=absolute","--git-common-dir"],{cwd:workspaceRoot,env:gitWorkspaceEnvironment(),timeout:1e4})).stdout.trim(),release=await acquireLock(commonDirectory,"merge-driver-config",120,"merge-install",!1,!0,5e3);try{const scope=await resolveMergeDriverConfigScope(workspaceRoot);let stdout;try{stdout=(await execFileAsync("git",["config",scope,"--null","--get-regexp","."],{cwd:workspaceRoot,env:gitWorkspaceEnvironment(),timeout:1e4})).stdout}catch(error){if(typeof error!="object"||error===null||!("code"in error)||error.code!==1)throw error;stdout=""}const current=new Map(stdout.split("\0").filter(Boolean).map(entry=>{const separator=entry.indexOf(`
7
- `);return[entry.slice(0,separator),entry.slice(separator+1)]}));for(const entry of entries)current.get(entry.key)!==entry.value&&await writeMergeDriverGitConfig(workspaceRoot,[scope,entry.key,entry.value])}finally{await release()}}async function installMergeFence(options){const pmRoot=path.resolve(options.pmRoot),workspaceRoot=path.resolve(options.workspaceRoot),dryRun=options.dryRun===!0;let canonicalPmRoot,canonicalWorkspaceRoot;try{[canonicalPmRoot,canonicalWorkspaceRoot]=await Promise.all([realpath(pmRoot),realpath(workspaceRoot)])}catch(error){throw new PmCliError(`Cannot resolve tracker root ${pmRoot} or workspace root ${workspaceRoot}: ${String(error)}`,EXIT_CODE.NOT_FOUND,{code:"merge_root_not_found",nextSteps:["Initialize the tracker and ensure both paths are accessible.",'Retry "pm merge install" from the repository workspace.']})}const trackerRelativeRoot=toPosixRelative(canonicalWorkspaceRoot,canonicalPmRoot);if(isPathOutsideRoot(trackerRelativeRoot))throw new PmCliError(`Tracker root ${pmRoot} is outside the git repository ${workspaceRoot}; there is nothing to configure for this repository.`,EXIT_CODE.USAGE);const settings=await readSettings(canonicalPmRoot),typeFolders=resolveProjectMergeTypeFolders(settings),patterns=buildMergeAttributePatterns(trackerRelativeRoot,typeFolders),gitConfigEntries=[],cliCommand=await resolveMergeDriverCliCommand();for(const definition of MERGE_DRIVER_DEFINITIONS)gitConfigEntries.push({key:`merge.${definition.key}.name`,value:definition.name}),gitConfigEntries.push({key:`merge.${definition.key}.driver`,value:`${cliCommand} merge driver ${definition.artifact} "%O" "%A" "%B"${definition.itemPath===void 0?"":` --item-path ${definition.itemPath}`}`});if(!dryRun)try{await installMergeDriverConfiguration(canonicalWorkspaceRoot,gitConfigEntries)}catch{throw new PmCliError(`Cannot install repository-local merge drivers in ${canonicalWorkspaceRoot}. Ensure the repository Git config is writable and no other Git process holds its lock, then retry.`,EXIT_CODE.DEPENDENCY_FAILED,{code:"merge_git_config_unwritable",why:"pm merge install must persist clone-local driver commands before publishing their shared .gitattributes mappings.",nextSteps:["Make the repository Git config writable or wait for the process holding its lock to finish.",'Retry "pm merge install"; the operation is idempotent.','Use "pm merge install --dry-run --json" when the workspace is intentionally read-only.']})}const gitattributes=await reconcileGitattributesBlock(canonicalWorkspaceRoot,trackerRelativeRoot,patterns,dryRun);return{ok:!0,dry_run:dryRun,workspace_root:canonicalWorkspaceRoot,pm_root:canonicalPmRoot,gitattributes:{path:gitattributes.path,changed:gitattributes.changed,patterns,schema_scope:"project"},git_config:gitConfigEntries,guidance:["Commit .gitattributes so every branch and collaborator shares the merge mapping.",'git config is per-clone: each collaborator (and each fresh worktree/clone) runs "pm merge install" once.','After merging branches, run "pm merge reconcile --dry-run" to inspect the complete receipt and history repair, then apply "pm merge reconcile"; use --force only after explicitly accepting any discarded values.',"Custom item types registered later refresh this fence automatically (pm schema add-type/remove-type); rerun pm merge install only after out-of-band type-folder edits."],generated_at:nowIso()}}export{PM_GITATTRIBUTES_END,PM_GITATTRIBUTES_START,PM_GITATTRIBUTES_V2_END,PM_GITATTRIBUTES_V2_START,auditMergeAttributeFence,auditMergeDriverConfiguration,buildMergeAttributePatterns,findGitWorkspaceRoot,installMergeFence,refreshMergeAttributeFenceIfInstalled,resolveMergeInstallContext,resolveProjectMergeTypeFolders,runMergeInstall};
6
+ `}return next===current?{path:gitattributesPath,changed:!1}:(dryRun||await writeFileAtomic(gitattributesPath,next),{path:gitattributesPath,changed:!0})}finally{await releaseLock?.()}}function resolveProjectMergeTypeFolders(settings){const registry=resolveItemTypeRegistry(settings,null);return[...new Set(Object.values(registry.type_to_folder))].sort((left,right)=>left.localeCompare(right))}async function auditMergeAttributeFence(pmRoot,typeFolders){const resolvedRoot=path.resolve(pmRoot);let fenceDirectory=null,fenceContent="",directory=resolvedRoot;for(let depth=0;depth<8;depth+=1){const candidate=path.join(directory,".gitattributes");try{const raw=await readFile(candidate,"utf8");if(raw.includes(PM_GITATTRIBUTES_V2_START)||raw.includes(PM_GITATTRIBUTES_START)){fenceDirectory=directory,fenceContent=raw;break}}catch{}const parent=path.dirname(directory);if(parent===directory)break;directory=parent}if(fenceDirectory===null)return{status:"not_installed",missing_patterns:[],stale_patterns:[],schema_scope:"project"};const fencePath=path.join(fenceDirectory,".gitattributes"),relativeRoot=toPosixRelative(fenceDirectory,resolvedRoot),blocks=managedMergeBlocks(fenceContent),expected=buildMergeAttributePatterns(relativeRoot,typeFolders),owned=blocks.filter(block=>blockBelongsToTracker(block.lines,expected,relativeRoot)),committed=owned.flatMap(block=>block.closed?block.lines:[]),committedSet=new Set(committed),expectedSet=new Set(expected),missing=expected.filter(line=>!committedSet.has(line)),stale=committed.filter(line=>!expectedSet.has(line));return{status:owned.length!==1||!owned[0].closed||missing.length>0||stale.length>0?"drift":"ok",path:fencePath,missing_patterns:missing,stale_patterns:stale,schema_scope:"project"}}async function auditMergeDriverConfiguration(workspaceRoot){const missingKeys=[],driftedKeys=[];for(const definition of MERGE_DRIVER_DEFINITIONS){const key=`merge.${definition.key}.driver`,expectedSuffix=` merge driver ${definition.artifact} "%O" "%A" "%B"${definition.itemPath===void 0?"":` --item-path ${definition.itemPath}`}`;try{const{stdout}=await execFileAsync("git",["config","--get",key],{cwd:workspaceRoot,env:gitWorkspaceEnvironment(),encoding:"utf8",windowsHide:!0,timeout:1e4}),configured=stdout.trim();await isPortableMergeDriverCommand(configured,expectedSuffix,workspaceRoot)||driftedKeys.push(key)}catch{missingKeys.push(key)}}return{status:missingKeys.length>0?"missing":driftedKeys.length>0?"drift":"ok",missing_keys:missingKeys,drifted_keys:driftedKeys}}async function refreshMergeAttributeFenceIfInstalled(pmRoot){let workspaceRoot;try{workspaceRoot=await resolveGitWorkspaceRoot(path.resolve(pmRoot))}catch{return{status:"no_git"}}const trackerRelativeRoot=toPosixRelative(workspaceRoot,path.resolve(pmRoot)),lockSettings=await readSettings(pmRoot),owner=resolveAuthor(void 0,lockSettings.author_default),releaseLock=await acquireLock(pmRoot,MERGE_FENCE_LOCK_ID,lockSettings.locks.ttl_seconds,owner,!1,resolveGovernanceKnobs(lockSettings).force_required_for_stale_lock,lockSettings.locks.wait_ms);try{const gitattributesPath=path.join(workspaceRoot,".gitattributes");let current;try{current=await readFile(gitattributesPath,"utf8")}catch(error){if(error instanceof Error&&"code"in error&&isFileMissingError(error))return{status:"not_installed",path:gitattributesPath};throw error}if(!current.includes(PM_GITATTRIBUTES_V2_START)&&!current.includes(PM_GITATTRIBUTES_START))return{status:"not_installed",path:gitattributesPath};const settings=await readSettings(pmRoot),typeFolders=resolveProjectMergeTypeFolders(settings),outcome=await reconcileGitattributesBlock(workspaceRoot,trackerRelativeRoot,buildMergeAttributePatterns(trackerRelativeRoot,typeFolders),!1);return{status:outcome.changed?"refreshed":"unchanged",path:outcome.path}}finally{await releaseLock()}}async function runMergeInstall(options,global2){const context=resolveMergeInstallContext({cwd:process.cwd(),explicitPath:global2.path,dryRun:options.dryRun===!0,environment:process.env}),pmRoot=context.pm_root;await assertInitializedTracker(pmRoot);const workspaceRoot=await resolveGitWorkspaceRoot(context.workspace_start);return installMergeFence({pmRoot,workspaceRoot,dryRun:options.dryRun===!0,preCommitHook:options.preCommitHook===!0})}async function installMergeDriverConfiguration(workspaceRoot,entries){const commonDirectory=(await execFileAsync("git",["rev-parse","--path-format=absolute","--git-common-dir"],{cwd:workspaceRoot,env:gitWorkspaceEnvironment(),timeout:1e4})).stdout.trim(),release=await acquireLock(commonDirectory,"merge-driver-config",120,"merge-install",!1,!0,5e3);try{const scope=await resolveMergeDriverConfigScope(workspaceRoot);let stdout;try{stdout=(await execFileAsync("git",["config",scope,"--null","--get-regexp","."],{cwd:workspaceRoot,env:gitWorkspaceEnvironment(),timeout:1e4})).stdout}catch(error){if(typeof error!="object"||error===null||!("code"in error)||error.code!==1)throw error;stdout=""}const current=new Map(stdout.split("\0").filter(Boolean).map(entry=>{const separator=entry.indexOf(`
7
+ `);return[entry.slice(0,separator),entry.slice(separator+1)]}));for(const entry of entries)current.get(entry.key)!==entry.value&&await writeMergeDriverGitConfig(workspaceRoot,[scope,entry.key,entry.value])}finally{await release()}}async function installMergeFence(options){const pmRoot=path.resolve(options.pmRoot),workspaceRoot=path.resolve(options.workspaceRoot),dryRun=options.dryRun===!0;let canonicalPmRoot,canonicalWorkspaceRoot;try{[canonicalPmRoot,canonicalWorkspaceRoot]=await Promise.all([realpath(pmRoot),realpath(workspaceRoot)])}catch(error){throw new PmCliError(`Cannot resolve tracker root ${pmRoot} or workspace root ${workspaceRoot}: ${String(error)}`,EXIT_CODE.NOT_FOUND,{code:"merge_root_not_found",nextSteps:["Initialize the tracker and ensure both paths are accessible.",'Retry "pm merge install" from the repository workspace.']})}const trackerRelativeRoot=toPosixRelative(canonicalWorkspaceRoot,canonicalPmRoot);if(isPathOutsideRoot(trackerRelativeRoot))throw new PmCliError(`Tracker root ${pmRoot} is outside the git repository ${workspaceRoot}; there is nothing to configure for this repository.`,EXIT_CODE.USAGE);const settings=await readSettings(canonicalPmRoot),typeFolders=resolveProjectMergeTypeFolders(settings),patterns=buildMergeAttributePatterns(trackerRelativeRoot,typeFolders),gitConfigEntries=[],cliCommand=await resolveMergeDriverCliCommand(),settlementHook=options.preCommitHook===!0?await import("./settlement-hook.js").then(async hooks=>({plan:await hooks.prepareMergeSettlementHook(canonicalWorkspaceRoot,`${cliCommand} --pm-path ${quoteMergeDriverArgument(trackerRelativeRoot||".")}`),write:hooks.writeMergeSettlementHook})):void 0;for(const definition of MERGE_DRIVER_DEFINITIONS)gitConfigEntries.push({key:`merge.${definition.key}.name`,value:definition.name}),gitConfigEntries.push({key:`merge.${definition.key}.driver`,value:`${cliCommand} merge driver ${definition.artifact} "%O" "%A" "%B"${definition.itemPath===void 0?"":` --item-path ${definition.itemPath}`}`});if(!dryRun)try{await installMergeDriverConfiguration(canonicalWorkspaceRoot,gitConfigEntries)}catch{throw new PmCliError(`Cannot install repository-local merge drivers in ${canonicalWorkspaceRoot}. Ensure the repository Git config is writable and no other Git process holds its lock, then retry.`,EXIT_CODE.DEPENDENCY_FAILED,{code:"merge_git_config_unwritable",why:"pm merge install must persist clone-local driver commands before publishing their shared .gitattributes mappings.",nextSteps:["Make the repository Git config writable or wait for the process holding its lock to finish.",'Retry "pm merge install"; the operation is idempotent.','Use "pm merge install --dry-run --json" when the workspace is intentionally read-only.']})}const gitattributes=await reconcileGitattributesBlock(canonicalWorkspaceRoot,trackerRelativeRoot,patterns,dryRun);return settlementHook!==void 0&&!dryRun&&await settlementHook.write(settlementHook.plan),{ok:!0,dry_run:dryRun,workspace_root:canonicalWorkspaceRoot,pm_root:canonicalPmRoot,gitattributes:{path:gitattributes.path,changed:gitattributes.changed,patterns,schema_scope:"project"},git_config:gitConfigEntries,...settlementHook===void 0?{}:{settlement_hook:{path:settlementHook.plan.path,changed:settlementHook.plan.changed}},guidance:["Commit .gitattributes so every branch and collaborator shares the merge mapping.",'git config is per-clone: each collaborator (and each fresh worktree/clone) runs "pm merge install" once.','After merging branches, run "pm merge reconcile --dry-run" to inspect the complete receipt and history repair, then apply "pm merge reconcile"; use --force only after explicitly accepting any discarded values.',"Custom item types registered later refresh this fence automatically (pm schema add-type/remove-type); rerun pm merge install only after out-of-band type-folder edits."],generated_at:nowIso()}}export{PM_GITATTRIBUTES_END,PM_GITATTRIBUTES_START,PM_GITATTRIBUTES_V2_END,PM_GITATTRIBUTES_V2_START,auditMergeAttributeFence,auditMergeDriverConfiguration,buildMergeAttributePatterns,findGitWorkspaceRoot,installMergeFence,refreshMergeAttributeFenceIfInstalled,resolveMergeInstallContext,resolveProjectMergeTypeFolders,runMergeInstall};
8
8
  //# sourceMappingURL=install.js.map
@@ -109,6 +109,10 @@ generated_at:string;
109
109
  export interface MergeReceiptEvidenceScan {
110
110
  /** Valid receipts that passed bounded-file, schema, and identity validation. */
111
111
  receipts:MergeDecisionReceipt[];
112
+ /** Valid counterparts with matching immutable provenance in the active Git index; absent evidence never authorizes a tracked history reference. */
113
+ durable_receipt_ids?:string[];
114
+ /** Valid filesystem sidecars not represented by matching indexed evidence; usable only for a read-only preview before guarded staging. */
115
+ unstaged_durable_receipt_ids?:string[];
112
116
  /** Number of JSON receipt candidates that could not be validated safely. */
113
117
  invalid_evidence_count:number;
114
118
  /** Bounded privacy-safe identities and reason codes for rejected evidence. */
@@ -178,6 +182,8 @@ includeReconciled?:boolean;
178
182
  includeLossless?:boolean;
179
183
  pmRoot?:string;
180
184
  }):Promise<MergeDecisionReceipt[]>;
185
+ /** Refuse audit references unless every requested identity has validated portable evidence. */
186
+ export declare function assertPortableMergeReceiptReferences(receiptIds:Iterable<string>,evidence:Pick<MergeReceiptEvidenceScan,"durable_receipt_ids" | "unstaged_durable_receipt_ids"> | undefined,preview:boolean):void;
181
187
  /** Split merge provenance into receipts with discarded values and receipts whose composition was lossless. */
182
188
  export declare function partitionMergeReceipts(receipts:MergeDecisionReceipt[]):{
183
189
  /** Receipts that require an explicit decision because at least one competing scalar value was discarded. */
@@ -1,5 +1,5 @@
1
- (function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="6e5e4691-4858-5a54-82b0-65b97d929b57")}catch{}})();import{execFile}from"node:child_process";import{randomUUID}from"node:crypto";import{readdir,stat}from"node:fs/promises";import path from"node:path";import{promisify}from"node:util";import{readHistoryEntries}from"../../core/history/read.js";import{verifyHistoryChainWithVersion}from"../../core/history/replay.js";import{ensureDir,isFileAbsentError,isFileMissingError,pathExists,writeFileAtomic}from"../../core/fs/fs-utils.js";import{sha256Hex,stableStringify}from"../../core/shared/serialization.js";import{nowIso}from"../../core/shared/time.js";import{encodeItemScalarDecisionValue,hashItemScalarDecisionValue,isItemScalarMissingValue}from"./three-way.js";import{readBoundedRegularFile}from"./receipt-file-boundary.js";import{DURABLE_VALUE_POLICY,durableValueEvidence,isSafeReceiptId,isMergeDecisionReceipt,isPrehashedValue,normalizeLegacyReceipt,receiptCollectionValidationPath,receiptValidationError}from"./receipt-schema.js";import{isSafeReceiptId as isSafeReceiptId2}from"./receipt-schema.js";const execFileAsync=promisify(execFile),RECEIPT_FILE_MAX_BYTES=16*1024*1024,RECEIPT_INVALID_EVIDENCE_DETAIL_LIMIT=100;async function resolveReceiptDirectory(cwd){try{const{stdout}=await execFileAsync("git",["rev-parse","--path-format=absolute","--git-path","pm-merge-receipts"],{cwd,encoding:"utf8",windowsHide:!0,timeout:1e4});return stdout.trim()}catch{return null}}async function resolveTrackerRootFromItemPath(cwd,itemPath){let directory=path.dirname(path.resolve(cwd,itemPath));for(let depth=0;depth<12;depth+=1){if(await pathExists(path.join(directory,"settings.json")))return directory;const parent=path.dirname(directory);if(parent===directory)break;directory=parent}return null}function durableReceiptDirectory(pmRoot){return path.join(pmRoot,"merge-receipts")}function receiptFileName(id){return`${id}.json`}async function prepareReceiptSettlement(params){let raw;try{raw=params.readReceipt?await params.readReceipt(params.receiptPath):await readBoundedRegularFile(params.receiptPath,RECEIPT_FILE_MAX_BYTES)}catch(error){if(isFileAbsentError(error))return null;throw error}if(raw===null)throw new Error(`Receipt ${params.receiptId} settlement source is not a bounded regular file.`);let parsed;try{parsed=normalizeLegacyReceipt(JSON.parse(raw))}catch{throw new Error(`Receipt ${params.receiptId} settlement source is not valid JSON.`)}if(!isMergeDecisionReceipt(parsed,params.evidenceSource)||parsed.id!==params.receiptId||params.settlement!==void 0&&parsed.operation===void 0||path.basename(params.receiptPath)!==receiptFileName(parsed.id))throw new Error(`Receipt ${params.receiptId} settlement source failed schema or identity validation.`);const{evidence_source:_evidenceSource,...persistedReceipt}=parsed;return{path:params.receiptPath,fingerprints:receiptProvenanceFingerprints(parsed),modern:parsed.value_policy!==void 0,content:`${JSON.stringify({...persistedReceipt,state:"reconciled",reconciled_at:params.reconciledAt,...params.settlement?{settlement:params.settlement}:{}},null,2)}
1
+ (function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="21c15a9e-da38-5fec-9a7d-57556f77ce0a")}catch{}})();import{execFile}from"node:child_process";import{randomUUID}from"node:crypto";import{readdir,stat}from"node:fs/promises";import path from"node:path";import{promisify}from"node:util";import{readHistoryEntries}from"../../core/history/read.js";import{verifyHistoryChainWithVersion}from"../../core/history/replay.js";import{ensureDir,isFileAbsentError,isFileMissingError,pathExists,writeFileAtomic}from"../../core/fs/fs-utils.js";import{sha256Hex,stableStringify}from"../../core/shared/serialization.js";import{nowIso}from"../../core/shared/time.js";import{EXIT_CODE}from"../../core/shared/constants.js";import{PmCliError}from"../../core/shared/errors.js";import{encodeItemScalarDecisionValue,hashItemScalarDecisionValue,isItemScalarMissingValue}from"./three-way.js";import{readBoundedRegularFile}from"./receipt-file-boundary.js";import{DURABLE_VALUE_POLICY,durableValueEvidence,isSafeReceiptId,isMergeDecisionReceipt,isPrehashedValue,normalizeLegacyReceipt,receiptCollectionValidationPath,receiptValidationError}from"./receipt-schema.js";import{isSafeReceiptId as isSafeReceiptId2}from"./receipt-schema.js";const execFileAsync=promisify(execFile),RECEIPT_FILE_MAX_BYTES=16*1024*1024,RECEIPT_INVALID_EVIDENCE_DETAIL_LIMIT=100;async function resolveReceiptDirectory(cwd){try{const{stdout}=await execFileAsync("git",["rev-parse","--path-format=absolute","--git-path","pm-merge-receipts"],{cwd,encoding:"utf8",windowsHide:!0,timeout:1e4});return stdout.trim()}catch{return null}}async function resolveTrackerRootFromItemPath(cwd,itemPath){let directory=path.dirname(path.resolve(cwd,itemPath));for(let depth=0;depth<12;depth+=1){if(await pathExists(path.join(directory,"settings.json")))return directory;const parent=path.dirname(directory);if(parent===directory)break;directory=parent}return null}function durableReceiptDirectory(pmRoot){return path.join(pmRoot,"merge-receipts")}function receiptFileName(id){return`${id}.json`}async function prepareReceiptSettlement(params){let raw;try{raw=params.readReceipt?await params.readReceipt(params.receiptPath):await readBoundedRegularFile(params.receiptPath,RECEIPT_FILE_MAX_BYTES)}catch(error){if(isFileAbsentError(error))return null;throw error}if(raw===null)throw new Error(`Receipt ${params.receiptId} settlement source is not a bounded regular file.`);let parsed;try{parsed=normalizeLegacyReceipt(JSON.parse(raw))}catch{throw new Error(`Receipt ${params.receiptId} settlement source is not valid JSON.`)}if(!isMergeDecisionReceipt(parsed,params.evidenceSource)||parsed.id!==params.receiptId||params.settlement!==void 0&&parsed.operation===void 0||path.basename(params.receiptPath)!==receiptFileName(parsed.id))throw new Error(`Receipt ${params.receiptId} settlement source failed schema or identity validation.`);const{evidence_source:_evidenceSource,...persistedReceipt}=parsed;return{path:params.receiptPath,fingerprints:receiptProvenanceFingerprints(parsed),modern:parsed.value_policy!==void 0,content:`${JSON.stringify({...persistedReceipt,state:"reconciled",reconciled_at:params.reconciledAt,...params.settlement?{settlement:params.settlement}:{}},null,2)}
2
2
  `}}function receiptProvenanceFingerprint(receipt,legacy=!1){return sha256Hex(stableStringify({id:receipt.id,item_id:receipt.item_id,item_path:receipt.item_path,created_at:receipt.created_at,requested_preference:receipt.requested_preference??receipt.preferred??"ours",requested_preference_applied:receipt.requested_preference_applied??(receipt.conflict_resolution??"preferred_side")==="preferred_side",conflict_resolution:receipt.conflict_resolution??"preferred_side",fields_from_theirs:receipt.fields_from_theirs,union_fields:receipt.union_fields,merged_field_hashes:receipt.merged_field_hashes??null,operation:receipt.operation??null,decisions:legacy?receipt.decisions.map(decision=>({field:decision.field,retained_hash:sha256Hex(stableStringify(encodeItemScalarDecisionValue(decision.retained))),discarded_hash:sha256Hex(stableStringify(encodeItemScalarDecisionValue(decision.discarded)))})):summarizeMergeReceipt(receipt).decisions}))}function receiptProvenanceFingerprints(receipt){const fingerprints=[receiptProvenanceFingerprint(receipt)];return receipt.value_policy===void 0&&(receipt.value_availability===void 0||receipt.value_availability==="clone_local")&&fingerprints.push(receiptProvenanceFingerprint(receipt,!0)),fingerprints}function mergeReceiptCopyLifecycle(local,durable){const{reconciled_at:_reconciledAt,settlement:_settlement,...pendingLocal}=local;return durable.state==="reconciled"?{...pendingLocal,state:"reconciled",reconciled_at:durable.reconciled_at,...durable.settlement===void 0?{}:{settlement:durable.settlement}}:{...pendingLocal,state:"pending"}}async function retainAuditedDurableSettlements(receipts,durableReceipts,pmRoot){const histories=new Map;for(const durable of durableReceipts){const receipt=receipts.get(durable.id);if(durable.state!=="reconciled"||receipt===void 0)continue;let entries=histories.get(receipt.item_id);if(entries===void 0){try{entries=await readHistoryEntries(path.join(pmRoot,"history",`${receipt.item_id}.jsonl`),receipt.item_id),verifyHistoryChainWithVersion(entries).ok||(entries=[])}catch{entries=[]}histories.set(receipt.item_id,entries)}const abandoned=receipt.settlement==="original_git_state_restored",expected=stableStringify({...summarizeMergeReceipt(receipt),...abandoned?{reason:receipt.settlement,operation:receipt.operation}:{}});if(!entries.some(entry=>{if(entry.op!=="merge_reconcile"||entry.record_hash_version===void 0)return!1;const merge=entry.context?.merge;if(typeof merge!="object"||merge===null||Array.isArray(merge))return!1;const summaries=merge[abandoned?"abandoned_receipts":"receipts"];return Array.isArray(summaries)&&summaries.some(summary=>stableStringify(summary)===expected)})){const{reconciled_at:_reconciledAt,settlement:_settlement,...pending}=receipt;receipts.set(receipt.id,{...pending,state:"pending"})}}}function summarizeMergeReceipt(receipt){return{receipt_id:receipt.id,item_id:receipt.item_id,item_path:receipt.item_path,conflict_fields:receipt.decisions.map(decision=>decision.field),fields_from_theirs:receipt.fields_from_theirs,union_fields:receipt.union_fields,requested_preference:receipt.requested_preference??receipt.preferred??"ours",requested_preference_applied:receipt.requested_preference_applied??(receipt.conflict_resolution??"preferred_side")==="preferred_side",conflict_resolution:receipt.conflict_resolution,decisions:receipt.decisions.map(decision=>({field:decision.field,retained_hash:isPrehashedValue(decision.retained)??hashItemScalarDecisionValue(isItemScalarMissingValue(decision.retained)?void 0:decision.retained),discarded_hash:isPrehashedValue(decision.discarded)??hashItemScalarDecisionValue(isItemScalarMissingValue(decision.discarded)?void 0:decision.discarded)}))}}async function writeMergeReceipt(params){const directory=await resolveReceiptDirectory(params.cwd);if(directory===null)return null;const trimmedItemPath=params.itemPath.trim(),itemPath=/^(['"])(.*)\1$/su.exec(trimmedItemPath)?.[2]??trimmedItemPath,itemId=path.basename(itemPath,path.extname(itemPath)),receipt={version:1,id:randomUUID(),item_path:itemPath.replaceAll("\\","/"),item_id:itemId,requested_preference:params.preferred,requested_preference_applied:(params.conflictResolution??"preferred_side")==="preferred_side",conflict_resolution:params.conflictResolution??"preferred_side",fields_from_theirs:[...params.fieldsFromTheirs],union_fields:[...params.unionFields],...params.mergedFieldHashes?{merged_field_hashes:{...params.mergedFieldHashes}}:{},decisions:structuredClone(params.decisions).map(decision=>({...decision,base:encodeItemScalarDecisionValue(decision.base),ours:encodeItemScalarDecisionValue(decision.ours),theirs:encodeItemScalarDecisionValue(decision.theirs),retained:encodeItemScalarDecisionValue(decision.retained),discarded:encodeItemScalarDecisionValue(decision.discarded)})),...params.operation?{operation:params.operation}:{},state:"pending",created_at:nowIso(),value_availability:"clone_local"};await ensureDir(directory),await writeFileAtomic(path.join(directory,receiptFileName(receipt.id)),`${JSON.stringify(receipt,null,2)}
3
3
  `);const trackerRoot=await resolveTrackerRootFromItemPath(params.cwd,receipt.item_path);if(trackerRoot!==null){const durableDirectory=durableReceiptDirectory(trackerRoot),durableDecisions=receipt.decisions.map(decision=>({field:decision.field,base:null,ours:null,theirs:null,retained:durableValueEvidence(decision.field,decision.retained),discarded:durableValueEvidence(decision.field,decision.discarded),retained_side:decision.retained_side,resolution_basis:decision.resolution_basis})),inlineValueCount=durableDecisions.reduce((count,decision)=>count+(Object.hasOwn(decision.retained,"pm_value")?1:0)+(Object.hasOwn(decision.discarded,"pm_value")?1:0),0),durableReceipt={...receipt,decisions:durableDecisions,value_availability:inlineValueCount===0?"hash_only":inlineValueCount===durableDecisions.length*2?"bounded_inline":"mixed",value_policy:DURABLE_VALUE_POLICY};await ensureDir(durableDirectory),await writeFileAtomic(path.join(durableDirectory,receiptFileName(receipt.id)),`${JSON.stringify(durableReceipt,null,2)}
4
- `)}return receipt}async function readReceiptsFromDirectory(directory,evidenceSource){let names;try{names=await readdir(directory)}catch(error){const absent=await receiptDirectoryFailureMeansAbsent(directory,error);return{receipts:[],invalid_evidence_count:absent?0:1,invalid_evidence:absent?[]:[{evidence_source:evidenceSource,reason:"directory_unreadable"}],invalid_evidence_truncated:!1}}const receipts=[];let invalidEvidenceCount=0;const invalidEvidence=[],recordInvalidEvidence=(name,candidate)=>{if(invalidEvidenceCount+=1,invalidEvidence.length>=RECEIPT_INVALID_EVIDENCE_DETAIL_LIMIT)return;const receiptId=name.slice(0,-5);invalidEvidence.push({evidence_source:evidenceSource,reason:candidate.reason,...candidate.validation_path?{validation_path:candidate.validation_path}:{},...candidate.validation_error===void 0?{}:{validation_error:candidate.validation_error},...isSafeReceiptId(receiptId)?{receipt_id:receiptId}:{candidate_name_hash:sha256Hex(name)}})};for(const name of names.sort((left,right)=>left.localeCompare(right))){if(!name.endsWith(".json"))continue;const candidate=await inspectReceiptCandidate(directory,name,evidenceSource);candidate.receipt!==void 0?receipts.push(candidate.receipt):recordInvalidEvidence(name,candidate)}return{receipts,invalid_evidence_count:invalidEvidenceCount,invalid_evidence:invalidEvidence,invalid_evidence_truncated:invalidEvidenceCount>invalidEvidence.length}}async function receiptDirectoryFailureMeansAbsent(directory,error,inspectAncestor=stat){if(!isFileMissingError(error))return!1;let ancestor=directory;for(;;)try{return(await inspectAncestor(ancestor)).isDirectory()}catch(ancestorError){if(!isFileMissingError(ancestorError))return!1;const parent=path.dirname(ancestor);if(parent===ancestor)return!0;ancestor=parent}}async function inspectReceiptCandidate(directory,name,evidenceSource){let raw;try{raw=await readBoundedRegularFile(path.join(directory,name),RECEIPT_FILE_MAX_BYTES)}catch{return{reason:"candidate_unreadable"}}if(raw===null)return{reason:"candidate_not_bounded_regular_file"};let parsed;try{parsed=normalizeLegacyReceipt(JSON.parse(raw))}catch{return{reason:"candidate_invalid_json"}}const validationError=receiptValidationError(parsed,evidenceSource);if(validationError!==null)return{reason:new Set(["receipt_id","item_id","item_path"]).has(validationError)?"identity_invalid":"schema_invalid",validation_error:validationError,...validationError==="collections"?{validation_path:receiptCollectionValidationPath(parsed)}:{}};const receipt=parsed;if(name!==receiptFileName(receipt.id))return{reason:"identity_invalid",validation_error:"filename"};const{preferred:legacyPreference,evidence_source:_serializedEvidenceSource,...receiptWithoutRuntimeKeys}=receipt;return{receipt:{...receiptWithoutRuntimeKeys,requested_preference:receipt.requested_preference??legacyPreference??"ours",requested_preference_applied:receipt.requested_preference_applied??(receipt.conflict_resolution??"preferred_side")==="preferred_side",conflict_resolution:receipt.conflict_resolution??"preferred_side",evidence_source:evidenceSource}}}async function inspectMergeReceiptEvidence(cwd,options={}){const directory=await resolveReceiptDirectory(cwd),local=directory===null?{receipts:[],invalid_evidence_count:0,invalid_evidence:[],invalid_evidence_truncated:!1}:await readReceiptsFromDirectory(directory,"clone_local"),trackerRoot=options.pmRoot??path.join(cwd,".agents","pm"),durable=await readReceiptsFromDirectory(durableReceiptDirectory(trackerRoot),"durable"),receipts=new Map(durable.receipts.map(receipt=>[receipt.id,receipt]));let divergentCopyCount=0;const divergentCopyEvidence=[];for(const receipt of local.receipts){const durableCopy=receipts.get(receipt.id);if(durableCopy!==void 0&&!(durableCopy.value_policy===void 0?receiptProvenanceFingerprints(receipt):[receiptProvenanceFingerprint(receipt)]).includes(receiptProvenanceFingerprint(durableCopy))){receipts.delete(receipt.id),divergentCopyCount+=1,local.invalid_evidence.length+durable.invalid_evidence.length+divergentCopyEvidence.length<RECEIPT_INVALID_EVIDENCE_DETAIL_LIMIT&&divergentCopyEvidence.push({evidence_source:"clone_local_and_durable",reason:"copy_provenance_mismatch",receipt_id:receipt.id});continue}receipts.set(receipt.id,durableCopy===void 0?receipt:mergeReceiptCopyLifecycle(receipt,durableCopy))}return await retainAuditedDurableSettlements(receipts,durable.receipts,trackerRoot),{receipts:[...receipts.values()].filter(receipt=>(options.includeReconciled||receipt.state==="pending")&&(options.includeLossless!==!1||receipt.decisions.length>0)).sort((left,right)=>left.created_at.localeCompare(right.created_at)),invalid_evidence_count:local.invalid_evidence_count+durable.invalid_evidence_count+divergentCopyCount,invalid_evidence:[...local.invalid_evidence,...durable.invalid_evidence,...divergentCopyEvidence].slice(0,RECEIPT_INVALID_EVIDENCE_DETAIL_LIMIT),invalid_evidence_truncated:local.invalid_evidence_truncated||durable.invalid_evidence_truncated||local.invalid_evidence_count+durable.invalid_evidence_count+divergentCopyCount>RECEIPT_INVALID_EVIDENCE_DETAIL_LIMIT,clone_local_evidence_resolved:directory!==null}}async function listMergeReceipts(cwd,options={}){return(await inspectMergeReceiptEvidence(cwd,options)).receipts}function partitionMergeReceipts(receipts){const pendingDecisions=[],lossless=[];for(const receipt of receipts)(Array.isArray(receipt.decisions)&&receipt.decisions.length>0?pendingDecisions:lossless).push(receipt);return{pendingDecisions,lossless}}async function markMergeReceiptReconciled(cwd,receipt,options={}){const directory=await resolveReceiptDirectory(cwd);if(directory===null){if(options.requireExisting===!0)throw new Error(`Receipt ${receipt.id} disappeared before settlement.`);return}const receiptEvidenceSource=receipt.evidence_source??"clone_local";if(!isMergeDecisionReceipt(receipt,receiptEvidenceSource))throw new Error("Merge receipt trusted settlement input failed schema validation.");const reconciledAt=nowIso(),localPath=path.join(directory,receiptFileName(receipt.id)),trackerRoot=await resolveTrackerRootFromItemPath(cwd,receipt.item_path),durablePath=trackerRoot===null?null:path.join(durableReceiptDirectory(trackerRoot),receiptFileName(receipt.id)),writes=(await Promise.all([prepareReceiptSettlement({receiptPath:localPath,receiptId:receipt.id,evidenceSource:"clone_local",reconciledAt,settlement:options.settlement}),...durablePath===null?[]:[prepareReceiptSettlement({receiptPath:durablePath,receiptId:receipt.id,evidenceSource:"durable",reconciledAt,settlement:options.settlement})]])).filter(entry=>entry!==null);if(writes.length===0){if(options.requireExisting===!0)throw new Error(`Receipt ${receipt.id} disappeared before settlement.`);return}if(!(writes.some(write=>write.modern)?[receiptProvenanceFingerprint(receipt)]:receiptProvenanceFingerprints(receipt)).some(fingerprint=>writes.every(write=>write.fingerprints.includes(fingerprint))))throw new Error(`Receipt ${receipt.id} settlement copies disagree on immutable merge provenance.`);for(const write of writes)await writeFileAtomic(write.path,write.content)}async function runMergeReceiptReport(options){const receipts=await listMergeReceipts(options.cwd??process.cwd(),{...options,includeLossless:!0});return{ok:!0,count:receipts.length,receipts,generated_at:nowIso()}}async function runMergeReceiptEvidenceReport(options){const evidence=await inspectMergeReceiptEvidence(options.cwd??process.cwd(),{...options,includeLossless:!0}),complete=evidence.clone_local_evidence_resolved===!0&&evidence.invalid_evidence_count===0;return{ok:complete,complete,count:evidence.receipts.length,invalid_evidence_count:evidence.invalid_evidence_count,invalid_evidence:evidence.invalid_evidence,invalid_evidence_truncated:evidence.invalid_evidence_truncated,clone_local_evidence_resolved:evidence.clone_local_evidence_resolved,receipts:evidence.receipts,generated_at:nowIso()}}const _testOnlyMergeReceipts={prepareReceiptSettlement,receiptDirectoryFailureMeansAbsent};export{_testOnlyMergeReceipts,inspectMergeReceiptEvidence,isSafeReceiptId2 as isSafeReceiptId,listMergeReceipts,markMergeReceiptReconciled,partitionMergeReceipts,runMergeReceiptEvidenceReport,runMergeReceiptReport,summarizeMergeReceipt,writeMergeReceipt};
4
+ `)}return receipt}async function readReceiptsFromDirectory(directory,evidenceSource){let names;try{names=await readdir(directory)}catch(error){const absent=await receiptDirectoryFailureMeansAbsent(directory,error);return{receipts:[],invalid_evidence_count:absent?0:1,invalid_evidence:absent?[]:[{evidence_source:evidenceSource,reason:"directory_unreadable"}],invalid_evidence_truncated:!1}}const receipts=[];let invalidEvidenceCount=0;const invalidEvidence=[],recordInvalidEvidence=(name,candidate)=>{if(invalidEvidenceCount+=1,invalidEvidence.length>=RECEIPT_INVALID_EVIDENCE_DETAIL_LIMIT)return;const receiptId=name.slice(0,-5);invalidEvidence.push({evidence_source:evidenceSource,reason:candidate.reason,...candidate.validation_path?{validation_path:candidate.validation_path}:{},...candidate.validation_error===void 0?{}:{validation_error:candidate.validation_error},...isSafeReceiptId(receiptId)?{receipt_id:receiptId}:{candidate_name_hash:sha256Hex(name)}})};for(const name of names.sort((left,right)=>left.localeCompare(right))){if(!name.endsWith(".json"))continue;const candidate=await inspectReceiptCandidate(directory,name,evidenceSource);candidate.receipt!==void 0?receipts.push(candidate.receipt):recordInvalidEvidence(name,candidate)}return{receipts,invalid_evidence_count:invalidEvidenceCount,invalid_evidence:invalidEvidence,invalid_evidence_truncated:invalidEvidenceCount>invalidEvidence.length}}async function receiptDirectoryFailureMeansAbsent(directory,error,inspectAncestor=stat){if(!isFileMissingError(error))return!1;let ancestor=directory;for(;;)try{return(await inspectAncestor(ancestor)).isDirectory()}catch(ancestorError){if(!isFileMissingError(ancestorError))return!1;const parent=path.dirname(ancestor);if(parent===ancestor)return!0;ancestor=parent}}async function inspectReceiptCandidate(directory,name,evidenceSource){let raw;try{raw=await readBoundedRegularFile(path.join(directory,name),RECEIPT_FILE_MAX_BYTES)}catch{return{reason:"candidate_unreadable"}}if(raw===null)return{reason:"candidate_not_bounded_regular_file"};let parsed;try{parsed=normalizeLegacyReceipt(JSON.parse(raw))}catch{return{reason:"candidate_invalid_json"}}const validationError=receiptValidationError(parsed,evidenceSource);if(validationError!==null)return{reason:new Set(["receipt_id","item_id","item_path"]).has(validationError)?"identity_invalid":"schema_invalid",validation_error:validationError,...validationError==="collections"?{validation_path:receiptCollectionValidationPath(parsed)}:{}};const receipt=parsed;if(name!==receiptFileName(receipt.id))return{reason:"identity_invalid",validation_error:"filename"};const{preferred:legacyPreference,evidence_source:_serializedEvidenceSource,...receiptWithoutRuntimeKeys}=receipt;return{receipt:{...receiptWithoutRuntimeKeys,requested_preference:receipt.requested_preference??legacyPreference??"ours",requested_preference_applied:receipt.requested_preference_applied??(receipt.conflict_resolution??"preferred_side")==="preferred_side",conflict_resolution:receipt.conflict_resolution??"preferred_side",evidence_source:evidenceSource}}}async function indexedDurableReceiptIds(cwd,trackerRoot,receipts){if(receipts.length===0)return[];const{readIndexedReceiptBlobs}=await import("./indexed-receipt-blobs.js"),blobs=await readIndexedReceiptBlobs(cwd,durableReceiptDirectory(trackerRoot),receipts.map(receipt=>receiptFileName(receipt.id)),RECEIPT_FILE_MAX_BYTES);return receipts.flatMap(receipt=>{try{const indexed=normalizeLegacyReceipt(JSON.parse(blobs.get(receiptFileName(receipt.id))??""));if(receiptValidationError(indexed,"durable")!==null)return[];const stored=indexed,fingerprints=receiptProvenanceFingerprints(receipt);return stored.id===receipt.id&&receiptProvenanceFingerprints(stored).some(fingerprint=>fingerprints.includes(fingerprint))?[receipt.id]:[]}catch{return[]}})}async function inspectMergeReceiptEvidence(cwd,options={}){const directory=await resolveReceiptDirectory(cwd),local=directory===null?{receipts:[],invalid_evidence_count:0,invalid_evidence:[],invalid_evidence_truncated:!1}:await readReceiptsFromDirectory(directory,"clone_local"),trackerRoot=options.pmRoot??path.join(cwd,".agents","pm"),durable=await readReceiptsFromDirectory(durableReceiptDirectory(trackerRoot),"durable"),receipts=new Map(durable.receipts.map(receipt=>[receipt.id,receipt]));let divergentCopyCount=0;const divergentCopyEvidence=[];for(const receipt of local.receipts){const durableCopy=receipts.get(receipt.id);if(durableCopy!==void 0&&!(durableCopy.value_policy===void 0?receiptProvenanceFingerprints(receipt):[receiptProvenanceFingerprint(receipt)]).includes(receiptProvenanceFingerprint(durableCopy))){receipts.delete(receipt.id),divergentCopyCount+=1,local.invalid_evidence.length+durable.invalid_evidence.length+divergentCopyEvidence.length<RECEIPT_INVALID_EVIDENCE_DETAIL_LIMIT&&divergentCopyEvidence.push({evidence_source:"clone_local_and_durable",reason:"copy_provenance_mismatch",receipt_id:receipt.id});continue}receipts.set(receipt.id,durableCopy===void 0?receipt:mergeReceiptCopyLifecycle(receipt,durableCopy))}await retainAuditedDurableSettlements(receipts,durable.receipts,trackerRoot);const validDurable=durable.receipts.filter(receipt=>receipts.has(receipt.id)),indexedIds=new Set(await indexedDurableReceiptIds(cwd,trackerRoot,validDurable));return{durable_receipt_ids:[...indexedIds],unstaged_durable_receipt_ids:validDurable.filter(receipt=>!indexedIds.has(receipt.id)).map(receipt=>receipt.id),receipts:[...receipts.values()].filter(receipt=>(options.includeReconciled||receipt.state==="pending")&&(options.includeLossless!==!1||receipt.decisions.length>0)).sort((left,right)=>left.created_at.localeCompare(right.created_at)),invalid_evidence_count:local.invalid_evidence_count+durable.invalid_evidence_count+divergentCopyCount,invalid_evidence:[...local.invalid_evidence,...durable.invalid_evidence,...divergentCopyEvidence].slice(0,RECEIPT_INVALID_EVIDENCE_DETAIL_LIMIT),invalid_evidence_truncated:local.invalid_evidence_truncated||durable.invalid_evidence_truncated||local.invalid_evidence_count+durable.invalid_evidence_count+divergentCopyCount>RECEIPT_INVALID_EVIDENCE_DETAIL_LIMIT,clone_local_evidence_resolved:directory!==null}}async function listMergeReceipts(cwd,options={}){return(await inspectMergeReceiptEvidence(cwd,options)).receipts}function assertPortableMergeReceiptReferences(receiptIds,evidence,preview){const portable=new Set([...evidence?.durable_receipt_ids??[],...preview?evidence?.unstaged_durable_receipt_ids??[]:[]]),nonportable=[...receiptIds].filter(id=>!portable.has(id));if(nonportable.length!==0)throw new PmCliError(`Merge reconciliation refused ${nonportable.length} receipt(s) without validated tracked evidence; no audit history was written.`,EXIT_CODE.CONFLICT,{code:"merge_receipt_not_portable",required:"Every receipt cited by tracked history must have matching validated evidence in the Git index before any audit write.",recovery:{suggested_retry:"pm merge report --include-reconciled"},nextSteps:["Stage valid project sidecars after reviewing git diff, or use pm merge reconcile --stage for guarded lossless settlement. Restore missing original counterparts or preserve unrelated stale receipts outside the active store. Never copy private clone-local values into tracked evidence. Then rerun pm merge reconcile --dry-run."]})}function partitionMergeReceipts(receipts){const pendingDecisions=[],lossless=[];for(const receipt of receipts)(Array.isArray(receipt.decisions)&&receipt.decisions.length>0?pendingDecisions:lossless).push(receipt);return{pendingDecisions,lossless}}async function markMergeReceiptReconciled(cwd,receipt,options={}){const directory=await resolveReceiptDirectory(cwd);if(directory===null){if(options.requireExisting===!0)throw new Error(`Receipt ${receipt.id} disappeared before settlement.`);return}const receiptEvidenceSource=receipt.evidence_source??"clone_local";if(!isMergeDecisionReceipt(receipt,receiptEvidenceSource))throw new Error("Merge receipt trusted settlement input failed schema validation.");const reconciledAt=nowIso(),localPath=path.join(directory,receiptFileName(receipt.id)),trackerRoot=await resolveTrackerRootFromItemPath(cwd,receipt.item_path),durablePath=trackerRoot===null?null:path.join(durableReceiptDirectory(trackerRoot),receiptFileName(receipt.id)),writes=(await Promise.all([prepareReceiptSettlement({receiptPath:localPath,receiptId:receipt.id,evidenceSource:"clone_local",reconciledAt,settlement:options.settlement}),...durablePath===null?[]:[prepareReceiptSettlement({receiptPath:durablePath,receiptId:receipt.id,evidenceSource:"durable",reconciledAt,settlement:options.settlement})]])).filter(entry=>entry!==null);if(writes.length===0){if(options.requireExisting===!0)throw new Error(`Receipt ${receipt.id} disappeared before settlement.`);return}if(!(writes.some(write=>write.modern)?[receiptProvenanceFingerprint(receipt)]:receiptProvenanceFingerprints(receipt)).some(fingerprint=>writes.every(write=>write.fingerprints.includes(fingerprint))))throw new Error(`Receipt ${receipt.id} settlement copies disagree on immutable merge provenance.`);for(const write of writes)await writeFileAtomic(write.path,write.content)}async function runMergeReceiptReport(options){const receipts=await listMergeReceipts(options.cwd??process.cwd(),{...options,includeLossless:!0});return{ok:!0,count:receipts.length,receipts,generated_at:nowIso()}}async function runMergeReceiptEvidenceReport(options){const evidence=await inspectMergeReceiptEvidence(options.cwd??process.cwd(),{...options,includeLossless:!0}),complete=evidence.clone_local_evidence_resolved===!0&&evidence.invalid_evidence_count===0;return{ok:complete,complete,count:evidence.receipts.length,invalid_evidence_count:evidence.invalid_evidence_count,invalid_evidence:evidence.invalid_evidence,invalid_evidence_truncated:evidence.invalid_evidence_truncated,clone_local_evidence_resolved:evidence.clone_local_evidence_resolved,receipts:evidence.receipts,generated_at:nowIso()}}const _testOnlyMergeReceipts={prepareReceiptSettlement,receiptDirectoryFailureMeansAbsent};export{_testOnlyMergeReceipts,assertPortableMergeReceiptReferences,inspectMergeReceiptEvidence,isSafeReceiptId2 as isSafeReceiptId,listMergeReceipts,markMergeReceiptReconciled,partitionMergeReceipts,runMergeReceiptEvidenceReport,runMergeReceiptReport,summarizeMergeReceipt,writeMergeReceipt};
5
5
  //# sourceMappingURL=receipts.js.map
@@ -12,6 +12,8 @@ author?:string;
12
12
  message?:string;
13
13
  /** Permit ownership overrides already supported by history repair. */
14
14
  force?:boolean;
15
+ /** Stage only authenticated lossless audit artifacts, refusing force, conflicts and unrelated drift. */
16
+ stage?:boolean;
15
17
  }
16
18
  /** Structured post-merge reconciliation and verification result. */
17
19
  export interface MergeReconcileResult {
@@ -32,6 +34,8 @@ reconciled:number;
32
34
  /** Receipts proven to restore their original Git state; these do not represent applied merges. */
33
35
  abandoned:number;
34
36
  summaries:MergeDecisionReceiptSummary[];
37
+ /** Valid sidecars awaiting matching Git index evidence; a preview never authorizes audit writes. */
38
+ unstaged_durable_receipt_ids?:string[];
35
39
  /** Missing durable references discovered before this reconciliation. */
36
40
  missing_history_references_before:number;
37
41
  /** Pre-durable references eligible for explicit audited disposition. */
@@ -47,6 +51,8 @@ validation:ValidateResult;
47
51
  guidance:string[];
48
52
  /** ISO timestamp copied from the validation pass. */
49
53
  generated_at:string;
54
+ /** Repository-relative artifacts staged by an explicit stage request; dry runs return an empty list. */
55
+ staged_paths?:string[];
50
56
  }
51
57
  /**
52
58
  * Preview or apply post-merge history reconciliation and immediately validate
@@ -1,2 +1,2 @@
1
- (function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="7b1ffef4-9e2b-5ff9-8b8c-030ea89ea27e")}catch{}})();import{settleAbandonedMergeReceipts}from"./abandoned-receipts.js";import{readFile}from"node:fs/promises";import path from"node:path";import{EXIT_CODE}from"../../core/shared/constants.js";import{PmCliError}from"../../core/shared/errors.js";import{compareTimestampStrings}from"../../core/shared/time.js";import{resolvePmRoot}from"../../core/store/paths.js";import{mapWithFixedConcurrency}from"../extension/concurrency.js";import{runHealth}from"../governance/health.js";import{DURABLE_MERGE_RECEIPT_INTRODUCED_AT,extractHistoryMergeReceiptReferences,isPreDurableDispositionEligibleReference}from"../governance/merge-receipt-history.js";import{runValidate}from"../governance/validate.js";import{runHistoryRepair,runHistoryRepairAll}from"../history-repair.js";import{findGitWorkspaceRoot}from"./install.js";import{inspectMergeReceiptEvidence,markMergeReceiptReconciled,summarizeMergeReceipt}from"./receipts.js";const RECEIPT_ONLY_REPAIR_CONCURRENCY=4;function missingReceiptCoordinates(health){const integrity=health.checks.find(check=>check.name==="integrity"),counts=integrity?.details.counts,count=typeof counts=="object"&&counts!==null&&!Array.isArray(counts)&&typeof counts.missing_merge_receipt_history_references=="number"?counts.missing_merge_receipt_history_references:0,details=integrity?.details.missing_merge_receipt_history_reference_details,truncated=integrity?.details.missing_merge_receipt_history_reference_details_truncated===!0,coordinates=Array.isArray(details)?details.flatMap(detail=>{if(typeof detail!="object"||detail===null||Array.isArray(detail))return[];const record=detail;return typeof record.item_id=="string"&&typeof record.history_line=="number"&&Number.isSafeInteger(record.history_line)&&typeof record.receipt_id=="string"?[{item_id:record.item_id,history_line:record.history_line,receipt_id:record.receipt_id}]:[]}):[];return{count,truncated,coordinates}}async function identifyLegacyDispositionCandidates(pmRoot,coordinates){const candidates=[];for(const coordinate of coordinates){let raw;try{raw=await readFile(path.join(pmRoot,"history",`${coordinate.item_id}.jsonl`),"utf8")}catch{continue}const line=raw.split(/\r?\n/)[coordinate.history_line-1];if(line===void 0||line.trim().length===0)continue;let parsed;try{parsed=JSON.parse(line)}catch{continue}const reference=extractHistoryMergeReceiptReferences(parsed,coordinate.item_id).find(entry=>entry.receiptId===coordinate.receipt_id);reference===void 0||!isPreDurableDispositionEligibleReference(reference)||reference.eventTimestamp===void 0||compareTimestampStrings(reference.eventTimestamp,DURABLE_MERGE_RECEIPT_INTRODUCED_AT)>=0||candidates.push({...coordinate,original_event_ts:reference.eventTimestamp})}return candidates}async function recordLegacyReceiptDispositions(params){if(params.options.dryRun===!0||params.options.force!==!0||params.candidates.length===0)return 0;const byItem=new Map;for(const candidate of params.candidates){const itemCandidates=byItem.get(candidate.item_id)??[];itemCandidates.push(candidate),byItem.set(candidate.item_id,itemCandidates)}for(const[itemId,itemCandidates]of byItem)await runHistoryRepair(itemId,{author:params.options.author??params.global.author,message:params.options.message??"accept unrecoverable pre-durable merge receipt references",force:!0,auditOperation:"merge_reconcile",forceAuditEntry:!0,auditContext:{merge:{missing_receipt_dispositions:itemCandidates.map(candidate=>({receipt_id:candidate.receipt_id,original_history_line:candidate.history_line,original_event_ts:candidate.original_event_ts,reason:"legacy_clone_local_only"}))}}},params.global);return params.candidates.length}async function runReceiptOnlyRepair(params){try{return{status:"fulfilled",value:await runHistoryRepair(params.id,{dryRun:params.dryRun,author:params.options.author??params.global.author,message:params.options.message??"record field-aware branch merge provenance",force:params.options.force,auditOperation:"merge_reconcile",auditContext:params.auditContext,forceAuditEntry:!0,mergeReceiptProof:params.mergeReceiptProof},params.global)}}catch(reason){return{status:"rejected",reason}}}function appendReceiptOnlyResults(params){params.results.forEach((settled,index)=>{const id=params.receiptOnlyIds[index];if(settled.status==="fulfilled"){const outcome=settled.value.changed?"repaired":"skipped_clean";params.repair.streams.push({id,outcome,entries_rehashed:settled.value.history.entries_rehashed,entries_patch_repaired:settled.value.history.entries_patch_repaired,reconciled_with_item:settled.value.history.reconciled_with_item,...settled.value.merge_receipt_proof?{merge_receipt_proof:settled.value.merge_receipt_proof}:{},warnings:settled.value.warnings}),params.repair.totals[outcome]+=1;return}params.repair.streams.push({id,outcome:"failed",error:settled.reason instanceof Error?settled.reason.message:String(settled.reason)}),params.repair.totals.failed+=1})}async function settleProvenReceipts(params){if(params.dryRun||params.repair.totals.failed>0)return 0;const trustedReceiptIds=new Set(params.repair.streams.flatMap(stream=>stream.merge_receipt_proof?.trusted?stream.merge_receipt_proof.receipt_ids:[])),receiptsToSettle=params.pendingReceipts.filter(receipt=>trustedReceiptIds.has(receipt.id));return await Promise.all(receiptsToSettle.map(receipt=>markMergeReceiptReconciled(params.receiptWorkspaceRoot,receipt,{requireExisting:!0}))),receiptsToSettle.length}function receiptReviewGuidance(pendingCount,complete){if(!(pendingCount===0||complete))return'If receipt proof is unavailable or competing values were discarded, first review receipts.summaries and repair.streams and re-apply intended values. Only with explicit authority to accept that review, run pm merge reconcile --force --message "<reviewed receipt disposition and rationale>". This appends an audit event; it does not restore missing evidence or discarded values.'}async function runMergeReconcile(options,global2){const dryRun=options.dryRun===!0,pmRoot=resolvePmRoot(process.cwd(),global2.path),preflightHealth=await runHealth(global2,{checkOnly:!0,full:!0,skipVectors:!0,skipDrift:!0}),missingBefore=missingReceiptCoordinates(preflightHealth),legacyDispositionCandidates=await identifyLegacyDispositionCandidates(pmRoot,missingBefore.coordinates),gitWorkspaceRoot=await findGitWorkspaceRoot(pmRoot),receiptWorkspaceRoot=gitWorkspaceRoot??pmRoot,receiptEvidence=await inspectMergeReceiptEvidence(receiptWorkspaceRoot,{includeLossless:!0,pmRoot});if(receiptEvidence.invalid_evidence_count>0)throw new PmCliError(`Merge reconciliation refused ${receiptEvidence.invalid_evidence_count} invalid receipt evidence file(s).`,EXIT_CODE.CONFLICT,{code:"merge_receipt_evidence_invalid",required:"Every clone-local and durable receipt candidate must pass bounded-file, schema, and identity validation.",recovery:{suggested_retry:"pm health --check-only --full"}});const recordedLegacyDispositionCount=await recordLegacyReceiptDispositions({candidates:legacyDispositionCandidates,options,global:global2}),pendingReceipts=receiptEvidence.receipts,abandonedReceiptIds=await settleAbandonedMergeReceipts({cwd:gitWorkspaceRoot,receipts:pendingReceipts,dryRun,author:options.author,message:options.message,global:global2}),abandonedIds=new Set(abandonedReceiptIds),receiptsByItem=new Map;for(const receipt of pendingReceipts.filter(entry=>!abandonedIds.has(entry.id))){const receipts=receiptsByItem.get(receipt.item_id)??[];receipts.push(receipt),receiptsByItem.set(receipt.item_id,receipts)}const auditContextById=Object.fromEntries([...receiptsByItem].map(([id,receipts])=>[id,{merge:{receipts:receipts.map(summarizeMergeReceipt)}}])),mergeReceiptProofById=Object.fromEntries([...receiptsByItem].map(([id,receipts])=>[id,{gitWorkspaceRoot,receipts}])),repair=await runHistoryRepairAll({dryRun,author:options.author??global2.author,message:options.message??"post-merge reconciliation of field-aware tracker history",force:options.force,auditOperation:"merge_reconcile",auditContextById,...pendingReceipts.length>0?{mergeReceiptProofById}:{}},global2),representedIds=new Set(repair.streams.map(stream=>stream.id)),receiptOnlyIds=[...receiptsByItem.keys()].filter(id=>!representedIds.has(id)),receiptOnlyResults=await mapWithFixedConcurrency(receiptOnlyIds,RECEIPT_ONLY_REPAIR_CONCURRENCY,id=>runReceiptOnlyRepair({id,dryRun,options,global:global2,auditContext:auditContextById[id],mergeReceiptProof:mergeReceiptProofById[id]}));appendReceiptOnlyResults({receiptOnlyIds,results:receiptOnlyResults,repair});const reconciledReceiptCount=await settleProvenReceipts({dryRun,pendingReceipts,repair,receiptWorkspaceRoot}),validation=await runValidate({checkHistoryDrift:!0,checkStorageIntegrity:!0},global2),postflightHealth=recordedLegacyDispositionCount===0?preflightHealth:await runHealth(global2,{checkOnly:!0,full:!0,skipVectors:!0,skipDrift:!0}),missingAfter=missingReceiptCoordinates(postflightHealth),mergeChecksGreen=validation.checks.every(check=>check.status==="ok"),receiptSettlementComplete=dryRun?pendingReceipts.length===0:reconciledReceiptCount+abandonedReceiptIds.length===pendingReceipts.length,ok=[repair.totals.failed===0,mergeChecksGreen,receiptSettlementComplete,missingAfter.count===0,!missingAfter.truncated].every(Boolean),boundedBatchGuidance=missingBefore.truncated||missingAfter.truncated?"Missing receipt coordinates exceed the bounded detail response; each reviewed --force pass can disposition only the reported eligible batch. Repeat dry-run and --force passes until receipts.missing_history_references_after=0 and pm merge reconcile returns ok=true.":void 0,explicitReviewGuidance=receiptReviewGuidance(pendingReceipts.length,ok),guidance=(dryRun?["Review repair.streams, then rerun pm merge reconcile without --dry-run to apply audited repairs.",boundedBatchGuidance,explicitReviewGuidance,"No Git hook is installed automatically; invoke this command from an explicit post-merge hook only when your repository policy opts in."]:ok?["Reconciliation is complete; commit changed history streams with the merge result.","Rerun pm merge reconcile --dry-run after future tracker-data merges as a non-mutating integrity check."]:["Reconciliation remains incomplete; inspect repair failures and non-green validation checks before retrying.",boundedBatchGuidance,explicitReviewGuidance,...legacyDispositionCandidates.length>0&&recordedLegacyDispositionCount===0?["Pre-durable clone-local-only receipt references are eligible for an audited disposition; rerun with --force after reviewing receipts.legacy_disposition_eligible."]:[],"Do not commit repaired history streams as reconciled until pm merge reconcile returns ok=true."]).filter(message=>message!==void 0);return{ok,dry_run:dryRun,repair,receipts:{pending_before:pendingReceipts.length,reconciled:reconciledReceiptCount,abandoned:abandonedReceiptIds.length,summaries:pendingReceipts.map(summarizeMergeReceipt),missing_history_references_before:missingBefore.count,legacy_disposition_eligible:legacyDispositionCandidates.length,legacy_disposition_recorded:recordedLegacyDispositionCount,missing_history_references_after:missingAfter.count},validation,guidance,generated_at:validation.generated_at}}export{runMergeReconcile};
1
+ (function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="4d506b3c-f1c1-5642-a258-45d93ba64d81")}catch{}})();import{settleAbandonedMergeReceipts}from"./abandoned-receipts.js";import{readFile}from"node:fs/promises";import path from"node:path";import{EXIT_CODE}from"../../core/shared/constants.js";import{PmCliError}from"../../core/shared/errors.js";import{compareTimestampStrings}from"../../core/shared/time.js";import{resolvePmRoot}from"../../core/store/paths.js";import{mapWithFixedConcurrency}from"../extension/concurrency.js";import{runHealth}from"../governance/health.js";import{DURABLE_MERGE_RECEIPT_INTRODUCED_AT,extractHistoryMergeReceiptReferences,isPreDurableDispositionEligibleReference}from"../governance/merge-receipt-history.js";import{runValidate}from"../governance/validate.js";import{runHistoryRepair,runHistoryRepairAll}from"../history-repair.js";import{findGitWorkspaceRoot}from"./install.js";import{assertPortableMergeReceiptReferences,inspectMergeReceiptEvidence,markMergeReceiptReconciled,summarizeMergeReceipt}from"./receipts.js";const RECEIPT_ONLY_REPAIR_CONCURRENCY=4;function missingReceiptCoordinates(health){const integrity=health.checks.find(check=>check.name==="integrity"),counts=integrity?.details.counts,count=typeof counts=="object"&&counts!==null&&!Array.isArray(counts)&&typeof counts.missing_merge_receipt_history_references=="number"?counts.missing_merge_receipt_history_references:0,details=integrity?.details.missing_merge_receipt_history_reference_details,truncated=integrity?.details.missing_merge_receipt_history_reference_details_truncated===!0,coordinates=Array.isArray(details)?details.flatMap(detail=>{if(typeof detail!="object"||detail===null||Array.isArray(detail))return[];const record=detail;return typeof record.item_id=="string"&&typeof record.history_line=="number"&&Number.isSafeInteger(record.history_line)&&typeof record.receipt_id=="string"?[{item_id:record.item_id,history_line:record.history_line,receipt_id:record.receipt_id}]:[]}):[];return{count,truncated,coordinates}}async function identifyLegacyDispositionCandidates(pmRoot,coordinates){const candidates=[];for(const coordinate of coordinates){let raw;try{raw=await readFile(path.join(pmRoot,"history",`${coordinate.item_id}.jsonl`),"utf8")}catch{continue}const line=raw.split(/\r?\n/)[coordinate.history_line-1];if(line===void 0||line.trim().length===0)continue;let parsed;try{parsed=JSON.parse(line)}catch{continue}const reference=extractHistoryMergeReceiptReferences(parsed,coordinate.item_id).find(entry=>entry.receiptId===coordinate.receipt_id);reference===void 0||!isPreDurableDispositionEligibleReference(reference)||reference.eventTimestamp===void 0||compareTimestampStrings(reference.eventTimestamp,DURABLE_MERGE_RECEIPT_INTRODUCED_AT)>=0||candidates.push({...coordinate,original_event_ts:reference.eventTimestamp})}return candidates}async function recordLegacyReceiptDispositions(params){if(params.options.dryRun===!0||params.options.force!==!0||params.candidates.length===0)return 0;const byItem=new Map;for(const candidate of params.candidates){const itemCandidates=byItem.get(candidate.item_id)??[];itemCandidates.push(candidate),byItem.set(candidate.item_id,itemCandidates)}for(const[itemId,itemCandidates]of byItem)await runHistoryRepair(itemId,{author:params.options.author??params.global.author,message:params.options.message??"accept unrecoverable pre-durable merge receipt references",force:!0,auditOperation:"merge_reconcile",forceAuditEntry:!0,auditContext:{merge:{missing_receipt_dispositions:itemCandidates.map(candidate=>({receipt_id:candidate.receipt_id,original_history_line:candidate.history_line,original_event_ts:candidate.original_event_ts,reason:"legacy_clone_local_only"}))}}},params.global);return params.candidates.length}async function runReceiptOnlyRepair(params){try{return{status:"fulfilled",value:await runHistoryRepair(params.id,{dryRun:params.dryRun,author:params.options.author??params.global.author,message:params.options.message??"record field-aware branch merge provenance",force:params.options.force,auditOperation:"merge_reconcile",auditContext:params.auditContext,forceAuditEntry:!0,mergeReceiptProof:params.mergeReceiptProof},params.global)}}catch(reason){return{status:"rejected",reason}}}function appendReceiptOnlyResults(params){params.results.forEach((settled,index)=>{const id=params.receiptOnlyIds[index];if(settled.status==="fulfilled"){const outcome=settled.value.changed?"repaired":"skipped_clean";params.repair.streams.push({id,outcome,entries_rehashed:settled.value.history.entries_rehashed,entries_patch_repaired:settled.value.history.entries_patch_repaired,reconciled_with_item:settled.value.history.reconciled_with_item,...settled.value.merge_receipt_proof?{merge_receipt_proof:settled.value.merge_receipt_proof}:{},warnings:settled.value.warnings}),params.repair.totals[outcome]+=1;return}params.repair.streams.push({id,outcome:"failed",error:settled.reason instanceof Error?settled.reason.message:String(settled.reason)}),params.repair.totals.failed+=1})}async function settleProvenReceipts(params){if(params.dryRun||params.repair.totals.failed>0)return 0;const trustedReceiptIds=new Set(params.repair.streams.flatMap(stream=>stream.merge_receipt_proof?.trusted?stream.merge_receipt_proof.receipt_ids:[])),receiptsToSettle=params.pendingReceipts.filter(receipt=>trustedReceiptIds.has(receipt.id));return await Promise.all(receiptsToSettle.map(receipt=>markMergeReceiptReconciled(params.receiptWorkspaceRoot,receipt,{requireExisting:!0}))),receiptsToSettle.length}function receiptReviewGuidance(pendingCount,complete){if(!(pendingCount===0||complete))return'If receipt proof is unavailable or competing values were discarded, first review receipts.summaries and repair.streams and re-apply intended values. Only with explicit authority to accept that review, run pm merge reconcile --force --message "<reviewed receipt disposition and rationale>". This appends an audit event; it does not restore missing evidence or discarded values.'}async function runStagedMergeReconcile(options,global2){if(options.force===!0)throw new PmCliError("Automatic staging cannot accept forced reconciliation.",EXIT_CODE.CONFLICT,{code:"merge_settlement_review_required",recovery:{suggested_retry:"pm merge reconcile --dry-run"}});const unstagedOptions={...options,stage:!1},{stageLosslessMergeReconciliation,resolveMergeSettlementGlobal}=await import("./stage.js"),settledGlobal=await resolveMergeSettlementGlobal(global2),preview=await runMergeReconcile({...unstagedOptions,dryRun:!0},settledGlobal);return stageLosslessMergeReconciliation({preview,dryRun:options.dryRun===!0,global:settledGlobal,apply:()=>runMergeReconcile({...unstagedOptions,dryRun:!1},settledGlobal)})}async function runMergeReconcile(options,global2){return options.stage===!0?runStagedMergeReconcile(options,global2):reconcileMergeHistory(options,global2)}async function reconcileMergeHistory(options,global2){const dryRun=options.dryRun===!0,pmRoot=resolvePmRoot(process.cwd(),global2.path),preflightHealth=await runHealth(global2,{checkOnly:!0,full:!0,skipVectors:!0,skipDrift:!0}),missingBefore=missingReceiptCoordinates(preflightHealth),legacyDispositionCandidates=await identifyLegacyDispositionCandidates(pmRoot,missingBefore.coordinates),gitWorkspaceRoot=await findGitWorkspaceRoot(pmRoot),receiptWorkspaceRoot=gitWorkspaceRoot??pmRoot,receiptEvidence=await inspectMergeReceiptEvidence(receiptWorkspaceRoot,{includeLossless:!0,pmRoot});if(receiptEvidence.invalid_evidence_count>0)throw new PmCliError(`Merge reconciliation refused ${receiptEvidence.invalid_evidence_count} invalid receipt evidence file(s).`,EXIT_CODE.CONFLICT,{code:"merge_receipt_evidence_invalid",required:"Every clone-local and durable receipt candidate must pass bounded-file, schema, and identity validation.",recovery:{suggested_retry:"pm health --check-only --full"}});assertPortableMergeReceiptReferences(receiptEvidence.receipts.map(receipt=>receipt.id),receiptEvidence,dryRun);const recordedLegacyDispositionCount=await recordLegacyReceiptDispositions({candidates:legacyDispositionCandidates,options,global:global2}),pendingReceipts=receiptEvidence.receipts,abandonedReceiptIds=await settleAbandonedMergeReceipts({cwd:gitWorkspaceRoot,receipts:pendingReceipts,dryRun,author:options.author,message:options.message,global:global2}),abandonedIds=new Set(abandonedReceiptIds),receiptsByItem=new Map;for(const receipt of pendingReceipts.filter(entry=>!abandonedIds.has(entry.id))){const receipts=receiptsByItem.get(receipt.item_id)??[];receipts.push(receipt),receiptsByItem.set(receipt.item_id,receipts)}const auditContextById=Object.fromEntries([...receiptsByItem].map(([id,receipts])=>[id,{merge:{receipts:receipts.map(summarizeMergeReceipt)}}])),mergeReceiptProofById=Object.fromEntries([...receiptsByItem].map(([id,receipts])=>[id,{gitWorkspaceRoot,receipts}])),repair=await runHistoryRepairAll({dryRun,author:options.author??global2.author,message:options.message??"post-merge reconciliation of field-aware tracker history",force:options.force,auditOperation:"merge_reconcile",auditContextById,...pendingReceipts.length>0?{mergeReceiptProofById}:{}},global2),representedIds=new Set(repair.streams.map(stream=>stream.id)),receiptOnlyIds=[...receiptsByItem.keys()].filter(id=>!representedIds.has(id)),receiptOnlyResults=await mapWithFixedConcurrency(receiptOnlyIds,RECEIPT_ONLY_REPAIR_CONCURRENCY,id=>runReceiptOnlyRepair({id,dryRun,options,global:global2,auditContext:auditContextById[id],mergeReceiptProof:mergeReceiptProofById[id]}));appendReceiptOnlyResults({receiptOnlyIds,results:receiptOnlyResults,repair});const reconciledReceiptCount=await settleProvenReceipts({dryRun,pendingReceipts,repair,receiptWorkspaceRoot}),validation=await runValidate({checkHistoryDrift:!0,checkStorageIntegrity:!0},global2),postflightHealth=recordedLegacyDispositionCount===0?preflightHealth:await runHealth(global2,{checkOnly:!0,full:!0,skipVectors:!0,skipDrift:!0}),missingAfter=missingReceiptCoordinates(postflightHealth),mergeChecksGreen=validation.checks.every(check=>check.status==="ok"),receiptSettlementComplete=dryRun?pendingReceipts.length===0:reconciledReceiptCount+abandonedReceiptIds.length===pendingReceipts.length,ok=[repair.totals.failed===0,mergeChecksGreen,receiptSettlementComplete,missingAfter.count===0,!missingAfter.truncated].every(Boolean),boundedBatchGuidance=missingBefore.truncated||missingAfter.truncated?"Missing receipt coordinates exceed the bounded detail response; each reviewed --force pass can disposition only the reported eligible batch. Repeat dry-run and --force passes until receipts.missing_history_references_after=0 and pm merge reconcile returns ok=true.":void 0,explicitReviewGuidance=receiptReviewGuidance(pendingReceipts.length,ok),guidance=(dryRun?["Review repair.streams, then rerun pm merge reconcile without --dry-run to apply audited repairs.",boundedBatchGuidance,explicitReviewGuidance,"No Git hook is installed automatically; invoke this command from an explicit post-merge hook only when your repository policy opts in."]:ok?["Reconciliation is complete; commit changed history streams with the merge result.","Rerun pm merge reconcile --dry-run after future tracker-data merges as a non-mutating integrity check."]:["Reconciliation remains incomplete; inspect repair failures and non-green validation checks before retrying.",boundedBatchGuidance,explicitReviewGuidance,...legacyDispositionCandidates.length>0&&recordedLegacyDispositionCount===0?["Pre-durable clone-local-only receipt references are eligible for an audited disposition; rerun with --force after reviewing receipts.legacy_disposition_eligible."]:[],"Do not commit repaired history streams as reconciled until pm merge reconcile returns ok=true."]).filter(message=>message!==void 0);return{ok,dry_run:dryRun,repair,receipts:{pending_before:pendingReceipts.length,reconciled:reconciledReceiptCount,abandoned:abandonedReceiptIds.length,summaries:pendingReceipts.map(summarizeMergeReceipt),unstaged_durable_receipt_ids:receiptEvidence.unstaged_durable_receipt_ids,missing_history_references_before:missingBefore.count,legacy_disposition_eligible:legacyDispositionCandidates.length,legacy_disposition_recorded:recordedLegacyDispositionCount,missing_history_references_after:missingAfter.count},validation,guidance,generated_at:validation.generated_at}}export{runMergeReconcile};
2
2
  //# sourceMappingURL=reconcile.js.map
@@ -0,0 +1,12 @@
1
+ /** Preview an owned hook without replacing existing user policy or following file links. */
2
+ export declare function prepareMergeSettlementHook(workspaceRoot:string,command:string):Promise<{
3
+ path:string;
4
+ changed:boolean;
5
+ content:string;
6
+ }>;
7
+ /** Publish an explicitly requested hook after its command and merge fence are installed. */
8
+ export declare function writeMergeSettlementHook(hook:{
9
+ path:string;
10
+ changed:boolean;
11
+ content:string;
12
+ }):Promise<void>;
@@ -0,0 +1,7 @@
1
+ (function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="5b6eab08-f707-586f-bb05-484bd9160551")}catch{}})();import{execFile}from"node:child_process";import{chmod,lstat}from"node:fs/promises";import{promisify}from"node:util";import{isFileMissingError,writeFileAtomic}from"../../core/fs/fs-utils.js";import{EXIT_CODE}from"../../core/shared/constants.js";import{PmCliError}from"../../core/shared/errors.js";import{readBoundedRegularFile}from"./receipt-file-boundary.js";import{gitWorkspaceEnvironment}from"./worktree-config.js";const execFileAsync=promisify(execFile),HOOK_HEADER=`#!/bin/sh
2
+ # pm-cli:lossless-merge-settlement:v1
3
+ `;async function prepareMergeSettlementHook(workspaceRoot,command){const hookPath=(await execFileAsync("git",["rev-parse","--path-format=absolute","--git-path","hooks/pre-commit"],{cwd:workspaceRoot,env:gitWorkspaceEnvironment(),timeout:1e4})).stdout.trim();let content,executable=!1;try{content=await readBoundedRegularFile(hookPath,65536)??"",executable=process.platform==="win32"||((await lstat(hookPath)).mode&73)!==0}catch(error){if(!isFileMissingError(error))throw error}if(content!==void 0&&!content.startsWith(HOOK_HEADER))throw new PmCliError("The pre-commit hook already exists and is not owned by pm; it was preserved.",EXIT_CODE.CONFLICT,{code:"merge_settlement_hook_conflict",recovery:{suggested_retry:"pm merge install --dry-run"},nextSteps:["Integrate pm merge reconcile --stage into your existing hook after reviewing its policy, or keep manual settlement. pm never replaces an existing unowned hook."]});const expected=`${HOOK_HEADER}merge_head=$(git rev-parse --git-path MERGE_HEAD) || exit 1
4
+ [ -f "$merge_head" ] || exit 0
5
+ exec ${command} merge reconcile --stage
6
+ `;return{path:hookPath,changed:content!==expected||!executable,content:expected}}async function writeMergeSettlementHook(hook){hook.changed&&(await writeFileAtomic(hook.path,hook.content),await chmod(hook.path,493))}export{prepareMergeSettlementHook,writeMergeSettlementHook};
7
+ //# sourceMappingURL=settlement-hook.js.map
@@ -0,0 +1,13 @@
1
+ import type { GlobalOptions } from "../../core/shared/command-types.js";
2
+ import type { MergeReconcileResult } from "./reconcile.js";
3
+ /** Bind preview, proof verification and staging to one physical tracker location. */
4
+ export declare function resolveMergeSettlementGlobal(global:GlobalOptions):Promise<GlobalOptions & {
5
+ path:string;
6
+ }>;
7
+ /** Stage only the verified history and receipt paths, preserving all unrelated index entries. */
8
+ export declare function stageLosslessMergeReconciliation(params:{
9
+ preview:MergeReconcileResult;
10
+ dryRun:boolean;
11
+ global:GlobalOptions;
12
+ apply:() => Promise<MergeReconcileResult>;
13
+ }):Promise<MergeReconcileResult>;
@@ -0,0 +1,2 @@
1
+ (function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="fe5937fc-def3-5ed4-9663-70a6e9d258a1")}catch{}})();import{execFile}from"node:child_process";import{realpath}from"node:fs/promises";import path from"node:path";import{promisify}from"node:util";import{EXIT_CODE}from"../../core/shared/constants.js";import{PmCliError}from"../../core/shared/errors.js";import{resolvePmRoot}from"../../core/store/paths.js";import{findGitWorkspaceRoot}from"./install.js";import{gitWorkspaceEnvironment}from"./worktree-config.js";import{isPathOutsideRoot}from"../workspace.js";const execFileAsync=promisify(execFile);function stagingRefusal(reason){return new PmCliError(`Automatic merge settlement refused: ${reason}`,EXIT_CODE.CONFLICT,{code:"merge_settlement_review_required",recovery:{suggested_retry:"pm merge reconcile --dry-run"},nextSteps:["Inspect receipts, repair streams and Git changes. Resolve competing values or unrelated drift explicitly, then run pm merge reconcile and stage the reviewed artifacts manually."]})}async function assertNormalGitIndex(git){const indexOverride=process.env.GIT_INDEX_FILE;if(indexOverride===void 0)return;const indexPath=(await git(["rev-parse","--path-format=absolute","--git-path","index"])).stdout.trim();if(path.resolve(indexOverride)!==path.resolve(indexPath))throw stagingRefusal("an alternate Git index requires explicit manual settlement")}async function newReceiptPaths(git,receiptPaths){if(receiptPaths.length===0)return[];const pathspecs=receiptPaths.map(target=>`:(literal)${target}`),indexed=new Set((await git(["ls-files","--cached","-z","--",...pathspecs])).stdout.split("\0")),missing=receiptPaths.filter(target=>!indexed.has(target)).map(target=>`:(literal)${target}`);if(missing.length>0&&(await git(["diff","--cached","--name-only","--diff-filter=D","-z","--",...missing])).stdout.length>0)throw stagingRefusal("a receipt has a staged deletion that requires explicit review");return missing}async function applyAndStage(params){const{git,preview,paths,newReceiptPathspecs}=params;let preparedReceipts=!1;try{newReceiptPathspecs.length>0&&(await git(["add","--",...newReceiptPathspecs]),preparedReceipts=!0);const result=await params.apply();if(!result.ok||JSON.stringify(result.receipts.summaries)!==JSON.stringify(preview.receipts.summaries)||result.repair.streams.some(stream=>!preview.repair.streams.some(prior=>prior.id===stream.id)))throw stagingRefusal("reconciliation changed or did not finish; inspect the audit artifacts before staging");return paths.length>0&&await git(["add","--",...paths.map(target=>`:(literal)${target}`)]),{...result,staged_paths:paths,guidance:["Lossless reconciliation artifacts are staged for the merge commit. Review git diff --cached before committing."]}}catch(error){throw preparedReceipts&&await git(["reset","--quiet","--",...newReceiptPathspecs]),error}}function assertLosslessPreview(preview){if(preview.repair.totals.failed>0||preview.receipts.missing_history_references_after>0||preview.receipts.summaries.some(receipt=>receipt.conflict_fields.length>0)||preview.repair.streams.some(stream=>stream.merge_receipt_proof?.trusted!==!0)||preview.validation.checks.some(check=>check.name!=="history_drift"&&check.status!=="ok"))throw stagingRefusal("only fully authenticated receipt-backed repairs without discarded values can be staged")}async function resolveMergeSettlementGlobal(global2){try{return{...global2,path:await realpath(resolvePmRoot(process.cwd(),global2.path))}}catch{throw stagingRefusal("the tracker root cannot be resolved")}}async function stageLosslessMergeReconciliation(params){const{preview}=params;assertLosslessPreview(preview);const{path:pmRoot}=await resolveMergeSettlementGlobal(params.global),workspaceRoot=await findGitWorkspaceRoot(pmRoot);if(workspaceRoot===null)throw stagingRefusal("a Git workspace is required");const git=args=>execFileAsync("git",args,{cwd:workspaceRoot,env:gitWorkspaceEnvironment(),timeout:1e4});if(await assertNormalGitIndex(git),(await git(["ls-files","--unmerged","-z"])).stdout.length>0)throw stagingRefusal("Git still has unresolved paths");const paths=[...new Set([...[...new Set([...preview.repair.streams.map(stream=>stream.id),...preview.receipts.summaries.map(receipt=>receipt.item_id)])].map(id=>path.join(pmRoot,"history",`${id}.jsonl`)),...preview.receipts.summaries.map(receipt=>path.join(pmRoot,"merge-receipts",`${receipt.receipt_id}.json`))])].map(target=>path.relative(workspaceRoot,target).replaceAll("\\","/"));if(paths.some(isPathOutsideRoot))throw stagingRefusal("reconciliation artifacts must belong to this Git workspace");const pathspecs=paths.map(target=>`:(literal)${target}`),receiptPaths=preview.receipts.summaries.map(receipt=>path.relative(workspaceRoot,path.join(pmRoot,"merge-receipts",`${receipt.receipt_id}.json`)).replaceAll("\\","/"));if(pathspecs.length>0)try{await git(["diff","--quiet","--",...pathspecs])}catch{throw stagingRefusal("reconciliation paths have unstaged edits or Git cannot verify their index state")}const newReceiptPathspecs=await newReceiptPaths(git,receiptPaths);return params.dryRun?{...preview,staged_paths:[]}:applyAndStage({...params,git,paths,newReceiptPathspecs})}export{resolveMergeSettlementGlobal,stageLosslessMergeReconciliation};
2
+ //# sourceMappingURL=stage.js.map
@@ -1,2 +1,2 @@
1
- (function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="3483e997-6bee-52bf-a048-8fe5c2427d06")}catch{}})();import{assertInitializedTracker}from"./environment/tracker-preflight.js";import{createHash}from"node:crypto";import{setTimeout as delay}from"node:timers/promises";import{queryHistoryEventIndex,queryHistoryEventStreams,rebuildHistoryEventIndex}from"../core/history/event-index.js";import{EXIT_CODE}from"../core/shared/constants.js";import{PmCliError}from"../core/shared/errors.js";import{stableStringify}from"../core/shared/serialization.js";import{resolvePmRoot}from"../core/store/paths.js";import{readSettings}from"../core/store/settings.js";import{parseHistoryProvenanceFilters,projectHistoryProvenance,resolveHistoryProvenanceDimensions,summarizeHistoryProvenance}from"./history-provenance.js";const MUTATION_EVENT_CURSOR_VERSION=1,MUTATION_EVENT_CURSOR_PATTERN=/^[A-Za-z0-9_-]+$/,DEFAULT_EVENT_LIMIT=100,MAX_EVENT_LIMIT=1e3,DEFAULT_FOLLOW_INTERVAL_MS=250,MUTATION_EVENT_WIRE_BUDGET=Object.freeze({batch_to_row_ratio:.6,fixture_events:200});function normalizeFilter(value){if(value===void 0)return;const values=typeof value=="string"?[value]:value,normalized=[...new Set(values.flatMap(entry=>entry.split(",")).map(entry=>entry.trim()).filter(Boolean))].sort((left,right)=>left.localeCompare(right));return normalized.length===0?void 0:normalized}function eventQueryFingerprint(options){return createHash("sha256").update(stableStringify({type:normalizeFilter(options.type),author:normalizeFilter(options.author),item:normalizeFilter(options.item),harness:normalizeFilter(options.harness),agent_instance:normalizeFilter(options.agentInstance),provenance:normalizeFilter(options.provenanceFilter)})).digest("hex").slice(0,24)}function encodeMutationEventCursor(event,fingerprint){const envelope={version:MUTATION_EVENT_CURSOR_VERSION,fingerprint,ts:event.entry.ts,stream_id:event.stream_id,stream_offset:event.stream_offset};return Buffer.from(JSON.stringify(envelope),"utf8").toString("base64url")}function isMutationEventCursorEnvelope(value,fingerprint){if(typeof value!="object"||value===null)return!1;const candidate=value;return[candidate.version===MUTATION_EVENT_CURSOR_VERSION,candidate.fingerprint===fingerprint,typeof candidate.ts=="string",typeof candidate.stream_id=="string",typeof candidate.stream_offset=="number",Number.isSafeInteger(candidate.stream_offset),Number(candidate.stream_offset)>=0].every(Boolean)}function decodeMutationEventCursor(cursor,fingerprint){const normalized=cursor.trim();if(normalized.length===0||normalized.length>4096||!MUTATION_EVENT_CURSOR_PATTERN.test(normalized))throw new PmCliError("Invalid mutation event cursor.",EXIT_CODE.USAGE,{code:"invalid_event_cursor"});let parsed;try{parsed=JSON.parse(Buffer.from(normalized,"base64url").toString("utf8"))}catch{throw new PmCliError("Invalid mutation event cursor.",EXIT_CODE.USAGE,{code:"invalid_event_cursor"})}if(!isMutationEventCursorEnvelope(parsed,fingerprint))throw new PmCliError("Mutation event cursor does not match this query.",EXIT_CODE.USAGE,{code:"event_cursor_query_mismatch"});return parsed}function parseMutationEventLimit(value){const limit=value??DEFAULT_EVENT_LIMIT;if(!Number.isSafeInteger(limit)||limit<0||limit>MAX_EVENT_LIMIT)throw new PmCliError(`Mutation event limit must be an integer from 0 to ${MAX_EVENT_LIMIT}.`,EXIT_CODE.USAGE,{code:"invalid_event_limit"});return limit}function isAbortError(error){return error instanceof Error&&error.name==="AbortError"}function resolveMutationEventStart(sinceValue,fingerprint){const since=sinceValue?.trim();if(!since)return{};if(MUTATION_EVENT_CURSOR_PATTERN.test(since))return{cursor:decodeMutationEventCursor(since,fingerprint)};const milliseconds=Date.parse(since);if(!Number.isFinite(milliseconds))throw new PmCliError("Mutation event --since must be an event cursor or ISO timestamp.",EXIT_CODE.USAGE,{code:"invalid_event_since"});return{sinceTimestamp:new Date(milliseconds).toISOString()}}async function resolveIndexedMutationEvents(pmRoot,query){let indexed=await queryHistoryEventIndex(pmRoot,query);if(indexed===null){if(!await rebuildHistoryEventIndex(pmRoot))return{...await queryHistoryEventStreams(pmRoot,query),source:"authoritative_history"};indexed=await queryHistoryEventIndex(pmRoot,query)}if(indexed===null)throw new PmCliError("Mutation event index could not be opened after rebuild.",EXIT_CODE.GENERIC_FAILURE,{code:"event_index_unavailable"});return{...indexed,source:"derived_index"}}function resolveMutationEventCursorMode(value){const cursorMode=value??"batch";if(cursorMode!=="batch"&&cursorMode!=="row")throw new PmCliError("Mutation event cursor mode must be batch or row.",EXIT_CODE.USAGE,{code:"invalid_event_cursor_mode"});return cursorMode}async function listMutationEvents(options={}){const pmRoot=resolvePmRoot(options.cwd??process.cwd(),options.pmRoot);await assertInitializedTracker(pmRoot);const requestedLimit=parseMutationEventLimit(options.limit),cursorMode=resolveMutationEventCursorMode(options.cursorMode);if(options.full===!0&&options.provenance===!0)throw new PmCliError("Mutation event projections are mutually exclusive. Use --provenance or --full.",EXIT_CODE.USAGE);const settings=await readSettings(pmRoot),vocabulary=settings.agent_identity.identity_vocabulary,dimensions=resolveHistoryProvenanceDimensions(settings.agent_identity.harness_signals),provenancePredicates=parseHistoryProvenanceFilters({provenance:options.provenanceFilter},dimensions),harnesses=normalizeFilter(options.harness),fingerprint=eventQueryFingerprint(options),{cursor,sinceTimestamp}=resolveMutationEventStart(options.since,fingerprint),query={...cursor?{after_ts:cursor.ts,after_stream_id:cursor.stream_id,after_stream_offset:cursor.stream_offset}:sinceTimestamp?{since_ts:sinceTimestamp}:{},ops:normalizeFilter(options.type),authors:normalizeFilter(options.author),harnesses,harness_alias_authors:harnesses?Object.entries(vocabulary.aliases).filter(([,harness])=>harnesses.includes(harness)).map(([author])=>author):void 0,agent_instances:normalizeFilter(options.agentInstance),provenance:[...provenancePredicates].map(([dimension,values])=>({dimension,values:[...values]})),stream_ids:normalizeFilter(options.item),limit:requestedLimit},indexed=await resolveIndexedMutationEvents(pmRoot,query),events=indexed.events.map(event=>({...cursorMode==="row"?{cursor:encodeMutationEventCursor(event,fingerprint)}:{},item_id:event.stream_id,version:event.stream_offset+1,ts:event.entry.ts,author:event.entry.author,type:event.entry.op,...event.entry.message===void 0?{}:{message:event.entry.message},patch_count:event.entry.patch.length,...options.full===!0?{entry:event.entry}:{},...options.provenance===!0?{provenance:projectHistoryProvenance(event.entry,vocabulary,{itemId:event.stream_id,version:event.stream_offset+1})}:{}}));return{events,count:events.length,has_more:indexed.has_more,cursor_mode:cursorMode,...indexed.events.length===0?{}:{next_cursor:encodeMutationEventCursor(indexed.events[indexed.events.length-1],fingerprint)},source:indexed.source,...options.provenanceSummary===!0?{provenance_summary:summarizeHistoryProvenance(indexed.events.map(event=>event.entry),vocabulary,dimensions)}:{}}}async function*subscribeMutationEventBatches(options={}){const intervalMs=options.intervalMs??DEFAULT_FOLLOW_INTERVAL_MS;if(!Number.isSafeInteger(intervalMs)||intervalMs<10)throw new PmCliError("Mutation event follow interval must be an integer of at least 10ms.",EXIT_CODE.USAGE,{code:"invalid_event_follow_interval"});let cursor=options.since;for(;options.signal?.aborted!==!0;){const page=await listMutationEvents({...options,cursorMode:options.cursorMode??"batch",since:cursor});if(yield page.next_cursor===void 0&&cursor!==void 0?{...page,next_cursor:cursor}:page,page.events.length>0){cursor=page.next_cursor;continue}try{await delay(intervalMs,void 0,{signal:options.signal})}catch(error){if(isAbortError(error))return;throw error}}}async function*subscribeMutationEvents(options={}){for await(const page of subscribeMutationEventBatches({...options,cursorMode:"row"}))for(const event of page.events)yield event}const _testOnlyMutationEvents={decodeMutationEventCursor,encodeMutationEventCursor,eventQueryFingerprint,isAbortError,isMutationEventCursorEnvelope,parseMutationEventLimit,resolveMutationEventStart};export{MUTATION_EVENT_WIRE_BUDGET,_testOnlyMutationEvents,listMutationEvents,subscribeMutationEventBatches,subscribeMutationEvents};
1
+ (function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="62e71554-f62d-55c5-811c-198f573d9426")}catch{}})();import{assertInitializedTracker}from"./environment/tracker-preflight.js";import{createHash}from"node:crypto";import{setTimeout as delay}from"node:timers/promises";import{readHistoryEventIndexPage,queryHistoryEventStreams,rebuildHistoryEventIndex}from"../core/history/event-index.js";import{EXIT_CODE}from"../core/shared/constants.js";import{PmCliError}from"../core/shared/errors.js";import{stableStringify}from"../core/shared/serialization.js";import{toErrorMessage}from"../core/shared/primitives.js";import{resolvePmRoot}from"../core/store/paths.js";import{readSettings}from"../core/store/settings.js";import{parseHistoryProvenanceFilters,projectHistoryProvenance,resolveHistoryProvenanceDimensions,summarizeHistoryProvenance}from"./history-provenance.js";const MUTATION_EVENT_CURSOR_VERSION=1,MUTATION_EVENT_CURSOR_PATTERN=/^[A-Za-z0-9_-]+$/,DEFAULT_EVENT_LIMIT=100,MAX_EVENT_LIMIT=1e3,DEFAULT_FOLLOW_INTERVAL_MS=250,MUTATION_EVENT_WIRE_BUDGET=Object.freeze({batch_to_row_ratio:.6,fixture_events:200});function normalizeFilter(value){if(value===void 0)return;const values=typeof value=="string"?[value]:value,normalized=[...new Set(values.flatMap(entry=>entry.split(",")).map(entry=>entry.trim()).filter(Boolean))].sort((left,right)=>left.localeCompare(right));return normalized.length===0?void 0:normalized}function eventQueryFingerprint(options){return createHash("sha256").update(stableStringify({type:normalizeFilter(options.type),author:normalizeFilter(options.author),item:normalizeFilter(options.item),harness:normalizeFilter(options.harness),agent_instance:normalizeFilter(options.agentInstance),provenance:normalizeFilter(options.provenanceFilter)})).digest("hex").slice(0,24)}function encodeMutationEventCursor(event,fingerprint){const envelope={version:MUTATION_EVENT_CURSOR_VERSION,fingerprint,ts:event.entry.ts,stream_id:event.stream_id,stream_offset:event.stream_offset};return Buffer.from(JSON.stringify(envelope),"utf8").toString("base64url")}function isMutationEventCursorEnvelope(value,fingerprint){if(typeof value!="object"||value===null)return!1;const candidate=value;return[candidate.version===MUTATION_EVENT_CURSOR_VERSION,candidate.fingerprint===fingerprint,typeof candidate.ts=="string",typeof candidate.stream_id=="string",typeof candidate.stream_offset=="number",Number.isSafeInteger(candidate.stream_offset),Number(candidate.stream_offset)>=0].every(Boolean)}function decodeMutationEventCursor(cursor,fingerprint){const normalized=cursor.trim();if(normalized.length===0||normalized.length>4096||!MUTATION_EVENT_CURSOR_PATTERN.test(normalized))throw new PmCliError("Invalid mutation event cursor.",EXIT_CODE.USAGE,{code:"invalid_event_cursor"});let parsed;try{parsed=JSON.parse(Buffer.from(normalized,"base64url").toString("utf8"))}catch{throw new PmCliError("Invalid mutation event cursor.",EXIT_CODE.USAGE,{code:"invalid_event_cursor"})}if(!isMutationEventCursorEnvelope(parsed,fingerprint))throw new PmCliError("Mutation event cursor does not match this query.",EXIT_CODE.USAGE,{code:"event_cursor_query_mismatch"});return parsed}function parseMutationEventLimit(value){const limit=value??DEFAULT_EVENT_LIMIT;if(!Number.isSafeInteger(limit)||limit<0||limit>MAX_EVENT_LIMIT)throw new PmCliError(`Mutation event limit must be an integer from 0 to ${MAX_EVENT_LIMIT}.`,EXIT_CODE.USAGE,{code:"invalid_event_limit"});return limit}function isAbortError(error){return error instanceof Error&&error.name==="AbortError"}function resolveMutationEventStart(sinceValue,fingerprint){const since=sinceValue?.trim();if(!since)return{};if(MUTATION_EVENT_CURSOR_PATTERN.test(since))return{cursor:decodeMutationEventCursor(since,fingerprint)};const milliseconds=Date.parse(since);if(!Number.isFinite(milliseconds))throw new PmCliError("Mutation event --since must be an event cursor or ISO timestamp.",EXIT_CODE.USAGE,{code:"invalid_event_since"});return{sinceTimestamp:new Date(milliseconds).toISOString()}}async function resolveIndexedMutationEvents(pmRoot,query){const attempt=await readHistoryEventIndexPage(pmRoot,query);let indexed=attempt.result;if(indexed===null&&attempt.repair_needed)try{await rebuildHistoryEventIndex(pmRoot,{lockWaitMs:0})&&(indexed=(await readHistoryEventIndexPage(pmRoot,query)).result)}catch{}if(indexed===null)try{return{...await queryHistoryEventStreams(pmRoot,query),source:"authoritative_history"}}catch(error){throw error instanceof PmCliError?error:new PmCliError("Mutation events are unavailable because authoritative history could not be read.",EXIT_CODE.GENERIC_FAILURE,{code:"event_index_unavailable",why:toErrorMessage(error)})}return{...indexed,source:"derived_index"}}function resolveMutationEventCursorMode(value){const cursorMode=value??"batch";if(cursorMode!=="batch"&&cursorMode!=="row")throw new PmCliError("Mutation event cursor mode must be batch or row.",EXIT_CODE.USAGE,{code:"invalid_event_cursor_mode"});return cursorMode}async function listMutationEvents(options={}){const pmRoot=resolvePmRoot(options.cwd??process.cwd(),options.pmRoot);await assertInitializedTracker(pmRoot);const requestedLimit=parseMutationEventLimit(options.limit),cursorMode=resolveMutationEventCursorMode(options.cursorMode);if(options.full===!0&&options.provenance===!0)throw new PmCliError("Mutation event projections are mutually exclusive. Use --provenance or --full.",EXIT_CODE.USAGE);const settings=await readSettings(pmRoot),vocabulary=settings.agent_identity.identity_vocabulary,dimensions=resolveHistoryProvenanceDimensions(settings.agent_identity.harness_signals),provenancePredicates=parseHistoryProvenanceFilters({provenance:options.provenanceFilter},dimensions),harnesses=normalizeFilter(options.harness),fingerprint=eventQueryFingerprint(options),{cursor,sinceTimestamp}=resolveMutationEventStart(options.since,fingerprint),query={...cursor?{after_ts:cursor.ts,after_stream_id:cursor.stream_id,after_stream_offset:cursor.stream_offset}:sinceTimestamp?{since_ts:sinceTimestamp}:{},ops:normalizeFilter(options.type),authors:normalizeFilter(options.author),harnesses,harness_alias_authors:harnesses?Object.entries(vocabulary.aliases).filter(([,harness])=>harnesses.includes(harness)).map(([author])=>author):void 0,agent_instances:normalizeFilter(options.agentInstance),provenance:[...provenancePredicates].map(([dimension,values])=>({dimension,values:[...values]})),stream_ids:normalizeFilter(options.item),limit:requestedLimit},indexed=await resolveIndexedMutationEvents(pmRoot,query),events=indexed.events.map(event=>({...cursorMode==="row"?{cursor:encodeMutationEventCursor(event,fingerprint)}:{},item_id:event.stream_id,version:event.stream_offset+1,ts:event.entry.ts,author:event.entry.author,type:event.entry.op,...event.entry.message===void 0?{}:{message:event.entry.message},patch_count:event.entry.patch.length,...options.full===!0?{entry:event.entry}:{},...options.provenance===!0?{provenance:projectHistoryProvenance(event.entry,vocabulary,{itemId:event.stream_id,version:event.stream_offset+1})}:{}}));return{events,count:events.length,has_more:indexed.has_more,cursor_mode:cursorMode,...indexed.events.length===0?{}:{next_cursor:encodeMutationEventCursor(indexed.events[indexed.events.length-1],fingerprint)},source:indexed.source,...options.provenanceSummary===!0?{provenance_summary:summarizeHistoryProvenance(indexed.events.map(event=>event.entry),vocabulary,dimensions)}:{}}}async function*subscribeMutationEventBatches(options={}){const intervalMs=options.intervalMs??DEFAULT_FOLLOW_INTERVAL_MS;if(!Number.isSafeInteger(intervalMs)||intervalMs<10)throw new PmCliError("Mutation event follow interval must be an integer of at least 10ms.",EXIT_CODE.USAGE,{code:"invalid_event_follow_interval"});let cursor=options.since;for(;options.signal?.aborted!==!0;){const page=await listMutationEvents({...options,cursorMode:options.cursorMode??"batch",since:cursor});if(yield page.next_cursor===void 0&&cursor!==void 0?{...page,next_cursor:cursor}:page,page.events.length>0){cursor=page.next_cursor;continue}try{await delay(intervalMs,void 0,{signal:options.signal})}catch(error){if(isAbortError(error))return;throw error}}}async function*subscribeMutationEvents(options={}){for await(const page of subscribeMutationEventBatches({...options,cursorMode:"row"}))for(const event of page.events)yield event}const _testOnlyMutationEvents={decodeMutationEventCursor,encodeMutationEventCursor,eventQueryFingerprint,isAbortError,isMutationEventCursorEnvelope,parseMutationEventLimit,resolveMutationEventStart};export{MUTATION_EVENT_WIRE_BUDGET,_testOnlyMutationEvents,listMutationEvents,subscribeMutationEventBatches,subscribeMutationEvents};
2
2
  //# sourceMappingURL=mutation-events.js.map