@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
@@ -1,6 +1,19 @@
1
1
  import type { PmReadOutputCursorEnvelope, PmReadOutputSurface } from "../read-output-contracts.js";
2
- /** Fingerprint the complete canonical row snapshot so changed evidence fails closed. */
3
- export declare function readOutputCollectionFingerprint(path: string, value: unknown[] | Record<string, unknown>): string;
2
+ /** Stable snapshot policy for a read surface whose probes refresh observation metadata. */
3
+ export interface PmReadOutputContinuationFingerprintPolicy {
4
+ /** Version included in fingerprints so policy changes invalidate older cursors. */
5
+ version: 3;
6
+ /** Declared continuation row paths governed by this policy. */
7
+ paths: readonly string[];
8
+ /** Exact direct detail fields excluded for each named dynamic row. */
9
+ ignored_detail_field_names_by_row: Readonly<Record<string, readonly string[]>>;
10
+ /** Promise that verdicts, stable configuration, and nonvolatile evidence remain bound. */
11
+ guarantee: "nonvolatile_snapshot_and_stable_configuration";
12
+ }
13
+ /** Command-specific exceptions to complete-row continuation fingerprinting. */
14
+ export declare const PM_READ_OUTPUT_CONTINUATION_FINGERPRINT_POLICIES: Readonly<Partial<Record<PmReadOutputSurface, PmReadOutputContinuationFingerprintPolicy>>>;
15
+ /** Fingerprint the policy-normalized row snapshot so stable evidence changes fail closed. */
16
+ export declare function readOutputCollectionFingerprint(path: string, value: unknown[] | Record<string, unknown>, command?: PmReadOutputSurface): string;
4
17
  /** Encode one stable declared-row continuation for CLI, SDK, and MCP callers. */
5
18
  export declare function encodeReadOutputContinuationCursor(cursor: Omit<PmReadOutputCursorEnvelope, "version">): string;
6
19
  /** Decode and validate one universal declared-row continuation cursor. */
@@ -6,7 +6,7 @@
6
6
  * resolution and budget policy.
7
7
  */
8
8
 
9
- !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]="1c73c31e-6246-5730-8c8a-d3ba7c9ec112")}catch(e){}}();
9
+ !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]="9e63b4b0-09a5-50bf-9f56-5f239c75ffe5")}catch(e){}}();
10
10
  import { createHash } from "node:crypto";
11
11
  import { EXIT_CODE } from "../../core/shared/constants.js";
12
12
  import { PmCliError } from "../../core/shared/errors.js";
@@ -14,13 +14,93 @@ import { stableStringify } from "../../core/shared/serialization.js";
14
14
  import { resolveReadOutputSurface } from "../read-output-contracts.js";
15
15
  import { readOutputContinuationRowCollections, sliceReadOutputRowCollection, } from "../read-output-rows.js";
16
16
  const MAX_READ_OUTPUT_CURSOR_LENGTH = 4096;
17
+ const HEALTH_TELEMETRY_VOLATILE_DETAIL_FIELDS = Object.freeze([
18
+ "queue_draining",
19
+ "queue_entries",
20
+ "queue_exists",
21
+ "queue_high_retry_entries",
22
+ "queue_invalid_rows",
23
+ "queue_max_attempts",
24
+ "queue_rows_total",
25
+ "queue_size_bytes",
26
+ "pending_otel_spans",
27
+ "last_attempted_flush_at",
28
+ "last_failed_flush_at",
29
+ "last_otel_attempt_at",
30
+ "last_otel_failure_at",
31
+ "last_otel_success_at",
32
+ "last_successful_flush_at",
33
+ ]);
34
+ /** Command-specific exceptions to complete-row continuation fingerprinting. */
35
+ export const PM_READ_OUTPUT_CONTINUATION_FINGERPRINT_POLICIES = Object.freeze({
36
+ health: Object.freeze({
37
+ version: 3,
38
+ paths: Object.freeze(["checks"]),
39
+ ignored_detail_field_names_by_row: Object.freeze({
40
+ telemetry: HEALTH_TELEMETRY_VOLATILE_DETAIL_FIELDS,
41
+ }),
42
+ guarantee: "nonvolatile_snapshot_and_stable_configuration",
43
+ }),
44
+ });
17
45
  function isRecord(value) {
18
46
  return value !== null && typeof value === "object" && !Array.isArray(value);
19
47
  }
20
- /** Fingerprint the complete canonical row snapshot so changed evidence fails closed. */
21
- export function readOutputCollectionFingerprint(path, value) {
48
+ function normalizeReadOutputFingerprintValue(value) {
49
+ if (Array.isArray(value)) {
50
+ return value.map(normalizeReadOutputFingerprintValue);
51
+ }
52
+ if (!isRecord(value))
53
+ return value;
54
+ return Object.fromEntries(Object.entries(value).map(([key, entry]) => [
55
+ key,
56
+ normalizeReadOutputFingerprintValue(entry),
57
+ ]));
58
+ }
59
+ function normalizeReadOutputFingerprintRow(value, policy) {
60
+ if (!isRecord(value))
61
+ return normalizeReadOutputFingerprintValue(value);
62
+ const rowName = typeof value.name === "string" ? value.name : undefined;
63
+ const ignoredDetailFields = rowName !== undefined &&
64
+ Object.hasOwn(policy.ignored_detail_field_names_by_row, rowName)
65
+ ? policy.ignored_detail_field_names_by_row[rowName]
66
+ : undefined;
67
+ return Object.fromEntries(Object.entries(value)
68
+ .map(([key, entry]) => {
69
+ if (key !== "details" || !ignoredDetailFields || !isRecord(entry)) {
70
+ return [key, normalizeReadOutputFingerprintValue(entry)];
71
+ }
72
+ return [
73
+ key,
74
+ Object.fromEntries(Object.entries(entry)
75
+ .filter(([detailKey]) => !ignoredDetailFields.includes(detailKey))
76
+ .map(([detailKey, detailValue]) => [
77
+ detailKey,
78
+ normalizeReadOutputFingerprintValue(detailValue),
79
+ ])),
80
+ ];
81
+ }));
82
+ }
83
+ function normalizeReadOutputFingerprintSnapshot(value, policy) {
84
+ return Array.isArray(value)
85
+ ? value.map((row) => normalizeReadOutputFingerprintRow(row, policy))
86
+ : normalizeReadOutputFingerprintRow(value, policy);
87
+ }
88
+ /** Fingerprint the policy-normalized row snapshot so stable evidence changes fail closed. */
89
+ export function readOutputCollectionFingerprint(path, value, command) {
90
+ const policy = command
91
+ ? PM_READ_OUTPUT_CONTINUATION_FINGERPRINT_POLICIES[command]
92
+ : undefined;
93
+ const policyApplies = policy?.paths.includes(path) === true;
22
94
  return createHash("sha256")
23
- .update(stableStringify({ path, value }))
95
+ .update(stableStringify({
96
+ path,
97
+ value: policyApplies
98
+ ? normalizeReadOutputFingerprintSnapshot(value, policy)
99
+ : value,
100
+ ...(policyApplies
101
+ ? { fingerprint_policy_version: policy.version }
102
+ : {}),
103
+ }))
24
104
  .digest("base64url")
25
105
  .slice(0, 16);
26
106
  }
@@ -105,11 +185,11 @@ export function applyReadOutputContinuation(result, command, cursor) {
105
185
  if (!collection ||
106
186
  totalRows !== cursor.total_rows ||
107
187
  cursor.offset > totalRows ||
108
- readOutputCollectionFingerprint(cursor.path, collection.value) !==
188
+ readOutputCollectionFingerprint(cursor.path, collection.value, command) !==
109
189
  cursor.fingerprint) {
110
190
  throw new PmCliError("The read-output continuation no longer matches the declared row collection; restart the bounded read.", EXIT_CODE.USAGE, { code: "read_output_cursor_stale" });
111
191
  }
112
192
  return sliceReadOutputRowCollection(result, cursor.path, cursor.offset);
113
193
  }
114
194
  //# sourceMappingURL=continuation.js.map
115
- //# debugId=1c73c31e-6246-5730-8c8a-d3ba7c9ec112
195
+ //# debugId=9e63b4b0-09a5-50bf-9f56-5f239c75ffe5
@@ -1,6 +1,7 @@
1
1
  export { resolveReadOutputRecoveryBudget } from "./read-output-budget.js";
2
2
  export type { PmReadOutputRecoveryBudget, PmReadOutputRecoveryBudgetInput, } from "./read-output-budget.js";
3
- export { decodeReadOutputContinuationCursor, encodeReadOutputContinuationCursor, } from "./read-output/continuation.js";
3
+ export { decodeReadOutputContinuationCursor, encodeReadOutputContinuationCursor, PM_READ_OUTPUT_CONTINUATION_FINGERPRINT_POLICIES, readOutputCollectionFingerprint, } from "./read-output/continuation.js";
4
+ export type { PmReadOutputContinuationFingerprintPolicy } from "./read-output/continuation.js";
4
5
  import { type PmReadOutputSessionReceipt, type PmReadOutputSessionState } from "./read-output-session.js";
5
6
  /** Stable output dimensions shared by every read surface. */
6
7
  export declare const PM_READ_OUTPUT_DIMENSIONS: readonly ["include", "amount", "cost", "encoding"];
@@ -61,6 +62,8 @@ export interface PmReadOutputSurfaceContract {
61
62
  precedence: readonly ["canonical", "legacy", "intent", "default"];
62
63
  /** Stable policy used before budget compaction selects retained rows. */
63
64
  budget_retention_policy: "ordered_prefix" | "verdict_priority";
65
+ /** Exact command-local whole-result modes accepted by `outputInclude`. */
66
+ projection_modes?: readonly string[];
64
67
  }
65
68
  /** Source that selected an effective output dimension. */
66
69
  export type PmReadOutputDimensionSource = "canonical" | "legacy" | "intent" | "default";
@@ -5,7 +5,7 @@
5
5
  * surface without coupling package authors to command-specific option names.
6
6
  */
7
7
 
8
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="76c9c2d0-951d-579e-916e-e8d54d4a8be9")}catch(e){}}();
8
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="d084da05-c967-58f1-afe4-069f09838e6f")}catch(e){}}();
9
9
  import { EXIT_CODE } from "../core/shared/constants.js";
10
10
  import { PmCliError } from "../core/shared/errors.js";
11
11
  import { compactReadOutputToBudget, estimateReadOutputTokens, resolveReadOutputRecoveryBudget, updateReadOutputReceiptEstimate, } from "./read-output-budget.js";
@@ -13,7 +13,7 @@ export { resolveReadOutputRecoveryBudget } from "./read-output-budget.js";
13
13
  import { resolvePmCommandOutputBudget } from "./cli-contracts/agent-output-contracts.js";
14
14
  import { boundReadOutputRows, countReadOutputRows, mapReadOutputRows, readOutputBudgetCollections, readOutputContinuationRowCollections, readOutputRowPaths, } from "./read-output-rows.js";
15
15
  import { applyReadOutputContinuation, decodeReadOutputContinuationCursor, encodeReadOutputContinuationCursor, prioritizeAssuranceAssertions, readOutputCollectionFingerprint, } from "./read-output/continuation.js";
16
- export { decodeReadOutputContinuationCursor, encodeReadOutputContinuationCursor, } from "./read-output/continuation.js";
16
+ export { decodeReadOutputContinuationCursor, encodeReadOutputContinuationCursor, PM_READ_OUTPUT_CONTINUATION_FINGERPRINT_POLICIES, readOutputCollectionFingerprint, } from "./read-output/continuation.js";
17
17
  import { applyReadOutputSessionReferences, attachReadOutputSessionReceipt, parseReadOutputSession, readOutputSessionRemainingTokens, } from "./read-output-session.js";
18
18
  import { decodeQueryCursorEnvelope, encodeQueryCursor } from "./pagination.js";
19
19
  /** Stable output dimensions shared by every read surface. */
@@ -205,6 +205,9 @@ function buildSurfaceContract(command) {
205
205
  dimensions,
206
206
  precedence: READ_OUTPUT_PRECEDENCE,
207
207
  budget_retention_policy: command === "assurance" ? "verdict_priority" : "ordered_prefix",
208
+ projection_modes: Object.freeze([
209
+ ...readOutputIncludeModeOptions(command).keys(),
210
+ ]),
208
211
  });
209
212
  }
210
213
  /** Universal output contract for every built-in read surface. */
@@ -1092,7 +1095,7 @@ function rebaseBudgetCompactedCursor(projected, originalItemCount, cursorSource,
1092
1095
  }
1093
1096
  return true;
1094
1097
  }
1095
- function captureReadOutputContinuationState(projected, cursor, options) {
1098
+ function captureReadOutputContinuationState(projected, command, cursor, options) {
1096
1099
  const collectionsBeforeBudget = new Map(readOutputContinuationRowCollections(projected).map((collection) => {
1097
1100
  const rows = Array.isArray(collection.value)
1098
1101
  ? collection.value.length
@@ -1106,7 +1109,7 @@ function captureReadOutputContinuationState(projected, cursor, options) {
1106
1109
  baseOffset: continued ? cursor.offset : 0,
1107
1110
  fingerprint: continued
1108
1111
  ? cursor.fingerprint
1109
- : readOutputCollectionFingerprint(collection.path, collection.value),
1112
+ : readOutputCollectionFingerprint(collection.path, collection.value, command),
1110
1113
  },
1111
1114
  ];
1112
1115
  }));
@@ -1154,7 +1157,7 @@ export function applyReadOutputDimensions(command, options, result) {
1154
1157
  const continuationReadyResult = attachValidateDiagnosticRowContract(resolved.command, result);
1155
1158
  const bindingBudget = resolveBindingReadOutputBudget(resolved, session);
1156
1159
  let projected = projectReadOutputRows(continuationReadyResult, resolved, session, cursor);
1157
- const continuationState = captureReadOutputContinuationState(projected, cursor, options);
1160
+ const continuationState = captureReadOutputContinuationState(projected, resolved.command, cursor, options);
1158
1161
  const receipt = {
1159
1162
  contract_version: 1,
1160
1163
  command: resolved.command,
@@ -1207,4 +1210,4 @@ export function resolveReadOutputEncoding(command, options) {
1207
1210
  : undefined;
1208
1211
  }
1209
1212
  //# sourceMappingURL=read-output-contracts.js.map
1210
- //# debugId=76c9c2d0-951d-579e-916e-e8d54d4a8be9
1213
+ //# debugId=d084da05-c967-58f1-afe4-069f09838e6f
@@ -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]="be6f7869-f197-5d38-b8d9-faf8f390cd56")}catch(e){}}();
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=be6f7869-f197-5d38-b8d9-faf8f390cd56
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]="7659721c-21f5-55da-8cce-31b290de5bfb")}catch(e){}}();
7
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="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=7659721c-21f5-55da-8cce-31b290de5bfb
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 `clientInfo.provenance` map during initialize.
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 every mutation. A
86
- missing MCP signal is recorded as explicit `null`; it is never confused with a
87
- legacy entry that predates the dimension.
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 captures `clientInfo.name` and `clientInfo.version` during initialize and
160
- scopes all later tool calls to that client signal. Optional host-provided
161
- `model`, `session`, `provenance`, and `episode` fields are supported, but
162
- version is not
163
- misclassified as a model or session. Provenance keys must use the bounded
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. The MCP initialize handshake supplies the
77
- client name/version to pm's bounded identity detector, so normal agent
78
- mutations do not need a hard-wired `PM_AUTHOR`.
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 initialize, 31 tools present, full workflow (init → create → claim → update → link files/docs/tests → get → context → search → validate → health), and session-start hook.
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 `2025-06-18`.
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
@@ -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, and MCP custom-field diagnostics are tracked by [pm-lw6acw](../.agents/pm/issues/pm-lw6acw.toon), [pm-6z0wzf](../.agents/pm/issues/pm-6z0wzf.toon), and [pm-yfdav2](../.agents/pm/issues/pm-yfdav2.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).
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.
@@ -0,0 +1,138 @@
1
+ # MCP 2026-07-28 Protocol and Compatibility Decision
2
+
3
+ Tracker references: [pm-sqvshj](../.agents/pm/decisions/pm-sqvshj.toon),
4
+ [pm-vae5ec](../.agents/pm/features/pm-vae5ec.toon), and
5
+ [pm-55yf1t](../.agents/pm/tasks/pm-55yf1t.toon). MRTR, durable tasks,
6
+ and the cache/schema surface are tracked by
7
+ [pm-rz9gep](../.agents/pm/features/pm-rz9gep.toon),
8
+ [pm-rzs24j](../.agents/pm/features/pm-rzs24j.toon), and
9
+ [pm-hv1x1x](../.agents/pm/features/pm-hv1x1x.toon). Subscriptions,
10
+ Streamable HTTP, remote authorization, and the deprecation ratchet are tracked
11
+ by [pm-v7e337](../.agents/pm/features/pm-v7e337.toon),
12
+ [pm-3zh9s4](../.agents/pm/features/pm-3zh9s4.toon), and
13
+ [pm-vzcisw](../.agents/pm/chores/pm-vzcisw.toon).
14
+
15
+ Status: accepted. MCP `2026-07-28` is pm's canonical protocol revision.
16
+
17
+ ## Decision
18
+
19
+ The public pm SDK owns protocol versions, request metadata validation,
20
+ capability checks, discovery, result envelopes, error codes, HTTP header
21
+ parity, subscriptions, authorization boundaries, trace isolation, and legacy
22
+ result interpretation. `pm-mcp` is a JSON-RPC/stdio adapter over those
23
+ contracts; `pm-mcp-http` binds the same dispatcher to sessionless Streamable
24
+ HTTP POST.
25
+
26
+ Modern requests are stateless. Every request carries:
27
+
28
+ - `io.modelcontextprotocol/protocolVersion` = `2026-07-28`;
29
+ - `io.modelcontextprotocol/clientCapabilities` as an object, including `{}`;
30
+ - optional `io.modelcontextprotocol/clientInfo` with `name` and `version`.
31
+
32
+ Every modern result carries an explicit `resultType` (`complete`,
33
+ `input_required`, or `task`) and `io.modelcontextprotocol/serverInfo` in
34
+ result `_meta`. The mandatory
35
+ `server/discover` method returns the supported modern revisions, deterministic
36
+ capabilities, public cache policy, server identity, and bounded instructions.
37
+ No modern request reads identity, capabilities, or version from a previous
38
+ request.
39
+
40
+ ## Legacy boundary
41
+
42
+ The sole supported legacy revision is `2025-06-18`, accepted through the
43
+ existing stdio adapter. Unversioned requests stay on this legacy path because
44
+ they cannot claim the current revision; `initialize` enriches their client
45
+ identity but is not required for compatibility with older pm hosts. The
46
+ adapter has no session id, does not affect modern requests, and is excluded
47
+ from `server/discover`'s `supportedVersions` because it cannot be selected
48
+ through modern per-request metadata.
49
+
50
+ The adapter is scheduled for removal only after published-client telemetry and
51
+ release probes show no required legacy consumers for two consecutive release
52
+ windows. Removal is a reviewed compatibility change, never a history rewrite.
53
+
54
+ ## Transport behavior
55
+
56
+ - Stdio modern clients call `server/discover` with current request metadata,
57
+ then send the same version and capability keys on every request.
58
+ - Stdio legacy clients retain their existing response shapes and may use
59
+ `initialize` with `2025-06-18` to supply client identity.
60
+ - Streamable HTTP requires `MCP-Protocol-Version` and `Mcp-Method` on every
61
+ request. `Mcp-Name` is required only for `prompts/get`, `resources/read`, and
62
+ `tools/call`; other methods omit it. Schema-declared `x-mcp-header` values are
63
+ encoded, decoded, and compared with tool arguments before dispatch.
64
+ Header/version mismatch uses code `-32020` and HTTP 400.
65
+ - `subscriptions/listen` is a request-scoped stdio or SSE stream. The first
66
+ message acknowledges the supported filter; later messages carry its request
67
+ id as `io.modelcontextprotocol/subscriptionId`. Disconnect deletes the
68
+ subscription and a caller retries lost work with a new request id.
69
+ - The remote adapter defaults to loopback, enforces exact browser origins,
70
+ bounds bodies, maps parse/invalid requests to HTTP 400, and can require an
71
+ issuer-, audience-, and scope-bound bearer token.
72
+ - Unsupported modern versions return `-32022` plus the exact supported modern
73
+ list. Missing required capabilities return `-32021` with a structured
74
+ capability map. Malformed metadata uses JSON-RPC Invalid Params `-32602`.
75
+
76
+ ## Removed, migrated, and deprecated behavior
77
+
78
+ | Prior behavior | Disposition |
79
+ | ----------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
80
+ | `initialize` and `notifications/initialized` | Legacy stdio adapter only; absent from modern behavior. |
81
+ | `Mcp-Session-Id` and protocol sessions | Removed; explicit application handles are ordinary arguments. |
82
+ | `ping` | Legacy adapter only; modern calls return Method Not Found. |
83
+ | `resources/subscribe` and `resources/unsubscribe` | Replaced by the implemented `subscriptions/listen` filter. |
84
+ | SSE event ids and `Last-Event-ID` replay | Removed; callers retry with a new request id. |
85
+ | Reverse `roots/list`, sampling, and elicitation requests | Migrate to MRTR `input_required` under `pm-rz9gep`. |
86
+ | Core tasks | Migrate to `io.modelcontextprotocol/tasks` under `pm-rzs24j`. |
87
+ | `logging/setLevel` | Removed; optional request-local log metadata is never shared, and remote operations use host OpenTelemetry. |
88
+ | Roots, Sampling, Logging, HTTP+SSE, and non-none `includeContext` | Retain only in bounded legacy compatibility while `pm-vzcisw` drives deprecation removal. |
89
+ | Dynamic Client Registration | Legacy authorization fallback; Client ID Metadata Documents are canonical under `pm-3zh9s4`. |
90
+
91
+ ## Rollout, rollback, and proof
92
+
93
+ Local and hosted gates must prove SDK primitives, direct server calls, real
94
+ stdio, packed artifacts, npx, bunx, and published artifacts agree on discovery
95
+ and the canonical revision. Negative controls cover unsupported versions,
96
+ missing/malformed metadata, missing capabilities, header mismatch, removed
97
+ methods, and omitted modern `resultType`.
98
+
99
+ Rollback preserves the SDK contract and re-enables only the reviewed legacy
100
+ adapter. It must never reintroduce session ids or make modern behavior depend
101
+ on initialization. Release evidence records exact source, package, tag, and
102
+ consumer revisions separately.
103
+
104
+ ## MRTR, tasks, and cache behavior
105
+
106
+ An SDK handler that cannot finish without host input throws
107
+ `PmMcpInputRequiredError`. The modern adapter validates the requested
108
+ `elicitation/create`, `roots/list`, or `sampling/createMessage` capability and
109
+ returns `resultType: "input_required"`. Continuation state can be bounded,
110
+ HMAC-sealed, expiry-bound, method-bound, parameter-bound, principal-bound, and
111
+ protected from in-process replay with the public interaction helpers. Retry
112
+ payloads arrive as request-local `inputResponses`; they never depend on a
113
+ protocol session.
114
+
115
+ Clients negotiate `io.modelcontextprotocol/tasks` in request capabilities.
116
+ Eligible long-running `tools/call` operations can then return a durable task
117
+ handle. `tasks/get`, `tasks/update`, and `tasks/cancel` are principal-scoped,
118
+ persist records atomically under the ignored tracker runtime area, enforce
119
+ immutable terminal states, expire abandoned work deterministically, and turn
120
+ a disappeared worker into an actionable terminal failure. Task state remains
121
+ retrieved through the task methods. Change subscriptions do not become
122
+ task-progress channels, and pm's current handlers do not emit request progress
123
+ or deprecated log-message notifications.
124
+
125
+ Modern tool, resource, resource-template, and prompt list/read results carry
126
+ explicit `ttlMs` and `cacheScope`. Tool schemas are validated as bounded JSON
127
+ Schema 2020-12 documents before advertisement. Tool and resource data stay
128
+ private; public metadata lists may be cached for their advertised lifetime.
129
+
130
+ ## Public SDK
131
+
132
+ Use `PM_MCP_PROTOCOL_VERSION`, `resolveMcpRequestContext()`,
133
+ `buildMcpDiscoverResult()`, `buildMcpCompleteResult()`,
134
+ `PmMcpSubscriptionRegistry`, `buildMcpHttpRequestHeaders()`,
135
+ `validateMcpHttpRequestHeaders()`, `buildMcpProtectedResourceMetadata()`, and
136
+ the issuer/trace authorization helpers from `@unbrained/pm-cli/sdk`. See
137
+ [MCP interaction and task SDK](SDK_MCP_INTERACTIONS.md) and
138
+ [remote transport, authorization, and migration](MCP_REMOTE_TRANSPORT_SECURITY.md).