@unbrained/pm-cli 2026.8.10 → 2026.8.11

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 (175) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/CHANGELOG.md +102 -80
  3. package/dist/cli/commander-usage.js +79 -7
  4. package/dist/cli/error-guidance.d.ts +10 -0
  5. package/dist/cli/error-guidance.js +76 -25
  6. package/dist/cli/main.js +6 -2
  7. package/dist/cli/register-assurance.d.ts +8 -0
  8. package/dist/cli/register-assurance.js +52 -0
  9. package/dist/cli/register-files-lookup.js +14 -3
  10. package/dist/cli/register-mutation.js +30 -9
  11. package/dist/cli/register-operations.js +16 -11
  12. package/dist/cli-bundle/bundle-manifest.json +159 -159
  13. package/dist/cli-bundle/chunks/{chunk-TRWXF476.js → chunk-2OOU2AFH.js} +2 -2
  14. package/dist/cli-bundle/chunks/chunk-2RA2YTHJ.js +33 -0
  15. package/dist/cli-bundle/chunks/chunk-A2SJGHSI.js +3 -0
  16. package/dist/cli-bundle/chunks/chunk-DSKYD4LG.js +13 -0
  17. package/dist/cli-bundle/chunks/chunk-HC3GM6YY.js +194 -0
  18. package/dist/cli-bundle/chunks/chunk-IUNRVDAI.js +2 -0
  19. package/dist/cli-bundle/chunks/chunk-W7CPZGSB.js +2 -0
  20. package/dist/cli-bundle/chunks/{chunk-EBO7OUUE.js → chunk-WMEZHCFI.js} +59 -53
  21. package/dist/cli-bundle/chunks/{chunk-ETML5C6H.js → chunk-ZU34C3LH.js} +47 -47
  22. package/dist/cli-bundle/chunks/chunk-ZXOC6HG5.js +5 -0
  23. package/dist/cli-bundle/chunks/{register-list-query-OJ6ZXKV4.js → register-list-query-HAUXVUPY.js} +8 -8
  24. package/dist/cli-bundle/chunks/register-mutation-7Y5U7ABT.js +20 -0
  25. package/dist/cli-bundle/chunks/register-operations-EQX3DLAZ.js +2 -0
  26. package/dist/cli-bundle/chunks/{register-setup-S3KT2LTH.js → register-setup-NOCOOWQY.js} +2 -2
  27. package/dist/cli-bundle/focused-chunks/{chunk-34SX654S.js → chunk-23FTGO3W.js} +2 -2
  28. package/dist/cli-bundle/focused-chunks/{chunk-2COZ67XT.js → chunk-2DL3MRTD.js} +2 -2
  29. package/dist/cli-bundle/focused-chunks/chunk-3AYKXMYX.js +2 -0
  30. package/dist/cli-bundle/focused-chunks/chunk-4AHEC74N.js +2 -0
  31. package/dist/cli-bundle/focused-chunks/{chunk-7UN6BHHO.js → chunk-4H5IA6AV.js} +2 -2
  32. package/dist/cli-bundle/focused-chunks/chunk-4SV3ZKG3.js +16 -0
  33. package/dist/cli-bundle/focused-chunks/chunk-55BIB6DV.js +2 -0
  34. package/dist/cli-bundle/focused-chunks/chunk-6JW6Z2P5.js +8 -0
  35. package/dist/cli-bundle/focused-chunks/{chunk-IW43E4XL.js → chunk-BKPIY34S.js} +2 -2
  36. package/dist/cli-bundle/focused-chunks/chunk-D4PIWE6D.js +2 -0
  37. package/dist/cli-bundle/focused-chunks/{chunk-KR5RVXLQ.js → chunk-DPO7TEFX.js} +29 -29
  38. package/dist/cli-bundle/focused-chunks/{chunk-JMVA4HKR.js → chunk-E3KCL7AY.js} +2 -2
  39. package/dist/cli-bundle/focused-chunks/chunk-ERAAFLPS.js +153 -0
  40. package/dist/cli-bundle/focused-chunks/{chunk-RIIN5V4N.js → chunk-F6E4JZKG.js} +2 -2
  41. package/dist/cli-bundle/focused-chunks/chunk-FPSYO5ZG.js +18 -0
  42. package/dist/cli-bundle/focused-chunks/{chunk-M6RPIXKC.js → chunk-J5EKAIVD.js} +2 -2
  43. package/dist/cli-bundle/focused-chunks/{chunk-2EXI2DKR.js → chunk-KDH5OX3D.js} +2 -2
  44. package/dist/cli-bundle/focused-chunks/chunk-OJ3LVVIS.js +2 -0
  45. package/dist/cli-bundle/focused-chunks/{chunk-DZGAHZZV.js → chunk-TL5M2PAY.js} +2 -2
  46. package/dist/cli-bundle/focused-chunks/{chunk-75774JWP.js → chunk-UDAZWT25.js} +2 -2
  47. package/dist/cli-bundle/focused-chunks/chunk-VABB4L5Y.js +17 -0
  48. package/dist/cli-bundle/main.js +13 -13
  49. package/dist/cli-bundle/sdk-authoring.js +1 -1
  50. package/dist/cli-bundle/sdk-contracts.js +1 -1
  51. package/dist/cli-bundle/sdk-core.js +41 -39
  52. package/dist/cli-bundle/sdk-governance.js +1 -1
  53. package/dist/cli-bundle/sdk-graph.js +1 -1
  54. package/dist/cli-bundle/sdk-merge.js +1 -1
  55. package/dist/cli-bundle/sdk-query.js +1 -1
  56. package/dist/cli-bundle/sdk-runtime.js +1 -1
  57. package/dist/cli-bundle/sdk-testing.js +1 -1
  58. package/dist/cli-bundle/sdk.js +2 -2
  59. package/dist/core/session/session-state.d.ts +47 -0
  60. package/dist/core/session/session-state.js +207 -6
  61. package/dist/core/shared/author.d.ts +7 -1
  62. package/dist/core/shared/author.js +66 -46
  63. package/dist/core/shared/errors.d.ts +8 -0
  64. package/dist/core/shared/errors.js +2 -2
  65. package/dist/sdk/agent/refusal-reachability.d.ts +40 -0
  66. package/dist/sdk/agent/refusal-reachability.js +72 -0
  67. package/dist/sdk/agent/subcommand-recovery.d.ts +40 -0
  68. package/dist/sdk/agent/subcommand-recovery.js +84 -0
  69. package/dist/sdk/cli-bootstrap.d.ts +8 -0
  70. package/dist/sdk/cli-bootstrap.js +7 -7
  71. package/dist/sdk/cli-contracts/completeness.js +13 -3
  72. package/dist/sdk/cli-contracts/enum-contracts.d.ts +1 -1
  73. package/dist/sdk/cli-contracts/enum-contracts.js +3 -2
  74. package/dist/sdk/cli-contracts/flag-contracts.d.ts +2 -0
  75. package/dist/sdk/cli-contracts/flag-contracts.js +17 -2
  76. package/dist/sdk/cli-contracts/runtime-contracts.js +4 -3
  77. package/dist/sdk/cli-contracts/schemas/assurance-definition.d.ts +513 -0
  78. package/dist/sdk/cli-contracts/schemas/assurance-definition.js +251 -0
  79. package/dist/sdk/cli-contracts/tool-parameter-tables.js +79 -2
  80. package/dist/sdk/cli-contracts/tool-schema.d.ts +2 -2
  81. package/dist/sdk/cli-contracts/tool-schema.js +34 -4
  82. package/dist/sdk/cli-contracts.d.ts +1 -1
  83. package/dist/sdk/cli-contracts.js +3 -3
  84. package/dist/sdk/completion.js +9 -3
  85. package/dist/sdk/config.js +9 -3
  86. package/dist/sdk/context/semantic-session-attribution.d.ts +32 -0
  87. package/dist/sdk/context/semantic-session-attribution.js +83 -0
  88. package/dist/sdk/core-governance.d.ts +3 -1
  89. package/dist/sdk/core-governance.js +5 -3
  90. package/dist/sdk/core.d.ts +1 -1
  91. package/dist/sdk/core.js +2 -2
  92. package/dist/sdk/error-code-catalog.d.ts +13 -0
  93. package/dist/sdk/error-code-catalog.js +35 -2
  94. package/dist/sdk/extension.js +18 -17
  95. package/dist/sdk/files.d.ts +11 -0
  96. package/dist/sdk/files.js +82 -24
  97. package/dist/sdk/generated-error-code-catalog.js +32 -13
  98. package/dist/sdk/governance/assurance-action-contracts.d.ts +7 -0
  99. package/dist/sdk/governance/assurance-action-contracts.js +5 -0
  100. package/dist/sdk/governance/assurance-action.d.ts +70 -0
  101. package/dist/sdk/governance/assurance-action.js +198 -0
  102. package/dist/sdk/governance/assurance-limits.d.ts +7 -0
  103. package/dist/sdk/governance/assurance-limits.js +11 -0
  104. package/dist/sdk/governance/assurance-runtime.d.ts +20 -0
  105. package/dist/sdk/governance/assurance-runtime.js +131 -0
  106. package/dist/sdk/governance/assurance.d.ts +432 -0
  107. package/dist/sdk/governance/assurance.js +689 -0
  108. package/dist/sdk/graph/run.js +8 -3
  109. package/dist/sdk/index.d.ts +6 -1
  110. package/dist/sdk/index.js +8 -3
  111. package/dist/sdk/lifecycle/claim.js +18 -3
  112. package/dist/sdk/lifecycle/focus.js +13 -3
  113. package/dist/sdk/lifecycle/plan.js +8 -3
  114. package/dist/sdk/provenance.d.ts +38 -0
  115. package/dist/sdk/provenance.js +64 -2
  116. package/dist/sdk/query/context.js +19 -8
  117. package/dist/sdk/runtime-extended-actions.js +22 -5
  118. package/dist/sdk/runtime-primitives.d.ts +2 -0
  119. package/dist/sdk/runtime-primitives.js +4 -2
  120. package/dist/sdk/runtime.d.ts +4 -0
  121. package/dist/sdk/runtime.js +31 -27
  122. package/dist/sdk/telemetry.js +7 -4
  123. package/dist/sdk/traceability/runtime-files-lookup.d.ts +8 -0
  124. package/dist/sdk/traceability/runtime-files-lookup.js +33 -0
  125. package/dist/sdk/traceability/source-traceability.d.ts +102 -0
  126. package/dist/sdk/traceability/source-traceability.js +293 -0
  127. package/dist/types.d.ts +8 -2
  128. package/dist/types.js +2 -2
  129. package/docs/ASSURANCE.md +132 -0
  130. package/docs/README.md +1 -0
  131. package/docs/RELEASING.md +9 -5
  132. package/docs/SDK.md +12 -1
  133. package/docs/SDK_AGENT_SESSION_CONTEXT.md +40 -4
  134. package/docs/SDK_EVIDENCE_TRACEABILITY.md +39 -1
  135. package/docs/SDK_RUNTIME_BOUNDARIES.md +36 -1
  136. package/docs/TESTING.md +12 -9
  137. package/docs/generated/AGENT_COMMAND_SURFACE.md +1 -0
  138. package/marketplace.json +2 -2
  139. package/package.json +3 -3
  140. package/packages/pm-beads/package.json +1 -1
  141. package/packages/pm-calendar/package.json +1 -1
  142. package/packages/pm-command-kit/package.json +1 -1
  143. package/packages/pm-digital-twin/package.json +1 -1
  144. package/packages/pm-governance-audit/package.json +1 -1
  145. package/packages/pm-guide-shell/package.json +1 -1
  146. package/packages/pm-kanban/package.json +1 -1
  147. package/packages/pm-lifecycle-hooks/package.json +1 -1
  148. package/packages/pm-linked-test-adapters/package.json +1 -1
  149. package/packages/pm-search-advanced/package.json +1 -1
  150. package/packages/pm-templates/extensions/templates/index.ts +9 -3
  151. package/packages/pm-templates/package.json +1 -1
  152. package/packages/pm-todos/package.json +1 -1
  153. package/packages/pm-vcs/package.json +1 -1
  154. package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
  155. package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
  156. package/sdk/public-surface.json +840 -28
  157. package/dist/cli-bundle/chunks/chunk-2QYH4QMI.js +0 -30
  158. package/dist/cli-bundle/chunks/chunk-JUDU4RO4.js +0 -194
  159. package/dist/cli-bundle/chunks/chunk-JZSNADRR.js +0 -5
  160. package/dist/cli-bundle/chunks/chunk-MENWVGJW.js +0 -2
  161. package/dist/cli-bundle/chunks/chunk-S4EITGLA.js +0 -3
  162. package/dist/cli-bundle/chunks/chunk-TIGKWVLA.js +0 -13
  163. package/dist/cli-bundle/chunks/chunk-TSLDTJLQ.js +0 -2
  164. package/dist/cli-bundle/chunks/register-mutation-DKI7QC7O.js +0 -20
  165. package/dist/cli-bundle/chunks/register-operations-P5OGMT6I.js +0 -2
  166. package/dist/cli-bundle/focused-chunks/chunk-36FTZ2NT.js +0 -2
  167. package/dist/cli-bundle/focused-chunks/chunk-3CQECMKR.js +0 -16
  168. package/dist/cli-bundle/focused-chunks/chunk-CHIBMEZR.js +0 -8
  169. package/dist/cli-bundle/focused-chunks/chunk-FF4MNAYB.js +0 -153
  170. package/dist/cli-bundle/focused-chunks/chunk-GHTN2ILR.js +0 -2
  171. package/dist/cli-bundle/focused-chunks/chunk-I4YM2I7S.js +0 -2
  172. package/dist/cli-bundle/focused-chunks/chunk-IMDTEN7K.js +0 -2
  173. package/dist/cli-bundle/focused-chunks/chunk-KSNJG4KI.js +0 -16
  174. package/dist/cli-bundle/focused-chunks/chunk-MXYSIQ56.js +0 -18
  175. package/dist/cli-bundle/focused-chunks/chunk-RKWSCYYN.js +0 -2
@@ -4,12 +4,57 @@
4
4
  * Maintains lightweight agent session state for contextual workflows.
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]="611f01b6-0c72-5ea2-a44f-78877c69706c")}catch(e){}}();
8
- import { promises as fs } from "node:fs";
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]="424b9eb3-6b34-5ab0-a3f5-db61fbca12cf")}catch(e){}}();
8
+ import { createHash } from "node:crypto";
9
+ import { existsSync, promises as fs, readFileSync } from "node:fs";
9
10
  import path from "node:path";
10
11
  import { writeFileAtomic } from "../fs/fs-utils.js";
11
12
  import { getRuntimePath } from "../store/paths.js";
12
13
  const SESSION_FILENAME = "session.json";
14
+ function isBoundedNonEmptyString(value, maximum) {
15
+ return (typeof value === "string" && value.length > 0 && value.length <= maximum);
16
+ }
17
+ function parseSemanticAttribution(value) {
18
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
19
+ return undefined;
20
+ }
21
+ const record = value;
22
+ const validRole = ["implementer", "planner", "release-operator"].includes(String(record.role));
23
+ const validConfidence = ["high", "medium"].includes(String(record.confidence));
24
+ const validTopic = isBoundedNonEmptyString(record.topic, 256);
25
+ const validEvidence = Array.isArray(record.evidence) &&
26
+ record.evidence.length <= 32 &&
27
+ record.evidence.every((entry) => typeof entry === "string" && entry.length <= 128);
28
+ const validActiveItems = Array.isArray(record.active_item_ids) &&
29
+ record.active_item_ids.length <= 64 &&
30
+ record.active_item_ids.every((entry) => isBoundedNonEmptyString(entry, 128));
31
+ const validFocus = record.focused_item_id === undefined ||
32
+ isBoundedNonEmptyString(record.focused_item_id, 128);
33
+ if (![
34
+ validRole,
35
+ validConfidence,
36
+ validTopic,
37
+ validEvidence,
38
+ validActiveItems,
39
+ validFocus,
40
+ record.rule_version === "v2",
41
+ ].every(Boolean)) {
42
+ return undefined;
43
+ }
44
+ return record;
45
+ }
46
+ function parseSemanticAttributionMap(value) {
47
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
48
+ return undefined;
49
+ }
50
+ const entries = Object.entries(value)
51
+ .slice(0, 128)
52
+ .flatMap(([key, attribution]) => {
53
+ const parsed = parseSemanticAttribution(attribution);
54
+ return parsed && key.length <= 128 ? [[key, parsed]] : [];
55
+ });
56
+ return entries.length > 0 ? Object.fromEntries(entries) : undefined;
57
+ }
13
58
  /** Implements get session state path for the public runtime surface of this module. */
14
59
  export function getSessionStatePath(pmRoot) {
15
60
  return path.join(getRuntimePath(pmRoot), SESSION_FILENAME);
@@ -24,11 +69,20 @@ export async function readSessionState(pmRoot) {
24
69
  Array.isArray(parsed)) {
25
70
  return {};
26
71
  }
27
- const focused = parsed.focused_item;
72
+ const record = parsed;
73
+ const focused = record.focused_item;
74
+ const semanticAttribution = parseSemanticAttributionMap(record.semantic_attribution);
28
75
  if (typeof focused === "string" && focused.trim().length > 0) {
29
- return { focused_item: focused };
76
+ return {
77
+ focused_item: focused,
78
+ ...(semanticAttribution
79
+ ? { semantic_attribution: semanticAttribution }
80
+ : {}),
81
+ };
30
82
  }
31
- return {};
83
+ return semanticAttribution
84
+ ? { semantic_attribution: semanticAttribution }
85
+ : {};
32
86
  }
33
87
  catch {
34
88
  return {};
@@ -39,6 +93,153 @@ async function writeSessionState(pmRoot, state) {
39
93
  await fs.mkdir(path.dirname(target), { recursive: true });
40
94
  await writeFileAtomic(target, JSON.stringify(state));
41
95
  }
96
+ /** Derive a non-secret state key from a claim principal. */
97
+ export function semanticAttributionKey(principal) {
98
+ const instance = /#([a-f0-9]{24})$/u.exec(principal.trim())?.[1];
99
+ return (instance ??
100
+ `author-${createHash("sha256").update(principal).digest("hex").slice(0, 24)}`);
101
+ }
102
+ function normalizedEvidence(values) {
103
+ return [...new Set(values.map((value) => value.trim()).filter(Boolean))]
104
+ .slice(0, 32)
105
+ .map((value) => value.slice(0, 128));
106
+ }
107
+ function worksetTopic(activeItemIds) {
108
+ const ids = [...new Set(activeItemIds)].sort((left, right) => left.localeCompare(right));
109
+ if (ids.length === 1)
110
+ return ids[0];
111
+ const complete = `workset:${ids.join("+")}`;
112
+ if (complete.length <= 256)
113
+ return complete;
114
+ const visible = ids.join("+").slice(0, 230);
115
+ return `workset:${visible}+${createHash("sha256").update(ids.join("\0")).digest("hex").slice(0, 12)}`;
116
+ }
117
+ async function updateSemanticAttribution(pmRoot, key, mutate) {
118
+ const state = await readSessionState(pmRoot);
119
+ const attributions = { ...state.semantic_attribution };
120
+ const next = mutate(attributions[key]);
121
+ if (next === undefined)
122
+ delete attributions[key];
123
+ else
124
+ attributions[key] = next;
125
+ const nextState = { ...state };
126
+ if (Object.keys(attributions).length === 0) {
127
+ delete nextState.semantic_attribution;
128
+ }
129
+ else {
130
+ nextState.semantic_attribution = attributions;
131
+ }
132
+ await writeSessionState(pmRoot, nextState);
133
+ }
134
+ /** Record a successful claim as incremental semantic session evidence. */
135
+ export async function recordClaimedWorkAttribution(params) {
136
+ const key = semanticAttributionKey(params.principal);
137
+ await updateSemanticAttribution(params.pmRoot, key, (current) => {
138
+ const activeItemIds = [
139
+ ...new Set([...(current?.active_item_ids ?? []), params.itemId]),
140
+ ]
141
+ .slice(-64)
142
+ .sort((left, right) => left.localeCompare(right));
143
+ const focusedItemId = current?.focused_item_id;
144
+ return {
145
+ role: "implementer",
146
+ topic: focusedItemId ?? worksetTopic(activeItemIds),
147
+ confidence: focusedItemId ? "high" : "medium",
148
+ rule_version: "v2",
149
+ evidence: normalizedEvidence([
150
+ ...activeItemIds.map((id) => `claim:${id}`),
151
+ ...(focusedItemId ? [`focus:${focusedItemId}`] : []),
152
+ ...(params.lineageIds ?? []).map((id) => `lineage:${id}`),
153
+ ]),
154
+ active_item_ids: activeItemIds,
155
+ ...(focusedItemId ? { focused_item_id: focusedItemId } : {}),
156
+ };
157
+ });
158
+ }
159
+ /** Remove a released item from incremental semantic session evidence. */
160
+ export async function releaseClaimedWorkAttribution(params) {
161
+ const key = semanticAttributionKey(params.principal);
162
+ await updateSemanticAttribution(params.pmRoot, key, (current) => {
163
+ if (!current)
164
+ return undefined;
165
+ const activeItemIds = current.active_item_ids.filter((id) => id !== params.itemId);
166
+ if (activeItemIds.length === 0 && !current.focused_item_id)
167
+ return undefined;
168
+ const focusedItemId = current.focused_item_id;
169
+ return {
170
+ ...current,
171
+ role: "release-operator",
172
+ topic: focusedItemId ?? worksetTopic(activeItemIds),
173
+ confidence: focusedItemId ? "high" : "medium",
174
+ evidence: normalizedEvidence([
175
+ ...activeItemIds.map((id) => `claim:${id}`),
176
+ ...(focusedItemId ? [`focus:${focusedItemId}`] : []),
177
+ `release:${params.itemId}`,
178
+ ]),
179
+ active_item_ids: activeItemIds,
180
+ };
181
+ });
182
+ }
183
+ /** Set or clear focus as the highest-confidence semantic topic. */
184
+ export async function recordFocusedWorkAttribution(params) {
185
+ const key = semanticAttributionKey(params.principal);
186
+ await updateSemanticAttribution(params.pmRoot, key, (current) => {
187
+ const activeItemIds = current?.active_item_ids ?? [];
188
+ if (!params.itemId && activeItemIds.length === 0)
189
+ return undefined;
190
+ const focusedItemId = params.itemId;
191
+ return {
192
+ role: focusedItemId ? "planner" : current.role,
193
+ topic: focusedItemId ?? worksetTopic(activeItemIds),
194
+ confidence: focusedItemId ? "high" : "medium",
195
+ rule_version: "v2",
196
+ evidence: normalizedEvidence([
197
+ ...activeItemIds.map((id) => `claim:${id}`),
198
+ ...(focusedItemId ? [`focus:${focusedItemId}`] : []),
199
+ ...(params.lineageIds ?? []).map((id) => `lineage:${id}`),
200
+ ]),
201
+ active_item_ids: activeItemIds,
202
+ ...(focusedItemId ? { focused_item_id: focusedItemId } : {}),
203
+ };
204
+ });
205
+ }
206
+ function resolveAttributionRoot(cwd, env) {
207
+ const configured = env.PM_PATH?.trim();
208
+ if (configured) {
209
+ const resolved = path.resolve(cwd, configured);
210
+ if (existsSync(path.join(resolved, "settings.json")))
211
+ return resolved;
212
+ const nested = path.join(resolved, ".agents", "pm");
213
+ if (existsSync(path.join(nested, "settings.json")))
214
+ return nested;
215
+ }
216
+ let cursor = path.resolve(cwd);
217
+ for (let depth = 0; depth < 32; depth += 1) {
218
+ const candidate = path.join(cursor, ".agents", "pm");
219
+ if (existsSync(path.join(candidate, "settings.json")))
220
+ return candidate;
221
+ const parent = path.dirname(cursor);
222
+ if (parent === cursor)
223
+ break;
224
+ cursor = parent;
225
+ }
226
+ return undefined;
227
+ }
228
+ /** Read one privacy-bounded inferred attribution without making mutations fail. */
229
+ export function readAgentSemanticAttributionSync(params) {
230
+ if (!params.key)
231
+ return undefined;
232
+ const pmRoot = resolveAttributionRoot(params.cwd, params.env);
233
+ if (!pmRoot)
234
+ return undefined;
235
+ try {
236
+ const parsed = JSON.parse(readFileSync(getSessionStatePath(pmRoot), "utf8"));
237
+ return parseSemanticAttribution(parsed.semantic_attribution?.[params.key]);
238
+ }
239
+ catch {
240
+ return undefined;
241
+ }
242
+ }
42
243
  /** Implements get focused item for the public runtime surface of this module. */
43
244
  export async function getFocusedItem(pmRoot) {
44
245
  const state = await readSessionState(pmRoot);
@@ -57,4 +258,4 @@ export async function clearFocusedItem(pmRoot) {
57
258
  await writeSessionState(pmRoot, next);
58
259
  }
59
260
  //# sourceMappingURL=session-state.js.map
60
- //# debugId=611f01b6-0c72-5ea2-a44f-78877c69706c
261
+ //# debugId=424b9eb3-6b34-5ab0-a3f5-db61fbca12cf
@@ -2,7 +2,7 @@ import { type AgentEpisodeIdentity, type AgentSessionContext } from "./agent-ses
2
2
  /** Stable provenance categories recorded beside newly appended history authors. */
3
3
  export type AuthorSource = "asserted" | "configured" | "detected" | "unknown";
4
4
  /** Stable source categories for model observations recorded in mutation history. */
5
- export type AgentModelSource = "override" | "environment" | "mcp_client" | "argv" | "host" | "session" | "probe";
5
+ export type AgentModelSource = "override" | "environment" | "mcp_client" | "argv" | "host" | "session" | "probe" | "inferred";
6
6
  /** Stable built-in provenance dimensions understood by the default runtime. */
7
7
  export declare const AGENT_PROVENANCE_DIMENSIONS: readonly ["model", "effort", "role", "topic", "version"];
8
8
  /** Built-in, bounded local resolver names accepted by harness descriptors. */
@@ -13,6 +13,12 @@ export interface AgentProvenanceObservation {
13
13
  value: string;
14
14
  /** Signal class that supplied the value. */
15
15
  source: AgentModelSource;
16
+ /** Confidence attached to a semantic inference. */
17
+ confidence?: "high" | "medium" | "low";
18
+ /** Versioned inference rule when the value was derived. */
19
+ rule_version?: "v2";
20
+ /** Bounded item and lineage references supporting an inference. */
21
+ evidence?: readonly string[];
16
22
  }
17
23
  /**
18
24
  * Extensible provenance dimensions. A null value explicitly means that the
@@ -4,13 +4,14 @@
4
4
  * Provides shared primitives and utilities for Author.
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]="6c075a14-1a65-59c7-a9c6-af348e30f821")}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]="e0ab2d6f-0925-540d-8d24-a7f0687b4bf0")}catch(e){}}();
8
8
  import { AsyncLocalStorage } from "node:async_hooks";
9
9
  import { createHash } from "node:crypto";
10
10
  import fs from "node:fs";
11
11
  import os from "node:os";
12
12
  import path from "node:path";
13
13
  import { resolveAgentSessionContextFromSignals, } from "./agent-session-context.js";
14
+ import { readAgentSemanticAttributionSync, } from "../session/session-state.js";
14
15
  /** Stable built-in provenance dimensions understood by the default runtime. */
15
16
  export const AGENT_PROVENANCE_DIMENSIONS = [
16
17
  "model",
@@ -191,15 +192,6 @@ function firstEnvironmentValue(env, keys) {
191
192
  return undefined;
192
193
  }
193
194
  function provenanceFromArgv(argv, dimension) {
194
- const flaggedValue = provenanceFlagValue(argv, dimension);
195
- if (flaggedValue !== undefined)
196
- return flaggedValue;
197
- if (dimension === "topic") {
198
- return argv
199
- .slice(0, 16)
200
- .map((token) => token.trim())
201
- .find((token) => /^pm-[a-z0-9][a-z0-9-]{2,63}$/u.test(token));
202
- }
203
195
  if (dimension === "role") {
204
196
  const command = argv
205
197
  .slice(0, 8)
@@ -347,7 +339,31 @@ function descriptorMatchesSignals(descriptor, env, labels, clientName) {
347
339
  const clientMatch = descriptor.client_names.some((name) => clientName === name || literalSignalMatches(clientName, name));
348
340
  return environmentMatch || argvMatch || clientMatch;
349
341
  }
350
- function resolveAgentProvenanceObservation(dimension, signals, env, descriptor, sessionContext) {
342
+ function inferredProvenanceObservation(dimension, semanticAttribution) {
343
+ if (!semanticAttribution)
344
+ return undefined;
345
+ let value;
346
+ if (dimension === "topic")
347
+ value = semanticAttribution.topic;
348
+ if (dimension === "role")
349
+ value = semanticAttribution.role;
350
+ if (!value)
351
+ return undefined;
352
+ return {
353
+ value,
354
+ source: "inferred",
355
+ confidence: semanticAttribution.confidence,
356
+ rule_version: semanticAttribution.rule_version,
357
+ evidence: semanticAttribution.evidence,
358
+ };
359
+ }
360
+ function firstProvenanceObservation(candidates) {
361
+ const observed = candidates.find(([value]) => value !== undefined);
362
+ return observed?.[0]
363
+ ? { value: observed[0], source: observed[1] }
364
+ : undefined;
365
+ }
366
+ function resolveAgentProvenanceObservation(dimension, signals, env, descriptor, sessionContext, semanticAttribution) {
351
367
  const overrideKey = `PM_AGENT_${dimension.toUpperCase().replaceAll("-", "_")}`;
352
368
  const hostResolvedDimensions = new Set([
353
369
  ...AGENT_PROVENANCE_DIMENSIONS,
@@ -355,9 +371,11 @@ function resolveAgentProvenanceObservation(dimension, signals, env, descriptor,
355
371
  ]);
356
372
  let overrideValue;
357
373
  let argvValue;
374
+ let argvFlagValue;
358
375
  if (hostResolvedDimensions.has(dimension)) {
359
376
  overrideValue = normalizeProvenanceValue(dimension, nonBlank(env[overrideKey])?.slice(0, 256));
360
- argvValue = provenanceFromArgv(signals.argv ?? [], dimension);
377
+ argvFlagValue = normalizeProvenanceValue(dimension, provenanceFlagValue(signals.argv ?? [], dimension));
378
+ argvValue = normalizeProvenanceValue(dimension, provenanceFromArgv(signals.argv ?? [], dimension));
361
379
  }
362
380
  const environmentKeysByDimension = {
363
381
  ...descriptor?.provenance_environment_keys,
@@ -367,31 +385,33 @@ function resolveAgentProvenanceObservation(dimension, signals, env, descriptor,
367
385
  ...signals.client_info?.provenance,
368
386
  model: signals.client_info?.model,
369
387
  };
370
- const values = [
371
- overrideValue,
372
- boundedProvenanceValue(sessionContext?.provenance, dimension),
373
- normalizeProvenanceValue(dimension, firstEnvironmentValue(env, environmentKeysByDimension[dimension])),
374
- normalizeProvenanceValue(dimension, boundedProvenanceValue(clientProvenance, dimension)),
375
- normalizeProvenanceValue(dimension, boundedProvenanceValue(signals.provenance, dimension)),
376
- normalizeProvenanceValue(dimension, argvValue),
377
- resolveProvenanceProbe(dimension, signals, env, descriptor),
378
- ];
379
- const sources = [
380
- "override",
381
- "session",
382
- "environment",
383
- "mcp_client",
384
- "host",
385
- "argv",
386
- "probe",
387
- ];
388
- const candidates = sources.map((source, index) => ({
389
- value: values[index],
390
- source,
391
- }));
392
- return candidates.find((candidate) => candidate.value !== undefined);
388
+ const sessionValue = boundedProvenanceValue(sessionContext?.provenance, dimension);
389
+ const environmentValue = normalizeProvenanceValue(dimension, firstEnvironmentValue(env, environmentKeysByDimension[dimension]));
390
+ const clientValue = normalizeProvenanceValue(dimension, boundedProvenanceValue(clientProvenance, dimension));
391
+ const hostValue = normalizeProvenanceValue(dimension, boundedProvenanceValue(signals.provenance, dimension));
392
+ const probeValue = resolveProvenanceProbe(dimension, signals, env, descriptor);
393
+ const observed = firstProvenanceObservation([
394
+ [overrideValue, "override"],
395
+ [sessionValue, "session"],
396
+ [argvFlagValue, "argv"],
397
+ [environmentValue, "environment"],
398
+ [clientValue, "mcp_client"],
399
+ [hostValue, "host"],
400
+ [probeValue, "probe"],
401
+ ]);
402
+ return (observed ??
403
+ inferredProvenanceObservation(dimension, semanticAttribution) ??
404
+ (argvValue ? { value: argvValue, source: "argv" } : undefined));
405
+ }
406
+ function resolveAgentInstance(harness, session) {
407
+ if (!harness || !session)
408
+ return undefined;
409
+ return createHash("sha256")
410
+ .update(`pm-agent-instance:v1\0${harness}\0${session}`)
411
+ .digest("hex")
412
+ .slice(0, 24);
393
413
  }
394
- function resolveAgentProvenance(signals, env, descriptor, sessionContext) {
414
+ function resolveAgentProvenance(signals, env, descriptor, sessionContext, semanticAttribution) {
395
415
  const descriptorDimensions = Object.keys(descriptor?.provenance_environment_keys ?? {});
396
416
  const dimensions = new Set([
397
417
  ...AGENT_PROVENANCE_DIMENSIONS,
@@ -403,7 +423,7 @@ function resolveAgentProvenance(signals, env, descriptor, sessionContext) {
403
423
  ]);
404
424
  const observations = {};
405
425
  for (const dimension of dimensions) {
406
- const observed = resolveAgentProvenanceObservation(dimension, signals, env, descriptor, sessionContext);
426
+ const observed = resolveAgentProvenanceObservation(dimension, signals, env, descriptor, sessionContext, semanticAttribution);
407
427
  if (observed) {
408
428
  observations[dimension] = observed;
409
429
  }
@@ -495,18 +515,18 @@ export function detectAgentIdentity(signals) {
495
515
  const clientName = effectiveSignals.client_info?.name.trim().toLowerCase() ?? "";
496
516
  const descriptor = descriptors.find((candidate) => descriptorMatchesSignals(candidate, env, labels, clientName));
497
517
  const sessionContext = resolveAgentSessionContextFromSignals(effectiveSignals, env);
498
- const provenance = resolveAgentProvenance(effectiveSignals, env, descriptor, sessionContext);
499
- const modelCandidate = provenance.model;
500
518
  const session = [
501
519
  firstEnvironmentValue(env, descriptor?.session_environment_keys),
502
520
  nonBlank(effectiveSignals.client_info?.session)?.slice(0, 256),
503
521
  ].find((candidate) => candidate !== undefined);
504
- const instance = descriptor?.harness && session
505
- ? createHash("sha256")
506
- .update(`pm-agent-instance:v1\0${descriptor.harness}\0${session}`)
507
- .digest("hex")
508
- .slice(0, 24)
509
- : undefined;
522
+ const instance = resolveAgentInstance(descriptor?.harness, session);
523
+ const semanticAttribution = readAgentSemanticAttributionSync({
524
+ cwd: effectiveSignals.cwd ?? process.cwd(),
525
+ env,
526
+ key: instance,
527
+ });
528
+ const provenance = resolveAgentProvenance(effectiveSignals, env, descriptor, sessionContext, semanticAttribution);
529
+ const modelCandidate = provenance.model;
510
530
  const entries = [
511
531
  ["harness", descriptor?.harness],
512
532
  ["model", modelCandidate?.value],
@@ -693,4 +713,4 @@ export function resolveAuthor(candidate, fallback) {
693
713
  return identity.author;
694
714
  }
695
715
  //# sourceMappingURL=author.js.map
696
- //# debugId=6c075a14-1a65-59c7-a9c6-af348e30f821
716
+ //# debugId=e0ab2d6f-0925-540d-8d24-a7f0687b4bf0
@@ -19,6 +19,14 @@ export interface PmCliErrorRecoveryPayload {
19
19
  missing_required_fields?: string[];
20
20
  /** Value that configures or reports suggested flags for this contract. */
21
21
  suggested_flags?: string[];
22
+ /** Complete allowed value set for a refused positional or enum token. */
23
+ allowed_values?: string[];
24
+ /** Ranked command paths that accept an option rejected on the attempted path. */
25
+ candidate_commands?: string[];
26
+ /** Complete number of accepting command paths before the recovery ceiling. */
27
+ candidate_commands_total?: number;
28
+ /** Whether additional accepting command paths were omitted from this payload. */
29
+ candidate_commands_truncated?: boolean;
22
30
  /** Value that configures or reports suggested retry for this contract. */
23
31
  suggested_retry?: string;
24
32
  /** Elapsed time in milliseconds for retry after. */
@@ -1,6 +1,6 @@
1
1
  /** Implements the exported pm cli error runtime abstraction for core/shared/errors.ts. */
2
2
 
3
- !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]="31191ce6-fb17-5b76-aa3d-3c3773cd85e0")}catch(e){}}();
3
+ !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]="df6f6758-f972-5bf4-aaa8-b16dd9a247f3")}catch(e){}}();
4
4
  export class PmCliError extends Error {
5
5
  /** Value that configures or reports exit code for this contract. */
6
6
  exitCode;
@@ -18,4 +18,4 @@ export class PmCliError extends Error {
18
18
  }
19
19
  }
20
20
  //# sourceMappingURL=errors.js.map
21
- //# debugId=31191ce6-fb17-5b76-aa3d-3c3773cd85e0
21
+ //# debugId=df6f6758-f972-5bf4-aaa8-b16dd9a247f3
@@ -0,0 +1,40 @@
1
+ /**
2
+ * @module sdk/agent/refusal-reachability
3
+ *
4
+ * Verifies real-entrypoint observations against refusal states declared by the
5
+ * generated public error-code catalog.
6
+ */
7
+ import type { PmErrorCodeContract, PmErrorCodeClass } from "../error-code-catalog.js";
8
+ /** One recorded result from driving a real public entrypoint into a refusal. */
9
+ export interface PmRefusalProbeObservation {
10
+ /** Stable probe identifier from the owning error-code contract. */
11
+ probe_id: string;
12
+ /** Public command entrypoint exercised by the probe. */
13
+ entrypoint: string;
14
+ /** Machine-readable code observed at the transport boundary. */
15
+ code: string;
16
+ /** Semantic exit class observed at the transport boundary. */
17
+ exit_class: PmErrorCodeClass;
18
+ }
19
+ /** One actionable conformance failure. */
20
+ export interface PmRefusalReachabilityFinding {
21
+ /** Stable finding kind for automation. */
22
+ kind: "duplicate_probe" | "missing_probe" | "wrong_entrypoint" | "wrong_error_code" | "wrong_exit_class" | "undeclared_probe";
23
+ /** Probe whose declaration or observation failed. */
24
+ probe_id: string;
25
+ /** Human-readable mismatch summary. */
26
+ detail: string;
27
+ }
28
+ /** Complete refusal-reachability conformance receipt. */
29
+ export interface PmRefusalReachabilityReport {
30
+ /** Whether every declaration and observation agreed. */
31
+ ok: boolean;
32
+ /** Number of declared probes evaluated. */
33
+ declared_probe_count: number;
34
+ /** Number of real-entrypoint observations supplied. */
35
+ observed_probe_count: number;
36
+ /** Stable, sorted conformance findings. */
37
+ findings: PmRefusalReachabilityFinding[];
38
+ }
39
+ /** Compare declared refusal states with real-entrypoint observations. */
40
+ export declare function verifyPmRefusalReachability(catalog: readonly PmErrorCodeContract[], observations: readonly PmRefusalProbeObservation[]): PmRefusalReachabilityReport;
@@ -0,0 +1,72 @@
1
+ /** Compare declared refusal states with real-entrypoint observations. */
2
+
3
+ !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]="55318c8e-1fbd-5abd-b18f-f0cc1f81dd5c")}catch(e){}}();
4
+ export function verifyPmRefusalReachability(catalog, observations) {
5
+ const declarations = catalog.flatMap((contract) => (contract.owned_states ?? []).map((state) => ({ contract, state })));
6
+ const findings = [];
7
+ const observationsByProbe = new Map();
8
+ for (const observation of observations) {
9
+ if (observationsByProbe.has(observation.probe_id)) {
10
+ findings.push({
11
+ kind: "duplicate_probe",
12
+ probe_id: observation.probe_id,
13
+ detail: `Observation ${observation.probe_id} was supplied more than once.`,
14
+ });
15
+ continue;
16
+ }
17
+ observationsByProbe.set(observation.probe_id, observation);
18
+ }
19
+ const declaredProbeIds = new Set(declarations.map(({ state }) => state.probe_id));
20
+ for (const { contract, state } of declarations) {
21
+ const observation = observationsByProbe.get(state.probe_id);
22
+ if (!observation) {
23
+ findings.push({
24
+ kind: "missing_probe",
25
+ probe_id: state.probe_id,
26
+ detail: `No entrypoint observation was recorded for ${contract.code}.`,
27
+ });
28
+ continue;
29
+ }
30
+ if (observation.code !== contract.code) {
31
+ findings.push({
32
+ kind: "wrong_error_code",
33
+ probe_id: state.probe_id,
34
+ detail: `Expected ${contract.code}; observed ${observation.code}.`,
35
+ });
36
+ }
37
+ if (observation.exit_class !== state.expected_exit_class) {
38
+ findings.push({
39
+ kind: "wrong_exit_class",
40
+ probe_id: state.probe_id,
41
+ detail: `Expected ${state.expected_exit_class}; observed ${observation.exit_class}.`,
42
+ });
43
+ }
44
+ if (!state.entrypoints.includes(observation.entrypoint)) {
45
+ findings.push({
46
+ kind: "wrong_entrypoint",
47
+ probe_id: state.probe_id,
48
+ detail: `Expected one of ${state.entrypoints.join(", ")}; observed ${observation.entrypoint}.`,
49
+ });
50
+ }
51
+ }
52
+ for (const observation of observations) {
53
+ if (!declaredProbeIds.has(observation.probe_id)) {
54
+ findings.push({
55
+ kind: "undeclared_probe",
56
+ probe_id: observation.probe_id,
57
+ detail: `Observation ${observation.probe_id} has no catalog declaration.`,
58
+ });
59
+ }
60
+ }
61
+ findings.sort((left, right) => left.probe_id !== right.probe_id
62
+ ? left.probe_id.localeCompare(right.probe_id)
63
+ : left.kind.localeCompare(right.kind));
64
+ return {
65
+ ok: findings.length === 0,
66
+ declared_probe_count: declarations.length,
67
+ observed_probe_count: observations.length,
68
+ findings,
69
+ };
70
+ }
71
+ //# sourceMappingURL=refusal-reachability.js.map
72
+ //# debugId=55318c8e-1fbd-5abd-b18f-f0cc1f81dd5c
@@ -0,0 +1,40 @@
1
+ import { PmCliError, type PmCliErrorRecoveryPayload } from "../../core/shared/errors.js";
2
+ /** Catalog-only alias retained for consumers of the former package lifecycle code. */
3
+ export declare const UNKNOWN_SUBCOMMAND_COMPATIBILITY_CODES: readonly [{
4
+ readonly code: "unknown_lifecycle_action";
5
+ }];
6
+ /** Inputs for constructing a portable unknown-subcommand refusal. */
7
+ export interface UnknownSubcommandErrorOptions {
8
+ /** Command family path without the rejected token. */
9
+ command_path: string;
10
+ /** Rejected positional token. */
11
+ token: string;
12
+ /** Complete declared subcommand vocabulary for the family. */
13
+ allowed: readonly string[];
14
+ /** Human-readable command family, defaulting to `pm <command_path>`. */
15
+ display_name?: string;
16
+ /** Positional token role used in the compatibility message. */
17
+ token_kind?: "subcommand" | "action" | "path";
18
+ /** Arguments that follow the rejected token and should survive a retry. */
19
+ trailing_args?: readonly string[];
20
+ /** Family-aware retry that takes precedence over edit-distance recovery. */
21
+ retry_command?: string;
22
+ /** Compatibility suffix appended after the allowed-value sentence. */
23
+ message_suffix?: string;
24
+ /** Additional ranked recovery candidates supplied by the command family. */
25
+ fallback_candidates?: PmCliErrorRecoveryPayload["fallback_candidates"];
26
+ /** Transport exit used by non-CLI protocol adapters. */
27
+ exit_code?: number;
28
+ /** Additional family-specific examples. */
29
+ examples?: readonly string[];
30
+ }
31
+ declare function nearestSubcommand(token: string, allowed: readonly string[]): string | undefined;
32
+ declare function renderCommand(tokens: readonly string[]): string;
33
+ /** Build the shared typed refusal used by CLI, SDK, MCP, and packages. */
34
+ export declare function createUnknownSubcommandError(options: UnknownSubcommandErrorOptions): PmCliError;
35
+ /** Test-only access to deterministic nearest-match behavior. */
36
+ export declare const _testOnly: {
37
+ nearestSubcommand: typeof nearestSubcommand;
38
+ renderCommand: typeof renderCommand;
39
+ };
40
+ export {};