@unbrained/pm-cli 2026.8.24 → 2026.8.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 (143) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/CHANGELOG.md +46 -0
  3. package/dist/cli-bundle/bundle-manifest.json +149 -149
  4. package/dist/cli-bundle/chunks/chunk-E2GCFJSU.js +35 -0
  5. package/dist/cli-bundle/chunks/{chunk-TEQDR5KI.js → chunk-ES25LX3D.js} +62 -62
  6. package/dist/cli-bundle/chunks/{chunk-I5Q7QSZX.js → chunk-FRDWWB6R.js} +2 -2
  7. package/dist/cli-bundle/chunks/{chunk-X7WWQDYQ.js → chunk-ICQ3RVIY.js} +2 -2
  8. package/dist/cli-bundle/chunks/{chunk-YOLLRCLY.js → chunk-IV64RJVE.js} +2 -2
  9. package/dist/cli-bundle/chunks/{chunk-L22NP5MA.js → chunk-KBFP3E4E.js} +86 -86
  10. package/dist/cli-bundle/chunks/{chunk-AGUL3HUT.js → chunk-MVYLQ67M.js} +2 -2
  11. package/dist/cli-bundle/chunks/{chunk-DJNJBWAQ.js → chunk-S4U76VZF.js} +2 -2
  12. package/dist/cli-bundle/chunks/{chunk-IW72J5FW.js → chunk-UKBCRPA2.js} +2 -2
  13. package/dist/cli-bundle/chunks/chunk-WRHJ3MB6.js +8 -0
  14. package/dist/cli-bundle/chunks/{chunk-ZAVCS2GH.js → chunk-ZNRLJ54C.js} +60 -60
  15. package/dist/cli-bundle/chunks/{register-list-query-37A4P6O4.js → register-list-query-EMCPMICY.js} +2 -2
  16. package/dist/cli-bundle/chunks/{register-mutation-TBJDHYIT.js → register-mutation-OJ67ABCB.js} +3 -3
  17. package/dist/cli-bundle/chunks/{register-operations-WRZNEBUM.js → register-operations-H2GLP7LT.js} +2 -2
  18. package/dist/cli-bundle/chunks/{register-setup-H4PDLACD.js → register-setup-GLZAHLVI.js} +2 -2
  19. package/dist/cli-bundle/focused-chunks/{chunk-IMWTYKII.js → chunk-4K2II4TV.js} +2 -2
  20. package/dist/cli-bundle/focused-chunks/chunk-4XNH2HM7.js +2 -0
  21. package/dist/cli-bundle/focused-chunks/{chunk-GJR6YEMR.js → chunk-57XY346D.js} +2 -2
  22. package/dist/cli-bundle/focused-chunks/{chunk-4WP7LNE3.js → chunk-66VGB23P.js} +2 -2
  23. package/dist/cli-bundle/focused-chunks/chunk-72T6JGAE.js +50 -0
  24. package/dist/cli-bundle/focused-chunks/{chunk-BFA6KKCI.js → chunk-7I23XGWO.js} +2 -2
  25. package/dist/cli-bundle/focused-chunks/{chunk-6Z24ZMU3.js → chunk-7YCDTCBC.js} +2 -2
  26. package/dist/cli-bundle/focused-chunks/{chunk-O4ZXNJMT.js → chunk-A644DUFQ.js} +2 -2
  27. package/dist/cli-bundle/focused-chunks/chunk-FXDLT6FL.js +16 -0
  28. package/dist/cli-bundle/focused-chunks/{chunk-RASVBJX5.js → chunk-IBHXMFE7.js} +2 -2
  29. package/dist/cli-bundle/focused-chunks/chunk-LMKG3DFE.js +5 -0
  30. package/dist/cli-bundle/focused-chunks/{chunk-6MUTDXMP.js → chunk-LV5N3LK5.js} +2 -2
  31. package/dist/cli-bundle/focused-chunks/{chunk-CWCN5RLO.js → chunk-MMXUPDDJ.js} +2 -2
  32. package/dist/cli-bundle/focused-chunks/{chunk-WZLSSGEU.js → chunk-OHIHZ7HS.js} +2 -2
  33. package/dist/cli-bundle/focused-chunks/chunk-P2E6LDAE.js +19 -0
  34. package/dist/cli-bundle/focused-chunks/{chunk-IZNRRCU3.js → chunk-TMJDFHVD.js} +2 -2
  35. package/dist/cli-bundle/focused-chunks/{chunk-QBP5FSBX.js → chunk-UYBA57GY.js} +4 -4
  36. package/dist/cli-bundle/focused-chunks/{chunk-MNPECIRL.js → chunk-YO3ZF3FI.js} +2 -2
  37. package/dist/cli-bundle/main.js +2 -2
  38. package/dist/cli-bundle/sdk-authoring.js +1 -1
  39. package/dist/cli-bundle/sdk-contracts.js +2 -2
  40. package/dist/cli-bundle/sdk-core.js +41 -41
  41. package/dist/cli-bundle/sdk-governance.js +1 -1
  42. package/dist/cli-bundle/sdk-graph.js +1 -1
  43. package/dist/cli-bundle/sdk-merge.js +32 -33
  44. package/dist/cli-bundle/sdk-query.js +1 -1
  45. package/dist/cli-bundle/sdk-runtime.js +1 -1
  46. package/dist/cli-bundle/sdk-testing.js +1 -1
  47. package/dist/cli-bundle/sdk.js +7 -6
  48. package/dist/core/diagnostics/remediation.js +17 -2
  49. package/dist/core/extensions/manifest-schema.d.ts +20 -0
  50. package/dist/core/extensions/manifest-schema.js +28 -10
  51. package/dist/mcp/http-server.d.ts +60 -0
  52. package/dist/mcp/http-server.js +451 -0
  53. package/dist/mcp/legacy-adapter.d.ts +50 -0
  54. package/dist/mcp/legacy-adapter.js +64 -0
  55. package/dist/mcp/server.d.ts +42 -9
  56. package/dist/mcp/server.js +457 -58
  57. package/dist/sdk/agent/refusal-closure-census.d.ts +6 -2
  58. package/dist/sdk/agent/refusal-closure-census.js +16 -8
  59. package/dist/sdk/cli-contracts/runtime-contracts.d.ts +10 -0
  60. package/dist/sdk/cli-contracts/runtime-contracts.js +20 -3
  61. package/dist/sdk/compose.d.ts +4 -1
  62. package/dist/sdk/compose.js +23 -36
  63. package/dist/sdk/extension/author-manifest.d.ts +22 -0
  64. package/dist/sdk/extension/author-manifest.js +93 -0
  65. package/dist/sdk/extension.js +6 -3
  66. package/dist/sdk/generated/generated-error-code-catalog-part-1.js +67 -28
  67. package/dist/sdk/generated/generated-error-code-catalog-part-2.js +30 -3
  68. package/dist/sdk/governance/health.js +103 -11
  69. package/dist/sdk/governance/validate.js +15 -9
  70. package/dist/sdk/history-repair.d.ts +24 -0
  71. package/dist/sdk/history-repair.js +145 -4
  72. package/dist/sdk/index.d.ts +6 -0
  73. package/dist/sdk/index.js +8 -2
  74. package/dist/sdk/mcp/authorization.d.ts +134 -0
  75. package/dist/sdk/mcp/authorization.js +405 -0
  76. package/dist/sdk/mcp/interactions.d.ts +118 -0
  77. package/dist/sdk/mcp/interactions.js +337 -0
  78. package/dist/sdk/mcp/protocol.d.ts +142 -0
  79. package/dist/sdk/mcp/protocol.js +174 -0
  80. package/dist/sdk/mcp/subscriptions.d.ts +65 -0
  81. package/dist/sdk/mcp/subscriptions.js +212 -0
  82. package/dist/sdk/mcp/tasks.d.ts +107 -0
  83. package/dist/sdk/mcp/tasks.js +431 -0
  84. package/dist/sdk/mcp/transport.d.ts +30 -0
  85. package/dist/sdk/mcp/transport.js +261 -0
  86. package/dist/sdk/merge/driver.js +25 -4
  87. package/dist/sdk/merge/receipt-file-boundary.d.ts +21 -0
  88. package/dist/sdk/merge/receipt-file-boundary.js +47 -0
  89. package/dist/sdk/merge/receipts.d.ts +40 -1
  90. package/dist/sdk/merge/receipts.js +343 -36
  91. package/dist/sdk/merge/reconcile.js +117 -47
  92. package/dist/sdk/read-output/continuation.d.ts +15 -2
  93. package/dist/sdk/read-output/continuation.js +86 -6
  94. package/dist/sdk/read-output-contracts.d.ts +4 -1
  95. package/dist/sdk/read-output-contracts.js +9 -6
  96. package/dist/sdk/runtime-primitives.d.ts +1 -1
  97. package/dist/sdk/runtime-primitives.js +3 -3
  98. package/dist/sdk/test/execution.d.ts +6 -0
  99. package/dist/sdk/test/execution.js +32 -3
  100. package/docs/AGENT_PROVENANCE_ADR.md +6 -4
  101. package/docs/AGENT_RUNTIME_PRIMITIVES.md +6 -5
  102. package/docs/CLAUDE_CODE_PLUGIN.md +12 -5
  103. package/docs/DIAGNOSTIC_OUTPUT_CONTRACTS.md +8 -0
  104. package/docs/EXTENSIONS.md +3 -4
  105. package/docs/MCP_2026_07_28.md +138 -0
  106. package/docs/MCP_2026_07_28_CONFORMANCE.md +30 -0
  107. package/docs/MCP_REMOTE_TRANSPORT_SECURITY.md +180 -0
  108. package/docs/MERGE_SAFETY.md +34 -5
  109. package/docs/OUTPUT_PROJECTION_CONTRACTS.md +13 -1
  110. package/docs/README.md +4 -0
  111. package/docs/READ_OUTPUT_CONTRACTS.md +25 -2
  112. package/docs/RELEASING.md +3 -2
  113. package/docs/SDK.md +22 -1
  114. package/docs/SDK_AGENT_SESSION_CONTEXT.md +18 -13
  115. package/docs/SDK_MCP_INTERACTIONS.md +227 -0
  116. package/docs/TESTING.md +4 -0
  117. package/docs/generated/REFUSAL_CLOSURE_CENSUS.md +13 -8
  118. package/marketplace.json +2 -2
  119. package/package.json +7 -5
  120. package/packages/pm-beads/package.json +1 -1
  121. package/packages/pm-calendar/package.json +1 -1
  122. package/packages/pm-command-kit/package.json +1 -1
  123. package/packages/pm-digital-twin/package.json +1 -1
  124. package/packages/pm-governance-audit/package.json +1 -1
  125. package/packages/pm-guide-shell/package.json +1 -1
  126. package/packages/pm-kanban/package.json +1 -1
  127. package/packages/pm-lifecycle-hooks/package.json +1 -1
  128. package/packages/pm-linked-test-adapters/package.json +1 -1
  129. package/packages/pm-search-advanced/package.json +1 -1
  130. package/packages/pm-templates/package.json +1 -1
  131. package/packages/pm-todos/package.json +1 -1
  132. package/packages/pm-vcs/package.json +1 -1
  133. package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
  134. package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
  135. package/scripts/finalize-build.mjs +1 -0
  136. package/sdk/public-surface.json +762 -15
  137. package/dist/cli-bundle/chunks/chunk-MKE6MD2K.js +0 -36
  138. package/dist/cli-bundle/chunks/chunk-NXBFO63A.js +0 -8
  139. package/dist/cli-bundle/focused-chunks/chunk-FCTT4CZU.js +0 -5
  140. package/dist/cli-bundle/focused-chunks/chunk-Q63PSLO4.js +0 -2
  141. package/dist/cli-bundle/focused-chunks/chunk-QV5UP27O.js +0 -51
  142. package/dist/cli-bundle/focused-chunks/chunk-RPRLENVA.js +0 -16
  143. package/dist/cli-bundle/focused-chunks/chunk-TOFVXBWA.js +0 -19
@@ -0,0 +1,261 @@
1
+ /**
2
+ * @module sdk/mcp/transport
3
+ *
4
+ * Implements MCP 2026-07-28 Streamable HTTP header encoding, schema-derived
5
+ * custom header extraction, and body/header parity validation.
6
+ */
7
+
8
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="5ffe7128-0fbb-59af-89ef-74d5d799a00d")}catch(e){}}();
9
+ import { PM_MCP_ERROR_CODES, PM_MCP_PROTOCOL_VERSION, PmMcpProtocolError, isMcpRecord, resolveMcpRequestContext, } from "./protocol.js";
10
+ const BASE64_SENTINEL_PATTERN = /^=\?base64\?([A-Za-z0-9+/]*={0,2})\?=$/u;
11
+ const HEADER_NAME_TOKEN_PATTERN = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/u;
12
+ const PLAIN_HEADER_VALUE_PATTERN = /^[\t\x20-\x7E]*$/u;
13
+ const REQUIRED_NAME_METHODS = new Set([
14
+ "prompts/get",
15
+ "resources/read",
16
+ "tools/call",
17
+ ]);
18
+ function normalizedHeaders(headers) {
19
+ const normalized = new Map();
20
+ for (const [name, value] of Object.entries(headers)) {
21
+ if (value === undefined)
22
+ continue;
23
+ const entry = typeof value === "string" ? value : value.join(", ");
24
+ normalized.set(name.toLowerCase(), entry);
25
+ }
26
+ return normalized;
27
+ }
28
+ function failHeader(message, header) {
29
+ throw new PmMcpProtocolError(message, PM_MCP_ERROR_CODES.headerMismatch, {
30
+ header,
31
+ });
32
+ }
33
+ /** Encode a name or primitive parameter into the protocol's safe header form. */
34
+ export function encodeMcpHttpHeaderValue(value) {
35
+ const serialized = typeof value === "boolean" ? String(value).toLowerCase() : String(value);
36
+ if (serialized.length > 0 &&
37
+ serialized.trim() === serialized &&
38
+ PLAIN_HEADER_VALUE_PATTERN.test(serialized) &&
39
+ !BASE64_SENTINEL_PATTERN.test(serialized)) {
40
+ return serialized;
41
+ }
42
+ return `=?base64?${Buffer.from(serialized, "utf8").toString("base64")}?=`;
43
+ }
44
+ /** Decode and validate a mirrored MCP header value. */
45
+ export function decodeMcpHttpHeaderValue(value, header) {
46
+ const sentinel = BASE64_SENTINEL_PATTERN.exec(value);
47
+ if (sentinel) {
48
+ const encoded = sentinel[1];
49
+ const decoded = Buffer.from(encoded, "base64");
50
+ if (decoded.toString("base64") !== encoded || decoded.length === 0) {
51
+ return failHeader("Malformed MCP Base64 header value", header);
52
+ }
53
+ return decoded.toString("utf8");
54
+ }
55
+ if (value.length === 0 ||
56
+ value.trim() !== value ||
57
+ !PLAIN_HEADER_VALUE_PATTERN.test(value)) {
58
+ return failHeader("Malformed MCP header value", header);
59
+ }
60
+ return value;
61
+ }
62
+ function containsHeaderAnnotation(value) {
63
+ if (Array.isArray(value))
64
+ return value.some(containsHeaderAnnotation);
65
+ if (!isMcpRecord(value))
66
+ return false;
67
+ return (Object.prototype.hasOwnProperty.call(value, "x-mcp-header") ||
68
+ Object.values(value).some(containsHeaderAnnotation));
69
+ }
70
+ /** Validate and normalize one schema-derived custom-header annotation. */
71
+ function parseMcpHeaderAnnotation(propertySchema, propertyPath, names) {
72
+ const annotation = propertySchema["x-mcp-header"];
73
+ if (annotation === undefined)
74
+ return undefined;
75
+ const type = propertySchema.type;
76
+ if (typeof annotation !== "string" ||
77
+ !HEADER_NAME_TOKEN_PATTERN.test(annotation) ||
78
+ !["boolean", "integer", "string"].includes(String(type)) ||
79
+ names.has(annotation.toLowerCase())) {
80
+ throw new PmMcpProtocolError("Invalid MCP x-mcp-header annotation", PM_MCP_ERROR_CODES.invalidParams, { field: propertyPath.join("."), annotation });
81
+ }
82
+ names.add(annotation.toLowerCase());
83
+ return {
84
+ name: annotation,
85
+ path: propertyPath,
86
+ type: type,
87
+ };
88
+ }
89
+ /** Parse valid statically reachable custom-header annotations from a tool schema. */
90
+ export function collectMcpHeaderAnnotations(schema) {
91
+ if (!isMcpRecord(schema)) {
92
+ throw new PmMcpProtocolError("Invalid MCP tool schema", PM_MCP_ERROR_CODES.invalidParams, { field: "inputSchema", expected: "object" });
93
+ }
94
+ const annotations = [];
95
+ const names = new Set();
96
+ const visitProperties = (node, path) => {
97
+ const properties = node.properties;
98
+ if (properties === undefined)
99
+ return;
100
+ if (!isMcpRecord(properties)) {
101
+ throw new PmMcpProtocolError("Invalid MCP tool schema properties", PM_MCP_ERROR_CODES.invalidParams, { field: "inputSchema.properties" });
102
+ }
103
+ for (const [property, propertySchema] of Object.entries(properties)) {
104
+ if (!isMcpRecord(propertySchema))
105
+ continue;
106
+ const propertyPath = [...path, property];
107
+ const annotation = parseMcpHeaderAnnotation(propertySchema, propertyPath, names);
108
+ if (annotation)
109
+ annotations.push(annotation);
110
+ for (const [keyword, nested] of Object.entries(propertySchema)) {
111
+ if (keyword !== "properties" &&
112
+ keyword !== "x-mcp-header" &&
113
+ containsHeaderAnnotation(nested)) {
114
+ throw new PmMcpProtocolError("MCP x-mcp-header is not statically reachable", PM_MCP_ERROR_CODES.invalidParams, { field: propertyPath.join("."), keyword });
115
+ }
116
+ }
117
+ visitProperties(propertySchema, propertyPath);
118
+ }
119
+ };
120
+ for (const [keyword, nested] of Object.entries(schema)) {
121
+ if (keyword !== "properties" && containsHeaderAnnotation(nested)) {
122
+ throw new PmMcpProtocolError("MCP x-mcp-header is not statically reachable", PM_MCP_ERROR_CODES.invalidParams, { field: "inputSchema", keyword });
123
+ }
124
+ }
125
+ visitProperties(schema, []);
126
+ return annotations;
127
+ }
128
+ function valueAtPath(value, path) {
129
+ let current = value;
130
+ for (const part of path) {
131
+ if (!isMcpRecord(current))
132
+ return undefined;
133
+ current = current[part];
134
+ }
135
+ return current;
136
+ }
137
+ function mirroredName(method, params) {
138
+ const value = method === "resources/read" ? params.uri : params.name;
139
+ return typeof value === "string" ? value : undefined;
140
+ }
141
+ function buildMcpCustomRequestHeaders(params, toolSchema) {
142
+ const argumentsValue = isMcpRecord(params.arguments) ? params.arguments : {};
143
+ const headers = {};
144
+ for (const annotation of collectMcpHeaderAnnotations(toolSchema)) {
145
+ const value = valueAtPath(argumentsValue, annotation.path);
146
+ if (value === undefined || value === null)
147
+ continue;
148
+ const valid = (annotation.type === "integer" &&
149
+ typeof value === "number" &&
150
+ Number.isSafeInteger(value)) ||
151
+ (annotation.type === "boolean" && typeof value === "boolean") ||
152
+ (annotation.type === "string" && typeof value === "string");
153
+ if (!valid) {
154
+ throw new PmMcpProtocolError("Invalid MCP custom header argument", PM_MCP_ERROR_CODES.invalidParams, { field: annotation.path.join("."), expected: annotation.type });
155
+ }
156
+ headers[`Mcp-Param-${annotation.name}`] = encodeMcpHttpHeaderValue(value);
157
+ }
158
+ return headers;
159
+ }
160
+ /** Build required Streamable HTTP headers for one modern client request. */
161
+ export function buildMcpHttpRequestHeaders(input) {
162
+ const params = isMcpRecord(input.request.params)
163
+ ? input.request.params
164
+ : undefined;
165
+ const context = resolveMcpRequestContext(params);
166
+ const method = input.request.method;
167
+ if (typeof method !== "string" || method.length === 0) {
168
+ return failHeader("Missing MCP request method", "Mcp-Method");
169
+ }
170
+ const headers = {
171
+ Accept: "application/json, text/event-stream",
172
+ "Content-Type": "application/json",
173
+ "MCP-Protocol-Version": context.protocolVersion,
174
+ "Mcp-Method": method,
175
+ };
176
+ const name = mirroredName(method, params);
177
+ if (REQUIRED_NAME_METHODS.has(method)) {
178
+ if (name === undefined)
179
+ return failHeader("Missing MCP request name", "Mcp-Name");
180
+ headers["Mcp-Name"] = encodeMcpHttpHeaderValue(name);
181
+ }
182
+ if (method === "tools/call" && input.toolSchema !== undefined) {
183
+ Object.assign(headers, buildMcpCustomRequestHeaders(params, input.toolSchema));
184
+ }
185
+ return headers;
186
+ }
187
+ function validateMcpRequiredRequestHeaders(headers, request) {
188
+ const method = request.method;
189
+ if (typeof method !== "string" || method.length === 0) {
190
+ failHeader("Missing MCP request method", "Mcp-Method");
191
+ }
192
+ const params = isMcpRecord(request.params) ? request.params : undefined;
193
+ const expectedProtocol = resolveMcpRequestContext(params).protocolVersion;
194
+ if (headers.get("mcp-protocol-version") !== expectedProtocol) {
195
+ failHeader("MCP protocol header does not match request metadata", "MCP-Protocol-Version");
196
+ }
197
+ if (headers.get("mcp-method") !== method) {
198
+ failHeader("MCP method header does not match request body", "Mcp-Method");
199
+ }
200
+ if (REQUIRED_NAME_METHODS.has(method)) {
201
+ const headerName = headers.get("mcp-name");
202
+ const name = mirroredName(method, params);
203
+ if (headerName === undefined ||
204
+ name === undefined ||
205
+ decodeMcpHttpHeaderValue(headerName, "Mcp-Name") !== name) {
206
+ failHeader("MCP name header does not match request body", "Mcp-Name");
207
+ }
208
+ }
209
+ if (headers.has("last-event-id")) {
210
+ // The preceding match is an mcp-deprecation-negative-control: no resume/redelivery.
211
+ failHeader("MCP SSE resumability is not supported", "Last-Event-ID");
212
+ }
213
+ }
214
+ function customHeaderMatches(annotation, value, decoded) {
215
+ if (annotation.type === "integer") {
216
+ return (typeof value === "number" &&
217
+ Number.isSafeInteger(value) &&
218
+ Number(decoded) === value);
219
+ }
220
+ if (annotation.type === "boolean") {
221
+ return typeof value === "boolean" && decoded === String(value);
222
+ }
223
+ return typeof value === "string" && decoded === value;
224
+ }
225
+ function validateMcpCustomRequestHeaders(input) {
226
+ const argumentsValue = isMcpRecord(input.params.arguments)
227
+ ? input.params.arguments
228
+ : {};
229
+ for (const annotation of collectMcpHeaderAnnotations(input.toolSchema)) {
230
+ const value = valueAtPath(argumentsValue, annotation.path);
231
+ const header = `Mcp-Param-${annotation.name}`;
232
+ const mirrored = input.headers.get(header.toLowerCase());
233
+ if (value === undefined || value === null) {
234
+ if (mirrored !== undefined)
235
+ failHeader("Unexpected MCP parameter header", header);
236
+ continue;
237
+ }
238
+ if (mirrored === undefined)
239
+ failHeader("Missing MCP parameter header", header);
240
+ if (!customHeaderMatches(annotation, value, decodeMcpHttpHeaderValue(mirrored, header))) {
241
+ failHeader("MCP parameter header does not match request body", header);
242
+ }
243
+ }
244
+ }
245
+ /** Validate required Streamable HTTP headers against the JSON-RPC body. */
246
+ export function validateMcpHttpRequestHeaders(input) {
247
+ const headers = normalizedHeaders(input.headers);
248
+ const method = typeof input.request.method === "string" ? input.request.method : "";
249
+ validateMcpRequiredRequestHeaders(headers, input.request);
250
+ if (method !== "tools/call" || input.toolSchema === undefined)
251
+ return;
252
+ validateMcpCustomRequestHeaders({
253
+ headers,
254
+ params: input.request.params,
255
+ toolSchema: input.toolSchema,
256
+ });
257
+ }
258
+ /** Required protocol version exposed for clients constructing headers manually. */
259
+ export const PM_MCP_HTTP_PROTOCOL_VERSION = PM_MCP_PROTOCOL_VERSION;
260
+ //# sourceMappingURL=transport.js.map
261
+ //# debugId=5ffe7128-0fbb-59af-89ef-74d5d799a00d
@@ -9,14 +9,16 @@
9
9
  * merge never corrupts tracker storage the way raw conflict markers do.
10
10
  */
11
11
 
12
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="8d79e3a4-6104-5855-837f-69a041b87b27")}catch(e){}}();
12
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="2c8046a5-06aa-58f8-b347-e757086082d1")}catch(e){}}();
13
13
  import { readFile, writeFile } from "node:fs/promises";
14
14
  import path from "node:path";
15
15
  import { getActiveExtensionRegistrations } from "../../core/extensions/index.js";
16
16
  import { collectRegisteredItemFieldNames } from "../../core/extensions/item-fields.js";
17
17
  import { pathExists } from "../../core/fs/fs-utils.js";
18
+ import { parseItemDocument } from "../../core/item/item-format.js";
18
19
  import { EXIT_CODE } from "../../core/shared/constants.js";
19
20
  import { PmCliError } from "../../core/shared/errors.js";
21
+ import { sha256Hex, stableStringify } from "../../core/shared/serialization.js";
20
22
  import { nowIso } from "../../core/shared/time.js";
21
23
  import { getSettingsPath, resolvePmRoot } from "../../core/store/paths.js";
22
24
  import { readSettings } from "../../core/store/settings.js";
@@ -122,10 +124,12 @@ export async function runMergeDriver(options, global) {
122
124
  }
123
125
  else if (artifact === "item") {
124
126
  const settings = await loadOptionalSettings(global);
127
+ const itemFormat = itemFormatForPath(options.itemPath ?? outputPath, settings);
128
+ const extensionFieldNames = collectRegisteredItemFieldNames(getActiveExtensionRegistrations());
125
129
  const itemMerge = mergeItemDocuments(baseRaw, oursRaw, theirsRaw, {
126
- format: itemFormatForPath(options.itemPath ?? outputPath, settings),
130
+ format: itemFormat,
127
131
  schema: settings?.schema,
128
- extensionFieldNames: collectRegisteredItemFieldNames(getActiveExtensionRegistrations()),
132
+ extensionFieldNames,
129
133
  preferred,
130
134
  conflictResolution: "stable_value_order",
131
135
  });
@@ -137,6 +141,19 @@ export async function runMergeDriver(options, global) {
137
141
  conflict_resolution: "stable_value_order",
138
142
  };
139
143
  if (options.itemPath !== undefined) {
144
+ const mergedDocument = parseItemDocument(merged, {
145
+ format: itemFormat,
146
+ schema: settings?.schema,
147
+ extensionFieldNames,
148
+ });
149
+ const mergedMetadata = mergedDocument.metadata;
150
+ const receiptFields = [
151
+ ...new Set([
152
+ ...itemMerge.fields_from_theirs,
153
+ ...itemMerge.union_fields,
154
+ ...itemMerge.conflict_decisions.map((decision) => decision.field),
155
+ ]),
156
+ ];
140
157
  receiptInput = {
141
158
  cwd: process.cwd(),
142
159
  itemPath: options.itemPath,
@@ -144,6 +161,10 @@ export async function runMergeDriver(options, global) {
144
161
  conflictResolution: "stable_value_order",
145
162
  fieldsFromTheirs: itemMerge.fields_from_theirs,
146
163
  unionFields: itemMerge.union_fields,
164
+ mergedFieldHashes: Object.fromEntries(receiptFields.map((field) => [
165
+ field,
166
+ sha256Hex(stableStringify(field === "body" ? mergedDocument.body : mergedMetadata[field])),
167
+ ])),
147
168
  decisions: itemMerge.conflict_decisions,
148
169
  };
149
170
  }
@@ -187,4 +208,4 @@ export async function runMergeDriver(options, global) {
187
208
  };
188
209
  }
189
210
  //# sourceMappingURL=driver.js.map
190
- //# debugId=8d79e3a4-6104-5855-837f-69a041b87b27
211
+ //# debugId=2c8046a5-06aa-58f8-b347-e757086082d1
@@ -0,0 +1,21 @@
1
+ /**
2
+ * @module sdk/merge/receipt-file-boundary
3
+ *
4
+ * Reads untrusted receipt candidates through a bounded, no-follow file
5
+ * boundary whose race and short-read behavior can be exercised directly.
6
+ */
7
+ import { type Stats } from "node:fs";
8
+ import { type FileHandle } from "node:fs/promises";
9
+ /** Minimal filesystem operations required by the receipt reader. */
10
+ export interface ReceiptFileBoundary {
11
+ /** Inspect the path without following its final symbolic link. */
12
+ lstat(receiptPath: string): Promise<Stats>;
13
+ /** Open the candidate and return the handle used for verified reads. */
14
+ open(receiptPath: string, flags: number): Promise<FileHandle>;
15
+ }
16
+ /** Resolve the platform no-follow flag, falling back only where Node omits it. */
17
+ export declare function resolveReceiptNoFollowFlag(constants: {
18
+ O_NOFOLLOW?: number;
19
+ }): number;
20
+ /** Read one regular file only when its opened identity remains bounded and stable. */
21
+ export declare function readBoundedRegularFile(receiptPath: string, maximumBytes: number, boundary?: ReceiptFileBoundary): Promise<string | null>;
@@ -0,0 +1,47 @@
1
+ /**
2
+ * @module sdk/merge/receipt-file-boundary
3
+ *
4
+ * Reads untrusted receipt candidates through a bounded, no-follow file
5
+ * boundary whose race and short-read behavior can be exercised directly.
6
+ */
7
+
8
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="6ece0e15-aff5-5e2d-b18a-16ffaa0fe2ea")}catch(e){}}();
9
+ import { constants as fsConstants } from "node:fs";
10
+ import { lstat, open } from "node:fs/promises";
11
+ const NODE_RECEIPT_FILE_BOUNDARY = { lstat, open };
12
+ /** Resolve the platform no-follow flag, falling back only where Node omits it. */
13
+ export function resolveReceiptNoFollowFlag(constants) {
14
+ return constants.O_NOFOLLOW ?? 0;
15
+ }
16
+ /** Read one regular file only when its opened identity remains bounded and stable. */
17
+ export async function readBoundedRegularFile(receiptPath, maximumBytes, boundary = NODE_RECEIPT_FILE_BOUNDARY) {
18
+ const pathStats = await boundary.lstat(receiptPath);
19
+ if (pathStats.isSymbolicLink() ||
20
+ !pathStats.isFile() ||
21
+ pathStats.size > maximumBytes) {
22
+ return null;
23
+ }
24
+ const handle = await boundary.open(receiptPath, fsConstants.O_RDONLY | resolveReceiptNoFollowFlag(fsConstants));
25
+ try {
26
+ const openedStats = await handle.stat();
27
+ if (!openedStats.isFile() || openedStats.size > maximumBytes)
28
+ return null;
29
+ const buffer = Buffer.alloc(openedStats.size);
30
+ let offset = 0;
31
+ while (offset < buffer.length) {
32
+ const { bytesRead } = await handle.read(buffer, offset, buffer.length - offset, offset);
33
+ if (bytesRead === 0)
34
+ break;
35
+ offset += bytesRead;
36
+ }
37
+ const finalStats = await handle.stat();
38
+ return finalStats.size === openedStats.size && offset === openedStats.size
39
+ ? buffer.toString("utf8", 0, offset)
40
+ : null;
41
+ }
42
+ finally {
43
+ await handle.close();
44
+ }
45
+ }
46
+ //# sourceMappingURL=receipt-file-boundary.js.map
47
+ //# debugId=6ece0e15-aff5-5e2d-b18a-16ffaa0fe2ea
@@ -19,6 +19,8 @@ export interface MergeDecisionReceipt {
19
19
  fields_from_theirs: string[];
20
20
  /** Collections combined from both branches. */
21
21
  union_fields: string[];
22
+ /** SHA-256 hashes of the merged values for every field represented by this receipt. Absent on legacy receipts, which cannot prove drift attribution. */
23
+ merged_field_hashes?: Record<string, string>;
22
24
  /** Full recoverable scalar decisions, retained only in the clone. */
23
25
  decisions: ItemMergeConflictDecision[];
24
26
  /** Whether a merge reconciliation history event consumed this receipt. */
@@ -29,6 +31,8 @@ export interface MergeDecisionReceipt {
29
31
  reconciled_at?: string;
30
32
  /** Whether decision values are recoverable locally or represented by hashes only. */
31
33
  value_availability?: "clone_local" | "hash_only";
34
+ /** Runtime-only provenance assigned by the reader; serialized values are ignored. */
35
+ evidence_source?: "clone_local" | "durable";
32
36
  }
33
37
  /** Privacy-safe receipt summary suitable for committed history context. */
34
38
  export interface MergeDecisionReceiptSummary {
@@ -66,6 +70,24 @@ export interface MergeReceiptReport {
66
70
  /** ISO timestamp for the report. */
67
71
  generated_at: string;
68
72
  }
73
+ /** Internal integrity scan result used by health without exposing malformed receipt contents. */
74
+ export interface MergeReceiptEvidenceScan {
75
+ /** Valid receipts that passed bounded-file, schema, and identity validation. */
76
+ receipts: MergeDecisionReceipt[];
77
+ /** Number of JSON receipt candidates that could not be validated safely. */
78
+ invalid_evidence_count: number;
79
+ }
80
+ declare function prepareReceiptSettlement(params: {
81
+ receiptPath: string;
82
+ receiptId: string;
83
+ evidenceSource: "clone_local" | "durable";
84
+ reconciledAt: string;
85
+ readReceipt?: (receiptPath: string) => Promise<string | null>;
86
+ }): Promise<{
87
+ path: string;
88
+ content: string;
89
+ fingerprint: string;
90
+ } | null>;
69
91
  /** Convert a raw receipt into privacy-safe history context. */
70
92
  export declare function summarizeMergeReceipt(receipt: MergeDecisionReceipt): MergeDecisionReceiptSummary;
71
93
  /** Persist one item-driver outcome in the clone-local Git directory. */
@@ -76,8 +98,18 @@ export declare function writeMergeReceipt(params: {
76
98
  conflictResolution?: "preferred_side" | "stable_value_order";
77
99
  fieldsFromTheirs: string[];
78
100
  unionFields: string[];
101
+ mergedFieldHashes?: Record<string, string>;
79
102
  decisions: ItemMergeConflictDecision[];
80
103
  }): Promise<MergeDecisionReceipt | null>;
104
+ /** Inspect valid receipts and count invalid evidence without returning untrusted file contents. */
105
+ export declare function inspectMergeReceiptEvidence(cwd: string, options?: {
106
+ /** Include receipts whose reconciliation state is already settled. */
107
+ includeReconciled?: boolean;
108
+ /** Include receipts that contain no discarded scalar values. */
109
+ includeLossless?: boolean;
110
+ /** Explicit tracker root used to locate durable receipt evidence. */
111
+ pmRoot?: string;
112
+ }): Promise<MergeReceiptEvidenceScan>;
81
113
  /** Read clone-local receipts with explicit reconciled/lossless classification controls. */
82
114
  export declare function listMergeReceipts(cwd: string, options?: {
83
115
  includeReconciled?: boolean;
@@ -92,7 +124,9 @@ export declare function partitionMergeReceipts(receipts: MergeDecisionReceipt[])
92
124
  lossless: MergeDecisionReceipt[];
93
125
  };
94
126
  /** Mark a receipt as represented by a committed merge history event. */
95
- export declare function markMergeReceiptReconciled(cwd: string, receipt: MergeDecisionReceipt): Promise<void>;
127
+ export declare function markMergeReceiptReconciled(cwd: string, receipt: MergeDecisionReceipt, options?: {
128
+ requireExisting?: boolean;
129
+ }): Promise<void>;
96
130
  /** Report pending or historical clone-local merge decisions. */
97
131
  export declare function runMergeReceiptReport(options: {
98
132
  /** Include receipts already represented by merge history events. */
@@ -100,3 +134,8 @@ export declare function runMergeReceiptReport(options: {
100
134
  /** Repository directory to inspect; defaults to the process working directory. */
101
135
  cwd?: string;
102
136
  }): Promise<MergeReceiptReport>;
137
+ /** Test-only seams for deterministic receipt-boundary fault coverage. */
138
+ export declare const _testOnlyMergeReceipts: {
139
+ prepareReceiptSettlement: typeof prepareReceiptSettlement;
140
+ };
141
+ export {};