@unbrained/pm-cli 2026.10.24 → 2026.10.26

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 (222) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/CHANGELOG.md +48 -0
  3. package/dist/cli/error-guidance.d.ts +5 -0
  4. package/dist/cli/error-guidance.js +2 -2
  5. package/dist/cli/extension-command-help.js +2 -2
  6. package/dist/cli/register-history-activity.js +1 -1
  7. package/dist/cli/register-mutation.js +2 -2
  8. package/dist/cli-bundle/bundle-manifest.json +1 -1
  9. package/dist/cli-bundle/chunks/{chunk-3S5FLCG7.js → chunk-25MTQAHX.js} +2 -2
  10. package/dist/cli-bundle/chunks/chunk-57YPOOED.js +2 -0
  11. package/dist/cli-bundle/chunks/{chunk-YEI4N4A6.js → chunk-7UDQAINZ.js} +22 -22
  12. package/dist/cli-bundle/chunks/{chunk-ZDVTXLA6.js → chunk-ER7RLJW5.js} +2 -2
  13. package/dist/cli-bundle/chunks/{chunk-CSE47ZAN.js → chunk-MWCG25PO.js} +2 -2
  14. package/dist/cli-bundle/chunks/chunk-N6FTEILW.js +20 -0
  15. package/dist/cli-bundle/chunks/{chunk-CDR5ES7Q.js → chunk-NGKTROAO.js} +90 -86
  16. package/dist/cli-bundle/chunks/chunk-OCDRHIVQ.js +6 -0
  17. package/dist/cli-bundle/chunks/{chunk-R7KWBFM2.js → chunk-ONC4BBQP.js} +2 -2
  18. package/dist/cli-bundle/chunks/{chunk-URZB3USE.js → chunk-OUDR2HWR.js} +42 -42
  19. package/dist/cli-bundle/chunks/{chunk-FASDGYQC.js → chunk-T7T4WLUY.js} +3 -3
  20. package/dist/cli-bundle/chunks/{chunk-Z62RXNHW.js → chunk-TGOG5BFM.js} +2 -2
  21. package/dist/cli-bundle/chunks/chunk-UF6EXZSP.js +2 -0
  22. package/dist/cli-bundle/chunks/chunk-VS2IIV3F.js +128 -0
  23. package/dist/cli-bundle/chunks/chunk-XAIR53QZ.js +2 -0
  24. package/dist/cli-bundle/chunks/{chunk-VSSHB6XI.js → chunk-Y42DYMZX.js} +2 -2
  25. package/dist/cli-bundle/chunks/{chunk-MA7SNKYK.js → chunk-YJBIPNDL.js} +2 -2
  26. package/dist/cli-bundle/chunks/{chunk-4PNAUT2E.js → chunk-Z35XAHUN.js} +2 -2
  27. package/dist/cli-bundle/chunks/chunk-ZLVFUZI4.js +30 -0
  28. package/dist/cli-bundle/chunks/{chunk-3VI4XDP2.js → chunk-ZT64RDMF.js} +2 -2
  29. package/dist/cli-bundle/chunks/{indexed-receipt-blobs-XANVELIV.js → indexed-receipt-blobs-NWQIDTH7.js} +2 -2
  30. package/dist/cli-bundle/chunks/{mutation-admission-NVTBZ7DH.js → mutation-admission-VTNV6JV2.js} +2 -2
  31. package/dist/cli-bundle/chunks/{register-list-query-2FE6K7FQ.js → register-list-query-LZDWUZS4.js} +3 -3
  32. package/dist/cli-bundle/chunks/register-mutation-M2A7PH42.js +20 -0
  33. package/dist/cli-bundle/chunks/register-operations-E7P2JTXM.js +2 -0
  34. package/dist/cli-bundle/chunks/{register-setup-P3VLCTDY.js → register-setup-DOV7KGSH.js} +2 -2
  35. package/dist/cli-bundle/chunks/{settlement-hook-YUVCO65H.js → settlement-hook-SVD6CO7U.js} +2 -2
  36. package/dist/cli-bundle/chunks/stage-LYLEOAQB.js +2 -0
  37. package/dist/cli-bundle/focused-chunks/chunk-226XKW3W.js +8 -0
  38. package/dist/cli-bundle/focused-chunks/chunk-ABMNYWV6.js +2 -0
  39. package/dist/cli-bundle/focused-chunks/{chunk-RSEGFZTY.js → chunk-ADSBGYLW.js} +54 -54
  40. package/dist/cli-bundle/focused-chunks/{chunk-JYXA4JIP.js → chunk-B266DDBJ.js} +2 -2
  41. package/dist/cli-bundle/focused-chunks/{chunk-R536272H.js → chunk-BH7RE2ZY.js} +2 -2
  42. package/dist/cli-bundle/focused-chunks/chunk-BL77VCRB.js +22 -0
  43. package/dist/cli-bundle/focused-chunks/chunk-CSCZYSNZ.js +2 -0
  44. package/dist/cli-bundle/focused-chunks/{chunk-6HJCSHBG.js → chunk-D5YQ4KAE.js} +2 -2
  45. package/dist/cli-bundle/focused-chunks/{chunk-TAHSXKAT.js → chunk-E5C24MCV.js} +2 -2
  46. package/dist/cli-bundle/focused-chunks/{chunk-SHUPX6G2.js → chunk-I7H3XF25.js} +2 -2
  47. package/dist/cli-bundle/focused-chunks/chunk-IF6C43IP.js +2 -0
  48. package/dist/cli-bundle/focused-chunks/chunk-KERJNFXY.js +2 -0
  49. package/dist/cli-bundle/focused-chunks/{chunk-BUZ2Z4P3.js → chunk-KTXU5WU6.js} +2 -2
  50. package/dist/cli-bundle/focused-chunks/chunk-NE27VCU7.js +91 -0
  51. package/dist/cli-bundle/focused-chunks/chunk-NFN2P4TW.js +2 -0
  52. package/dist/cli-bundle/focused-chunks/{chunk-7RQOFHMH.js → chunk-NLZUI234.js} +4 -4
  53. package/dist/cli-bundle/focused-chunks/chunk-PK5FJIVA.js +12 -0
  54. package/dist/cli-bundle/focused-chunks/{chunk-IUAYTD2S.js → chunk-UHWTPOM4.js} +24 -24
  55. package/dist/cli-bundle/focused-chunks/{chunk-ES2NGVHZ.js → chunk-UP765R2V.js} +2 -2
  56. package/dist/cli-bundle/focused-chunks/{chunk-POLMFGPQ.js → chunk-WDWHUJQW.js} +2 -2
  57. package/dist/cli-bundle/focused-chunks/{chunk-NX7TVNUM.js → chunk-YQKLKRMW.js} +2 -2
  58. package/dist/cli-bundle/focused-chunks/chunk-YTEC2EPM.js +3 -0
  59. package/dist/cli-bundle/focused-chunks/{chunk-RVUGTIIU.js → chunk-YW5GHQN2.js} +2 -2
  60. package/dist/cli-bundle/focused-chunks/{indexed-receipt-blobs-FVOA3XMI.js → indexed-receipt-blobs-YFDNGHCM.js} +2 -2
  61. package/dist/cli-bundle/focused-chunks/{mutation-admission-PB6YFY7S.js → mutation-admission-L42E3U5U.js} +2 -2
  62. package/dist/cli-bundle/focused-chunks/{settlement-hook-2VXEB6PP.js → settlement-hook-3CADO4XZ.js} +2 -2
  63. package/dist/cli-bundle/focused-chunks/stage-TUQHWU7N.js +2 -0
  64. package/dist/cli-bundle/main.js +19 -19
  65. package/dist/cli-bundle/sdk-authoring.js +1 -1
  66. package/dist/cli-bundle/sdk-contracts.js +2 -2
  67. package/dist/cli-bundle/sdk-core.js +23 -23
  68. package/dist/cli-bundle/sdk-governance.js +1 -1
  69. package/dist/cli-bundle/sdk-graph.js +1 -1
  70. package/dist/cli-bundle/sdk-merge.js +57 -58
  71. package/dist/cli-bundle/sdk-query.js +1 -1
  72. package/dist/cli-bundle/sdk-runtime.js +1 -1
  73. package/dist/cli-bundle/sdk-testing.js +1 -1
  74. package/dist/cli-bundle/sdk.js +9 -9
  75. package/dist/core/diagnostics/remediation.js +1 -1
  76. package/dist/core/extensions/extension-types.d.ts +3 -0
  77. package/dist/core/extensions/extension-types.js +1 -1
  78. package/dist/core/output/mutation-projection.js +1 -1
  79. package/dist/core/policy/workflow-policy-store.js +1 -1
  80. package/dist/core/shared/errors.d.ts +22 -0
  81. package/dist/core/shared/errors.js +1 -1
  82. package/dist/core/shared/secret-rules.d.ts +17 -0
  83. package/dist/core/shared/secret-rules.js +2 -1
  84. package/dist/core/store/item-mutation-precondition.d.ts +8 -0
  85. package/dist/core/store/item-mutation-precondition.js +1 -1
  86. package/dist/core/store/item-store.js +1 -1
  87. package/dist/core/store/settings.js +2 -2
  88. package/dist/sdk/agent/command-recovery.js +1 -1
  89. package/dist/sdk/cli-contracts/commander-mutation-options.js +1 -1
  90. package/dist/sdk/cli-contracts/commander-types.js +1 -1
  91. package/dist/sdk/cli-contracts/completeness.js +1 -1
  92. package/dist/sdk/cli-contracts/flag-contracts.d.ts +2 -0
  93. package/dist/sdk/cli-contracts/flag-contracts.js +1 -1
  94. package/dist/sdk/cli-contracts/flag-lexicon-contracts.js +1 -1
  95. package/dist/sdk/cli-contracts/grammar-contracts.js +1 -1
  96. package/dist/sdk/cli-contracts/registration-helpers.d.ts +3 -1
  97. package/dist/sdk/cli-contracts/registration-helpers.js +2 -2
  98. package/dist/sdk/cli-contracts/runtime-contracts.js +1 -1
  99. package/dist/sdk/cli-contracts/tool-option-contracts.d.ts +4 -0
  100. package/dist/sdk/cli-contracts/tool-option-contracts.js +1 -1
  101. package/dist/sdk/cli-contracts/tool-parameter-tables.js +1 -1
  102. package/dist/sdk/cli-contracts/tool-schema.d.ts +2 -2
  103. package/dist/sdk/cli-contracts/tool-schema.js +1 -1
  104. package/dist/sdk/completion/fish.js +3 -1
  105. package/dist/sdk/completion/zsh.js +3 -1
  106. package/dist/sdk/context-signal-store.js +2 -2
  107. package/dist/sdk/core.d.ts +1 -1
  108. package/dist/sdk/core.js +1 -1
  109. package/dist/sdk/extension-command-context.js +2 -2
  110. package/dist/sdk/generated/generated-error-code-catalog-part-1.js +1 -1
  111. package/dist/sdk/generated/generated-error-code-catalog-part-2.js +1 -1
  112. package/dist/sdk/governance/health-merge-evidence.d.ts +5 -0
  113. package/dist/sdk/governance/health-merge-evidence.js +1 -1
  114. package/dist/sdk/governance/health.js +1 -1
  115. package/dist/sdk/index.d.ts +3 -2
  116. package/dist/sdk/index.js +1 -1
  117. package/dist/sdk/item-transaction.js +1 -1
  118. package/dist/sdk/lifecycle/close-many.d.ts +3 -0
  119. package/dist/sdk/lifecycle/close-many.js +1 -1
  120. package/dist/sdk/lifecycle/close.d.ts +3 -1
  121. package/dist/sdk/lifecycle/close.js +1 -1
  122. package/dist/sdk/lifecycle/create.d.ts +2 -0
  123. package/dist/sdk/lifecycle/create.js +1 -1
  124. package/dist/sdk/lifecycle/plan.d.ts +2 -0
  125. package/dist/sdk/lifecycle/plan.js +1 -1
  126. package/dist/sdk/lifecycle/restore.js +1 -1
  127. package/dist/sdk/lifecycle-policy.d.ts +11 -0
  128. package/dist/sdk/lifecycle-policy.js +1 -1
  129. package/dist/sdk/merge/history-scalar-rebase.d.ts +9 -0
  130. package/dist/sdk/merge/history-scalar-rebase.js +2 -0
  131. package/dist/sdk/merge/ignored-artifacts.d.ts +13 -0
  132. package/dist/sdk/merge/ignored-artifacts.js +2 -0
  133. package/dist/sdk/merge/stage.js +1 -1
  134. package/dist/sdk/merge/three-way.js +1 -1
  135. package/dist/sdk/mutation-guard.d.ts +6 -1
  136. package/dist/sdk/mutation-guard.js +1 -1
  137. package/dist/sdk/output/fit-envelope.d.ts +98 -0
  138. package/dist/sdk/output/fit-envelope.js +2 -0
  139. package/dist/sdk/public-surface.json +1 -1
  140. package/dist/sdk/query/activity.d.ts +14 -2
  141. package/dist/sdk/query/activity.js +1 -1
  142. package/dist/sdk/query/calendar.d.ts +16 -0
  143. package/dist/sdk/query/calendar.js +3 -3
  144. package/dist/sdk/query/context.js +2 -2
  145. package/dist/sdk/query/get.js +1 -1
  146. package/dist/sdk/query/list.js +1 -1
  147. package/dist/sdk/query/search-contracts.js +1 -1
  148. package/dist/sdk/query/window-pagination.d.ts +20 -0
  149. package/dist/sdk/query/window-pagination.js +2 -0
  150. package/dist/sdk/query.d.ts +1 -0
  151. package/dist/sdk/query.js +1 -1
  152. package/dist/sdk/read-output-contracts.js +1 -1
  153. package/dist/sdk/read-output-session.d.ts +1 -1
  154. package/dist/sdk/read-output-session.js +1 -1
  155. package/dist/sdk/runtime-input.js +1 -1
  156. package/dist/sdk/runtime-primitives.d.ts +1 -1
  157. package/dist/sdk/runtime-primitives.js +1 -1
  158. package/dist/sdk/runtime.d.ts +3 -3
  159. package/dist/sdk/runtime.js +1 -1
  160. package/dist/sdk/scheduling-shortcuts.d.ts +2 -0
  161. package/dist/sdk/scheduling-shortcuts.js +1 -1
  162. package/dist/sdk/schema-migration.js +2 -2
  163. package/dist/sdk/templates.d.ts +7 -1
  164. package/dist/sdk/templates.js +2 -2
  165. package/dist/sdk/workspace-transaction.d.ts +3 -1
  166. package/dist/sdk/workspace-transaction.js +2 -2
  167. package/docs/COMMANDS.md +21 -2
  168. package/docs/EXTENSIONS.md +1 -1
  169. package/docs/MERGE_SAFETY.md +28 -0
  170. package/docs/MUTATION_INTEGRITY.md +33 -0
  171. package/docs/QUALITY_EVIDENCE.md +9 -0
  172. package/docs/READ_OUTPUT_CONTRACTS.md +55 -1
  173. package/docs/SDK.md +173 -12
  174. package/docs/WORKFLOW_POLICIES.md +14 -1
  175. package/docs/agent-task-token-baseline.json +1 -1
  176. package/docs/agent-task-transcripts.json +1 -1
  177. package/docs/generated/FLAG_LEXICON_BUDGETS.md +4 -4
  178. package/docs/generated/REFUSAL_CLOSURE_CENSUS.md +6 -3
  179. package/marketplace.json +1 -1
  180. package/package.json +1 -1
  181. package/packages/pm-beads/package.json +1 -1
  182. package/packages/pm-calendar/extensions/calendar/index.ts +6 -0
  183. package/packages/pm-calendar/extensions/calendar/shortcuts.ts +2 -2
  184. package/packages/pm-calendar/package.json +1 -1
  185. package/packages/pm-command-kit/package.json +1 -1
  186. package/packages/pm-digital-twin/package.json +1 -1
  187. package/packages/pm-governance-audit/package.json +1 -1
  188. package/packages/pm-guide-shell/package.json +1 -1
  189. package/packages/pm-kanban/package.json +1 -1
  190. package/packages/pm-lifecycle-hooks/package.json +1 -1
  191. package/packages/pm-linked-test-adapters/package.json +1 -1
  192. package/packages/pm-search-advanced/package.json +1 -1
  193. package/packages/pm-templates/extensions/templates/runtime.ts +3 -1
  194. package/packages/pm-templates/package.json +1 -1
  195. package/packages/pm-todos/package.json +1 -1
  196. package/packages/pm-vcs/package.json +1 -1
  197. package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
  198. package/plugins/pm-claude/package.json +1 -1
  199. package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
  200. package/plugins/pm-codex/package.json +1 -1
  201. package/plugins/pm-codex/plugin.json +1 -1
  202. package/runtime-dependencies.json +1 -1
  203. package/dist/cli-bundle/chunks/chunk-CNEUJHA4.js +0 -2
  204. package/dist/cli-bundle/chunks/chunk-HZNFNETE.js +0 -127
  205. package/dist/cli-bundle/chunks/chunk-KGZGXC7T.js +0 -30
  206. package/dist/cli-bundle/chunks/chunk-TZYQ4JDZ.js +0 -6
  207. package/dist/cli-bundle/chunks/chunk-UY5UHI4I.js +0 -2
  208. package/dist/cli-bundle/chunks/chunk-VPMQEVU6.js +0 -20
  209. package/dist/cli-bundle/chunks/register-mutation-KZXQUZUH.js +0 -20
  210. package/dist/cli-bundle/chunks/register-operations-BOCBWNKJ.js +0 -2
  211. package/dist/cli-bundle/chunks/stage-WRDTMCCF.js +0 -2
  212. package/dist/cli-bundle/focused-chunks/chunk-3QYTS4OY.js +0 -12
  213. package/dist/cli-bundle/focused-chunks/chunk-5MKNVJCM.js +0 -2
  214. package/dist/cli-bundle/focused-chunks/chunk-AGCEWJ27.js +0 -21
  215. package/dist/cli-bundle/focused-chunks/chunk-CEEOWDB7.js +0 -8
  216. package/dist/cli-bundle/focused-chunks/chunk-GXAXDPD4.js +0 -2
  217. package/dist/cli-bundle/focused-chunks/chunk-NFFPNMTH.js +0 -2
  218. package/dist/cli-bundle/focused-chunks/chunk-SBJ2IKTH.js +0 -3
  219. package/dist/cli-bundle/focused-chunks/chunk-X5YRJMAY.js +0 -2
  220. package/dist/cli-bundle/focused-chunks/chunk-XTYEASIY.js +0 -2
  221. package/dist/cli-bundle/focused-chunks/chunk-ZMWLV2CE.js +0 -91
  222. package/dist/cli-bundle/focused-chunks/stage-MQE4PFWT.js +0 -2
@@ -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]="ce212ffb-1f13-5893-9690-31dbb4e0de13")}catch{}})();import{assertInitializedTracker}from"../environment/tracker-preflight.js";import{normalizeItemFormatVersion,requiredItemFormatVersion}from"../../core/item/item-format-version.js";import{assertProjectRuntimeFormatGuard}from"../environment/project-runtime-compatibility.js";import{enforceWorkflowMutation}from"../../core/policy/workflow-policy-store.js";import jsonPatch from"fast-json-patch";import fs from"node:fs/promises";import{readLocatedItemSnapshot}from"../../core/store/item-store.js";import{readHistorySnapshot}from"../../core/history/read.js";import{verifyHistoryRewriteNoDrift}from"../../core/history/history-rewrite.js";import{resolveHistorySubject}from"../history/subject.js";import{historyVersionOffset}from"../../core/history/version-address.js";import{writeFileAtomic,appendHistoryEntry,createHistoryEntry,replayToCanonicalItemDocument,replayToItemDocument,toReplayDocument,enforceHistoryStreamPolicyForItem,canonicalDocument,serializeItemDocument,resolveItemTypeRegistry,acquireLockLease,EXIT_CODE,PmCliError,nowIso,getActiveExtensionRegistrations,projectAfterCommandItemSnapshot,recordAfterCommandAffectedItem,runActiveOnWriteHooks,runActiveBeforeMutationHooks,createMutationGuardSdk,getItemPath,resolvePmRoot,resolveWorkspaceRoot,readSettings,resolveAuthor}from"../runtime-primitives.js";import{applyHistoryPatch,ensureMaterializedHistoryTarget as ensureMaterializedRestoreTarget,extractPatchFailureContext,replayHistoryToTarget as replayToTarget,resolveHistoryTarget}from"../history-read.js";import{acquireItemMetadataDerivedIndexLock,refreshItemMetadataDerivedIndex}from"../item-metadata-index.js";async function readRestoreItem(located,settings,history){const snapshot=await readLocatedItemSnapshot(located,{schema:settings.schema});return snapshot.error===null?{raw:snapshot.raw,document:snapshot.document}:{raw:snapshot.raw,document:replayCurrentDocument(history),recovered:!0}}function ensureReplayTarget(target,history){return resolveHistoryTarget(target,history,{errorSubject:"restore",allowFutureTimestamp:!0})}function replayCurrentDocument(history){const currentReplay=replayToTarget(history,history.length-1);return Object.keys(currentReplay.metadata).length===0?{metadata:{},body:currentReplay.body}:canonicalDocument(replayToItemDocument(currentReplay))}async function resolveRestoreSubject(pmRoot,id,settings,typeToFolder){const subject=await resolveHistorySubject(pmRoot,id,settings,typeToFolder),historyPolicy=subject.located?await enforceHistoryStreamPolicyForItem({pmRoot,settings,itemId:subject.id,commandLabel:"restore"}):{warnings:[]};return{...subject,historyPolicyWarnings:historyPolicy.warnings}}function changedFields(beforeDocument,afterDocument){const beforeReplay=toReplayDocument(beforeDocument),afterReplay=toReplayDocument(afterDocument),patch=jsonPatch.compare(beforeReplay,afterReplay),fields=new Set;for(const op of patch){if(op.path==="/body"||op.path.startsWith("/body/")){fields.add("body");continue}const segment=op.path.replace(/^\/(?:metadata|front_matter)\/?/,"").split("/")[0];fields.add(segment.replaceAll("~1","/").replaceAll("~0","~"))}return Array.from(fields).sort((a,b)=>a.localeCompare(b))}async function loadRestoreStateUnderLock(params){const verified=await verifyHistoryRewriteNoDrift({pmRoot:params.pmRoot,subject:params.subject,settings:params.settings,typeRegistry:params.typeRegistry,historyRawBeforeLock:params.historyRawBeforeLock,currentItemRawBeforeLock:params.currentItemRawBeforeLock,operation:"restore",readItem:located=>readRestoreItem(located,params.settings,params.history)});return{loadedItemUnderLock:verified.loadedItemUnderLock,existingItemPath:verified.locatedUnderLock?.itemPath??null}}function resolveRestoreCurrentState(loadedItemUnderLock,history){return loadedItemUnderLock?{document:loadedItemUnderLock.document,originalRaw:loadedItemUnderLock.raw}:{document:replayCurrentDocument(history),originalRaw:null}}function collectRestoreOwnershipWarnings(params){const assigned=params.document.metadata.assignee?.trim();if(!(assigned&&assigned!==params.author&&!params.force))return[];if(params.enforcement==="strict")throw new PmCliError(`Item ${params.resolvedId} is assigned to ${assigned}. Use --force to override.`,EXIT_CODE.CONFLICT);return params.enforcement==="warn"?[`ownership_warning:assignee_conflict:${params.resolvedId}:${assigned}`]:[]}async function restorePreviousItemAfterHistoryFailure(params){if(params.existingItemPath&&params.resolvedOriginalRaw!==null&&params.restoredItemPath!==params.existingItemPath){await writeFileAtomic(params.existingItemPath,params.resolvedOriginalRaw),await fs.rm(params.restoredItemPath,{force:!0});return}if(params.existingItemPath&&params.resolvedOriginalRaw!==null){await writeFileAtomic(params.existingItemPath,params.resolvedOriginalRaw);return}await fs.rm(params.restoredItemPath,{force:!0})}async function runRestoreWriteHooks(params){return[...await runActiveOnWriteHooks({path:params.restoredItemPath,scope:"project",op:"restore",item_id:params.after.metadata.id,item_type:params.after.metadata.type,before:params.before,after:params.after,changed_fields:["restored"]}),...await runActiveOnWriteHooks({path:params.historyPath,scope:"project",op:"restore:history",item_id:params.after.metadata.id,item_type:params.after.metadata.type,before:params.before,after:params.after,changed_fields:["restored"]})]}async function runRestore(id,target,options,global2){const pmRoot=resolvePmRoot(process.cwd(),global2.path);await assertInitializedTracker(pmRoot);const settings=await readSettings(pmRoot),typeRegistry=resolveItemTypeRegistry(settings,getActiveExtensionRegistrations()),subject=await resolveRestoreSubject(pmRoot,id,settings,typeRegistry.type_to_folder),resolvedId=subject.id,{raw:historyRawBeforeLock,entries:history}=await readHistorySnapshot(subject.historyPath,resolvedId);if(history.length===0)throw new PmCliError(`No history exists for ${resolvedId}; restore is unavailable.`,EXIT_CODE.NOT_FOUND);const resolvedTarget=ensureReplayTarget(target,history),replayDocument=ensureMaterializedRestoreTarget(replayToTarget(history,resolvedTarget.historyIndex),resolvedTarget),restoredDocument=replayToCanonicalItemDocument(replayDocument,{schema:settings.schema});if(restoredDocument.metadata.id!==resolvedId)throw new PmCliError(`Restore target resolved to item ${restoredDocument.metadata.id}, expected ${resolvedId}.`,EXIT_CODE.GENERIC_FAILURE);const currentItemRawBeforeLock=(subject.located?await readRestoreItem(subject.located,settings,history):null)?.raw??null,author=resolveAuthor(options.author,settings.author_default),lease=await acquireLockLease(pmRoot,resolvedId,settings.locks.ttl_seconds,author,!!options.force,settings.governance.force_required_for_stale_lock,settings.locks.wait_ms,{waitPolicy:"progress"}),releaseLock=lease.release;try{const{loadedItemUnderLock,existingItemPath}=await loadRestoreStateUnderLock({pmRoot,subject,settings,typeRegistry,historyRawBeforeLock,currentItemRawBeforeLock,history}),itemFormat="toon",currentState=resolveRestoreCurrentState(loadedItemUnderLock,history);restoredDocument.metadata.pm_format_version=normalizeItemFormatVersion(Math.max(requiredItemFormatVersion(restoredDocument.metadata),requiredItemFormatVersion(currentState.document.metadata))),assertProjectRuntimeFormatGuard(resolveWorkspaceRoot(pmRoot),requiredItemFormatVersion(restoredDocument.metadata));const ownershipWarnings=collectRestoreOwnershipWarnings({document:currentState.document,author,force:options.force,enforcement:settings.governance.ownership_enforcement,resolvedId}),restoreChangedFields=changedFields(currentState.document,restoredDocument),workflowPolicy=await enforceWorkflowMutation({pmRoot,settings,operation:"restore",author,before:currentState.document,after:restoredDocument});await runActiveBeforeMutationHooks({pm_root:pmRoot,operation:"restore",before:currentState.document,after:restoredDocument,changed_fields:restoreChangedFields,sdk:createMutationGuardSdk({pmRoot,settings,typeToFolder:typeRegistry.type_to_folder})});const serializedRestore=serializeItemDocument(restoredDocument,{format:itemFormat,schema:settings.schema}),restoredItemPath=getItemPath(pmRoot,restoredDocument.metadata.type,resolvedId,itemFormat,typeRegistry.type_to_folder),derivedIndexWarnings=await lease.fence(async()=>{const releaseDerivedIndexLock=await acquireItemMetadataDerivedIndexLock(pmRoot,author);let derivedIndexWarnings2;try{await writeFileAtomic(restoredItemPath,serializedRestore),existingItemPath&&restoredItemPath!==existingItemPath&&await fs.rm(existingItemPath);const historyEntry=createHistoryEntry({nowIso:nowIso(),author,op:"restore",before:currentState.document,after:restoredDocument,message:options.message,context:workflowPolicy.decisions.length===0?void 0:{workflow_policies:workflowPolicy.decisions}});try{await appendHistoryEntry(subject.historyPath,historyEntry)}catch(error){throw await restorePreviousItemAfterHistoryFailure({existingItemPath,resolvedOriginalRaw:currentState.originalRaw,restoredItemPath}),error}derivedIndexWarnings2=await refreshItemMetadataDerivedIndex({pmRoot,preferredFormat:settings.item_format,typeToFolder:typeRegistry.type_to_folder,schema:settings.schema,itemPath:restoredItemPath,previousItemPath:existingItemPath??void 0,document:restoredDocument})}finally{await releaseDerivedIndexLock()}return derivedIndexWarnings2}),hookWarnings=await runRestoreWriteHooks({restoredItemPath,historyPath:subject.historyPath,before:currentState.document,after:restoredDocument});recordAfterCommandAffectedItem({id:restoredDocument.metadata.id,op:"restore",item_type:restoredDocument.metadata.type,previous_status:currentState.document.metadata.status,status:restoredDocument.metadata.status,previous:projectAfterCommandItemSnapshot(currentState.document.metadata,restoreChangedFields),current:projectAfterCommandItemSnapshot(restoredDocument.metadata,restoreChangedFields),changed_fields:restoreChangedFields});const targetEntry=history[resolvedTarget.historyIndex],offset=historyVersionOffset(history,!0);return{item:restoredDocument.metadata,restored_from:{kind:resolvedTarget.kind,target:resolvedTarget.raw,history_index:resolvedTarget.historyIndex+1,version:offset===null?null:offset+resolvedTarget.historyIndex+1,entry_ts:targetEntry.ts,entry_op:targetEntry.op},changed_fields:restoreChangedFields,warnings:[...workflowPolicy.warnings,...loadedItemUnderLock?.recovered?["restore_unreadable_item_recovered"]:[],...subject.historyPolicyWarnings,...ownershipWarnings,...derivedIndexWarnings,...hookWarnings]}}finally{await releaseLock()}}const _testOnlyRestoreCommand={applyHistoryPatch,changedFields,ensureMaterializedRestoreTarget,ensureReplayTarget,extractPatchFailureContext,replayCurrentDocument,replayToTarget,resolveRestoreSubject};export{_testOnlyRestoreCommand,runRestore};
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]="f3145f17-e911-571b-9da3-f014e6fdde1c")}catch{}})();import{assertInitializedTracker}from"../environment/tracker-preflight.js";import{normalizeItemFormatVersion,requiredItemFormatVersion}from"../../core/item/item-format-version.js";import{assertProjectRuntimeFormatGuard}from"../environment/project-runtime-compatibility.js";import{enforceWorkflowMutation}from"../../core/policy/workflow-policy-store.js";import jsonPatch from"fast-json-patch";import fs from"node:fs/promises";import{readLocatedItemSnapshot}from"../../core/store/item-store.js";import{assertItemMutationPreconditions}from"../../core/store/item-mutation-precondition.js";import{readHistorySnapshot}from"../../core/history/read.js";import{verifyHistoryRewriteNoDrift}from"../../core/history/history-rewrite.js";import{resolveHistorySubject}from"../history/subject.js";import{historyVersionOffset}from"../../core/history/version-address.js";import{writeFileAtomic,appendHistoryEntry,createHistoryEntry,replayToCanonicalItemDocument,replayToItemDocument,toReplayDocument,enforceHistoryStreamPolicyForItem,canonicalDocument,serializeItemDocument,resolveItemTypeRegistry,acquireLockLease,EXIT_CODE,PmCliError,nowIso,getActiveExtensionRegistrations,projectAfterCommandItemSnapshot,recordAfterCommandAffectedItem,runActiveOnWriteHooks,runActiveBeforeMutationHooks,createMutationGuardSdk,getItemPath,resolvePmRoot,resolveWorkspaceRoot,readSettings,resolveAuthor}from"../runtime-primitives.js";import{applyHistoryPatch,ensureMaterializedHistoryTarget as ensureMaterializedRestoreTarget,extractPatchFailureContext,replayHistoryToTarget as replayToTarget,resolveHistoryTarget}from"../history-read.js";import{acquireItemMetadataDerivedIndexLock,refreshItemMetadataDerivedIndex}from"../item-metadata-index.js";async function readRestoreItem(located,settings,history){const snapshot=await readLocatedItemSnapshot(located,{schema:settings.schema});return snapshot.error===null?{raw:snapshot.raw,document:snapshot.document}:{raw:snapshot.raw,document:replayCurrentDocument(history),recovered:!0}}function ensureReplayTarget(target,history){return resolveHistoryTarget(target,history,{errorSubject:"restore",allowFutureTimestamp:!0})}function replayCurrentDocument(history){const currentReplay=replayToTarget(history,history.length-1);return Object.keys(currentReplay.metadata).length===0?{metadata:{},body:currentReplay.body}:canonicalDocument(replayToItemDocument(currentReplay))}async function resolveRestoreSubject(pmRoot,id,settings,typeToFolder){const subject=await resolveHistorySubject(pmRoot,id,settings,typeToFolder),historyPolicy=subject.located?await enforceHistoryStreamPolicyForItem({pmRoot,settings,itemId:subject.id,commandLabel:"restore"}):{warnings:[]};return{...subject,historyPolicyWarnings:historyPolicy.warnings}}function changedFields(beforeDocument,afterDocument){const beforeReplay=toReplayDocument(beforeDocument),afterReplay=toReplayDocument(afterDocument),patch=jsonPatch.compare(beforeReplay,afterReplay),fields=new Set;for(const op of patch){if(op.path==="/body"||op.path.startsWith("/body/")){fields.add("body");continue}const segment=op.path.replace(/^\/(?:metadata|front_matter)\/?/,"").split("/")[0];fields.add(segment.replaceAll("~1","/").replaceAll("~0","~"))}return Array.from(fields).sort((a,b)=>a.localeCompare(b))}async function loadRestoreStateUnderLock(params){const verified=await verifyHistoryRewriteNoDrift({pmRoot:params.pmRoot,subject:params.subject,settings:params.settings,typeRegistry:params.typeRegistry,historyRawBeforeLock:params.historyRawBeforeLock,currentItemRawBeforeLock:params.currentItemRawBeforeLock,operation:"restore",readItem:located=>readRestoreItem(located,params.settings,params.history)});return{loadedItemUnderLock:verified.loadedItemUnderLock,existingItemPath:verified.locatedUnderLock?.itemPath??null}}function resolveRestoreCurrentState(loadedItemUnderLock,history){return loadedItemUnderLock?{document:loadedItemUnderLock.document,originalRaw:loadedItemUnderLock.raw}:{document:replayCurrentDocument(history),originalRaw:null}}function collectRestoreOwnershipWarnings(params){const assigned=params.document.metadata.assignee?.trim();if(!(assigned&&assigned!==params.author&&!params.force))return[];if(params.enforcement==="strict")throw new PmCliError(`Item ${params.resolvedId} is assigned to ${assigned}. Use --force to override.`,EXIT_CODE.CONFLICT);return params.enforcement==="warn"?[`ownership_warning:assignee_conflict:${params.resolvedId}:${assigned}`]:[]}async function restorePreviousItemAfterHistoryFailure(params){if(params.existingItemPath&&params.resolvedOriginalRaw!==null&&params.restoredItemPath!==params.existingItemPath){await writeFileAtomic(params.existingItemPath,params.resolvedOriginalRaw),await fs.rm(params.restoredItemPath,{force:!0});return}if(params.existingItemPath&&params.resolvedOriginalRaw!==null){await writeFileAtomic(params.existingItemPath,params.resolvedOriginalRaw);return}await fs.rm(params.restoredItemPath,{force:!0})}async function runRestoreWriteHooks(params){return[...await runActiveOnWriteHooks({path:params.restoredItemPath,scope:"project",op:"restore",item_id:params.after.metadata.id,item_type:params.after.metadata.type,before:params.before,after:params.after,changed_fields:["restored"]}),...await runActiveOnWriteHooks({path:params.historyPath,scope:"project",op:"restore:history",item_id:params.after.metadata.id,item_type:params.after.metadata.type,before:params.before,after:params.after,changed_fields:["restored"]})]}async function runRestore(id,target,options,global2){const pmRoot=resolvePmRoot(process.cwd(),global2.path);await assertInitializedTracker(pmRoot);const settings=await readSettings(pmRoot),typeRegistry=resolveItemTypeRegistry(settings,getActiveExtensionRegistrations()),subject=await resolveRestoreSubject(pmRoot,id,settings,typeRegistry.type_to_folder),resolvedId=subject.id,{raw:historyRawBeforeLock,entries:history}=await readHistorySnapshot(subject.historyPath,resolvedId);if(history.length===0)throw new PmCliError(`No history exists for ${resolvedId}; restore is unavailable.`,EXIT_CODE.NOT_FOUND);const resolvedTarget=ensureReplayTarget(target,history),replayDocument=ensureMaterializedRestoreTarget(replayToTarget(history,resolvedTarget.historyIndex),resolvedTarget),restoredDocument=replayToCanonicalItemDocument(replayDocument,{schema:settings.schema});if(restoredDocument.metadata.id!==resolvedId)throw new PmCliError(`Restore target resolved to item ${restoredDocument.metadata.id}, expected ${resolvedId}.`,EXIT_CODE.GENERIC_FAILURE);const currentItemRawBeforeLock=(subject.located?await readRestoreItem(subject.located,settings,history):null)?.raw??null,author=resolveAuthor(options.author,settings.author_default),lease=await acquireLockLease(pmRoot,resolvedId,settings.locks.ttl_seconds,author,!!options.force,settings.governance.force_required_for_stale_lock,settings.locks.wait_ms,{waitPolicy:"progress"}),releaseLock=lease.release;try{const{loadedItemUnderLock,existingItemPath}=await loadRestoreStateUnderLock({pmRoot,subject,settings,typeRegistry,historyRawBeforeLock,currentItemRawBeforeLock,history}),itemFormat="toon",currentState=resolveRestoreCurrentState(loadedItemUnderLock,history);assertItemMutationPreconditions(pmRoot,currentState.document),restoredDocument.metadata.pm_format_version=normalizeItemFormatVersion(Math.max(requiredItemFormatVersion(restoredDocument.metadata),requiredItemFormatVersion(currentState.document.metadata))),assertProjectRuntimeFormatGuard(resolveWorkspaceRoot(pmRoot),requiredItemFormatVersion(restoredDocument.metadata));const ownershipWarnings=collectRestoreOwnershipWarnings({document:currentState.document,author,force:options.force,enforcement:settings.governance.ownership_enforcement,resolvedId}),restoreChangedFields=changedFields(currentState.document,restoredDocument),workflowPolicy=await enforceWorkflowMutation({pmRoot,settings,operation:"restore",author,before:currentState.document,after:restoredDocument});await runActiveBeforeMutationHooks({pm_root:pmRoot,operation:"restore",before:currentState.document,after:restoredDocument,changed_fields:restoreChangedFields,sdk:createMutationGuardSdk({pmRoot,settings,typeToFolder:typeRegistry.type_to_folder})});const serializedRestore=serializeItemDocument(restoredDocument,{format:itemFormat,schema:settings.schema}),restoredItemPath=getItemPath(pmRoot,restoredDocument.metadata.type,resolvedId,itemFormat,typeRegistry.type_to_folder),derivedIndexWarnings=await lease.fence(async()=>{const releaseDerivedIndexLock=await acquireItemMetadataDerivedIndexLock(pmRoot,author);let derivedIndexWarnings2;try{await writeFileAtomic(restoredItemPath,serializedRestore),existingItemPath&&restoredItemPath!==existingItemPath&&await fs.rm(existingItemPath);const historyEntry=createHistoryEntry({nowIso:nowIso(),author,op:"restore",before:currentState.document,after:restoredDocument,message:options.message,context:workflowPolicy.decisions.length===0?void 0:{workflow_policies:workflowPolicy.decisions}});try{await appendHistoryEntry(subject.historyPath,historyEntry)}catch(error){throw await restorePreviousItemAfterHistoryFailure({existingItemPath,resolvedOriginalRaw:currentState.originalRaw,restoredItemPath}),error}derivedIndexWarnings2=await refreshItemMetadataDerivedIndex({pmRoot,preferredFormat:settings.item_format,typeToFolder:typeRegistry.type_to_folder,schema:settings.schema,itemPath:restoredItemPath,previousItemPath:existingItemPath??void 0,document:restoredDocument})}finally{await releaseDerivedIndexLock()}return derivedIndexWarnings2}),hookWarnings=await runRestoreWriteHooks({restoredItemPath,historyPath:subject.historyPath,before:currentState.document,after:restoredDocument});recordAfterCommandAffectedItem({id:restoredDocument.metadata.id,op:"restore",item_type:restoredDocument.metadata.type,previous_status:currentState.document.metadata.status,status:restoredDocument.metadata.status,previous:projectAfterCommandItemSnapshot(currentState.document.metadata,restoreChangedFields),current:projectAfterCommandItemSnapshot(restoredDocument.metadata,restoreChangedFields),changed_fields:restoreChangedFields});const targetEntry=history[resolvedTarget.historyIndex],offset=historyVersionOffset(history,!0);return{item:restoredDocument.metadata,restored_from:{kind:resolvedTarget.kind,target:resolvedTarget.raw,history_index:resolvedTarget.historyIndex+1,version:offset===null?null:offset+resolvedTarget.historyIndex+1,entry_ts:targetEntry.ts,entry_op:targetEntry.op},changed_fields:restoreChangedFields,warnings:[...workflowPolicy.warnings,...loadedItemUnderLock?.recovered?["restore_unreadable_item_recovered"]:[],...subject.historyPolicyWarnings,...ownershipWarnings,...derivedIndexWarnings,...hookWarnings]}}finally{await releaseLock()}}const _testOnlyRestoreCommand={applyHistoryPatch,changedFields,ensureMaterializedRestoreTarget,ensureReplayTarget,extractPatchFailureContext,replayCurrentDocument,replayToTarget,resolveRestoreSubject};export{_testOnlyRestoreCommand,runRestore};
2
2
  //# sourceMappingURL=restore.js.map
@@ -33,6 +33,8 @@ lifecyclePolicy?:Partial<TerminalTransitionPolicy>;
33
33
  }
34
34
  /** Result returned by the public close-item operation. */
35
35
  export interface CloseOperationResult {
36
+ /** Completion timestamp and the input that supplied it for this close event. */
37
+ completion?:CloseCompletionReceipt;
36
38
  /** Closed item projected as a stable record. */
37
39
  item:Record<string,unknown>;
38
40
  /** Metadata fields changed by the transition. */
@@ -42,6 +44,15 @@ follow_up?:CloseEvidenceFollowUp;
42
44
  /** Validation and lifecycle policy receipts. */
43
45
  warnings:string[];
44
46
  }
47
+ /** Per-close completion provenance, also sealed into the immutable close event. */
48
+ export interface CloseCompletionReceipt {
49
+ /** Actual persisted completed_at value. */
50
+ timestamp:string;
51
+ /** Caller assertion, preserved metadata, or inferred tracker close time. */
52
+ source:"asserted" | "existing" | "close_time";
53
+ /** Actionable correction guidance when completion was inferred. */
54
+ hint?:string;
55
+ }
45
56
  /** Optional evidence completion after a successful, durable close. */
46
57
  export interface CloseEvidenceFollowUp {
47
58
  /** Close succeeded; this guidance does not request another close attempt. */
@@ -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]="8e07b07a-fde3-5173-bb7c-7e8ade88570c")}catch{}})();import{EXIT_CODE}from"../core/shared/constants.js";import{PmCliError}from"../core/shared/errors.js";const DEFAULT_TERMINAL_TRANSITION_POLICY=Object.freeze({requireCloseReason:!0,orderingEdges:"preserve"});function resolveTerminalReason(input){const candidates=[{source:"explicit",value:input.explicit},{source:"duplicate",value:input.duplicateOf===void 0?void 0:`Duplicate of ${input.duplicateOf}`},{source:"resolution",value:input.resolution},{source:"message",value:input.message}];for(const candidate of candidates){const normalized=candidate.value?.trim();if(normalized)return{closeReason:normalized,source:candidate.source}}return{source:"none"}}function requireTerminalReason(input,required){return requireTerminalReasonWithRecovery(input,required,{operation:"close"})}function requireTerminalReasonWithRecovery(input,required,recovery){const resolution=resolveTerminalReason(input);if(!required||resolution.closeReason!==void 0)return resolution;const examples=recovery.operation==="create"?[`pm create --title "<title>" --type <type> --status ${recovery.status??"closed"} --close-reason "<closing summary>"`,`pm create --title "<title>" --type <type> --status ${recovery.status??"closed"} --resolution "<resolution summary>"`,"pm create --stdin-json < item.json"]:['pm close <id> "Done: <what changed and why>"','pm close <id> --reason "<closing summary>"','pm close <id> -m "<closing summary>"',"pm close <id> --duplicate-of <canonical-id>"];throw new PmCliError("Close reason text is required because governance.require_close_reason is enabled",EXIT_CODE.USAGE,{code:"close_reason_required",required:"Provide an author-controlled closing summary as the reason, message, resolution, or duplicate target.",why:"governance.require_close_reason is enabled, so tools must not invent immutable closure evidence.",examples,nextSteps:[`Re-run the ${recovery.operation} operation with a closing summary.`,"To stop requiring reasons, run: pm config set governance-require-close-reason --policy disabled"]})}function applyTerminalOrderingPolicy(metadata,policy){const previousBlockedBy=typeof metadata.blocked_by=="string"?metadata.blocked_by.trim():"",hadBlockedBy=metadata.blocked_by!==void 0,dependencies=metadata.dependencies??[],predecessorEdges=dependencies.filter(dependency=>dependency.kind==="blocked_by"),hadBlockedReason=metadata.blocked_reason!==void 0,changedFields=[];if(hadBlockedBy&&(delete metadata.blocked_by,changedFields.push("blocked_by")),hadBlockedReason&&(delete metadata.blocked_reason,changedFields.push("blocked_reason")),policy.orderingEdges==="remove"&&predecessorEdges.length>0){const removed=new Set(predecessorEdges),retained=dependencies.filter(dependency=>!removed.has(dependency));retained.length>0?metadata.dependencies=retained:delete metadata.dependencies,changedFields.push("dependencies")}if(policy.orderingEdges==="remove")return{changedFields,warnings:[...previousBlockedBy?[`closed_cleared_blocked_by:${metadata.id}:${previousBlockedBy}`]:[],...predecessorEdges.length>0?[`closed_removed_predecessors:${metadata.id}:${predecessorEdges.map(edge=>edge.id).join(",")}`,`closed_removed_predecessors_count:${metadata.id}:${predecessorEdges.length}`]:[]]};const blockerIds=[...new Set([...previousBlockedBy?[previousBlockedBy]:[],...predecessorEdges.map(dependency=>dependency.id)])];return blockerIds.length===0?{changedFields,warnings:[]}:{changedFields,warnings:[`closed_preserved_predecessors:${metadata.id}:${blockerIds.join(",")}`]}}export{DEFAULT_TERMINAL_TRANSITION_POLICY,applyTerminalOrderingPolicy,requireTerminalReason,requireTerminalReasonWithRecovery,resolveTerminalReason};
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]="1430dcf8-9363-556d-aa33-184cea5421f9")}catch{}})();import{EXIT_CODE}from"../core/shared/constants.js";import{PmCliError}from"../core/shared/errors.js";const DEFAULT_TERMINAL_TRANSITION_POLICY=Object.freeze({requireCloseReason:!0,orderingEdges:"preserve"});function resolveTerminalReason(input){const candidates=[{source:"explicit",value:input.explicit},{source:"duplicate",value:input.duplicateOf===void 0?void 0:`Duplicate of ${input.duplicateOf}`},{source:"resolution",value:input.resolution},{source:"message",value:input.message}];for(const candidate of candidates){const normalized=candidate.value?.trim();if(normalized)return{closeReason:normalized,source:candidate.source}}return{source:"none"}}function requireTerminalReason(input,required){return requireTerminalReasonWithRecovery(input,required,{operation:"close"})}function requireTerminalReasonWithRecovery(input,required,recovery){const resolution=resolveTerminalReason(input);if(!required||resolution.closeReason!==void 0)return resolution;const examples=recovery.operation==="create"?[`pm create --title "<title>" --type <type> --status ${recovery.status??"closed"} --close-reason "<closing summary>"`,`pm create --title "<title>" --type <type> --status ${recovery.status??"closed"} --resolution "<resolution summary>"`,"pm create --stdin-json < item.json"]:['pm close <id> "Done: <what changed and why>"','pm close <id> --reason "<closing summary>"','pm close <id> -m "<closing summary>"',"pm close <id> --duplicate-of <canonical-id>"];throw new PmCliError("Close reason text is required because governance.require_close_reason is enabled",EXIT_CODE.USAGE,{code:"close_reason_required",required:"Provide an author-controlled closing summary as the reason, message, resolution, or duplicate target.",why:"governance.require_close_reason is enabled, so tools must not invent immutable closure evidence.",examples,nextSteps:[`Re-run the ${recovery.operation} operation with a closing summary.`,"To stop requiring reasons, run: pm config set governance-require-close-reason --policy disabled"]})}function applyTerminalOrderingPolicy(metadata,policy){const previousBlockedBy=typeof metadata.blocked_by=="string"?metadata.blocked_by.trim():"",hadBlockedBy=metadata.blocked_by!==void 0,dependencies=metadata.dependencies??[],predecessorEdges=dependencies.filter(dependency=>dependency.kind==="blocked_by"),hadBlockedReason=metadata.blocked_reason!==void 0,changedFields=[];if(hadBlockedBy&&(delete metadata.blocked_by,changedFields.push("blocked_by")),hadBlockedReason&&(delete metadata.blocked_reason,changedFields.push("blocked_reason")),policy.orderingEdges==="remove"&&predecessorEdges.length>0){const removed=new Set(predecessorEdges),retained=dependencies.filter(dependency=>!removed.has(dependency));retained.length>0?metadata.dependencies=retained:delete metadata.dependencies,changedFields.push("dependencies")}if(policy.orderingEdges==="remove")return{changedFields,warnings:[...previousBlockedBy?[`closed_cleared_blocked_by:${metadata.id}:${previousBlockedBy}`]:[],...predecessorEdges.length>0?[`closed_removed_predecessors:${metadata.id}:${predecessorEdges.map(edge=>edge.id).join(",")}`,`closed_removed_predecessors_count:${metadata.id}:${predecessorEdges.length}`]:[]]};const blockerIds=[...new Set([...previousBlockedBy?[previousBlockedBy]:[],...predecessorEdges.map(dependency=>dependency.id)])];return blockerIds.length===0?{changedFields,warnings:[]}:{changedFields,warnings:[`closed_preserved_predecessors:${metadata.id}:${blockerIds.join(",")}`]}}export{DEFAULT_TERMINAL_TRANSITION_POLICY,applyTerminalOrderingPolicy,requireTerminalReason,requireTerminalReasonWithRecovery,resolveTerminalReason};
2
2
  //# sourceMappingURL=lifecycle-policy.js.map
@@ -0,0 +1,9 @@
1
+ /** Preserve whole-field scalar intent when another branch removed its target. */
2
+ import { type ReplayDocument } from "../../core/history/replay.js";
3
+ import type { HistoryPatchOp } from "../../types/index.js";
4
+ /** Rebase only validated whole-field assignments/removals; nested and conditional operations stay strict. */
5
+ export declare function rebaseHistoryScalarPatch(current:ReplayDocument,patch:HistoryPatchOp[],coordinate:{
6
+ branch:"ours" | "theirs";
7
+ line:number;
8
+ op:string;
9
+ }):ReplayDocument;
@@ -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]="b3ef9db1-1d20-5d88-927e-a08edf563cd5")}catch{}})();import{tryApplyReplayPatch}from"../../core/history/replay.js";import{EXIT_CODE}from"../../core/shared/constants.js";import{PmCliError}from"../../core/shared/errors.js";function rebaseHistoryScalarPatch(current,patch,coordinate){const direct=tryApplyReplayPatch(current,patch);if(direct.ok)return direct.document;let next=structuredClone(current);for(const[index,operation]of patch.entries()){const field=/^\/metadata\/([^/]*)$/u.exec(operation.path)?.[1],missingField=field!==void 0&&!Object.hasOwn(next.metadata,field.replaceAll("~1","/").replaceAll("~0","~"));if(missingField&&operation.op==="remove")continue;const projected=missingField&&operation.op==="replace"?{...operation,op:"add"}:operation,applied=tryApplyReplayPatch(next,[projected]);if(!applied.ok)throw new PmCliError(`History union cannot rebase a scalar patch at ${coordinate.branch} line ${coordinate.line} (${coordinate.op}), operation ${index+1} ${operation.op} ${operation.path}; preserve the source histories for manual recovery.`,EXIT_CODE.CONFLICT,{code:"history_union_rebase_conflict",field:operation.path,history_merge:{...coordinate,patch_index:index,patch_path:operation.path},nextSteps:["Preserve all three history inputs and both item snapshots. Review the named branch event against the merged replay state; do not drop the event or concatenate unchecked patches. After resolving the history and item deliberately, run pm history <id> --verify and pm merge reconcile --dry-run before committing."]});next=applied.document}return next}export{rebaseHistoryScalarPatch};
2
+ //# sourceMappingURL=history-scalar-rebase.js.map
@@ -0,0 +1,13 @@
1
+ /** Git's owning ignore rule for one untracked reconciliation artifact. */
2
+ export interface MergeArtifactIgnoreRule {
3
+ /** Repository-relative artifact path, decoded without Git quoting. */
4
+ path:string;
5
+ /** Ignore file reported by Git, including global and clone-local sources. */
6
+ source:string;
7
+ /** One-based ignore file line. */
8
+ line:number;
9
+ /** Exact effective pattern, without interpretation by pm. */
10
+ pattern:string;
11
+ }
12
+ /** Respect tracked files and negations while checking literal repository paths with Git's NUL protocol. */
13
+ export declare function inspectIgnoredMergeArtifacts(workspaceRoot:string,paths:string[]):Promise<MergeArtifactIgnoreRule[]>;
@@ -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]="e57ef62f-fd40-5a33-9816-e24d8dba4d1d")}catch{}})();import{execFile}from"node:child_process";import{promisify}from"node:util";import{EXIT_CODE}from"../../core/shared/constants.js";import{PmCliError}from"../../core/shared/errors.js";import{gitWorkspaceEnvironment}from"./worktree-config.js";const execFileAsync=promisify(execFile);async function inspectIgnoredMergeArtifacts(workspaceRoot,paths){if(paths.length===0)return[];let stdout;try{const pending=execFileAsync("git",["check-ignore","--verbose","-z","--stdin"],{cwd:workspaceRoot,env:gitWorkspaceEnvironment(),timeout:1e4}),input=pending.child.stdin;input!==null&&(input.on("error",()=>{pending.child.kill()}),input.end(`${paths.join("\0")}\0`)),{stdout}=await pending}catch(error){if(typeof error=="object"&&error!==null&&"code"in error&&error.code===1)return[];throw new PmCliError("Git could not verify ignore rules for merge artifacts; no automatic settlement is safe until that check succeeds.",EXIT_CODE.CONFLICT,{code:"merge_settlement_review_required",reason:"ignore_check_unavailable",nextSteps:["Check Git configuration and ignore-file readability, then retry pm merge reconcile --stage --dry-run."]})}const fields=stdout.split("\0"),ignored=[];for(let index=0;index+3<fields.length;index+=4){const[source,line,pattern,target]=fields.slice(index,index+4);pattern.startsWith("!")||ignored.push({path:target,source,line:Number(line),pattern})}return ignored}export{inspectIgnoredMergeArtifacts};
2
+ //# sourceMappingURL=ignored-artifacts.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]="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};
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]="7f4b2fc8-c9c6-51ff-a345-b76b8abdacf4")}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{inspectIgnoredMergeArtifacts}from"./ignored-artifacts.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&&(preparedReceipts=!0,await git(["add","--",...newReceiptPathspecs]));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 ignored=await inspectIgnoredMergeArtifacts(workspaceRoot,paths);if(ignored.length>0)throw new PmCliError(`Automatic merge settlement refused: required artifacts are ignored by Git: ${ignored.map(rule=>`${rule.path} (${rule.source}:${rule.line}: ${rule.pattern})`).join(", ")}. Remove or narrow the owning ignore rules because merge receipts must travel with the commit.`,EXIT_CODE.CONFLICT,{code:"merge_settlement_review_required",reason:"ignored_merge_artifacts",paths:ignored.map(rule=>rule.path),nextSteps:["Remove or narrow the named ignore rules, preserve receipt and history files, then retry pm merge reconcile --stage --dry-run before applying pm merge reconcile --stage. Do not force-add ignored proof as a substitute for fixing the shared policy."]});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
2
  //# sourceMappingURL=stage.js.map
@@ -1,4 +1,4 @@
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]="f6def6d2-fbf3-55ce-8654-0c4e76d78a2a")}catch{}})();import{cloneEmptyReplayDocument,historyEntriesToRaw,normalizeReplayPatchOps,reanchorHistoryEntries,toReplayDocument,tryApplyReplayPatch}from"../../core/history/replay.js";import jsonPatch from"fast-json-patch";import{canonicalDocument,parseItemDocument,serializeItemDocument}from"../../core/item/item-format.js";import{splitAcceptanceCriteria}from"../../core/item/parse.js";import{normalizeItemFormatVersion,requiredItemFormatVersion}from"../../core/item/item-format-version.js";import{EXIT_CODE}from"../../core/shared/constants.js";import{findFirstMergeConflictMarker}from"../../core/shared/conflict-markers.js";import{PmCliError}from"../../core/shared/errors.js";import{sha256Hex,stableStringify}from"../../core/shared/serialization.js";import{compareTimestampStrings}from"../../core/shared/time.js";const ITEM_SCALAR_MISSING_VALUE=Object.freeze({pm_item_scalar_missing:!0});function isItemScalarMissingValue(value){return typeof value=="object"&&value!==null&&!Array.isArray(value)&&Object.keys(value).length===1&&value.pm_item_scalar_missing===!0}function encodeItemScalarDecisionValue(value){return value===void 0?ITEM_SCALAR_MISSING_VALUE:value}function hashItemScalarDecisionValue(value){return sha256Hex(stableStringify(value===void 0?{pm_item_scalar_presence:"missing"}:{pm_item_scalar_presence:"present",value}))}function parseHistoryJsonl(raw,label){const conflictMarker=findFirstMergeConflictMarker(raw);if(conflictMarker)throw new PmCliError(`History merge input (${label}) contains unresolved conflict markers at line ${conflictMarker.line}. Merge drivers must receive clean per-side versions.`,EXIT_CODE.GENERIC_FAILURE);const entries=[];for(const[index,rawLine]of raw.split(/\r?\n/).entries()){const line=rawLine.trim();if(line)try{const parsed=JSON.parse(line);if(typeof parsed!="object"||parsed===null||Array.isArray(parsed))throw new TypeError("history entry must be an object");entries.push(parsed)}catch{throw new PmCliError(`History merge input (${label}) contains invalid JSON at line ${index+1}.`,EXIT_CODE.GENERIC_FAILURE)}}return entries}function historyEntryIdentity(entry){return stableStringify({ts:entry.ts,author:entry.author,op:entry.op,patch:entry.patch,message:entry.message??null})}function commonHistoryPrefixLength(ours,theirs){const limit=Math.min(ours.length,theirs.length);let shared=0;for(;shared<limit&&historyEntryIdentity(ours[shared])===historyEntryIdentity(theirs[shared]);)shared+=1;return shared}function orderHistoryUnionSuffix(ours,theirs,incoming,oursIdentities){const entries=[...ours,...incoming],positions=new Map(entries.map((entry,index)=>[entry,index])),aliases=new Map(entries.map((entry,index)=>[historyEntryIdentity(entry),index])),successors=entries.map(()=>new Set);for(const branch of[ours,theirs]){const coordinates=branch.map(entry=>positions.get(entry)??aliases.get(historyEntryIdentity(entry)));for(let index=1;index<coordinates.length;index+=1)coordinates[index-1]!==coordinates[index]&&successors[coordinates[index-1]].add(coordinates[index])}const predecessors=entries.map(()=>0);for(const targets of successors)for(const target of targets)predecessors[target]+=1;const ready=entries.map((_,index)=>index).filter(index=>predecessors[index]===0),result=[];for(;ready.length>0;){ready.sort((leftIndex,rightIndex)=>{const left=entries[leftIndex],right=entries[rightIndex],byTs=compareTimestampStrings(left.ts,right.ts),leftIdentity=historyEntryIdentity(left),rightIdentity=historyEntryIdentity(right);return byTs||Number(oursIdentities.has(rightIdentity))-Number(oursIdentities.has(leftIdentity))});const index=ready.shift();result.push(entries[index]);for(const target of successors[index])predecessors[target]-=1,predecessors[target]===0&&ready.push(target)}if(result.length!==entries.length)throw new PmCliError("History union cannot preserve contradictory original branch event orders; leave the file unresolved for manual recovery.",EXIT_CODE.GENERIC_FAILURE);return result}function createHistoryBranchProjection(entries){return{entries,positions:new Map(entries.map((entry,index)=>[historyEntryIdentity(entry),index])),index:-1,document:cloneEmptyReplayDocument()}}function advanceHistoryBranch(state,target){const end=target??state.index;for(;state.index<end;){const applied=tryApplyReplayPatch(state.document,state.entries[state.index+1].patch);if(!applied.ok)throw new PmCliError("History union cannot preserve a patch that does not apply in its original branch.",EXIT_CODE.GENERIC_FAILURE);state.document=applied.document,state.index+=1}return state.document}function advanceOrderedHistoryBranch(state,target){if(target!==void 0&&target>state.index+1)throw new PmCliError("History union cannot attribute multiple original branch events to one semantic identity; preserve the source histories for manual recovery.",EXIT_CODE.GENERIC_FAILURE);return advanceHistoryBranch(state,target)}function historyUnionPatchProjection(ours,theirs,shared){if(shared===0)return;const oursBranch=createHistoryBranchProjection(ours),theirsBranch=createHistoryBranchProjection(theirs),prefix=advanceHistoryBranch(oursBranch,shared-1);advanceHistoryBranch(theirsBranch,shared-1);let position=0;const unionFields=new Set([...ITEM_UNION_COLLECTION_FIELDS,...ITEM_UNION_DELIMITED_FIELDS]);return(entry,current)=>{if(position++<shared)return entry.patch;const identity=historyEntryIdentity(entry),oursState=advanceOrderedHistoryBranch(oursBranch,oursBranch.positions.get(identity)),theirsState=advanceOrderedHistoryBranch(theirsBranch,theirsBranch.positions.get(identity)),scalarPatch=normalizeReplayPatchOps(entry.patch).filter(operation=>{const[root,field]=operation.path.slice(1).split("/");return root!=="metadata"||!unionFields.has(field)}),scalar=tryApplyReplayPatch(current,scalarPatch);if(!scalar.ok)throw new PmCliError("History union cannot rebase a scalar patch onto the merged replay position; leave the file unresolved for manual recovery.",EXIT_CODE.GENERIC_FAILURE);const merged=mergeItemMetadataRecords(prefix.metadata,oursState.metadata,theirsState.metadata,"ours","latest_document_update",String(oursState.metadata.updated_at),String(theirsState.metadata.updated_at)),next=scalar.document;for(const field of unionFields)Object.hasOwn(merged.merged,field)?next.metadata[field]=merged.merged[field]:delete next.metadata[field];const canonical=toReplayDocument({metadata:next.metadata,body:next.body});return jsonPatch.compare(current,canonical)}}function mergeHistoryStreams(baseRaw,oursRaw,theirsRaw){const base=parseHistoryJsonl(baseRaw,"base"),ours=parseHistoryJsonl(oursRaw,"ours"),theirs=parseHistoryJsonl(theirsRaw,"theirs"),creates=[base,ours,theirs].map(entries=>entries.filter(entry=>entry.op==="create"));if(creates[0].length===0&&creates.slice(1).every(entries=>entries.length>0)||creates.some(entries=>entries.length>1)||new Set(creates.flat().map(historyEntryIdentity)).size>1)throw new PmCliError("Duplicate item identity: history contains independent or unproven create events. Preserve both branches and assign distinct item ids before merging.",EXIT_CODE.CONFLICT,{code:"item_identity_conflict"});const shared=commonHistoryPrefixLength(ours,theirs);if(shared===ours.length&&shared===theirs.length)return{merged:historyEntriesToRaw(ours),strategy:"identical",common_entries:shared,entries_from_ours:0,entries_from_theirs:0,entries_total:ours.length,reanchored:!1};if(shared===theirs.length)return{merged:historyEntriesToRaw(ours),strategy:"fast_forward_ours",common_entries:shared,entries_from_ours:ours.length-shared,entries_from_theirs:0,entries_total:ours.length,reanchored:!1};if(shared===ours.length)return{merged:historyEntriesToRaw(theirs),strategy:"fast_forward_theirs",common_entries:shared,entries_from_ours:0,entries_from_theirs:theirs.length-shared,entries_total:theirs.length,reanchored:!1};const oursSuffix=ours.slice(shared),seenIdentities=new Set(oursSuffix.map(historyEntryIdentity)),theirsSuffix=theirs.slice(shared).filter(entry=>!seenIdentities.has(historyEntryIdentity(entry))),mergedSuffix=orderHistoryUnionSuffix(oursSuffix,theirs.slice(shared),theirsSuffix,seenIdentities),reanchored=reanchorHistoryEntries([...ours.slice(0,shared),...mergedSuffix],void 0,{continuousHashSurface:!0,projectPatch:historyUnionPatchProjection(ours,theirs,shared)});if(reanchored.skippedOps>0)throw new PmCliError(`History union cannot preserve ${reanchored.skippedOps} patch operation(s) after deterministic suffix ordering; leave the file unresolved for manual recovery.`,EXIT_CODE.GENERIC_FAILURE);return{merged:historyEntriesToRaw(reanchored.entries),strategy:"union_reanchor",common_entries:shared,entries_from_ours:oursSuffix.length,entries_from_theirs:theirsSuffix.length,entries_total:reanchored.entries.length,reanchored:!0}}function parseRelationshipJsonl(raw,label){const conflictMarker=findFirstMergeConflictMarker(raw);if(conflictMarker)throw new PmCliError(`Relationship merge input (${label}) contains unresolved conflict markers at line ${conflictMarker.line}. Merge drivers must receive clean per-side versions.`,EXIT_CODE.GENERIC_FAILURE);const events=[];for(const[index,rawLine]of raw.split(/\r?\n/).entries()){const line=rawLine.trim();if(!line)continue;let parsed;try{parsed=JSON.parse(line)}catch{throw new PmCliError(`Relationship merge input (${label}) contains invalid JSON at line ${index+1}.`,EXIT_CODE.GENERIC_FAILURE)}if(typeof parsed!="object"||parsed===null||Array.isArray(parsed)||typeof parsed.eventId!="string"||typeof parsed.sequence!="number")throw new PmCliError(`Relationship merge input (${label}) line ${index+1} is not a relationship event (eventId + sequence required).`,EXIT_CODE.GENERIC_FAILURE);events.push(parsed)}return events}function relationshipEventsToRaw(events){return events.length===0?"":`${events.map(event=>JSON.stringify(event)).join(`
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]="425ef2aa-56fa-5d06-b8d0-316373ad2433")}catch{}})();import{cloneEmptyReplayDocument,historyEntriesToRaw,normalizeReplayPatchOps,reanchorHistoryEntries,toReplayDocument,tryApplyReplayPatch}from"../../core/history/replay.js";import jsonPatch from"fast-json-patch";import{rebaseHistoryScalarPatch}from"./history-scalar-rebase.js";import{canonicalDocument,parseItemDocument,serializeItemDocument}from"../../core/item/item-format.js";import{splitAcceptanceCriteria}from"../../core/item/parse.js";import{normalizeItemFormatVersion,requiredItemFormatVersion}from"../../core/item/item-format-version.js";import{EXIT_CODE}from"../../core/shared/constants.js";import{findFirstMergeConflictMarker}from"../../core/shared/conflict-markers.js";import{PmCliError}from"../../core/shared/errors.js";import{sha256Hex,stableStringify}from"../../core/shared/serialization.js";import{compareTimestampStrings}from"../../core/shared/time.js";const ITEM_SCALAR_MISSING_VALUE=Object.freeze({pm_item_scalar_missing:!0});function isItemScalarMissingValue(value){return typeof value=="object"&&value!==null&&!Array.isArray(value)&&Object.keys(value).length===1&&value.pm_item_scalar_missing===!0}function encodeItemScalarDecisionValue(value){return value===void 0?ITEM_SCALAR_MISSING_VALUE:value}function hashItemScalarDecisionValue(value){return sha256Hex(stableStringify(value===void 0?{pm_item_scalar_presence:"missing"}:{pm_item_scalar_presence:"present",value}))}function parseHistoryJsonl(raw,label){const conflictMarker=findFirstMergeConflictMarker(raw);if(conflictMarker)throw new PmCliError(`History merge input (${label}) contains unresolved conflict markers at line ${conflictMarker.line}. Merge drivers must receive clean per-side versions.`,EXIT_CODE.GENERIC_FAILURE);const entries=[];for(const[index,rawLine]of raw.split(/\r?\n/).entries()){const line=rawLine.trim();if(line)try{const parsed=JSON.parse(line);if(typeof parsed!="object"||parsed===null||Array.isArray(parsed))throw new TypeError("history entry must be an object");entries.push(parsed)}catch{throw new PmCliError(`History merge input (${label}) contains invalid JSON at line ${index+1}.`,EXIT_CODE.GENERIC_FAILURE)}}return entries}function historyEntryIdentity(entry){return stableStringify({ts:entry.ts,author:entry.author,op:entry.op,patch:entry.patch,message:entry.message??null})}function commonHistoryPrefixLength(ours,theirs){const limit=Math.min(ours.length,theirs.length);let shared=0;for(;shared<limit&&historyEntryIdentity(ours[shared])===historyEntryIdentity(theirs[shared]);)shared+=1;return shared}function orderHistoryUnionSuffix(ours,theirs,incoming,oursIdentities){const entries=[...ours,...incoming],positions=new Map(entries.map((entry,index)=>[entry,index])),aliases=new Map(entries.map((entry,index)=>[historyEntryIdentity(entry),index])),successors=entries.map(()=>new Set);for(const branch of[ours,theirs]){const coordinates=branch.map(entry=>positions.get(entry)??aliases.get(historyEntryIdentity(entry)));for(let index=1;index<coordinates.length;index+=1)coordinates[index-1]!==coordinates[index]&&successors[coordinates[index-1]].add(coordinates[index])}const predecessors=entries.map(()=>0);for(const targets of successors)for(const target of targets)predecessors[target]+=1;const ready=entries.map((_,index)=>index).filter(index=>predecessors[index]===0),result=[];for(;ready.length>0;){ready.sort((leftIndex,rightIndex)=>{const left=entries[leftIndex],right=entries[rightIndex],byTs=compareTimestampStrings(left.ts,right.ts),leftIdentity=historyEntryIdentity(left),rightIdentity=historyEntryIdentity(right);return byTs||Number(oursIdentities.has(rightIdentity))-Number(oursIdentities.has(leftIdentity))});const index=ready.shift();result.push(entries[index]);for(const target of successors[index])predecessors[target]-=1,predecessors[target]===0&&ready.push(target)}if(result.length!==entries.length)throw new PmCliError("History union cannot preserve contradictory original branch event orders; leave the file unresolved for manual recovery.",EXIT_CODE.GENERIC_FAILURE);return result}function createHistoryBranchProjection(entries){return{entries,positions:new Map(entries.map((entry,index)=>[historyEntryIdentity(entry),index])),index:-1,document:cloneEmptyReplayDocument()}}function advanceHistoryBranch(state,target){const end=target??state.index;for(;state.index<end;){const applied=tryApplyReplayPatch(state.document,state.entries[state.index+1].patch);if(!applied.ok)throw new PmCliError("History union cannot preserve a patch that does not apply in its original branch.",EXIT_CODE.GENERIC_FAILURE);state.document=applied.document,state.index+=1}return state.document}function advanceOrderedHistoryBranch(state,target){if(target!==void 0&&target>state.index+1)throw new PmCliError("History union cannot attribute multiple original branch events to one semantic identity; preserve the source histories for manual recovery.",EXIT_CODE.GENERIC_FAILURE);return advanceHistoryBranch(state,target)}function historyUnionPatchProjection(ours,theirs,shared){if(shared===0)return;const oursBranch=createHistoryBranchProjection(ours),theirsBranch=createHistoryBranchProjection(theirs),prefix=advanceHistoryBranch(oursBranch,shared-1);advanceHistoryBranch(theirsBranch,shared-1);let position=0;const unionFields=new Set([...ITEM_UNION_COLLECTION_FIELDS,...ITEM_UNION_DELIMITED_FIELDS]);return(entry,current)=>{if(position++<shared)return entry.patch;const identity=historyEntryIdentity(entry),oursState=advanceOrderedHistoryBranch(oursBranch,oursBranch.positions.get(identity)),theirsState=advanceOrderedHistoryBranch(theirsBranch,theirsBranch.positions.get(identity)),scalarPatch=normalizeReplayPatchOps(entry.patch).filter(operation=>{const[root,field]=operation.path.slice(1).split("/");return root!=="metadata"||!unionFields.has(field)}),oursPosition=oursBranch.positions.get(identity),next=rebaseHistoryScalarPatch(current,scalarPatch,{branch:oursPosition===void 0?"theirs":"ours",line:(oursPosition??theirsBranch.positions.get(identity))+1,op:entry.op}),merged=mergeItemMetadataRecords(prefix.metadata,oursState.metadata,theirsState.metadata,"ours","latest_document_update",String(oursState.metadata.updated_at),String(theirsState.metadata.updated_at));for(const field of unionFields)Object.hasOwn(merged.merged,field)?next.metadata[field]=merged.merged[field]:delete next.metadata[field];const canonical=toReplayDocument({metadata:next.metadata,body:next.body});return jsonPatch.compare(current,canonical)}}function mergeHistoryStreams(baseRaw,oursRaw,theirsRaw){const base=parseHistoryJsonl(baseRaw,"base"),ours=parseHistoryJsonl(oursRaw,"ours"),theirs=parseHistoryJsonl(theirsRaw,"theirs"),creates=[base,ours,theirs].map(entries=>entries.filter(entry=>entry.op==="create"));if(creates[0].length===0&&creates.slice(1).every(entries=>entries.length>0)||creates.some(entries=>entries.length>1)||new Set(creates.flat().map(historyEntryIdentity)).size>1)throw new PmCliError("Duplicate item identity: history contains independent or unproven create events. Preserve both branches and assign distinct item ids before merging.",EXIT_CODE.CONFLICT,{code:"item_identity_conflict"});const shared=commonHistoryPrefixLength(ours,theirs);if(shared===ours.length&&shared===theirs.length)return{merged:historyEntriesToRaw(ours),strategy:"identical",common_entries:shared,entries_from_ours:0,entries_from_theirs:0,entries_total:ours.length,reanchored:!1};if(shared===theirs.length)return{merged:historyEntriesToRaw(ours),strategy:"fast_forward_ours",common_entries:shared,entries_from_ours:ours.length-shared,entries_from_theirs:0,entries_total:ours.length,reanchored:!1};if(shared===ours.length)return{merged:historyEntriesToRaw(theirs),strategy:"fast_forward_theirs",common_entries:shared,entries_from_ours:0,entries_from_theirs:theirs.length-shared,entries_total:theirs.length,reanchored:!1};const oursSuffix=ours.slice(shared),seenIdentities=new Set(oursSuffix.map(historyEntryIdentity)),theirsSuffix=theirs.slice(shared).filter(entry=>!seenIdentities.has(historyEntryIdentity(entry))),mergedSuffix=orderHistoryUnionSuffix(oursSuffix,theirs.slice(shared),theirsSuffix,seenIdentities),reanchored=reanchorHistoryEntries([...ours.slice(0,shared),...mergedSuffix],void 0,{continuousHashSurface:!0,projectPatch:historyUnionPatchProjection(ours,theirs,shared)});if(reanchored.skippedOps>0)throw new PmCliError(`History union cannot preserve ${reanchored.skippedOps} patch operation(s) after deterministic suffix ordering; leave the file unresolved for manual recovery.`,EXIT_CODE.GENERIC_FAILURE);return{merged:historyEntriesToRaw(reanchored.entries),strategy:"union_reanchor",common_entries:shared,entries_from_ours:oursSuffix.length,entries_from_theirs:theirsSuffix.length,entries_total:reanchored.entries.length,reanchored:!0}}function parseRelationshipJsonl(raw,label){const conflictMarker=findFirstMergeConflictMarker(raw);if(conflictMarker)throw new PmCliError(`Relationship merge input (${label}) contains unresolved conflict markers at line ${conflictMarker.line}. Merge drivers must receive clean per-side versions.`,EXIT_CODE.GENERIC_FAILURE);const events=[];for(const[index,rawLine]of raw.split(/\r?\n/).entries()){const line=rawLine.trim();if(!line)continue;let parsed;try{parsed=JSON.parse(line)}catch{throw new PmCliError(`Relationship merge input (${label}) contains invalid JSON at line ${index+1}.`,EXIT_CODE.GENERIC_FAILURE)}if(typeof parsed!="object"||parsed===null||Array.isArray(parsed)||typeof parsed.eventId!="string"||typeof parsed.sequence!="number")throw new PmCliError(`Relationship merge input (${label}) line ${index+1} is not a relationship event (eventId + sequence required).`,EXIT_CODE.GENERIC_FAILURE);events.push(parsed)}return events}function relationshipEventsToRaw(events){return events.length===0?"":`${events.map(event=>JSON.stringify(event)).join(`
2
2
  `)}
3
3
  `}function renumberRelationshipEvents(events){return events.map((event,index)=>({...event,sequence:index+1}))}function mergeRelationshipEventStreams(baseRaw,oursRaw,theirsRaw){parseRelationshipJsonl(baseRaw,"base");const ours=parseRelationshipJsonl(oursRaw,"ours"),theirs=parseRelationshipJsonl(theirsRaw,"theirs"),limit=Math.min(ours.length,theirs.length);let shared=0;for(;shared<limit&&ours[shared].eventId===theirs[shared].eventId;)shared+=1;if(shared===ours.length&&shared===theirs.length)return{merged:relationshipEventsToRaw(ours),strategy:"identical",common_entries:shared,entries_from_ours:0,entries_from_theirs:0,entries_total:ours.length,reanchored:!1};if(shared===theirs.length)return{merged:relationshipEventsToRaw(ours),strategy:"fast_forward_ours",common_entries:shared,entries_from_ours:ours.length-shared,entries_from_theirs:0,entries_total:ours.length,reanchored:!1};if(shared===ours.length)return{merged:relationshipEventsToRaw(theirs),strategy:"fast_forward_theirs",common_entries:shared,entries_from_ours:0,entries_from_theirs:theirs.length-shared,entries_total:theirs.length,reanchored:!1};const oursSuffix=ours.slice(shared),oursEventIds=new Set(oursSuffix.map(event=>event.eventId)),theirsSuffix=theirs.slice(shared).filter(event=>!oursEventIds.has(event.eventId)),mergedSuffix=[...oursSuffix,...theirsSuffix].sort((left,right)=>{if(left.timestamp!==void 0&&right.timestamp!==void 0){const byTs=compareTimestampStrings(left.timestamp,right.timestamp);if(byTs!==0)return byTs}else{if(left.timestamp===void 0&&right.timestamp!==void 0)return 1;if(left.timestamp!==void 0)return-1}const leftFromOurs=oursEventIds.has(left.eventId),rightFromOurs=oursEventIds.has(right.eventId);return leftFromOurs!==rightFromOurs?Number(rightFromOurs)-Number(leftFromOurs):0}),merged=renumberRelationshipEvents([...ours.slice(0,shared),...mergedSuffix]);return{merged:relationshipEventsToRaw(merged),strategy:"union_reanchor",common_entries:shared,entries_from_ours:oursSuffix.length,entries_from_theirs:theirsSuffix.length,entries_total:merged.length,reanchored:!0}}const ITEM_UNION_COLLECTION_FIELDS=["tags","dependencies","comments","notes","learnings","files","tests","test_runs","docs","reminders","events"],ITEM_UNION_DELIMITED_FIELDS=["acceptance_criteria"],ITEM_LATEST_TIMESTAMP_FIELDS=["updated_at"];function jsonEquals(left,right){return left===void 0||right===void 0?left===void 0&&right===void 0:stableStringify(left)===stableStringify(right)}function unionCollection(base,ours,theirs){const toEntries=value=>Array.isArray(value)?value:[],baseIds=new Set(toEntries(base).map(entry=>stableStringify(entry))),oursEntries=toEntries(ours),oursIds=new Set(oursEntries.map(entry=>stableStringify(entry))),theirsEntries=toEntries(theirs),theirsIds=new Set(theirsEntries.map(entry=>stableStringify(entry))),merged=oursEntries.filter(entry=>{const identity=stableStringify(entry);return!baseIds.has(identity)||theirsIds.has(identity)});for(const entry of theirsEntries){const identity=stableStringify(entry);!oursIds.has(identity)&&!baseIds.has(identity)&&merged.push(entry)}return merged}function linkedTestDefinitionIdentity(value){if(typeof value!="object"||value===null||Array.isArray(value))return stableStringify(value);const{provenance:_provenance,...definition}=value;return stableStringify(definition)}function markMergedLinkedTest(value){if(typeof value!="object"||value===null||Array.isArray(value))return value;const test=value;return test.provenance?{...test,provenance:{...test.provenance,source_kind:"merge_union"}}:{...test,provenance:{source_kind:"merge_union"}}}function unionLinkedTests(base,ours,theirs){const toEntries=value=>Array.isArray(value)?value:[],baseIds=new Set(toEntries(base).map(linkedTestDefinitionIdentity)),oursEntries=toEntries(ours),oursIds=new Set(oursEntries.map(linkedTestDefinitionIdentity)),theirsEntries=toEntries(theirs),theirsIds=new Set(theirsEntries.map(linkedTestDefinitionIdentity)),merged=oursEntries.filter(entry=>{const identity=linkedTestDefinitionIdentity(entry);return!baseIds.has(identity)||theirsIds.has(identity)});for(const entry of theirsEntries){const identity=linkedTestDefinitionIdentity(entry);!oursIds.has(identity)&&!baseIds.has(identity)&&merged.push(markMergedLinkedTest(entry))}return merged}function latestTimestamp(ours,theirs){return compareTimestampStrings(String(ours),String(theirs))>=0?ours:theirs}function mergeScalarThreeWay(base,ours,theirs,preferred){return jsonEquals(ours,theirs)?{value:ours,from_theirs:!1,conflict:!1}:jsonEquals(base,ours)?{value:theirs,from_theirs:!0,conflict:!1}:jsonEquals(base,theirs)?{value:ours,from_theirs:!1,conflict:!1}:{value:preferred==="theirs"?theirs:ours,from_theirs:preferred==="theirs",conflict:!0}}function mergeItemScalarThreeWay(base,ours,theirs,preferred,conflictResolution,oursUpdatedAt,theirsUpdatedAt){const outcome=mergeScalarThreeWay(base,ours,theirs,preferred);if(!outcome.conflict)return{...outcome,conflict:!1};if(conflictResolution==="preferred_side")return{...outcome,resolution_basis:"requested_preference",retained_side:outcome.from_theirs?"theirs":"ours"};const timestampOrder=compareTimestampStrings(oursUpdatedAt,theirsUpdatedAt);if(conflictResolution==="latest_document_update"&&timestampOrder!==0){const retainTheirs2=timestampOrder<0;return{value:retainTheirs2?theirs:ours,from_theirs:retainTheirs2,conflict:!0,resolution_basis:"document_updated_at",retained_side:retainTheirs2?"theirs":"ours"}}const retainTheirs=stableStringify(encodeItemScalarDecisionValue(theirs)).localeCompare(stableStringify(encodeItemScalarDecisionValue(ours)))<0;return{value:retainTheirs?theirs:ours,from_theirs:retainTheirs,conflict:!0,resolution_basis:"stable_value_tiebreak",retained_side:retainTheirs?"theirs":"ours"}}function toMetadataRecord(document){return{...document.metadata}}function parseItemMergeSide(raw,label,options){try{const document=canonicalDocument(parseItemDocument(raw,{...options,requireSupportedFormat:!0}),options),reservedField=Object.entries(document.metadata).find(([,value])=>isItemScalarMissingValue(value))?.[0];if(reservedField!==void 0)throw new TypeError(`metadata field ${reservedField} uses the reserved missing-scalar evidence marker`);return document}catch(error){throw error instanceof PmCliError&&error.context?.code==="item_format_version_unsupported"?error:new PmCliError(`Item merge input (${label}) is not a readable item document: ${String(error)}`,EXIT_CODE.GENERIC_FAILURE)}}function mergeItemUnionField(params){if(!params.unionFieldSet.has(params.field))return!1;const union=params.field==="tests"?unionLinkedTests(params.baseValue,params.oursValue,params.theirsValue):params.field==="acceptance_criteria"?unionCollection(splitAcceptanceCriteria(params.baseValue),splitAcceptanceCriteria(params.oursValue),splitAcceptanceCriteria(params.theirsValue)).join("; "):unionCollection(params.baseValue,params.oursValue,params.theirsValue);return params.accumulator.merged[params.field]=union,jsonEquals(union,params.oursValue)||params.accumulator.unionFields.push(params.field),!0}function mergeItemScalarField(params){const outcome=mergeItemScalarThreeWay(params.baseValue,params.oursValue,params.theirsValue,params.preferred,params.conflictResolution,params.oursUpdatedAt,params.theirsUpdatedAt);outcome.value!==void 0&&(params.accumulator.merged[params.field]=outcome.value),outcome.conflict?(params.accumulator.conflictFields.push(params.field),params.accumulator.conflictDecisions.push({field:params.field,base:encodeItemScalarDecisionValue(params.baseValue),ours:encodeItemScalarDecisionValue(params.oursValue),theirs:encodeItemScalarDecisionValue(params.theirsValue),retained:encodeItemScalarDecisionValue(outcome.value),discarded:encodeItemScalarDecisionValue(outcome.from_theirs?params.oursValue:params.theirsValue),retained_side:outcome.retained_side,resolution_basis:outcome.resolution_basis})):outcome.from_theirs&&params.accumulator.fieldsFromTheirs.push(params.field)}function mergeItemMetadataRecords(baseRecord,oursRecord,theirsRecord,preferred,conflictResolution,oursUpdatedAt,theirsUpdatedAt){const unionFieldSet=new Set([...ITEM_UNION_COLLECTION_FIELDS,...ITEM_UNION_DELIMITED_FIELDS]),latestFieldSet=new Set(ITEM_LATEST_TIMESTAMP_FIELDS),fieldNames=[...new Set([...Object.keys(oursRecord),...Object.keys(theirsRecord)])].sort((left,right)=>left.localeCompare(right)),accumulator={merged:{},conflictFields:[],fieldsFromTheirs:[],unionFields:[],conflictDecisions:[]};for(const field of fieldNames){const baseValue=baseRecord[field],oursValue=oursRecord[field],theirsValue=theirsRecord[field];if(!mergeItemUnionField({field,baseValue,oursValue,theirsValue,unionFieldSet,accumulator})){if(latestFieldSet.has(field)){accumulator.merged[field]=latestTimestamp(oursValue,theirsValue);continue}mergeItemScalarField({field,baseValue,oursValue,theirsValue,preferred,conflictResolution,oursUpdatedAt,theirsUpdatedAt,accumulator})}}return accumulator}function mergeItemDocuments(baseRaw,oursRaw,theirsRaw,options={}){const preferred=options.preferred??"ours",conflictResolution=options.conflictResolution??"preferred_side",ours=parseItemMergeSide(oursRaw,"ours",options),theirs=parseItemMergeSide(theirsRaw,"theirs",options);if(baseRaw.trim().length===0)throw new PmCliError("Duplicate item identity: add/add item documents have no common ancestor. Preserve both branches and assign distinct item ids before merging.",EXIT_CODE.CONFLICT,{code:"item_identity_conflict",item_id:ours.metadata.id});const base=parseItemMergeSide(baseRaw,"base",options);if(base.metadata.id!==ours.metadata.id||base.metadata.id!==theirs.metadata.id)throw new PmCliError("Duplicate or mismatched item identity: all merge sides must address the common ancestor's item id.",EXIT_CODE.CONFLICT,{code:"item_identity_conflict",item_id:base.metadata.id});const baseRecord=toMetadataRecord(base),oursRecord=toMetadataRecord(ours),theirsRecord=toMetadataRecord(theirs),{merged,conflictFields,fieldsFromTheirs,unionFields,conflictDecisions}=mergeItemMetadataRecords(baseRecord,oursRecord,theirsRecord,preferred,conflictResolution,ours.metadata.updated_at,theirs.metadata.updated_at),bodyOutcome=mergeItemScalarThreeWay(base.body,ours.body,theirs.body,preferred,conflictResolution,ours.metadata.updated_at,theirs.metadata.updated_at);bodyOutcome.conflict?(conflictFields.push("body"),conflictDecisions.push({field:"body",base:encodeItemScalarDecisionValue(base.body),ours:encodeItemScalarDecisionValue(ours.body),theirs:encodeItemScalarDecisionValue(theirs.body),retained:encodeItemScalarDecisionValue(bodyOutcome.value),discarded:encodeItemScalarDecisionValue(bodyOutcome.from_theirs?ours.body:theirs.body),retained_side:bodyOutcome.retained_side,resolution_basis:bodyOutcome.resolution_basis})):bodyOutcome.from_theirs&&fieldsFromTheirs.push("body");const mergedMetadata=merged;return mergedMetadata.pm_format_version=normalizeItemFormatVersion(Math.max(...[base.metadata,ours.metadata,theirs.metadata,mergedMetadata].map(requiredItemFormatVersion))),{merged:serializeItemDocument({metadata:mergedMetadata,body:bodyOutcome.value},options),conflict_fields:conflictFields,fields_from_theirs:fieldsFromTheirs,union_fields:unionFields,conflict_decisions:conflictDecisions,requested_preference:preferred,requested_preference_applied:conflictResolution==="preferred_side",conflict_resolution:conflictResolution}}function isPlainObject(value){return typeof value=="object"&&value!==null&&!Array.isArray(value)}function parseJsonSide(raw,label){const conflictMarker=findFirstMergeConflictMarker(raw);if(conflictMarker)throw new PmCliError(`JSON merge input (${label}) contains unresolved conflict markers at line ${conflictMarker.line}.`,EXIT_CODE.GENERIC_FAILURE);if(raw.trim().length!==0)try{return JSON.parse(raw)}catch{throw new PmCliError(`JSON merge input (${label}) is not valid JSON.`,EXIT_CODE.GENERIC_FAILURE)}}function mergeAdditiveJsonArrays(base,ours,theirs){if(!Array.isArray(base)||!Array.isArray(ours)||!Array.isArray(theirs))return null;const countEntries=entries=>{const counts=new Map;for(const entry of entries){const identity=stableStringify(entry);counts.set(identity,(counts.get(identity)??0)+1)}return counts},baseCounts=countEntries(base),oursCounts=countEntries(ours),theirsCounts=countEntries(theirs);for(const[identity,baseCount]of baseCounts)if((oursCounts.get(identity)??0)<baseCount||(theirsCounts.get(identity)??0)<baseCount)return null;const unmatchedOursCounts=new Map(oursCounts),additionsFromTheirs=[];for(const entry of theirs){const identity=stableStringify(entry),unmatchedOursCount=unmatchedOursCounts.get(identity)??0;unmatchedOursCount>0?unmatchedOursCounts.set(identity,unmatchedOursCount-1):additionsFromTheirs.push(entry)}return{merged:[...ours,...additionsFromTheirs],acceptedTheirs:additionsFromTheirs.length>0}}function mergeJsonObjects(base,ours,theirs,preferred,pathPrefix,conflictPaths,pathsFromTheirs){if(!isPlainObject(ours)||!isPlainObject(theirs))return null;const baseObject=isPlainObject(base)?base:{},merged={},keys=[...new Set([...Object.keys(ours),...Object.keys(theirs)])].sort((left,right)=>left.localeCompare(right));for(const key of keys){const childPath=pathPrefix.length>0?`${pathPrefix}.${key}`:key,child=mergeJsonKey(baseObject,ours,theirs,key,preferred,childPath,conflictPaths,pathsFromTheirs);child!==void 0&&(merged[key]=child)}return merged}function mergeJsonValue(base,ours,theirs,preferred,pathPrefix,conflictPaths,pathsFromTheirs){if(jsonEquals(ours,theirs))return ours;const additiveArrayMerge=mergeAdditiveJsonArrays(base,ours,theirs);if(additiveArrayMerge!==null)return additiveArrayMerge.acceptedTheirs&&pathsFromTheirs.push(pathPrefix),additiveArrayMerge.merged;const mergedObject=mergeJsonObjects(base,ours,theirs,preferred,pathPrefix,conflictPaths,pathsFromTheirs);if(mergedObject!==null)return mergedObject;const outcome=mergeScalarThreeWay(base,ours,theirs,preferred);return outcome.conflict?conflictPaths.push(pathPrefix):outcome.from_theirs&&pathsFromTheirs.push(pathPrefix),outcome.value}function mergeJsonKey(base,ours,theirs,key,preferred,childPath,conflictPaths,pathsFromTheirs){const inOurs=Object.prototype.hasOwnProperty.call(ours,key),inTheirs=Object.prototype.hasOwnProperty.call(theirs,key),inBase=Object.prototype.hasOwnProperty.call(base,key);if(inOurs&&inTheirs)return mergeJsonValue(base[key],ours[key],theirs[key],preferred,childPath,conflictPaths,pathsFromTheirs);if(inOurs){if(inBase&&jsonEquals(base[key],ours[key])){pathsFromTheirs.push(childPath);return}return inBase?(conflictPaths.push(childPath),preferred==="theirs"?void 0:ours[key]):ours[key]}if(!(inBase&&jsonEquals(base[key],theirs[key])))return inBase?(conflictPaths.push(childPath),preferred==="theirs"?theirs[key]:void 0):(pathsFromTheirs.push(childPath),theirs[key])}function mergeJsonDocuments(baseRaw,oursRaw,theirsRaw,options={}){const preferred=options.preferred??"ours",base=parseJsonSide(baseRaw,"base"),ours=parseJsonSide(oursRaw,"ours"),theirs=parseJsonSide(theirsRaw,"theirs"),conflictPaths=[],pathsFromTheirs=[],merged=mergeJsonValue(base,ours,theirs,preferred,"",conflictPaths,pathsFromTheirs);return{merged:`${JSON.stringify(merged??null,null,2)}
4
4
  `,conflict_paths:conflictPaths,paths_from_theirs:pathsFromTheirs,preferred}}export{ITEM_LATEST_TIMESTAMP_FIELDS,ITEM_SCALAR_MISSING_VALUE,ITEM_UNION_COLLECTION_FIELDS,encodeItemScalarDecisionValue,hashItemScalarDecisionValue,isItemScalarMissingValue,mergeHistoryStreams,mergeItemDocuments,mergeJsonDocuments,mergeRelationshipEventStreams};
@@ -4,7 +4,8 @@
4
4
  * Provides SDK-first pre-write provenance and secret-detection guardrails for
5
5
  * CLI, MCP, and package-owned mutation dispatchers.
6
6
  */
7
- import { SECRET_RULES } from "../core/shared/secret-rules.js";
7
+ import { SECRET_RULES,type SecretNormalizationKind } from "../core/shared/secret-rules.js";
8
+ export type { SecretNormalizationKind } from "../core/shared/secret-rules.js";
8
9
  /** Configurable handling mode for credential-shaped mutation content. */
9
10
  export type SecretGuardMode = "off" | "advise" | "block";
10
11
  /** Settings consumed by the shared mutation guard. */
@@ -22,6 +23,10 @@ export interface SecretGuardFinding {
22
23
  rule:(typeof SECRET_RULES)[number]["mutationRule"];
23
24
  /** Object path or argument index containing the match. */
24
25
  path:string;
26
+ /** Detection-only transformations, without original or decoded coordinate values. */
27
+ normalizations?:SecretNormalizationKind[];
28
+ /** Remaining encoded input exceeded the finite inspection envelope and requires explicit review. */
29
+ normalization_incomplete?:true;
25
30
  }
26
31
  /** Result of a non-blocking or bypassed mutation guard evaluation. */
27
32
  export interface MutationGuardResult {
@@ -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]="b75bf20f-d341-5fa2-943d-db02211ca80d")}catch{}})();import{SECRET_RULES,acceptsSecretMatch}from"../core/shared/secret-rules.js";import{EXIT_CODE}from"../core/shared/constants.js";import{PmCliError}from"../core/shared/errors.js";const PM_MUTATION_ACTION_CONTRACTS=[{action:"append",history_scope:"item"},{action:"claim",history_scope:"item"},{action:"close",history_scope:"item"},{action:"close-many",history_scope:"item"},{action:"close-task",history_scope:"item"},{action:"comments",history_scope:"conditional"},{action:"config",history_scope:"conditional"},{action:"copy",history_scope:"item"},{action:"create",history_scope:"item"},{action:"delete",history_scope:"item"},{action:"deps",history_scope:"conditional"},{action:"discover",history_scope:"item"},{action:"docs",history_scope:"conditional"},{action:"files",history_scope:"conditional"},{action:"focus",history_scope:"conditional"},{action:"gc",history_scope:"conditional"},{action:"history-compact",history_scope:"history"},{action:"history-author-acknowledge",history_scope:"workspace"},{action:"history-redact",history_scope:"history"},{action:"history-repair",history_scope:"history"},{action:"init",history_scope:"workspace"},{action:"install",history_scope:"conditional"},{action:"learnings",history_scope:"conditional"},{action:"notes",history_scope:"conditional"},{action:"package",history_scope:"conditional"},{action:"pause-task",history_scope:"item"},{action:"plan",history_scope:"conditional"},{action:"profile",history_scope:"conditional"},{action:"release",history_scope:"item"},{action:"restore",history_scope:"item"},{action:"schema",history_scope:"conditional"},{action:"start-task",history_scope:"item"},{action:"templates",history_scope:"conditional"},{action:"test",history_scope:"conditional"},{action:"test-all",history_scope:"conditional"},{action:"update",history_scope:"item"},{action:"update-many",history_scope:"item"},{action:"upgrade",history_scope:"conditional"}],MUTATION_ACTION_CONTRACTS_BY_NAME=new Map(PM_MUTATION_ACTION_CONTRACTS.map(contract=>[contract.action,contract]));function stringLeaves(value,path="$",seen=new WeakSet){return typeof value=="string"?[{path,value}]:typeof value!="object"||value===null?[]:seen.has(value)?[]:(seen.add(value),Array.isArray(value)?value.flatMap((entry,index)=>stringLeaves(entry,`${path}[${index}]`,seen)):Object.entries(value).flatMap(([key,entry])=>stringLeaves(entry,`${path}.${key}`,seen)))}function scanMutationSecrets(payload){const findings=[];for(const leaf of stringLeaves(payload))for(const rule of SECRET_RULES){rule.regex.lastIndex=0;for(const match of leaf.value.matchAll(rule.regex))if(acceptsSecretMatch(rule,match)){findings.push({rule:rule.mutationRule,path:leaf.path});break}}return findings.filter((finding,index,all)=>all.findIndex(candidate=>candidate.rule===finding.rule&&candidate.path===finding.path)===index)}function evaluateMutationGuard(options){const author=options.author.trim()||"unknown";if(options.settings.require_attributed_author&&author.toLowerCase()==="unknown")throw new PmCliError("Mutation author is required by workspace policy.",EXIT_CODE.USAGE,{code:"mutation_author_required",required:"Pass --author <id>, set PM_AUTHOR, or configure author_default.",nextSteps:["Retry with --author <stable-agent-id>.","Set PM_AUTHOR for this agent session."]});if(options.settings.secret_guard==="off")return{author,findings:[],warnings:[],override_applied:!1};let findings;try{findings=scanMutationSecrets(options.payload)}catch{return{author,findings:[],warnings:["secret_guard_scan_failed_open"],override_applied:!1}}if(findings.length===0)return{author,findings,warnings:[],override_applied:!1};const ruleSummary=[...new Set(findings.map(finding=>finding.rule))].sort().join(",");if(options.settings.secret_guard==="block"&&options.force!==!0)throw new PmCliError(`Mutation blocked by secret guard (${ruleSummary}). No detected value was logged.`,EXIT_CODE.CONFLICT,{code:"mutation_secret_guard_blocked",required:"Remove the credential-shaped content or retry with --force after explicit review.",nextSteps:["Replace the value with a secret-manager reference.","Use pm history-redact if equivalent content already entered history."],recovery:{recovery_mode:"compact",attempted_command:"pm <mutation> [REDACTED]",normalized_args:["[REDACTED]"],suggested_retry:"Remove credential-shaped content, then retry the mutation."}});return{author,findings,warnings:[`secret_guard_detected:${findings.length}:rules=${ruleSummary}`,...options.settings.secret_guard==="block"&&options.force===!0?["secret_guard_force_override"]:[]],override_applied:options.settings.secret_guard==="block"&&options.force===!0}}function isMutationAction(action){return MUTATION_ACTION_CONTRACTS_BY_NAME.has(action.trim().toLowerCase())}function listMutationActions(){return[...MUTATION_ACTION_CONTRACTS_BY_NAME.keys()].sort((left,right)=>left.localeCompare(right))}function getMutationActionContract(action){return MUTATION_ACTION_CONTRACTS_BY_NAME.get(action.trim().toLowerCase())}export{PM_MUTATION_ACTION_CONTRACTS,evaluateMutationGuard,getMutationActionContract,isMutationAction,listMutationActions,scanMutationSecrets};
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]="d6059407-bf03-5ae8-8b01-e3e7c5b28030")}catch{}})();import{SECRET_RULES,findSecretRuleMatches}from"../core/shared/secret-rules.js";import{EXIT_CODE}from"../core/shared/constants.js";import{PmCliError}from"../core/shared/errors.js";const PM_MUTATION_ACTION_CONTRACTS=[{action:"append",history_scope:"item"},{action:"claim",history_scope:"item"},{action:"close",history_scope:"item"},{action:"close-many",history_scope:"item"},{action:"close-task",history_scope:"item"},{action:"comments",history_scope:"conditional"},{action:"config",history_scope:"conditional"},{action:"copy",history_scope:"item"},{action:"create",history_scope:"item"},{action:"delete",history_scope:"item"},{action:"deps",history_scope:"conditional"},{action:"discover",history_scope:"item"},{action:"docs",history_scope:"conditional"},{action:"files",history_scope:"conditional"},{action:"focus",history_scope:"conditional"},{action:"gc",history_scope:"conditional"},{action:"history-compact",history_scope:"history"},{action:"history-author-acknowledge",history_scope:"workspace"},{action:"history-redact",history_scope:"history"},{action:"history-repair",history_scope:"history"},{action:"init",history_scope:"workspace"},{action:"install",history_scope:"conditional"},{action:"learnings",history_scope:"conditional"},{action:"notes",history_scope:"conditional"},{action:"package",history_scope:"conditional"},{action:"pause-task",history_scope:"item"},{action:"plan",history_scope:"conditional"},{action:"profile",history_scope:"conditional"},{action:"release",history_scope:"item"},{action:"restore",history_scope:"item"},{action:"schema",history_scope:"conditional"},{action:"start-task",history_scope:"item"},{action:"templates",history_scope:"conditional"},{action:"test",history_scope:"conditional"},{action:"test-all",history_scope:"conditional"},{action:"update",history_scope:"item"},{action:"update-many",history_scope:"item"},{action:"upgrade",history_scope:"conditional"}],MUTATION_ACTION_CONTRACTS_BY_NAME=new Map(PM_MUTATION_ACTION_CONTRACTS.map(contract=>[contract.action,contract]));function stringLeaves(value,path="$",seen=new WeakSet){return typeof value=="string"?[{path,value}]:typeof value!="object"||value===null?[]:seen.has(value)?[]:(seen.add(value),Array.isArray(value)?value.flatMap((entry,index)=>stringLeaves(entry,`${path}[${index}]`,seen)):Object.entries(value).flatMap(([key,entry])=>stringLeaves(entry,`${path}.${key}`,seen)))}function preflightRuleMatch(rule,value){const matches=findSecretRuleMatches(rule,value),first=matches.next().value;if(!first||rule.mutationRule!=="absolute_home_path"||first.normalization_incomplete)return first;for(const later of matches)if(later.normalization_incomplete)return{...first,normalizations:[...new Set([first.normalizations,later.normalizations].flatMap(kinds=>kinds??[]))],normalization_incomplete:!0};return first}function scanMutationSecrets(payload){const findings=[];for(const leaf of stringLeaves(payload))for(const rule of SECRET_RULES){const match=preflightRuleMatch(rule,leaf.value);if(match){const finding={rule:rule.mutationRule,path:leaf.path};match.normalizations&&(finding.normalizations=match.normalizations),match.normalization_incomplete&&(finding.normalization_incomplete=!0),findings.push(finding)}}return findings.filter((finding,index,all)=>all.findIndex(candidate=>candidate.rule===finding.rule&&candidate.path===finding.path)===index)}function evaluateMutationGuard(options){const author=options.author.trim()||"unknown";if(options.settings.require_attributed_author&&author.toLowerCase()==="unknown")throw new PmCliError("Mutation author is required by workspace policy.",EXIT_CODE.USAGE,{code:"mutation_author_required",required:"Pass --author <id>, set PM_AUTHOR, or configure author_default.",nextSteps:["Retry with --author <stable-agent-id>.","Set PM_AUTHOR for this agent session."]});if(options.settings.secret_guard==="off")return{author,findings:[],warnings:[],override_applied:!1};let findings;try{findings=scanMutationSecrets(options.payload)}catch{return{author,findings:[],warnings:["secret_guard_scan_failed_open"],override_applied:!1}}if(findings.length===0)return{author,findings,warnings:[],override_applied:!1};const ruleSummary=[...new Set(findings.map(finding=>finding.rule))].sort().join(",");if(options.settings.secret_guard==="block"&&options.force!==!0)throw new PmCliError(`Mutation blocked by secret guard (${ruleSummary}). No detected value was logged.`,EXIT_CODE.CONFLICT,{code:"mutation_secret_guard_blocked",required:"Remove the credential-shaped content or retry with --force after explicit review.",nextSteps:["Replace the value with a secret-manager reference.","Use pm history-redact if equivalent content already entered history."],recovery:{recovery_mode:"compact",attempted_command:"pm <mutation> [REDACTED]",normalized_args:["[REDACTED]"],suggested_retry:"Remove credential-shaped content, then retry the mutation."}});return{author,findings,warnings:[`secret_guard_detected:${findings.length}:rules=${ruleSummary}`,...options.settings.secret_guard==="block"&&options.force===!0?["secret_guard_force_override"]:[]],override_applied:options.settings.secret_guard==="block"&&options.force===!0}}function isMutationAction(action){return MUTATION_ACTION_CONTRACTS_BY_NAME.has(action.trim().toLowerCase())}function listMutationActions(){return[...MUTATION_ACTION_CONTRACTS_BY_NAME.keys()].sort((left,right)=>left.localeCompare(right))}function getMutationActionContract(action){return MUTATION_ACTION_CONTRACTS_BY_NAME.get(action.trim().toLowerCase())}export{PM_MUTATION_ACTION_CONTRACTS,evaluateMutationGuard,getMutationActionContract,isMutationAction,listMutationActions,scanMutationSecrets};
2
2
  //# sourceMappingURL=mutation-guard.js.map
@@ -0,0 +1,98 @@
1
+ /** Deterministic admission policy; neither policy reorders or splits rows. */
2
+ export type OutputEnvelopeFitPolicy = "prefix" | "skip";
3
+ /** SDK-owned receipt included in every measured envelope as output_fit. */
4
+ export interface OutputEnvelopeFitReceipt {
5
+ /** Receipt schema version. */
6
+ version:1;
7
+ /** Admission policy applied in caller order. */
8
+ policy:OutputEnvelopeFitPolicy;
9
+ /** Number of rows offered before fitting. */
10
+ total_candidates:number;
11
+ /** Original positions of the retained rows. */
12
+ included_indices:readonly number[];
13
+ /** Number of whole rows withheld by the policy. */
14
+ omitted_count:number;
15
+ /** Binding ceiling in the caller's token estimator units. */
16
+ token_budget:number;
17
+ /** Greatest complete-envelope estimate across all required renderers. */
18
+ estimated_tokens:number;
19
+ /** Greatest UTF-8 byte count across all required renderers. */
20
+ output_bytes:number;
21
+ }
22
+ /** Envelope with the reserved, SDK-owned accounting and omission receipt. */
23
+ export type FittedOutputEnvelope<Envelope> = Omit<Envelope,"output_fit"> & {
24
+ /** Self-accounting receipt, including its own bytes and framing. */
25
+ readonly output_fit:Readonly<OutputEnvelopeFitReceipt>;
26
+ };
27
+ /** One complete rendering, ready to emit without reserializing or reframing. */
28
+ export interface OutputEnvelopeRendering {
29
+ /** Caller-selected transport identity. */
30
+ name:string;
31
+ /** Complete output, including any caller-supplied newline or framing. */
32
+ text:string;
33
+ /** UTF-8 bytes of text, including the receipt. */
34
+ bytes:number;
35
+ /** Estimate of text under the selected estimator. */
36
+ estimated_tokens:number;
37
+ }
38
+ /** Actual fitting work, kept outside the measured output to avoid self-counting. */
39
+ export interface OutputEnvelopeFitWork {
40
+ /** Candidate admission trials performed, excluding the minimum envelope. */
41
+ evaluated_candidates:number;
42
+ /** Renderer calls across all fixed-point passes and admission trials. */
43
+ render_calls:number;
44
+ /** Hard ceiling on renderer calls. */
45
+ max_render_calls:number;
46
+ }
47
+ /** Explicit failure to produce a verified packet; no oversized output is returned. */
48
+ export declare class OutputEnvelopeFitError extends RangeError {
49
+ /** Stable refusal reason suitable for package-specific error translation. */
50
+ readonly code:"output_envelope_infeasible" | "output_envelope_work_limit" | "output_envelope_unstable";
51
+ /** Work completed before refusal. */
52
+ readonly work:Readonly<OutputEnvelopeFitWork>;
53
+ /** Measured minimum-envelope estimate when the budget is infeasible. */
54
+ readonly minimum_tokens:number | undefined;
55
+ /** Capture immutable refusal evidence without serializing caller data. */
56
+ constructor(code:OutputEnvelopeFitError["code"],work:OutputEnvelopeFitWork,minimumTokens?:number);
57
+ }
58
+ /** Caller-owned envelope construction, renderers and finite work controls. */
59
+ export interface FitOutputEnvelopeOptions<Row,Envelope extends Record<string,unknown>> {
60
+ /** Maximum complete-envelope estimate accepted by every required renderer. */
61
+ tokenBudget:number;
62
+ /** Preserve a prefix by default; skip may retain later smaller rows. */
63
+ policy?:OutputEnvelopeFitPolicy;
64
+ /** Construct mandatory domain metadata; output_fit is reserved and overwritten by the SDK. Must be pure and deterministic. */
65
+ buildEnvelope:(rows:readonly Row[],receipt:Readonly<OutputEnvelopeFitReceipt>) => Envelope;
66
+ /** Nonempty set of pure deterministic renderers, including all output framing. */
67
+ renderers:Readonly<Record<string,(envelope:FittedOutputEnvelope<Envelope>) => string>>;
68
+ /** Estimate the exact rendered text; defaults to ceil(UTF-8 bytes / 4). Stronger estimators may be supplied. */
69
+ estimateTokens?:(text:string) => number;
70
+ /** Hard renderer-call ceiling; defaults to 10,000. */
71
+ maxRenderCalls?:number;
72
+ /** Fixed-point pass ceiling per admission trial; defaults to 16. */
73
+ maxStabilizationPasses?:number;
74
+ }
75
+ /** Verified envelope and cached renderings; work is diagnostic, not emitted packet data. */
76
+ export interface OutputEnvelopeFitResult<Row,Envelope> {
77
+ /** Whole caller rows retained in original order. */
78
+ rows:readonly Row[];
79
+ /** Domain metadata plus the exact measured receipt. */
80
+ envelope:FittedOutputEnvelope<Envelope>;
81
+ /** Cached complete outputs; every rendering satisfies the budget. */
82
+ renderings:readonly OutputEnvelopeRendering[];
83
+ /** True only when all candidates were retained. */
84
+ complete:boolean;
85
+ /** Actual bounded fitting work. */
86
+ work:Readonly<OutputEnvelopeFitWork>;
87
+ }
88
+ /**
89
+ * Fit whole ordered rows against every required complete rendering. First prove
90
+ * the empty mandatory envelope fits, then admit rows under prefix or skip
91
+ * policy. No monotonic-size assumption or binary search is used. Callbacks
92
+ * must be pure and return promptly; call/pass ceilings bound SDK fitting work,
93
+ * not arbitrary callback execution time. Emit the cached renderings directly.
94
+ *
95
+ * @throws {OutputEnvelopeFitError} When the minimum envelope is too large,
96
+ * accounting fails to stabilize, or the renderer-call ceiling is exhausted.
97
+ */
98
+ export declare function fitOutputEnvelope<Row,Envelope extends Record<string,unknown>>(candidates:readonly Row[],options:FitOutputEnvelopeOptions<Row,Envelope>):OutputEnvelopeFitResult<Row,Envelope>;
@@ -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]="f698608a-2f39-5972-b24b-93287af6b373")}catch{}})();import{estimatePmOutputTokens}from"../cli-contracts/agent-output-contracts.js";class OutputEnvelopeFitError extends RangeError{code;work;minimum_tokens;constructor(code,work,minimumTokens){super(code),this.name="OutputEnvelopeFitError",this.code=code,this.work=Object.freeze({...work}),this.minimum_tokens=minimumTokens}}function assertPositiveInteger(value,name){if(!Number.isSafeInteger(value)||value<1)throw new RangeError(`${name} must be a positive safe integer`)}function renderEnvelope(envelope,renderers,estimateTokens,work){return renderers.map(([name,render])=>{if(work.render_calls>=work.max_render_calls)throw new OutputEnvelopeFitError("output_envelope_work_limit",work);work.render_calls+=1;const text=render(envelope);if(typeof text!="string")throw new TypeError("Envelope renderers must return strings");const estimatedTokens=estimateTokens(text);if(!Number.isSafeInteger(estimatedTokens)||estimatedTokens<0)throw new RangeError("Envelope token estimates must be non-negative safe integers");return{name,text,bytes:Buffer.byteLength(text,"utf8"),estimated_tokens:estimatedTokens}})}function stabilizeEnvelope(rows,initial,options,work){let receipt=initial;const renderers=Object.entries(options.renderers),estimateTokens=options.estimateTokens??(text=>estimatePmOutputTokens(Buffer.byteLength(text,"utf8")));for(let pass=0;pass<(options.maxStabilizationPasses??16);pass+=1){const frozenReceipt=Object.freeze({...receipt}),envelope={...options.buildEnvelope(rows,frozenReceipt),output_fit:frozenReceipt},renderings=renderEnvelope(envelope,renderers,estimateTokens,work),bytes=Math.max(...renderings.map(rendering=>rendering.bytes)),tokens=Math.max(...renderings.map(rendering=>rendering.estimated_tokens));if(bytes===receipt.output_bytes&&tokens===receipt.estimated_tokens)return{envelope,renderings};receipt={...receipt,output_bytes:bytes,estimated_tokens:tokens}}throw new OutputEnvelopeFitError("output_envelope_unstable",work)}function fitOutputEnvelope(candidates,options){assertPositiveInteger(options.tokenBudget,"tokenBudget"),assertPositiveInteger(options.maxRenderCalls??1e4,"maxRenderCalls"),assertPositiveInteger(options.maxStabilizationPasses??16,"maxStabilizationPasses");const policy=options.policy??"prefix";if(policy!=="prefix"&&policy!=="skip")throw new TypeError("Unknown output envelope fit policy");if(Object.entries(options.renderers).length===0||Object.entries(options.renderers).some(([name,render])=>!name.trim()||typeof render!="function"))throw new TypeError("Envelope fitting requires named callable renderers");const work={evaluated_candidates:0,render_calls:0,max_render_calls:options.maxRenderCalls??1e4};let rows=[],included=[];const receiptFor=indices=>({version:1,policy,total_candidates:candidates.length,included_indices:Object.freeze([...indices]),omitted_count:candidates.length-indices.length,token_budget:options.tokenBudget,estimated_tokens:0,output_bytes:0});let fitted=stabilizeEnvelope(rows,receiptFor(included),options,work);if(fitted.envelope.output_fit.estimated_tokens>options.tokenBudget)throw new OutputEnvelopeFitError("output_envelope_infeasible",work,fitted.envelope.output_fit.estimated_tokens);for(const[index,candidate]of candidates.entries()){work.evaluated_candidates+=1;const trialRows=[...rows,candidate],trialIndices=[...included,index],trial=stabilizeEnvelope(trialRows,receiptFor(trialIndices),options,work);if(trial.envelope.output_fit.estimated_tokens<=options.tokenBudget)rows=trialRows,included=trialIndices,fitted=trial;else if(policy==="prefix")break}return{rows,...fitted,complete:rows.length===candidates.length,work:Object.freeze({...work})}}export{OutputEnvelopeFitError,fitOutputEnvelope};
2
+ //# sourceMappingURL=fit-envelope.js.map