@unbrained/pm-cli 2026.8.25 → 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.
- package/.claude-plugin/marketplace.json +2 -2
- package/CHANGELOG.md +33 -0
- package/dist/cli-bundle/bundle-manifest.json +133 -133
- package/dist/cli-bundle/chunks/chunk-E2GCFJSU.js +35 -0
- package/dist/cli-bundle/chunks/{chunk-QKGMHGEI.js → chunk-ES25LX3D.js} +51 -51
- package/dist/cli-bundle/chunks/{chunk-TPQIBSL2.js → chunk-FRDWWB6R.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-65MHLHAA.js → chunk-ICQ3RVIY.js} +2 -2
- package/dist/cli-bundle/chunks/chunk-IV64RJVE.js +13 -0
- package/dist/cli-bundle/chunks/{chunk-QLUORNIB.js → chunk-KBFP3E4E.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-T2ENPRXF.js → chunk-MVYLQ67M.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-5I5RWIJC.js → chunk-S4U76VZF.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-OWHNAR2B.js → chunk-UKBCRPA2.js} +2 -2
- package/dist/cli-bundle/chunks/chunk-WRHJ3MB6.js +8 -0
- package/dist/cli-bundle/chunks/{chunk-244MI4GS.js → chunk-ZNRLJ54C.js} +60 -60
- package/dist/cli-bundle/chunks/{register-list-query-XVN2ZLI7.js → register-list-query-EMCPMICY.js} +2 -2
- package/dist/cli-bundle/chunks/{register-mutation-QCKAEGIJ.js → register-mutation-OJ67ABCB.js} +2 -2
- package/dist/cli-bundle/chunks/{register-operations-SDEAXE7E.js → register-operations-H2GLP7LT.js} +2 -2
- package/dist/cli-bundle/chunks/{register-setup-LXVBRCJ3.js → register-setup-GLZAHLVI.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-NOOZGIXP.js → chunk-4K2II4TV.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-IBZZZGK3.js → chunk-4XNH2HM7.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-VXWATRFL.js → chunk-57XY346D.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-GQR3WH3F.js → chunk-66VGB23P.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-42S3GGZ7.js → chunk-72T6JGAE.js} +45 -45
- package/dist/cli-bundle/focused-chunks/{chunk-RAFKLNZX.js → chunk-FXDLT6FL.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-H5JZEIQV.js → chunk-IBHXMFE7.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-N7W67YIG.js → chunk-LV5N3LK5.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-2UIWOP3O.js → chunk-MMXUPDDJ.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-ONYQCALA.js → chunk-P2E6LDAE.js} +6 -6
- package/dist/cli-bundle/focused-chunks/{chunk-XUQPEKRN.js → chunk-TMJDFHVD.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-OVJL6NZE.js → chunk-UYBA57GY.js} +4 -4
- package/dist/cli-bundle/main.js +2 -2
- package/dist/cli-bundle/sdk-authoring.js +1 -1
- package/dist/cli-bundle/sdk-contracts.js +1 -1
- package/dist/cli-bundle/sdk-core.js +31 -31
- package/dist/cli-bundle/sdk-governance.js +1 -1
- package/dist/cli-bundle/sdk-graph.js +1 -1
- package/dist/cli-bundle/sdk-merge.js +18 -18
- package/dist/cli-bundle/sdk-query.js +1 -1
- package/dist/cli-bundle/sdk-runtime.js +1 -1
- package/dist/cli-bundle/sdk-testing.js +1 -1
- package/dist/cli-bundle/sdk.js +7 -6
- package/dist/core/extensions/manifest-schema.d.ts +20 -0
- package/dist/core/extensions/manifest-schema.js +28 -10
- package/dist/mcp/http-server.d.ts +60 -0
- package/dist/mcp/http-server.js +451 -0
- package/dist/mcp/legacy-adapter.d.ts +50 -0
- package/dist/mcp/legacy-adapter.js +64 -0
- package/dist/mcp/server.d.ts +42 -9
- package/dist/mcp/server.js +457 -58
- package/dist/sdk/agent/refusal-closure-census.d.ts +6 -2
- package/dist/sdk/agent/refusal-closure-census.js +16 -8
- package/dist/sdk/compose.d.ts +4 -1
- package/dist/sdk/compose.js +23 -36
- package/dist/sdk/extension/author-manifest.d.ts +22 -0
- package/dist/sdk/extension/author-manifest.js +93 -0
- package/dist/sdk/extension.js +6 -3
- package/dist/sdk/generated/generated-error-code-catalog-part-1.js +20 -5
- package/dist/sdk/generated/generated-error-code-catalog-part-2.js +6 -3
- package/dist/sdk/governance/health.js +7 -2
- package/dist/sdk/index.d.ts +6 -0
- package/dist/sdk/index.js +8 -2
- package/dist/sdk/mcp/authorization.d.ts +134 -0
- package/dist/sdk/mcp/authorization.js +405 -0
- package/dist/sdk/mcp/interactions.d.ts +118 -0
- package/dist/sdk/mcp/interactions.js +337 -0
- package/dist/sdk/mcp/protocol.d.ts +142 -0
- package/dist/sdk/mcp/protocol.js +174 -0
- package/dist/sdk/mcp/subscriptions.d.ts +65 -0
- package/dist/sdk/mcp/subscriptions.js +212 -0
- package/dist/sdk/mcp/tasks.d.ts +107 -0
- package/dist/sdk/mcp/tasks.js +431 -0
- package/dist/sdk/mcp/transport.d.ts +30 -0
- package/dist/sdk/mcp/transport.js +261 -0
- package/dist/sdk/merge/receipts.d.ts +16 -0
- package/dist/sdk/merge/receipts.js +9 -8
- package/dist/sdk/runtime-primitives.d.ts +1 -1
- package/dist/sdk/runtime-primitives.js +3 -3
- package/dist/sdk/test/execution.d.ts +6 -0
- package/dist/sdk/test/execution.js +32 -3
- package/docs/AGENT_PROVENANCE_ADR.md +6 -4
- package/docs/AGENT_RUNTIME_PRIMITIVES.md +6 -5
- package/docs/CLAUDE_CODE_PLUGIN.md +12 -5
- package/docs/DIAGNOSTIC_OUTPUT_CONTRACTS.md +8 -0
- package/docs/EXTENSIONS.md +3 -4
- package/docs/MCP_2026_07_28.md +138 -0
- package/docs/MCP_2026_07_28_CONFORMANCE.md +30 -0
- package/docs/MCP_REMOTE_TRANSPORT_SECURITY.md +180 -0
- package/docs/README.md +4 -0
- package/docs/RELEASING.md +3 -2
- package/docs/SDK.md +22 -1
- package/docs/SDK_AGENT_SESSION_CONTEXT.md +18 -13
- package/docs/SDK_MCP_INTERACTIONS.md +227 -0
- package/docs/TESTING.md +4 -0
- package/docs/generated/REFUSAL_CLOSURE_CENSUS.md +9 -8
- package/marketplace.json +2 -2
- package/package.json +7 -5
- package/packages/pm-beads/package.json +1 -1
- package/packages/pm-calendar/package.json +1 -1
- package/packages/pm-command-kit/package.json +1 -1
- package/packages/pm-digital-twin/package.json +1 -1
- package/packages/pm-governance-audit/package.json +1 -1
- package/packages/pm-guide-shell/package.json +1 -1
- package/packages/pm-kanban/package.json +1 -1
- package/packages/pm-lifecycle-hooks/package.json +1 -1
- package/packages/pm-linked-test-adapters/package.json +1 -1
- package/packages/pm-search-advanced/package.json +1 -1
- package/packages/pm-templates/package.json +1 -1
- package/packages/pm-todos/package.json +1 -1
- package/packages/pm-vcs/package.json +1 -1
- package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
- package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
- package/scripts/finalize-build.mjs +1 -0
- package/sdk/public-surface.json +628 -2
- package/dist/cli-bundle/chunks/chunk-2F3LUFMW.js +0 -8
- package/dist/cli-bundle/chunks/chunk-6C7GIMIL.js +0 -13
- package/dist/cli-bundle/chunks/chunk-YQMYF3YD.js +0 -35
|
@@ -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
|
|
@@ -77,6 +77,17 @@ export interface MergeReceiptEvidenceScan {
|
|
|
77
77
|
/** Number of JSON receipt candidates that could not be validated safely. */
|
|
78
78
|
invalid_evidence_count: number;
|
|
79
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>;
|
|
80
91
|
/** Convert a raw receipt into privacy-safe history context. */
|
|
81
92
|
export declare function summarizeMergeReceipt(receipt: MergeDecisionReceipt): MergeDecisionReceiptSummary;
|
|
82
93
|
/** Persist one item-driver outcome in the clone-local Git directory. */
|
|
@@ -123,3 +134,8 @@ export declare function runMergeReceiptReport(options: {
|
|
|
123
134
|
/** Repository directory to inspect; defaults to the process working directory. */
|
|
124
135
|
cwd?: string;
|
|
125
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 {};
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* value for the coordinator that performed the merge.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
!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]="
|
|
10
|
+
!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]="145a6d31-5e14-5b0b-95fb-6f837d11e2f4")}catch(e){}}();
|
|
11
11
|
import { execFile } from "node:child_process";
|
|
12
12
|
import { randomUUID } from "node:crypto";
|
|
13
13
|
import { readdir } from "node:fs/promises";
|
|
@@ -222,7 +222,7 @@ async function readBoundedRegularReceiptFile(receiptPath) {
|
|
|
222
222
|
async function prepareReceiptSettlement(params) {
|
|
223
223
|
let raw;
|
|
224
224
|
try {
|
|
225
|
-
raw = await readBoundedRegularReceiptFile(params.receiptPath);
|
|
225
|
+
raw = await (params.readReceipt ?? readBoundedRegularReceiptFile)(params.receiptPath);
|
|
226
226
|
}
|
|
227
227
|
catch (error) {
|
|
228
228
|
if (isFileAbsentError(error))
|
|
@@ -467,6 +467,10 @@ export async function markMergeReceiptReconciled(cwd, receipt, options = {}) {
|
|
|
467
467
|
}
|
|
468
468
|
return;
|
|
469
469
|
}
|
|
470
|
+
const receiptEvidenceSource = receipt.evidence_source ?? "clone_local";
|
|
471
|
+
if (!isMergeDecisionReceipt(receipt, receiptEvidenceSource)) {
|
|
472
|
+
throw new Error("Merge receipt trusted settlement input failed schema validation.");
|
|
473
|
+
}
|
|
470
474
|
const reconciledAt = nowIso();
|
|
471
475
|
const localPath = path.join(directory, receiptFileName(receipt.id));
|
|
472
476
|
const trackerRoot = await resolveTrackerRootFromItemPath(cwd, receipt.item_path);
|
|
@@ -498,11 +502,6 @@ export async function markMergeReceiptReconciled(cwd, receipt, options = {}) {
|
|
|
498
502
|
}
|
|
499
503
|
return;
|
|
500
504
|
}
|
|
501
|
-
const receiptEvidenceSource = receipt.evidence_source ?? "clone_local";
|
|
502
|
-
const trustedReceiptId = receipt.id;
|
|
503
|
-
if (!isMergeDecisionReceipt(receipt, receiptEvidenceSource)) {
|
|
504
|
-
throw new Error(`Receipt ${trustedReceiptId} trusted settlement input failed schema validation.`);
|
|
505
|
-
}
|
|
506
505
|
const expectedFingerprint = receiptProvenanceFingerprint(receipt);
|
|
507
506
|
if (new Set([expectedFingerprint, ...writes.map((write) => write.fingerprint)])
|
|
508
507
|
.size !== 1) {
|
|
@@ -525,5 +524,7 @@ export async function runMergeReceiptReport(options) {
|
|
|
525
524
|
generated_at: nowIso(),
|
|
526
525
|
};
|
|
527
526
|
}
|
|
527
|
+
/** Test-only seams for deterministic receipt-boundary fault coverage. */
|
|
528
|
+
export const _testOnlyMergeReceipts = { prepareReceiptSettlement };
|
|
528
529
|
//# sourceMappingURL=receipts.js.map
|
|
529
|
-
//# debugId=
|
|
530
|
+
//# debugId=145a6d31-5e14-5b0b-95fb-6f837d11e2f4
|
|
@@ -13,7 +13,7 @@ export { type ActiveExtensionHookContext, type ExtensionCommandRegistry, type Ex
|
|
|
13
13
|
export { applyRegisteredItemFieldDefaultsAndValidation, collectRegisteredItemFieldNames, parseRegisteredItemFieldAssignments, } from "../core/extensions/item-fields.js";
|
|
14
14
|
export { resolveExtensionRoots } from "../core/extensions/loader.js";
|
|
15
15
|
export { resolveRegisteredSearchProvider, resolveRegisteredVectorStoreAdapter, } from "../core/extensions/runtime-registrations.js";
|
|
16
|
-
export { pathExists, readFileIfExists, removeFileIfExists, writeFileAtomic, } from "../core/fs/fs-utils.js";
|
|
16
|
+
export { isFileMissingError, pathExists, readFileIfExists, removeFileIfExists, writeFileAtomic, } from "../core/fs/fs-utils.js";
|
|
17
17
|
export { normalizeBulkIdsValue, parseBulkIdsText, resolveCliBulkIdsInput, type BulkIdsFileReader, type BulkIdsInputReaders, type BulkIdsStdinReader, type BulkIdsValue, } from "../core/io/bulk-ids-input.js";
|
|
18
18
|
export { type HistoryDiffValueEntry, computeHistoryDiff, patchPathToChangedField, } from "../core/history/history-diff.js";
|
|
19
19
|
export { enforceHistoryStreamPolicyForItem, enforceHistoryStreamPolicyForItems, } from "../core/history/history-stream-policy.js";
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* prefer the typed operations exported by the main SDK barrel.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
!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]="
|
|
10
|
+
!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]="4be36c51-a9fc-507d-8f13-d764b5668ab9")}catch(e){}}();
|
|
11
11
|
export { createCheckpointId, loadMutationCheckpoint, restoreCheckpointItems, writeMutationCheckpoint, } from "../core/checkpoint/mutation-checkpoint.js";
|
|
12
12
|
export { flattenFlagListValue, resolveFlagValueKind, } from "../core/extensions/flag-value-types.js";
|
|
13
13
|
export { createUnknownSubcommandError, } from "./agent/subcommand-recovery.js";
|
|
@@ -15,7 +15,7 @@ export { activateExtensions, clearActiveExtensionHooks, consumeAfterCommandAffec
|
|
|
15
15
|
export { applyRegisteredItemFieldDefaultsAndValidation, collectRegisteredItemFieldNames, parseRegisteredItemFieldAssignments, } from "../core/extensions/item-fields.js";
|
|
16
16
|
export { resolveExtensionRoots } from "../core/extensions/loader.js";
|
|
17
17
|
export { resolveRegisteredSearchProvider, resolveRegisteredVectorStoreAdapter, } from "../core/extensions/runtime-registrations.js";
|
|
18
|
-
export { pathExists, readFileIfExists, removeFileIfExists, writeFileAtomic, } from "../core/fs/fs-utils.js";
|
|
18
|
+
export { isFileMissingError, pathExists, readFileIfExists, removeFileIfExists, writeFileAtomic, } from "../core/fs/fs-utils.js";
|
|
19
19
|
export { normalizeBulkIdsValue, parseBulkIdsText, resolveCliBulkIdsInput, } from "../core/io/bulk-ids-input.js";
|
|
20
20
|
export { computeHistoryDiff, patchPathToChangedField, } from "../core/history/history-diff.js";
|
|
21
21
|
export { enforceHistoryStreamPolicyForItem, enforceHistoryStreamPolicyForItems, } from "../core/history/history-stream-policy.js";
|
|
@@ -82,4 +82,4 @@ export { maybeRunFirstUseTelemetryPrompt } from "../core/telemetry/consent.js";
|
|
|
82
82
|
export { deriveTelemetryCommandResolution, } from "../core/telemetry/observability.js";
|
|
83
83
|
export { emitTelemetryErrorEvent, finishTelemetryCommand, startTelemetryCommand, } from "../core/telemetry/runtime.js";
|
|
84
84
|
//# sourceMappingURL=runtime-primitives.js.map
|
|
85
|
-
//# debugId=
|
|
85
|
+
//# debugId=4be36c51-a9fc-507d-8f13-d764b5668ab9
|
|
@@ -220,6 +220,11 @@ declare function copyIntoSandboxIfPresent(sourcePath: string, targetPath: string
|
|
|
220
220
|
}) => Promise<unknown>): Promise<void>;
|
|
221
221
|
declare function seedLinkedTestSandbox(sandboxPmPath: string, sandboxGlobalPath: string, sourceRoots: LinkedTestSandboxSourceRoots): Promise<void>;
|
|
222
222
|
declare function seedLinkedTestTrackerData(sourceRoot: string, sandboxRoot: string): Promise<void>;
|
|
223
|
+
/**
|
|
224
|
+
* Create an audited settings snapshot only for source trackers that predate
|
|
225
|
+
* workspace history, while preserving any existing source history verbatim.
|
|
226
|
+
*/
|
|
227
|
+
declare function seedMissingLinkedTestSettingsHistory(sourceRoot: string, sandboxRoot: string): Promise<void>;
|
|
223
228
|
declare function countLinkedTestItemFiles(pmRoot: string): Promise<number>;
|
|
224
229
|
/** Implements resolve linked test failure exit code for the public runtime surface of this module. */
|
|
225
230
|
export declare function resolveLinkedTestFailureExitCode(execution: Pick<LinkedTestExecutionResult, "exitCode" | "timedOut" | "maxBufferExceeded">): number;
|
|
@@ -321,6 +326,7 @@ export declare const _testOnlyTestCommand: {
|
|
|
321
326
|
resolveTestRunOptions: typeof resolveTestRunOptions;
|
|
322
327
|
resolveTrackedRunId: typeof resolveTrackedRunId;
|
|
323
328
|
runLinkedTestCommand: typeof runLinkedTestCommand;
|
|
329
|
+
seedMissingLinkedTestSettingsHistory: typeof seedMissingLinkedTestSettingsHistory;
|
|
324
330
|
seedLinkedTestSandbox: typeof seedLinkedTestSandbox;
|
|
325
331
|
seedLinkedTestTrackerData: typeof seedLinkedTestTrackerData;
|
|
326
332
|
segmentInvokesRecursiveTestAll: typeof segmentInvokesRecursiveTestAll;
|
|
@@ -4,14 +4,15 @@
|
|
|
4
4
|
* Implements the pm test command surface and its agent-facing runtime behavior.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
7
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="99ce92bb-ed45-5327-b148-b5bb26a0bf05")}catch(e){}}();
|
|
8
8
|
import { assertInitializedTracker } from "../environment/tracker-preflight.js";
|
|
9
9
|
import { spawn } from "node:child_process";
|
|
10
10
|
import { cp, mkdir, mkdtemp, open, readdir, rm, symlink, } from "node:fs/promises";
|
|
11
11
|
import { tmpdir } from "node:os";
|
|
12
12
|
import path from "node:path";
|
|
13
13
|
import { getActiveExtensionRegistrations } from "../../core/extensions/index.js";
|
|
14
|
-
import { isFileMissingError, pathExists } from "../../core/fs/fs-utils.js";
|
|
14
|
+
import { isFileMissingError, pathExists, readFileIfExists, } from "../../core/fs/fs-utils.js";
|
|
15
|
+
import { getWorkspaceHistoryPath, writeWorkspaceJsonWithHistory, } from "../../core/history/workspace-history.js";
|
|
15
16
|
import { resolveItemTypeRegistry } from "../../core/item/type-registry.js";
|
|
16
17
|
import { createStdinTokenResolver, parseCsvKv, parseOptionalNumber, } from "../../core/item/parse.js";
|
|
17
18
|
import { EXIT_CODE } from "../../core/shared/constants.js";
|
|
@@ -1043,6 +1044,31 @@ async function seedLinkedTestTrackerData(sourceRoot, sandboxRoot) {
|
|
|
1043
1044
|
}
|
|
1044
1045
|
}
|
|
1045
1046
|
}
|
|
1047
|
+
/**
|
|
1048
|
+
* Create an audited settings snapshot only for source trackers that predate
|
|
1049
|
+
* workspace history, while preserving any existing source history verbatim.
|
|
1050
|
+
*/
|
|
1051
|
+
async function seedMissingLinkedTestSettingsHistory(sourceRoot, sandboxRoot) {
|
|
1052
|
+
const sourceSettingsRaw = await readFileIfExists(getSettingsPath(sourceRoot));
|
|
1053
|
+
if (sourceSettingsRaw === null ||
|
|
1054
|
+
(await pathExists(getWorkspaceHistoryPath(sourceRoot)))) {
|
|
1055
|
+
return;
|
|
1056
|
+
}
|
|
1057
|
+
const sandboxSettings = await readSettings(sandboxRoot);
|
|
1058
|
+
await rm(getWorkspaceHistoryPath(sandboxRoot), { force: true });
|
|
1059
|
+
await rm(getSettingsPath(sandboxRoot), { force: true });
|
|
1060
|
+
await writeWorkspaceJsonWithHistory({
|
|
1061
|
+
pmRoot: sandboxRoot,
|
|
1062
|
+
filePath: getSettingsPath(sandboxRoot),
|
|
1063
|
+
raw: sourceSettingsRaw,
|
|
1064
|
+
op: "settings:write",
|
|
1065
|
+
author: resolveAuthor(undefined, sandboxSettings.author_default),
|
|
1066
|
+
lockTtlSeconds: sandboxSettings.locks.ttl_seconds,
|
|
1067
|
+
lockWaitMs: sandboxSettings.locks.wait_ms,
|
|
1068
|
+
recordCreation: true,
|
|
1069
|
+
message: "Seed linked-test tracker settings history",
|
|
1070
|
+
});
|
|
1071
|
+
}
|
|
1046
1072
|
async function countLinkedTestItemFiles(pmRoot) {
|
|
1047
1073
|
if (!(await pathExists(pmRoot))) {
|
|
1048
1074
|
return 0;
|
|
@@ -1339,6 +1365,8 @@ async function seedLinkedTestSandboxesFromSource(layout, sourceRoots, includeTra
|
|
|
1339
1365
|
await seedLinkedTestSandbox(layout.trackerProjectPmPath, layout.trackerGlobalPmPath, sourceRoots);
|
|
1340
1366
|
await seedLinkedTestTrackerData(sourceRoots.projectPmRoot, layout.trackerProjectPmPath);
|
|
1341
1367
|
await seedLinkedTestTrackerData(sourceRoots.globalPmRoot, layout.trackerGlobalPmPath);
|
|
1368
|
+
await seedMissingLinkedTestSettingsHistory(sourceRoots.projectPmRoot, layout.trackerProjectPmPath);
|
|
1369
|
+
await seedMissingLinkedTestSettingsHistory(sourceRoots.globalPmRoot, layout.trackerGlobalPmPath);
|
|
1342
1370
|
}
|
|
1343
1371
|
function linkedTestsRequireTrackerData(tests, runLevelPmContextMode, options) {
|
|
1344
1372
|
return tests.some((linkedTest) => {
|
|
@@ -2124,6 +2152,7 @@ export const _testOnlyTestCommand = {
|
|
|
2124
2152
|
resolveTestRunOptions,
|
|
2125
2153
|
resolveTrackedRunId,
|
|
2126
2154
|
runLinkedTestCommand,
|
|
2155
|
+
seedMissingLinkedTestSettingsHistory,
|
|
2127
2156
|
seedLinkedTestSandbox,
|
|
2128
2157
|
seedLinkedTestTrackerData,
|
|
2129
2158
|
segmentInvokesRecursiveTestAll,
|
|
@@ -2131,4 +2160,4 @@ export const _testOnlyTestCommand = {
|
|
|
2131
2160
|
summarizeRunResultStatuses,
|
|
2132
2161
|
};
|
|
2133
2162
|
//# sourceMappingURL=execution.js.map
|
|
2134
|
-
//# debugId=
|
|
2163
|
+
//# debugId=99ce92bb-ed45-5327-b148-b5bb26a0bf05
|
|
@@ -80,11 +80,13 @@ All new history fields are optional. Readers must accept:
|
|
|
80
80
|
- explicit `model: null`, `effort: null`, or `role: null` observations meaning
|
|
81
81
|
the dimension was declared but unavailable for a detected harness.
|
|
82
82
|
|
|
83
|
-
MCP clients may supply a bounded
|
|
83
|
+
Modern MCP clients may supply a bounded
|
|
84
|
+
`io.modelcontextprotocol/clientInfo.provenance` map in each request's `_meta`.
|
|
84
85
|
The server retains only valid dimension names and trimmed values, then resolves
|
|
85
|
-
that map inside the invocation-scoped identity context for
|
|
86
|
-
|
|
87
|
-
|
|
86
|
+
that map inside the invocation-scoped identity context for that request. The
|
|
87
|
+
bounded `2025-06-18` adapter accepts the same map during legacy `initialize`.
|
|
88
|
+
A missing MCP signal is recorded as explicit `null`; it is never confused with
|
|
89
|
+
a legacy entry that predates the dimension.
|
|
88
90
|
|
|
89
91
|
Unknown provenance dimensions are preserved as data and do not change author
|
|
90
92
|
resolution. Removing a dimension from a descriptor never rewrites existing
|
|
@@ -156,11 +156,12 @@ author literals at read time; every row reports `harness_source` and
|
|
|
156
156
|
`vocabulary_version`, and unresolved literals remain visible in the bounded
|
|
157
157
|
summary. No history entry is rewritten.
|
|
158
158
|
|
|
159
|
-
MCP
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
159
|
+
MCP 2026-07-28 reads `io.modelcontextprotocol/clientInfo` independently from
|
|
160
|
+
every request's `_meta` and scopes only that request to the client signal. The
|
|
161
|
+
bounded legacy adapter still reads `clientInfo` during `2025-06-18`
|
|
162
|
+
`initialize`. Optional host-provided `model`, `session`, `provenance`, and
|
|
163
|
+
`episode` fields are supported, but version is not misclassified as a model or
|
|
164
|
+
session. Provenance keys must use the bounded
|
|
164
165
|
lowercase dimension vocabulary; blank, malformed, and oversized data is
|
|
165
166
|
discarded before mutation context is created. See
|
|
166
167
|
[Agent Provenance ADR Amendment](AGENT_PROVENANCE_ADR.md) for precedence,
|
|
@@ -73,9 +73,10 @@ Add to the project's `.mcp.json`:
|
|
|
73
73
|
```
|
|
74
74
|
|
|
75
75
|
The repo root `.mcp.json` uses this approach and activates automatically when
|
|
76
|
-
Claude Code opens this repository.
|
|
77
|
-
|
|
78
|
-
|
|
76
|
+
Claude Code opens this repository. Under MCP `2026-07-28`, every request carries
|
|
77
|
+
the protocol version, client capabilities, and client identity in `_meta`, so
|
|
78
|
+
pm's bounded identity detector does not depend on session state and normal
|
|
79
|
+
agent mutations do not need a hard-wired `PM_AUTHOR`.
|
|
79
80
|
|
|
80
81
|
## MCP Server Launcher
|
|
81
82
|
|
|
@@ -115,7 +116,10 @@ node scripts/smoke-claude-plugin.mjs
|
|
|
115
116
|
pnpm smoke:claude-plugin
|
|
116
117
|
```
|
|
117
118
|
|
|
118
|
-
Verifies: plugin file structure, manifest name consistency, MCP
|
|
119
|
+
Verifies: plugin file structure, manifest name consistency, stateless MCP
|
|
120
|
+
discovery for `2026-07-28`, 31 tools present, full workflow (init → create →
|
|
121
|
+
claim → update → link files/docs/tests → get → context → search → validate →
|
|
122
|
+
health), and session-start hook.
|
|
119
123
|
|
|
120
124
|
### MCP server smoke test
|
|
121
125
|
|
|
@@ -182,7 +186,10 @@ After installing the plugin:
|
|
|
182
186
|
|
|
183
187
|
The authoritative plugin version is `plugins/pm-claude/.claude-plugin/plugin.json`; this row stays on the `1.x` major line so it does not drift with each plugin release.
|
|
184
188
|
|
|
185
|
-
The MCP server uses JSON-RPC 2.0 over stdio with protocol version
|
|
189
|
+
The MCP server uses JSON-RPC 2.0 over stdio with canonical protocol version
|
|
190
|
+
`2026-07-28`. A bounded legacy path remains for unversioned older hosts, with
|
|
191
|
+
`2025-06-18` initialize available to enrich client identity; current hosts
|
|
192
|
+
discover the server and send metadata on every request.
|
|
186
193
|
|
|
187
194
|
## Extension Policy Diagnostics
|
|
188
195
|
|
|
@@ -103,6 +103,14 @@ refusal; schema, items, history, settings, and package state must not change.
|
|
|
103
103
|
Ephemeral runtime lock/cache directories are excluded from that semantic
|
|
104
104
|
snapshot.
|
|
105
105
|
|
|
106
|
+
The complete error-code census currently retains executable evidence for 18
|
|
107
|
+
catalog rows across 17 canonical groups. Two of those rows are author-manifest
|
|
108
|
+
schema findings reached through a real `pm health --full --strict-exit --json`
|
|
109
|
+
process: an unknown top-level key and omitted canonical pm version bounds. The
|
|
110
|
+
remaining rows stay explicit `uncovered` obligations in
|
|
111
|
+
[the generated census](generated/REFUSAL_CLOSURE_CENSUS.md); the ratchet does not
|
|
112
|
+
turn partial catalog closure into an approval claim.
|
|
113
|
+
|
|
106
114
|
Run the focused proof with:
|
|
107
115
|
|
|
108
116
|
```bash
|
package/docs/EXTENSIONS.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Packages and Extensions
|
|
2
2
|
|
|
3
|
-
Extension flags declared with `list: true` accumulate repeated long/short alias occurrences and comma-separated values into one array. Dynamic commands preserve flag-like variadic content after `--`, and package handlers can use the public `suppressHostOutput()` protocol when they already emitted streaming, binary, or pre-rendered output. Declarative blueprints are also checked for reserved item-field collisions during SDK lint/preflight and harness activation, so a package cannot pass author-time validation and then fail only when users create or update items. Local archive installation, command ownership,
|
|
3
|
+
Extension flags declared with `list: true` accumulate repeated long/short alias occurrences and comma-separated values into one array. Dynamic commands preserve flag-like variadic content after `--`, and package handlers can use the public `suppressHostOutput()` protocol when they already emitted streaming, binary, or pre-rendered output. Declarative blueprints are also checked for reserved item-field collisions during SDK lint/preflight and harness activation, so a package cannot pass author-time validation and then fail only when users create or update items. Local archive installation, command ownership, MCP custom-field diagnostics, and author-manifest schema diagnostics are tracked by [pm-lw6acw](../.agents/pm/issues/pm-lw6acw.toon), [pm-6z0wzf](../.agents/pm/issues/pm-6z0wzf.toon), [pm-yfdav2](../.agents/pm/issues/pm-yfdav2.toon), and [pm-gh1091](../.agents/pm/issues/pm-gh1091.toon). Transactional mutation guards, host-bound command test SDKs, and installed custom-type lifecycle parity are tracked by [pm-hx23u5](../.agents/pm/issues/pm-hx23u5.toon), [pm-wx2lr5](../.agents/pm/issues/pm-wx2lr5.toon), and [pm-scga6k](../.agents/pm/issues/pm-scga6k.toon). Durable migration application, explicit source resolution, and composable preflight ownership are covered in [Extension Lifecycle Contracts](EXTENSION_LIFECYCLE.md).
|
|
4
4
|
|
|
5
5
|
Packages add optional `pm` workflows without changing the core CLI. A package can ship one or more runtime extensions plus metadata such as docs and examples. Prefer the package-first commands in new docs and automation:
|
|
6
6
|
|
|
@@ -191,10 +191,9 @@ useful for agents without inflating context or leaking private item content.
|
|
|
191
191
|
|
|
192
192
|
Runnable manifest examples are the source of truth: [starter extension manifest](examples/starter-extension/manifest.json) and [policy-restricted manifest](examples/policy-restricted-extension/manifest.json).
|
|
193
193
|
|
|
194
|
-
Use [extension-manifest.schema.json](schemas/extension-manifest.schema.json) as the `$schema` value for inline editor validation. The loader ignores `$schema` and tolerates future manifest fields, but the schema documents the fields pm reads.
|
|
195
|
-
|
|
196
|
-
Rules:
|
|
194
|
+
Use [extension-manifest.schema.json](schemas/extension-manifest.schema.json) as the `$schema` value for inline editor validation. The loader ignores `$schema` and tolerates future manifest fields, but the schema documents the fields pm reads. Rules:
|
|
197
195
|
|
|
196
|
+
- `manifest.json` is the runtime declaration; package catalog metadata belongs in `package.json#pm`, and a top-level `compatibility` object is ignored. Use the public `inspectExtensionManifestSchema` / `lintExtensionManifestSchema` helpers for schema-only checks. In author workspaces, `pm health --full`, `pm package doctor`, and `pm extension --doctor` expose the same read-only `author_manifest` findings without activating the extension.
|
|
198
197
|
- `entry` must resolve inside the extension directory.
|
|
199
198
|
- `manifest_version` is an optional integer identifying the manifest schema generation. Runtime contracts currently support manifest versions `1` and `2`, and first-party runnable examples use `2`. First-party packages declare it; the manifest governance test requires it on every first-party package.
|
|
200
199
|
- `pm_min_version` is an inclusive minimum pm CLI version. If the running CLI is older, discovery emits `extension_pm_min_version_unmet:<layer>:<name>:required=<version>:current=<version>` and skips the extension before import.
|