@unbrained/pm-cli 2026.9.1 → 2026.9.3

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 (133) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/CHANGELOG.md +27 -0
  3. package/dist/cli/after-command-context-usage.js +16 -3
  4. package/dist/cli-bundle/bundle-manifest.json +153 -153
  5. package/dist/cli-bundle/chunks/{chunk-6XIIGE7U.js → chunk-34HASX3S.js} +2 -2
  6. package/dist/cli-bundle/chunks/chunk-AY324UJ5.js +9 -0
  7. package/dist/cli-bundle/chunks/{chunk-TAG4VC22.js → chunk-DKGP3QLE.js} +27 -27
  8. package/dist/cli-bundle/chunks/chunk-E5RBBCXO.js +230 -0
  9. package/dist/cli-bundle/chunks/{chunk-DTGWEL23.js → chunk-EDPIS7EB.js} +2 -2
  10. package/dist/cli-bundle/chunks/{chunk-X4RCGLTX.js → chunk-IEHHE6GA.js} +5 -5
  11. package/dist/cli-bundle/chunks/{chunk-U45JXJCC.js → chunk-L7B7343B.js} +2 -2
  12. package/dist/cli-bundle/chunks/{chunk-2MC2LCXZ.js → chunk-LENC656S.js} +41 -41
  13. package/dist/cli-bundle/chunks/chunk-LISESA7W.js +35 -0
  14. package/dist/cli-bundle/chunks/{chunk-3WICXLVT.js → chunk-UPPM2FOZ.js} +2 -2
  15. package/dist/cli-bundle/chunks/{chunk-Y7VOMSZT.js → chunk-Y4PJ6HPK.js} +2 -2
  16. package/dist/cli-bundle/chunks/{register-list-query-XYNX4ASQ.js → register-list-query-W2ZTOMKP.js} +2 -2
  17. package/dist/cli-bundle/chunks/{register-mutation-2MVLRS3J.js → register-mutation-RVND5NCG.js} +2 -2
  18. package/dist/cli-bundle/chunks/{register-operations-WCIDXBU3.js → register-operations-TTROIVC4.js} +2 -2
  19. package/dist/cli-bundle/chunks/{register-setup-KVNC5KPL.js → register-setup-D3ZFHY3L.js} +2 -2
  20. package/dist/cli-bundle/focused-chunks/chunk-3ZBI35HU.js +176 -0
  21. package/dist/cli-bundle/focused-chunks/{chunk-SVXJYD6G.js → chunk-4HJFY4MB.js} +2 -2
  22. package/dist/cli-bundle/focused-chunks/chunk-4RRHC3S4.js +6 -0
  23. package/dist/cli-bundle/focused-chunks/chunk-4WR46RMG.js +9 -0
  24. package/dist/cli-bundle/focused-chunks/chunk-5RFXUPN5.js +3 -0
  25. package/dist/cli-bundle/focused-chunks/chunk-BWKHEIVF.js +2 -0
  26. package/dist/cli-bundle/focused-chunks/{chunk-4O4CJAR6.js → chunk-D7KXFBXV.js} +2 -2
  27. package/dist/cli-bundle/focused-chunks/{chunk-MKHG3T76.js → chunk-GHCZ3YFR.js} +3 -3
  28. package/dist/cli-bundle/focused-chunks/{chunk-KB5OFSFD.js → chunk-GT7MXDLW.js} +2 -2
  29. package/dist/cli-bundle/focused-chunks/chunk-KBLZRU62.js +2 -0
  30. package/dist/cli-bundle/focused-chunks/chunk-KZ2NB3ET.js +2 -0
  31. package/dist/cli-bundle/focused-chunks/chunk-M5M6SVHS.js +3 -0
  32. package/dist/cli-bundle/focused-chunks/{chunk-BEVHCEWG.js → chunk-OL5EH6I3.js} +2 -2
  33. package/dist/cli-bundle/focused-chunks/{chunk-MEASX544.js → chunk-Q45NHPF2.js} +2 -2
  34. package/dist/cli-bundle/focused-chunks/{chunk-Y6JJIX6I.js → chunk-QKHCSUGJ.js} +48 -48
  35. package/dist/cli-bundle/focused-chunks/chunk-S27XMBIN.js +8 -0
  36. package/dist/cli-bundle/focused-chunks/chunk-TXE4TSHM.js +12 -0
  37. package/dist/cli-bundle/focused-chunks/chunk-UCLO3XDU.js +2 -0
  38. package/dist/cli-bundle/main.js +15 -15
  39. package/dist/cli-bundle/sdk-authoring.js +1 -1
  40. package/dist/cli-bundle/sdk-contracts.js +1 -1
  41. package/dist/cli-bundle/sdk-core.js +28 -28
  42. package/dist/cli-bundle/sdk-governance.js +1 -1
  43. package/dist/cli-bundle/sdk-graph.js +1 -1
  44. package/dist/cli-bundle/sdk-merge.js +35 -33
  45. package/dist/cli-bundle/sdk-query.js +1 -1
  46. package/dist/cli-bundle/sdk-runtime.js +1 -1
  47. package/dist/cli-bundle/sdk-testing.js +1 -1
  48. package/dist/cli-bundle/sdk.js +9 -9
  49. package/dist/core/diagnostics/remediation.js +9 -4
  50. package/dist/core/history/event-classification.d.ts +21 -0
  51. package/dist/core/history/event-classification.js +109 -0
  52. package/dist/core/history/event-index.d.ts +14 -3
  53. package/dist/core/history/event-index.js +543 -88
  54. package/dist/core/history/history.js +47 -18
  55. package/dist/core/history/workspace-history.js +7 -6
  56. package/dist/core/lock/lock.d.ts +22 -2
  57. package/dist/core/lock/lock.js +121 -19
  58. package/dist/core/output/output.js +31 -7
  59. package/dist/core/shared/time.d.ts +2 -0
  60. package/dist/core/shared/time.js +8 -2
  61. package/dist/core/store/item-metadata-query-index.js +5 -5
  62. package/dist/sdk/context/recency.d.ts +9 -0
  63. package/dist/sdk/context/recency.js +34 -0
  64. package/dist/sdk/context/usage-egress.d.ts +9 -0
  65. package/dist/sdk/context/usage-egress.js +30 -0
  66. package/dist/sdk/context-intent-contracts.js +7 -3
  67. package/dist/sdk/context-relevance.d.ts +23 -0
  68. package/dist/sdk/context-relevance.js +94 -23
  69. package/dist/sdk/context-signal-store.d.ts +7 -3
  70. package/dist/sdk/context-signal-store.js +155 -42
  71. package/dist/sdk/context-usage.d.ts +54 -0
  72. package/dist/sdk/context-usage.js +245 -16
  73. package/dist/sdk/generated/generated-error-code-catalog-part-1.js +26 -14
  74. package/dist/sdk/generated/generated-error-code-catalog-part-2.js +14 -2
  75. package/dist/sdk/governance/health.js +77 -11
  76. package/dist/sdk/governance/validate.js +4 -2
  77. package/dist/sdk/index.d.ts +2 -0
  78. package/dist/sdk/index.js +4 -2
  79. package/dist/sdk/init.d.ts +2 -0
  80. package/dist/sdk/init.js +18 -9
  81. package/dist/sdk/merge/index.d.ts +1 -1
  82. package/dist/sdk/merge/index.js +2 -2
  83. package/dist/sdk/merge/receipts.d.ts +24 -0
  84. package/dist/sdk/merge/receipts.js +122 -30
  85. package/dist/sdk/query/context.d.ts +3 -1
  86. package/dist/sdk/query/context.js +21 -9
  87. package/dist/sdk/query/next.js +18 -9
  88. package/dist/sdk/runtime-input.d.ts +2 -0
  89. package/dist/sdk/runtime-input.js +25 -2
  90. package/dist/sdk/runtime.d.ts +1 -0
  91. package/dist/sdk/runtime.js +17 -26
  92. package/dist/types.d.ts +2 -0
  93. package/dist/types.js +2 -2
  94. package/docs/CONTEXT_RECOVERY_PRIMITIVES.md +11 -2
  95. package/docs/CONTEXT_RELEVANCE.md +44 -1
  96. package/docs/MERGE_SAFETY.md +16 -2
  97. package/docs/PERFORMANCE.md +6 -3
  98. package/docs/READ_OUTPUT_CONTRACTS.md +9 -1
  99. package/docs/SDK.md +11 -8
  100. package/docs/SDK_CONTEXT.md +7 -0
  101. package/docs/generated/REFUSAL_CLOSURE_CENSUS.md +5 -3
  102. package/marketplace.json +2 -2
  103. package/package.json +4 -4
  104. package/packages/pm-beads/package.json +1 -1
  105. package/packages/pm-calendar/package.json +1 -1
  106. package/packages/pm-command-kit/package.json +1 -1
  107. package/packages/pm-digital-twin/package.json +1 -1
  108. package/packages/pm-governance-audit/package.json +1 -1
  109. package/packages/pm-guide-shell/package.json +1 -1
  110. package/packages/pm-kanban/package.json +1 -1
  111. package/packages/pm-lifecycle-hooks/package.json +1 -1
  112. package/packages/pm-linked-test-adapters/package.json +1 -1
  113. package/packages/pm-search-advanced/package.json +1 -1
  114. package/packages/pm-templates/package.json +1 -1
  115. package/packages/pm-todos/package.json +1 -1
  116. package/packages/pm-vcs/package.json +1 -1
  117. package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
  118. package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
  119. package/sdk/public-surface.json +268 -28
  120. package/dist/cli-bundle/chunks/chunk-ALKKAUFN.js +0 -35
  121. package/dist/cli-bundle/chunks/chunk-GOACULJW.js +0 -13
  122. package/dist/cli-bundle/chunks/chunk-NOPL37X4.js +0 -205
  123. package/dist/cli-bundle/focused-chunks/chunk-2FEZ5SKM.js +0 -3
  124. package/dist/cli-bundle/focused-chunks/chunk-6GCRSLPG.js +0 -2
  125. package/dist/cli-bundle/focused-chunks/chunk-7VATS5QY.js +0 -16
  126. package/dist/cli-bundle/focused-chunks/chunk-FWPLDEXF.js +0 -2
  127. package/dist/cli-bundle/focused-chunks/chunk-JEFKLWMM.js +0 -2
  128. package/dist/cli-bundle/focused-chunks/chunk-PF3E6Q36.js +0 -8
  129. package/dist/cli-bundle/focused-chunks/chunk-QDFJIRKN.js +0 -4
  130. package/dist/cli-bundle/focused-chunks/chunk-RMRCQPWU.js +0 -2
  131. package/dist/cli-bundle/focused-chunks/chunk-S4U5CN5Q.js +0 -2
  132. package/dist/cli-bundle/focused-chunks/chunk-T2LXXSOD.js +0 -158
  133. package/dist/cli-bundle/focused-chunks/chunk-Z2USIBR2.js +0 -5
@@ -6,7 +6,7 @@
6
6
  * intents; it never enters item history and is safe to delete or rebuild.
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]="5f4ccd28-a001-54aa-8378-893025c69b96")}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]="997a09a2-cf0c-5384-b457-d9627d465b71")}catch(e){}}();
10
10
  import { randomUUID } from "node:crypto";
11
11
  import { appendFile, mkdir, readFile, rename, stat, writeFile, } from "node:fs/promises";
12
12
  import path from "node:path";
@@ -16,6 +16,154 @@ const DEFAULT_RETENTION_DAYS = 30;
16
16
  const DAY_MS = 86_400_000;
17
17
  const DEFAULT_COMPACTION_BYTES = 262_144;
18
18
  const CONTEXT_USAGE_LOCK_ID = "context-usage-ledger";
19
+ const CONTEXT_USAGE_SERVING_RECEIPT = Symbol.for("@unbrained/pm-cli/context-usage-serving-receipt");
20
+ function asObjectRecord(value) {
21
+ return typeof value === "object" && value !== null && !Array.isArray(value)
22
+ ? value
23
+ : null;
24
+ }
25
+ /** Attach a JSON-invisible serving receipt to a result for later egress. */
26
+ export function attachContextUsageServingReceipt(result, receipt) {
27
+ const record = asObjectRecord(result);
28
+ if (record !== null && receipt !== null) {
29
+ record[CONTEXT_USAGE_SERVING_RECEIPT] = receipt;
30
+ }
31
+ }
32
+ /** Copy an attached serving receipt across a result projection boundary. */
33
+ export function propagateContextUsageServingReceipt(source, target) {
34
+ const sourceRecord = asObjectRecord(source);
35
+ const targetRecord = asObjectRecord(target);
36
+ const receipt = sourceRecord?.[CONTEXT_USAGE_SERVING_RECEIPT];
37
+ if (targetRecord !== null && receipt !== undefined) {
38
+ targetRecord[CONTEXT_USAGE_SERVING_RECEIPT] = receipt;
39
+ }
40
+ }
41
+ function receiptFromResult(result) {
42
+ return (asObjectRecord(result)?.[CONTEXT_USAGE_SERVING_RECEIPT] ?? null);
43
+ }
44
+ function resultReceiptSaysOmitted(result) {
45
+ const record = asObjectRecord(result);
46
+ for (const key of ["read_output", "context_intent"]) {
47
+ const receipt = asObjectRecord(record?.[key]);
48
+ if (receipt?.result_omitted === true)
49
+ return true;
50
+ }
51
+ return false;
52
+ }
53
+ function addContextUsageId(ids, value) {
54
+ if (typeof value === "string" && value.trim().length > 0) {
55
+ ids.add(value.trim());
56
+ }
57
+ }
58
+ function addContextUsageDirectRows(ids, value) {
59
+ if (!Array.isArray(value))
60
+ return;
61
+ for (const row of value) {
62
+ addContextUsageId(ids, asObjectRecord(row)?.id);
63
+ }
64
+ }
65
+ function addContextUsageIdList(ids, value) {
66
+ if (!Array.isArray(value))
67
+ return;
68
+ for (const itemId of value)
69
+ addContextUsageId(ids, itemId);
70
+ }
71
+ function collectNextResultItemIds(record, ids) {
72
+ const rows = [
73
+ ...(asObjectRecord(record.recommended) === null
74
+ ? []
75
+ : [record.recommended]),
76
+ ...(Array.isArray(record.ready) ? record.ready : []),
77
+ ...(Array.isArray(record.decision_needed) ? record.decision_needed : []),
78
+ ...(Array.isArray(record.blocked) ? record.blocked : []),
79
+ ...(Array.isArray(record.held_by_others) ? record.held_by_others : []),
80
+ ];
81
+ for (const value of rows) {
82
+ const row = asObjectRecord(value);
83
+ addContextUsageId(ids, row?.id);
84
+ for (const blocker of Array.isArray(row?.blockers) ? row.blockers : []) {
85
+ addContextUsageId(ids, asObjectRecord(blocker)?.id);
86
+ }
87
+ for (const unblockedId of Array.isArray(row?.unblocks)
88
+ ? row.unblocks
89
+ : []) {
90
+ addContextUsageId(ids, unblockedId);
91
+ }
92
+ }
93
+ }
94
+ function collectContextResultItemIds(record, ids) {
95
+ for (const key of [
96
+ "high_level",
97
+ "low_level",
98
+ "blocked_fallback",
99
+ "recently_created",
100
+ "unparented",
101
+ "activity",
102
+ "progress",
103
+ "blockers",
104
+ "staleness",
105
+ ]) {
106
+ addContextUsageDirectRows(ids, record[key]);
107
+ }
108
+ for (const value of Array.isArray(record.blockers)
109
+ ? record.blockers
110
+ : []) {
111
+ addContextUsageId(ids, asObjectRecord(value)?.blocked_by);
112
+ }
113
+ collectContextNestedItemIds(record, ids);
114
+ }
115
+ function collectContextNestedItemIds(record, ids) {
116
+ for (const value of Array.isArray(record.hierarchy)
117
+ ? record.hierarchy
118
+ : []) {
119
+ const node = asObjectRecord(value);
120
+ addContextUsageId(ids, node?.id);
121
+ addContextUsageDirectRows(ids, node?.children);
122
+ }
123
+ for (const key of ["files", "workload"]) {
124
+ for (const value of Array.isArray(record[key]) ? record[key] : []) {
125
+ addContextUsageIdList(ids, asObjectRecord(value)?.items);
126
+ }
127
+ }
128
+ const agenda = asObjectRecord(record.agenda);
129
+ for (const value of Array.isArray(agenda?.events) ? agenda.events : []) {
130
+ addContextUsageId(ids, asObjectRecord(value)?.item_id);
131
+ }
132
+ const tests = asObjectRecord(record.tests);
133
+ addContextUsageIdList(ids, tests?.items_failing);
134
+ }
135
+ /**
136
+ * Collect every item identifier visible in a final context or next result.
137
+ * The traversal is shape-aware so summary counters and unrelated identifier
138
+ * fields cannot enter the privacy-minimal usage ledger.
139
+ */
140
+ export function collectContextUsageDeliveredItemIds(result, surface) {
141
+ const record = asObjectRecord(result);
142
+ if (record === null || resultReceiptSaysOmitted(result))
143
+ return [];
144
+ const ids = new Set();
145
+ if (surface === "next") {
146
+ collectNextResultItemIds(record, ids);
147
+ }
148
+ else {
149
+ collectContextResultItemIds(record, ids);
150
+ }
151
+ return [...ids];
152
+ }
153
+ /** Resolve and append one post-egress decision carried by a context result. */
154
+ export async function finalizeContextUsageDelivery(options) {
155
+ const receipt = receiptFromResult(options.result);
156
+ if (receipt === null)
157
+ return false;
158
+ const resultOmitted = resultReceiptSaysOmitted(options.result);
159
+ await recordContextUsageDelivery({
160
+ pmRoot: options.pmRoot,
161
+ receipt,
162
+ deliveredItemIds: collectContextUsageDeliveredItemIds(options.result, receipt.surface),
163
+ resultOmitted,
164
+ });
165
+ return true;
166
+ }
19
167
  /** Named validation failure emitted by context-usage SDK inputs. */
20
168
  export class ContextUsageValidationError extends TypeError {
21
169
  /** Stable machine-readable error code for package and CLI hosts. */
@@ -61,7 +209,7 @@ async function readEvents(options) {
61
209
  return [];
62
210
  }
63
211
  }
64
- async function appendEvents(options, events) {
212
+ async function appendEvents(options, events, deliveryServeId) {
65
213
  const maxEvents = options.maxEvents ?? DEFAULT_MAX_EVENTS;
66
214
  const retentionDays = options.retentionDays ?? DEFAULT_RETENTION_DAYS;
67
215
  if (!Number.isInteger(maxEvents) ||
@@ -75,6 +223,10 @@ async function appendEvents(options, events) {
75
223
  const releaseLock = await acquireLock(options.pmRoot, CONTEXT_USAGE_LOCK_ID, 30, `context-usage:${process.pid}`, false, false, 3_000);
76
224
  try {
77
225
  await mkdir(runtimeDirectory, { recursive: true });
226
+ if (deliveryServeId !== undefined &&
227
+ (await readEvents(options)).some((event) => event.kind === "delivery" && event.serve_id === deliveryServeId)) {
228
+ return;
229
+ }
78
230
  await appendFile(target, events.map((event) => `${JSON.stringify(event)}\n`).join(""), "utf8");
79
231
  const customBounds = options.maxEvents !== undefined || options.retentionDays !== undefined;
80
232
  if (!customBounds && (await stat(target)).size <= DEFAULT_COMPACTION_BYTES)
@@ -93,21 +245,70 @@ async function appendEvent(options, event) {
93
245
  }
94
246
  /** Records a propensity-complete context/next serving event. */
95
247
  export async function recordContextUsageServing(options) {
96
- if (process.env.PM_CONTEXT_USAGE_DISABLED === "1" ||
97
- options.enabled === false)
98
- return;
248
+ if ([
249
+ process.env.PM_CONTEXT_USAGE_DISABLED === "1",
250
+ options.enabled === false,
251
+ ].includes(true))
252
+ return null;
99
253
  if (!options.author.trim() ||
100
254
  options.rows.some((row) => !row.id.trim() || !Number.isInteger(row.rank) || row.rank < 1)) {
101
255
  throw new ContextUsageValidationError("Context usage serving requires an author and valid ranked rows");
102
256
  }
257
+ const normalizedRows = options.rows.map((row) => ({
258
+ ...row,
259
+ id: row.id.trim(),
260
+ }));
261
+ if (new Set(normalizedRows.map((row) => row.id)).size !== normalizedRows.length) {
262
+ throw new ContextUsageValidationError("Context usage serving requires unique normalized row ids");
263
+ }
264
+ const receipt = {
265
+ serve_id: randomUUID(),
266
+ author: options.author.trim(),
267
+ surface: options.surface,
268
+ rows: normalizedRows,
269
+ };
103
270
  await appendEvent(options, {
104
271
  kind: "serve",
272
+ schema_version: 2,
273
+ serve_id: receipt.serve_id,
105
274
  at: resolveNow(options).iso,
106
275
  author: options.author.trim(),
107
276
  surface: options.surface,
108
277
  profile: options.profile.trim() || "balanced",
109
- rows: options.rows,
278
+ rows: receipt.rows.map((row) => ({ ...row })),
279
+ result_omitted: false,
280
+ packed_item_ids: receipt.rows
281
+ .filter((row) => row.included)
282
+ .map((row) => row.id),
110
283
  });
284
+ return receipt;
285
+ }
286
+ /** Append the exact post-egress delivery decision for one serving receipt. */
287
+ export async function recordContextUsageDelivery(options) {
288
+ if (options.receipt === null ||
289
+ process.env.PM_CONTEXT_USAGE_DISABLED === "1" ||
290
+ options.enabled === false) {
291
+ return;
292
+ }
293
+ const rankedIds = new Set(options.receipt.rows.map((row) => row.id));
294
+ const deliveredItemIds = [
295
+ ...new Set(options.deliveredItemIds.map((id) => id.trim())),
296
+ ];
297
+ if (deliveredItemIds.some((id) => id.length === 0 || !rankedIds.has(id)) ||
298
+ (options.resultOmitted && deliveredItemIds.length > 0)) {
299
+ throw new ContextUsageValidationError("Context usage delivery requires emitted ranked ids and zero ids for an omitted result");
300
+ }
301
+ const delivery = {
302
+ kind: "delivery",
303
+ schema_version: 2,
304
+ serve_id: options.receipt.serve_id,
305
+ at: resolveNow(options).iso,
306
+ author: options.receipt.author,
307
+ surface: options.receipt.surface,
308
+ result_omitted: options.resultOmitted,
309
+ delivered_item_ids: deliveredItemIds,
310
+ };
311
+ await appendEvents(options, [delivery], delivery.serve_id);
111
312
  }
112
313
  /** Records one subsequent item read or mutation outcome. */
113
314
  export async function recordContextUsageTouch(options) {
@@ -165,6 +366,18 @@ function findTouchTimeInHorizon(touches, itemId, servedAt, horizonMs) {
165
366
  time >= servedAt &&
166
367
  time - servedAt <= horizonMs)?.time;
167
368
  }
369
+ function firstDeliveriesByServeId(events, author) {
370
+ const deliveries = new Map();
371
+ for (const event of events) {
372
+ if (event.kind === "delivery" &&
373
+ event.schema_version === 2 &&
374
+ event.author === author &&
375
+ !deliveries.has(event.serve_id)) {
376
+ deliveries.set(event.serve_id, event);
377
+ }
378
+ }
379
+ return deliveries;
380
+ }
168
381
  /**
169
382
  * Derives decayed served-then-touched affinity. A small exploration floor keeps
170
383
  * ignored and unseen items eligible, preventing a popularity feedback lock-in.
@@ -172,26 +385,41 @@ function findTouchTimeInHorizon(touches, itemId, servedAt, horizonMs) {
172
385
  export async function readContextUsageAffinity(options) {
173
386
  if (process.env.PM_CONTEXT_USAGE_DISABLED === "1" ||
174
387
  options.enabled === false)
175
- return { affinity: {}, positive_judgments: 0, serving_events: 0 };
388
+ return {
389
+ affinity: {},
390
+ positive_judgments: 0,
391
+ serving_events: 0,
392
+ untrusted_serving_events: 0,
393
+ };
176
394
  const events = await readEvents(options);
177
395
  const now = resolveNow(options).ms;
178
396
  const horizonMs = (options.horizonHours ?? 24) * 3_600_000;
179
- if (!Number.isFinite(horizonMs) || horizonMs <= 0)
397
+ if (![Number.isFinite(horizonMs), horizonMs > 0].every(Boolean))
180
398
  throw new ContextUsageValidationError("Context usage horizonHours must be positive");
181
399
  const author = options.author.trim();
182
400
  const touches = events
183
- .filter((event) => event.kind === "touch" && event.author === author)
401
+ .filter((event) => event.kind === "touch")
402
+ .filter((event) => event.author === author)
184
403
  .map((entry) => ({ entry, time: Date.parse(entry.at) }));
185
404
  const scores = new Map();
186
405
  let servingEvents = 0;
406
+ let untrustedServingEvents = 0;
187
407
  let positiveJudgments = 0;
188
- for (const event of events) {
189
- if (event.kind !== "serve" || event.author !== author)
190
- continue;
408
+ const deliveries = firstDeliveriesByServeId(events, author);
409
+ events
410
+ .filter((event) => event.kind === "serve")
411
+ .filter((event) => event.author === author)
412
+ .forEach((event) => {
413
+ if (![event.schema_version === 2, Boolean(event.serve_id)].every(Boolean)) {
414
+ untrustedServingEvents += 1;
415
+ return;
416
+ }
191
417
  servingEvents += 1;
192
418
  const servedAt = Date.parse(event.at);
419
+ const delivery = deliveries.get(event.serve_id);
420
+ const deliveredIds = new Set(delivery?.delivered_item_ids ?? []);
193
421
  for (const row of event.rows) {
194
- if (!row.included)
422
+ if (!deliveredIds.has(row.id))
195
423
  continue;
196
424
  const touchTime = findTouchTimeInHorizon(touches, row.id, servedAt, horizonMs);
197
425
  if (touchTime === undefined)
@@ -200,17 +428,18 @@ export async function readContextUsageAffinity(options) {
200
428
  const ageDays = Math.max(0, (now - touchTime) / DAY_MS);
201
429
  scores.set(row.id, (scores.get(row.id) ?? 0) + Math.exp(-ageDays / 14));
202
430
  }
203
- }
431
+ });
204
432
  const maximum = Math.max(0, ...scores.values());
205
433
  const affinity = Object.fromEntries([...scores.entries()].map(([id, score]) => [
206
434
  id,
207
- 0.05 + 0.95 * (maximum > 0 ? score / maximum : 0),
435
+ maximum === 0 ? 0.05 : 0.05 + (0.95 * score) / maximum,
208
436
  ]));
209
437
  return {
210
438
  affinity,
211
439
  positive_judgments: positiveJudgments,
212
440
  serving_events: servingEvents,
441
+ untrusted_serving_events: untrustedServingEvents,
213
442
  };
214
443
  }
215
444
  //# sourceMappingURL=context-usage.js.map
216
- //# debugId=5f4ccd28-a001-54aa-8378-893025c69b96
445
+ //# debugId=997a09a2-cf0c-5384-b457-d9627d465b71
@@ -1,6 +1,6 @@
1
1
  /** Generated partition 1 of the exhaustive error-code catalog. */
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]="0823f083-1719-5ec2-9f7e-43bd86a2a3a2")}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]="0a4690e9-4cde-5218-81fb-5d59807fa344")}catch(e){}}();
4
4
  export const PM_ERROR_CODE_CATALOG_PART_1 = [
5
5
  {
6
6
  code: "acceptance_criteria_mutation_conflict",
@@ -1271,6 +1271,18 @@ export const PM_ERROR_CODE_CATALOG_PART_1 = [
1271
1271
  canonical_code: "history_target_out_of_range",
1272
1272
  aliases: [],
1273
1273
  },
1274
+ {
1275
+ code: "history_timestamp_invalid",
1276
+ meaning: "History timestamp invalid condition.",
1277
+ stability: "provisional",
1278
+ exit_code: 1,
1279
+ class: "generic_failure",
1280
+ recovery: "Inspect the structured error guidance and retry the suggested command.",
1281
+ sources: ["core/history/event-index.ts", "core/history/history.ts"],
1282
+ emitting_commands: ["*"],
1283
+ canonical_code: "history_timestamp_invalid",
1284
+ aliases: [],
1285
+ },
1274
1286
  {
1275
1287
  code: "history_unknown_author_events",
1276
1288
  meaning: "History unknown author events condition.",
@@ -2076,6 +2088,18 @@ export const PM_ERROR_CODE_CATALOG_PART_1 = [
2076
2088
  canonical_code: "merge_receipt_evidence_invalid",
2077
2089
  aliases: [],
2078
2090
  },
2091
+ {
2092
+ code: "merge_receipt_history_reference_missing",
2093
+ meaning: "Merge receipt history reference missing condition.",
2094
+ stability: "provisional",
2095
+ exit_code: 1,
2096
+ class: "generic_failure",
2097
+ recovery: "Inspect the structured error guidance and retry the suggested command.",
2098
+ sources: ["core/diagnostics/remediation.ts"],
2099
+ emitting_commands: ["*"],
2100
+ canonical_code: "merge_receipt_history_reference_missing",
2101
+ aliases: [],
2102
+ },
2079
2103
  {
2080
2104
  code: "merge_receipts_pending",
2081
2105
  meaning: "Merge receipts pending condition.",
@@ -2136,18 +2160,6 @@ export const PM_ERROR_CODE_CATALOG_PART_1 = [
2136
2160
  canonical_code: "missing_budget",
2137
2161
  aliases: [],
2138
2162
  },
2139
- {
2140
- code: "missing_cli_flag",
2141
- meaning: "Missing cli flag condition.",
2142
- stability: "provisional",
2143
- exit_code: 1,
2144
- class: "generic_failure",
2145
- recovery: "Inspect the structured error guidance and retry the suggested command.",
2146
- sources: ["sdk/cli-contracts/tool-option-contracts.ts"],
2147
- emitting_commands: ["*"],
2148
- canonical_code: "missing_cli_flag",
2149
- aliases: [],
2150
- },
2151
2163
  ];
2152
2164
  //# sourceMappingURL=generated-error-code-catalog-part-1.js.map
2153
- //# debugId=0823f083-1719-5ec2-9f7e-43bd86a2a3a2
2165
+ //# debugId=0a4690e9-4cde-5218-81fb-5d59807fa344
@@ -1,7 +1,19 @@
1
1
  /** Generated partition 2 of the exhaustive error-code catalog. */
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]="41e6f81d-ccb5-546c-9b26-3b78027528b5")}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]="5d5a1c08-21ed-5823-8c1b-15f7907ab3b5")}catch(e){}}();
4
4
  export const PM_ERROR_CODE_CATALOG_PART_2 = [
5
+ {
6
+ code: "missing_cli_flag",
7
+ meaning: "Missing cli flag condition.",
8
+ stability: "provisional",
9
+ exit_code: 1,
10
+ class: "generic_failure",
11
+ recovery: "Inspect the structured error guidance and retry the suggested command.",
12
+ sources: ["sdk/cli-contracts/tool-option-contracts.ts"],
13
+ emitting_commands: ["*"],
14
+ canonical_code: "missing_cli_flag",
15
+ aliases: [],
16
+ },
5
17
  {
6
18
  code: "missing_destination",
7
19
  meaning: "Missing destination condition.",
@@ -2174,4 +2186,4 @@ export const PM_ERROR_CODE_CATALOG_PART_2 = [
2174
2186
  },
2175
2187
  ];
2176
2188
  //# sourceMappingURL=generated-error-code-catalog-part-2.js.map
2177
- //# debugId=41e6f81d-ccb5-546c-9b26-3b78027528b5
2189
+ //# debugId=5d5a1c08-21ed-5823-8c1b-15f7907ab3b5
@@ -4,7 +4,7 @@
4
4
  * Implements the pm health 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]="76693c03-9395-5bbf-bcc8-fa15e408f30f")}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]="f27cb24d-f2c8-5034-b655-84c577de7a16")}catch(e){}}();
8
8
  import fs from "node:fs/promises";
9
9
  import path from "node:path";
10
10
  import { assertInitializedTracker } from "../environment/tracker-preflight.js";
@@ -28,6 +28,7 @@ import { EXIT_CODE, PM_CORE_REQUIRED_SUBDIRS, TYPE_TO_FOLDER, } from "../../core
28
28
  import { findFirstMergeConflictMarker } from "../../core/shared/conflict-markers.js";
29
29
  import { PmCliError } from "../../core/shared/errors.js";
30
30
  import { toNonEmptyStringOrUndefined } from "../../core/shared/primitives.js";
31
+ import { sha256Hex } from "../../core/shared/serialization.js";
31
32
  import { nowIso } from "../../core/shared/time.js";
32
33
  import { parseItemDocument } from "../../core/item/item-format.js";
33
34
  import { effectiveItemFormatVersion, scanItemFormatVersions, } from "../../core/item/item-format-version.js";
@@ -46,7 +47,7 @@ import { buildCapabilityContractMetadata, buildRegistrationCollisionRemediation,
46
47
  import { scanStaleInProgressItems, } from "./stale-work.js";
47
48
  import { scanTrackedRuntimeCache } from "./tracked-runtime-cache.js";
48
49
  import { auditMergeDriverConfiguration, findGitWorkspaceRoot, } from "../merge/install.js";
49
- import { inspectMergeReceiptEvidence, partitionMergeReceipts, } from "../merge/receipts.js";
50
+ import { inspectMergeReceiptEvidence, isSafeReceiptId, partitionMergeReceipts, } from "../merge/receipts.js";
50
51
  import { runHistoryRepair } from "../history-repair.js";
51
52
  import { resolveWorkspaceRelationshipKindRegistry } from "../graph/assembly.js";
52
53
  import { analyzeHierarchyIntegrity, } from "../graph/hierarchy-integrity.js";
@@ -278,11 +279,41 @@ async function scanItemIntegrity(pmRoot, typeToFolder, schema) {
278
279
  }
279
280
  return { paths, unreadable, conflictMarkers, parseFailures, formatVersions };
280
281
  }
282
+ function mergeReceiptIdsFromHistoryEntry(value) {
283
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
284
+ return [];
285
+ }
286
+ const context = value.context;
287
+ if (typeof context !== "object" ||
288
+ context === null ||
289
+ Array.isArray(context)) {
290
+ return [];
291
+ }
292
+ const merge = context.merge;
293
+ if (typeof merge !== "object" || merge === null || Array.isArray(merge)) {
294
+ return [];
295
+ }
296
+ const receipts = merge.receipts;
297
+ if (!Array.isArray(receipts))
298
+ return [];
299
+ return receipts.flatMap((receipt) => {
300
+ if (typeof receipt !== "object" ||
301
+ receipt === null ||
302
+ Array.isArray(receipt)) {
303
+ return [];
304
+ }
305
+ const receiptId = receipt.receipt_id;
306
+ return typeof receiptId === "string" && receiptId.length > 0
307
+ ? [receiptId]
308
+ : [];
309
+ });
310
+ }
281
311
  async function scanHistoryIntegrity(pmRoot) {
282
312
  const historyDir = path.join(pmRoot, "history");
283
313
  const unreadable = [];
284
314
  const conflictMarkers = [];
285
315
  const invalidJson = [];
316
+ const mergeReceiptReferences = [];
286
317
  let files = [];
287
318
  try {
288
319
  files = (await fs.readdir(historyDir))
@@ -314,14 +345,25 @@ async function scanHistoryIntegrity(pmRoot) {
314
345
  if (!line.trim())
315
346
  return;
316
347
  try {
317
- JSON.parse(line);
348
+ const parsed = JSON.parse(line);
349
+ mergeReceiptReferences.push(...mergeReceiptIdsFromHistoryEntry(parsed).map((receiptId) => ({
350
+ itemId,
351
+ line: index + 1,
352
+ receiptId,
353
+ })));
318
354
  }
319
355
  catch {
320
356
  invalidJson.push({ id: itemId, line: index + 1 });
321
357
  }
322
358
  });
323
359
  }
324
- return { files, unreadable, conflictMarkers, invalidJson };
360
+ return {
361
+ files,
362
+ unreadable,
363
+ conflictMarkers,
364
+ invalidJson,
365
+ mergeReceiptReferences,
366
+ };
325
367
  }
326
368
  function buildHierarchyIntegrityWarnings(analysis) {
327
369
  return [
@@ -336,10 +378,15 @@ function buildHierarchyIntegrityWarnings(analysis) {
336
378
  : `integrity_hierarchy_divergence:${finding.child_id}`),
337
379
  ];
338
380
  }
339
- function buildMergeReceiptEvidenceWarnings(invalidEvidenceCount) {
340
- return invalidEvidenceCount === 0
341
- ? []
342
- : [`merge_receipt_evidence_invalid:${invalidEvidenceCount}`];
381
+ function buildMergeReceiptEvidenceWarnings(params) {
382
+ const warnings = [];
383
+ if (params.invalidEvidenceCount > 0) {
384
+ warnings.push(`merge_receipt_evidence_invalid:${params.invalidEvidenceCount}`);
385
+ }
386
+ if (params.missingHistoryReferenceCount > 0) {
387
+ warnings.push(`merge_receipt_history_reference_missing:${params.missingHistoryReferenceCount}`);
388
+ }
389
+ return warnings;
343
390
  }
344
391
  async function buildIntegrityCheck(pmRoot, typeToFolder, schema, requireMergeDrivers, items) {
345
392
  const itemScan = await scanItemIntegrity(pmRoot, typeToFolder, schema);
@@ -353,10 +400,21 @@ async function buildIntegrityCheck(pmRoot, typeToFolder, schema, requireMergeDri
353
400
  ? null
354
401
  : await auditMergeDriverConfiguration(gitWorkspaceRoot);
355
402
  const mergeReceiptEvidence = await inspectMergeReceiptEvidence(gitWorkspaceRoot ?? pmRoot, {
403
+ includeReconciled: true,
356
404
  includeLossless: true,
357
405
  pmRoot,
358
406
  });
359
- const pendingMergeReceipts = mergeReceiptEvidence.receipts;
407
+ const availableMergeReceiptIds = new Set(mergeReceiptEvidence.receipts.map((receipt) => receipt.id));
408
+ const missingMergeReceiptReferences = historyScan.mergeReceiptReferences
409
+ .filter((reference) => !availableMergeReceiptIds.has(reference.receiptId))
410
+ .map((reference) => ({
411
+ item_id: reference.itemId,
412
+ history_line: reference.line,
413
+ ...(isSafeReceiptId(reference.receiptId)
414
+ ? { receipt_id: reference.receiptId }
415
+ : { receipt_reference_hash: sha256Hex(reference.receiptId) }),
416
+ }));
417
+ const pendingMergeReceipts = mergeReceiptEvidence.receipts.filter((receipt) => receipt.state === "pending");
360
418
  const { pendingDecisions: pendingMergeDecisions, lossless: losslessMergeReceipts, } = partitionMergeReceipts(pendingMergeReceipts);
361
419
  const mergeDriverInstallationMissing = mergeDriverAudit?.status === "missing" &&
362
420
  mergeDriverAudit.missing_keys.length === 5 &&
@@ -389,7 +447,10 @@ async function buildIntegrityCheck(pmRoot, typeToFolder, schema, requireMergeDri
389
447
  ...(losslessMergeReceipts.length > 0
390
448
  ? [`merge_receipts_pending:${losslessMergeReceipts.length}`]
391
449
  : []),
392
- ...buildMergeReceiptEvidenceWarnings(mergeReceiptEvidence.invalid_evidence_count),
450
+ ...buildMergeReceiptEvidenceWarnings({
451
+ invalidEvidenceCount: mergeReceiptEvidence.invalid_evidence_count,
452
+ missingHistoryReferenceCount: missingMergeReceiptReferences.length,
453
+ }),
393
454
  ];
394
455
  const normalizedWarnings = [...new Set(warnings)].sort((left, right) => left.localeCompare(right));
395
456
  const actionableWarnings = normalizedWarnings.filter((warning) => !warning.startsWith("integrity_legacy_hierarchy_"));
@@ -421,6 +482,7 @@ async function buildIntegrityCheck(pmRoot, typeToFolder, schema, requireMergeDri
421
482
  pending_merge_decisions: pendingMergeDecisions.length,
422
483
  lossless_merge_receipts: losslessMergeReceipts.length,
423
484
  invalid_merge_receipt_evidence: mergeReceiptEvidence.invalid_evidence_count,
485
+ missing_merge_receipt_history_references: missingMergeReceiptReferences.length,
424
486
  },
425
487
  item_unreadable: itemScan.unreadable,
426
488
  item_conflict_markers: itemScan.conflictMarkers,
@@ -452,6 +514,10 @@ async function buildIntegrityCheck(pmRoot, typeToFolder, schema, requireMergeDri
452
514
  lossless_merge_receipt_items: [
453
515
  ...new Set(losslessMergeReceipts.map((receipt) => receipt.item_id)),
454
516
  ].sort((left, right) => left.localeCompare(right)),
517
+ invalid_merge_receipt_evidence_details: mergeReceiptEvidence.invalid_evidence,
518
+ invalid_merge_receipt_evidence_details_truncated: mergeReceiptEvidence.invalid_evidence_truncated,
519
+ missing_merge_receipt_history_reference_details: missingMergeReceiptReferences.slice(0, HEALTH_WARNING_LIMIT),
520
+ missing_merge_receipt_history_reference_details_truncated: missingMergeReceiptReferences.length > HEALTH_WARNING_LIMIT,
455
521
  },
456
522
  },
457
523
  warnings: normalizedWarnings,
@@ -2309,4 +2375,4 @@ export const _testOnlyHealthCommand = {
2309
2375
  warningCode,
2310
2376
  };
2311
2377
  //# sourceMappingURL=health.js.map
2312
- //# debugId=76693c03-9395-5bbf-bcc8-fa15e408f30f
2378
+ //# debugId=f27cb24d-f2c8-5034-b655-84c577de7a16
@@ -4,7 +4,7 @@
4
4
  * Implements the pm validate 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]="cde10e58-cfeb-5718-a471-3e4acf46cfe7")}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]="0d466293-869d-54bb-b07d-facb3be51e8c")}catch(e){}}();
8
8
  import { assertInitializedTracker } from "../environment/tracker-preflight.js";
9
9
  import fs from "node:fs/promises";
10
10
  import { realpathSync } from "node:fs";
@@ -2053,6 +2053,8 @@ async function buildStorageIntegrityCheck(pmRoot, items, typeToFolder, settings)
2053
2053
  ].sort((left, right) => left.localeCompare(right)),
2054
2054
  lossless_merge_receipt_count: losslessMergeReceipts.length,
2055
2055
  invalid_merge_receipt_evidence_count: mergeReceiptEvidence.invalid_evidence_count,
2056
+ invalid_merge_receipt_evidence_details: mergeReceiptEvidence.invalid_evidence,
2057
+ invalid_merge_receipt_evidence_details_truncated: mergeReceiptEvidence.invalid_evidence_truncated,
2056
2058
  lossless_merge_receipt_items: [
2057
2059
  ...new Set(losslessMergeReceipts.map((receipt) => receipt.item_id)),
2058
2060
  ].sort((left, right) => left.localeCompare(right)),
@@ -2273,4 +2275,4 @@ export async function runValidate(options, global, services = {}) {
2273
2275
  }
2274
2276
  /* c8 ignore stop */
2275
2277
  //# sourceMappingURL=validate.js.map
2276
- //# debugId=cde10e58-cfeb-5718-a471-3e4acf46cfe7
2278
+ //# debugId=0d466293-869d-54bb-b07d-facb3be51e8c
@@ -81,6 +81,7 @@ export * from "./environment/source-context.js";
81
81
  export * from "./environment/host-environment-errors.js";
82
82
  export * from "./cli-contracts/command-exit-contracts.js";
83
83
  export * from "./context-relevance.js";
84
+ export * from "./context/recency.js";
84
85
  export * from "./context-intent-contracts.js";
85
86
  export * from "./context-intent-runtime.js";
86
87
  export * from "./context-signal-store.js";
@@ -94,6 +95,7 @@ export * from "./read-output-session.js";
94
95
  export * from "./output-token-accounting.js";
95
96
  export * from "./context-packing.js";
96
97
  export * from "./context-usage.js";
98
+ export { classifyHistoryEvent, HISTORY_EVENT_CLASSIFICATION_VERSION, MAINTENANCE_HISTORY_OPERATIONS, SUBSTANTIVE_HISTORY_OPERATIONS, type HistoryEventClass, } from "../core/history/event-classification.js";
97
99
  export * from "./dependency-provenance.js";
98
100
  export * from "./duplicates.js";
99
101
  export * from "./lifecycle/reopen.js";