@unbrained/pm-cli 2026.9.2 → 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 (123) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/CHANGELOG.md +17 -0
  3. package/dist/cli/after-command-context-usage.js +16 -3
  4. package/dist/cli-bundle/bundle-manifest.json +154 -154
  5. package/dist/cli-bundle/chunks/{chunk-T6H5BZCP.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-UDW64J2W.js → chunk-L7B7343B.js} +2 -2
  12. package/dist/cli-bundle/chunks/{chunk-FZ4Z2J4F.js → chunk-LENC656S.js} +41 -41
  13. package/dist/cli-bundle/chunks/{chunk-ZLJIFSOE.js → chunk-LISESA7W.js} +33 -33
  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-WFQ7MJTX.js → register-mutation-RVND5NCG.js} +2 -2
  18. package/dist/cli-bundle/chunks/{register-operations-JVTI7JPP.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-O67VDLOT.js → chunk-QKHCSUGJ.js} +42 -42
  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/history/event-classification.d.ts +21 -0
  50. package/dist/core/history/event-classification.js +109 -0
  51. package/dist/core/history/event-index.d.ts +14 -3
  52. package/dist/core/history/event-index.js +543 -88
  53. package/dist/core/history/history.js +47 -18
  54. package/dist/core/history/workspace-history.js +7 -6
  55. package/dist/core/lock/lock.d.ts +22 -2
  56. package/dist/core/lock/lock.js +121 -19
  57. package/dist/core/output/output.js +31 -7
  58. package/dist/core/shared/time.d.ts +2 -0
  59. package/dist/core/shared/time.js +8 -2
  60. package/dist/core/store/item-metadata-query-index.js +5 -5
  61. package/dist/sdk/context/recency.d.ts +9 -0
  62. package/dist/sdk/context/recency.js +34 -0
  63. package/dist/sdk/context/usage-egress.d.ts +9 -0
  64. package/dist/sdk/context/usage-egress.js +30 -0
  65. package/dist/sdk/context-intent-contracts.js +7 -3
  66. package/dist/sdk/context-relevance.d.ts +23 -0
  67. package/dist/sdk/context-relevance.js +94 -23
  68. package/dist/sdk/context-signal-store.d.ts +7 -3
  69. package/dist/sdk/context-signal-store.js +155 -42
  70. package/dist/sdk/context-usage.d.ts +54 -0
  71. package/dist/sdk/context-usage.js +245 -16
  72. package/dist/sdk/generated/generated-error-code-catalog-part-1.js +14 -14
  73. package/dist/sdk/generated/generated-error-code-catalog-part-2.js +14 -2
  74. package/dist/sdk/index.d.ts +2 -0
  75. package/dist/sdk/index.js +4 -2
  76. package/dist/sdk/init.d.ts +2 -0
  77. package/dist/sdk/init.js +18 -9
  78. package/dist/sdk/query/context.d.ts +3 -1
  79. package/dist/sdk/query/context.js +21 -9
  80. package/dist/sdk/query/next.js +18 -9
  81. package/dist/sdk/runtime-input.d.ts +2 -0
  82. package/dist/sdk/runtime-input.js +25 -2
  83. package/dist/sdk/runtime.d.ts +1 -0
  84. package/dist/sdk/runtime.js +17 -26
  85. package/dist/types.d.ts +2 -0
  86. package/dist/types.js +2 -2
  87. package/docs/CONTEXT_RELEVANCE.md +44 -1
  88. package/docs/PERFORMANCE.md +6 -3
  89. package/docs/READ_OUTPUT_CONTRACTS.md +9 -1
  90. package/docs/SDK.md +11 -8
  91. package/docs/SDK_CONTEXT.md +7 -0
  92. package/docs/generated/REFUSAL_CLOSURE_CENSUS.md +4 -3
  93. package/marketplace.json +2 -2
  94. package/package.json +4 -4
  95. package/packages/pm-beads/package.json +1 -1
  96. package/packages/pm-calendar/package.json +1 -1
  97. package/packages/pm-command-kit/package.json +1 -1
  98. package/packages/pm-digital-twin/package.json +1 -1
  99. package/packages/pm-governance-audit/package.json +1 -1
  100. package/packages/pm-guide-shell/package.json +1 -1
  101. package/packages/pm-kanban/package.json +1 -1
  102. package/packages/pm-lifecycle-hooks/package.json +1 -1
  103. package/packages/pm-linked-test-adapters/package.json +1 -1
  104. package/packages/pm-search-advanced/package.json +1 -1
  105. package/packages/pm-templates/package.json +1 -1
  106. package/packages/pm-todos/package.json +1 -1
  107. package/packages/pm-vcs/package.json +1 -1
  108. package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
  109. package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
  110. package/sdk/public-surface.json +241 -24
  111. package/dist/cli-bundle/chunks/chunk-GOACULJW.js +0 -13
  112. package/dist/cli-bundle/chunks/chunk-NOPL37X4.js +0 -205
  113. package/dist/cli-bundle/focused-chunks/chunk-242AXYB2.js +0 -2
  114. package/dist/cli-bundle/focused-chunks/chunk-2FEZ5SKM.js +0 -3
  115. package/dist/cli-bundle/focused-chunks/chunk-6GCRSLPG.js +0 -2
  116. package/dist/cli-bundle/focused-chunks/chunk-7VATS5QY.js +0 -16
  117. package/dist/cli-bundle/focused-chunks/chunk-JEFKLWMM.js +0 -2
  118. package/dist/cli-bundle/focused-chunks/chunk-PF3E6Q36.js +0 -8
  119. package/dist/cli-bundle/focused-chunks/chunk-QDFJIRKN.js +0 -4
  120. package/dist/cli-bundle/focused-chunks/chunk-RMRCQPWU.js +0 -2
  121. package/dist/cli-bundle/focused-chunks/chunk-S4U5CN5Q.js +0 -2
  122. package/dist/cli-bundle/focused-chunks/chunk-T2LXXSOD.js +0 -158
  123. 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]="4734edf6-c174-50d0-b4e9-1003bc8a20bf")}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.",
@@ -2148,18 +2160,6 @@ export const PM_ERROR_CODE_CATALOG_PART_1 = [
2148
2160
  canonical_code: "missing_budget",
2149
2161
  aliases: [],
2150
2162
  },
2151
- {
2152
- code: "missing_cli_flag",
2153
- meaning: "Missing cli flag condition.",
2154
- stability: "provisional",
2155
- exit_code: 1,
2156
- class: "generic_failure",
2157
- recovery: "Inspect the structured error guidance and retry the suggested command.",
2158
- sources: ["sdk/cli-contracts/tool-option-contracts.ts"],
2159
- emitting_commands: ["*"],
2160
- canonical_code: "missing_cli_flag",
2161
- aliases: [],
2162
- },
2163
2163
  ];
2164
2164
  //# sourceMappingURL=generated-error-code-catalog-part-1.js.map
2165
- //# debugId=4734edf6-c174-50d0-b4e9-1003bc8a20bf
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
@@ -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";
package/dist/sdk/index.js CHANGED
@@ -4,7 +4,7 @@
4
4
  * Defines public SDK APIs and package-author helpers for Index.
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]="8a1a02de-32cb-5714-b950-6041ae8ce229")}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]="62e450f7-9ed9-5855-923a-7b2ffb8b49f4")}catch(e){}}();
8
8
  export { PM_COMMAND_CAPABILITY_CONTRACTS, PM_COMMAND_VISIBILITY_CONTRACTS, PM_MCP_PROMPT_CONTRACTS, PM_MCP_RESOURCE_CONTRACTS, PM_MCP_TOOL_COMMAND_CONTRACTS, listPmCommandsForFamily, listPmCommandsForTier, listPmMcpToolsForProfile, renderPmCommandVisibilityMarkdown, resolvePmCommandCapabilityFamily, resolvePmCommandVisibilityTier, } from "./agent-capability-contracts.js";
9
9
  export * from "./mcp/protocol.js";
10
10
  export * from "./mcp/interactions.js";
@@ -78,6 +78,7 @@ export * from "./environment/source-context.js";
78
78
  export * from "./environment/host-environment-errors.js";
79
79
  export * from "./cli-contracts/command-exit-contracts.js";
80
80
  export * from "./context-relevance.js";
81
+ export * from "./context/recency.js";
81
82
  export * from "./context-intent-contracts.js";
82
83
  export * from "./context-intent-runtime.js";
83
84
  export * from "./context-signal-store.js";
@@ -91,6 +92,7 @@ export * from "./read-output-session.js";
91
92
  export * from "./output-token-accounting.js";
92
93
  export * from "./context-packing.js";
93
94
  export * from "./context-usage.js";
95
+ export { classifyHistoryEvent, HISTORY_EVENT_CLASSIFICATION_VERSION, MAINTENANCE_HISTORY_OPERATIONS, SUBSTANTIVE_HISTORY_OPERATIONS, } from "../core/history/event-classification.js";
94
96
  export * from "./dependency-provenance.js";
95
97
  export * from "./duplicates.js";
96
98
  export * from "./lifecycle/reopen.js";
@@ -177,4 +179,4 @@ export * from "./mcp/discovery.js";
177
179
  export * from "./query.js";
178
180
  export * from "./testing.js";
179
181
  //# sourceMappingURL=index.js.map
180
- //# debugId=8a1a02de-32cb-5714-b950-6041ae8ce229
182
+ //# debugId=62e450f7-9ed9-5855-923a-7b2ffb8b49f4
@@ -5,6 +5,8 @@ import type { GovernancePreset, PmSettings } from "../types/index.js";
5
5
  import { type ExtensionCommandResult } from "./extension.js";
6
6
  import { type InitAgentGuidanceMode, type InitAgentGuidanceSummary } from "./init-agent-guidance.js";
7
7
  import { installMergeFence } from "./merge/install.js";
8
+ /** Symbol key for the captured working directory of an in-process init call. */
9
+ export declare const INIT_INVOCATION_CWD: unique symbol;
8
10
  /** Documents the init installed packages summary payload exchanged by command, SDK, and package integrations. */
9
11
  export interface InitInstalledPackagesSummary {
10
12
  /** Value that configures or reports installed all for this contract. */
package/dist/sdk/init.js CHANGED
@@ -4,7 +4,7 @@
4
4
  * Implements the pm init 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]="75c0d2d2-6a89-508f-b7dd-b43d55978b44")}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]="d06d4db7-8ab7-5a9f-84df-508bd305ea23")}catch(e){}}();
8
8
  import fs from "node:fs/promises";
9
9
  import path from "node:path";
10
10
  import readline from "node:readline/promises";
@@ -27,6 +27,8 @@ import { renderPmCommand } from "./command-line.js";
27
27
  import { runExtension } from "./extension.js";
28
28
  import { INIT_AGENT_GUIDANCE_MODE_VALUES, runInitAgentGuidance, } from "./init-agent-guidance.js";
29
29
  import { findGitWorkspaceRoot, installMergeFence } from "./merge/install.js";
30
+ /** Symbol key for the captured working directory of an in-process init call. */
31
+ export const INIT_INVOCATION_CWD = Symbol.for("pm.sdk.init-invocation-cwd");
30
32
  /** Implements summarize init result for the public runtime surface of this module. */
31
33
  export function summarizeInitResult(result) {
32
34
  return {
@@ -709,9 +711,21 @@ async function installInitMergeFence(pmRoot, gitWorkspaceRoot, warnings, install
709
711
  warnings.push(`merge_fence_skipped:install_failed:${error.context.code ?? "pm_error"}:${error.message}:run pm merge install after resolving the Git repository error`);
710
712
  }
711
713
  }
714
+ async function ensureInitWorkspaceGitignore(workspaceRoot, pmRoot, agentGuidanceMode, warnings) {
715
+ if (workspaceRoot === undefined ||
716
+ workspaceRoot === null ||
717
+ agentGuidanceMode === "status") {
718
+ return;
719
+ }
720
+ const gitignore = await ensurePmGitignore(workspaceRoot, { pmRoot });
721
+ if (gitignore.changed) {
722
+ warnings.push(`updated:gitignore:${gitignore.path}`);
723
+ }
724
+ }
712
725
  /** Implements run init for the public runtime surface of this module. */
713
726
  export async function runInit(prefixArg, global, options = {}) {
714
- const cwd = process.cwd();
727
+ const invocationCwd = global[INIT_INVOCATION_CWD];
728
+ const cwd = typeof invocationCwd === "string" ? invocationCwd : process.cwd();
715
729
  const invocation = resolveInitInvocation(cwd, global, prefixArg, options.workspace);
716
730
  const pmRoot = invocation.pmRoot;
717
731
  prefixArg = resolveInitPrefixInput(invocation.prefixArg, options.idPrefix);
@@ -773,12 +787,7 @@ export async function runInit(prefixArg, global, options = {}) {
773
787
  settings = await readSettings(pmRoot);
774
788
  }
775
789
  await ensureInitTypeDirectories({ pmRoot, settings, createdDirs, warnings });
776
- if (workspaceRoot && normalizedOptions.agentGuidanceMode !== "status") {
777
- const gitignore = await ensurePmGitignore(workspaceRoot, { pmRoot });
778
- if (gitignore.changed) {
779
- warnings.push(`updated:gitignore:${gitignore.path}`);
780
- }
781
- }
790
+ await ensureInitWorkspaceGitignore(workspaceRoot, pmRoot, normalizedOptions.agentGuidanceMode, warnings);
782
791
  if (!settingsExists &&
783
792
  options.mergeFence !== false &&
784
793
  normalizedOptions.agentGuidanceMode !== "status") {
@@ -809,4 +818,4 @@ export async function runInit(prefixArg, global, options = {}) {
809
818
  };
810
819
  }
811
820
  //# sourceMappingURL=init.js.map
812
- //# debugId=75c0d2d2-6a89-508f-b7dd-b43d55978b44
821
+ //# debugId=d06d4db7-8ab7-5a9f-84df-508bd305ea23
@@ -7,7 +7,7 @@ import { type GlobalOptions, type RuntimeStatusRegistry } from "../runtime-primi
7
7
  import type { ContextDepth, ContextSectionName, ContextSettings, ItemMetadata, ItemStatus, PmSettings } from "../../types/index.js";
8
8
  import { type CalendarRow } from "./calendar.js";
9
9
  import { type CompactActivityEntry } from "./activity.js";
10
- import { type ContextRelevanceContributions, type ContextRelevanceReport, type ContextRelevanceSignalName } from "../context-relevance.js";
10
+ import { type ContextRelevanceContributions, type ContextRecencyEvidence, type ContextRelevanceReport, type ContextRelevanceSignalName } from "../context-relevance.js";
11
11
  export { buildItemContextRelevanceCandidates } from "../context-relevance.js";
12
12
  import { type ContextPackingReport } from "../context-packing.js";
13
13
  import { type ContextSignalStoreReadResult } from "../context-signal-store.js";
@@ -398,6 +398,8 @@ export interface ContextRankingSummary {
398
398
  baseline_rank: number;
399
399
  score: number;
400
400
  contributions: ContextRelevanceContributions;
401
+ /** Authoritative temporal source used for the recency contribution. */
402
+ recency?: ContextRecencyEvidence;
401
403
  }>;
402
404
  /** Number of candidates considered before answer-sized projection. */
403
405
  candidate_count: number;
@@ -4,7 +4,7 @@
4
4
  * Implements the pm context 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]="a870e3ac-f731-5cbf-a66e-38d1d5b1d0aa")}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]="56205e5f-fc26-59ab-abdf-45bdcaf1c263")}catch(e){}}();
8
8
  import { SETTINGS_DEFAULTS, EXIT_CODE, PmCliError, compareTimestampStrings, nowIso, isTerminalStatus, normalizeStatusForRegistry, normalizeStatusInput, resolveRuntimeStatusRegistry, resolvePmRoot, readSettings, resolveAuthor, discoverExtensions, } from "../runtime-primitives.js";
9
9
  import { collectDependencyBlockedIds, resolveItemBlockers, } from "../actionability.js";
10
10
  import { CONTEXT_DEPTH_VALUES, CONTEXT_SECTION_VALUES, } from "../../types/index.js";
@@ -17,7 +17,7 @@ export { buildItemContextRelevanceCandidates } from "../context-relevance.js";
17
17
  import { createQueryFingerprint, encodeQueryCursor, resolveQueryCursorStart, selectCursorSemanticOptions, } from "../pagination.js";
18
18
  import { CONTEXT_FLAG_CONTRACTS } from "../cli-contracts/flag-contracts.js";
19
19
  import { packContextCandidates, } from "../context-packing.js";
20
- import { readContextUsageAffinity, recordContextUsageServing, } from "../context-usage.js";
20
+ import { attachContextUsageServingReceipt, collectContextUsageDeliveredItemIds, readContextUsageAffinity, recordContextUsageServing, } from "../context-usage.js";
21
21
  import { readWorkspaceContextSignals, } from "../context-signal-store.js";
22
22
  import { readWorkspaceMemory, selectWorkspaceMemory, } from "../workspace-memory.js";
23
23
  import { detectAgentIdentity } from "../../core/shared/author.js";
@@ -465,6 +465,9 @@ export function toContextRankingSummary(report, featureStore, servedItemIds) {
465
465
  baseline_rank: entry.baseline_rank,
466
466
  score: entry.score,
467
467
  contributions: entry.contributions,
468
+ ...(entry.signal_provenance === undefined
469
+ ? {}
470
+ : { recency: entry.signal_provenance.recency }),
468
471
  })),
469
472
  candidate_count: report.ranked.length,
470
473
  omitted_count: report.ranked.length - ranked.length,
@@ -1512,18 +1515,27 @@ function maybeAttachEmptyContextSuggestions(result, activeItems, blockedItems) {
1512
1515
  async function attachContextUsageFeedback(result, focusGroups, pmRoot, author) {
1513
1516
  try {
1514
1517
  const included = new Set(focusGroups.packing.included.map((entry) => entry.id));
1515
- await recordContextUsageServing({
1518
+ const rows = focusGroups.ranking.ranked.map((entry) => ({
1519
+ id: entry.id,
1520
+ rank: entry.rank,
1521
+ included: included.has(entry.id),
1522
+ }));
1523
+ const rankedIds = new Set(rows.map((row) => row.id));
1524
+ for (const id of collectContextUsageDeliveredItemIds(result, "context")) {
1525
+ if (!rankedIds.has(id)) {
1526
+ rows.push({ id, rank: rows.length + 1, included: false });
1527
+ rankedIds.add(id);
1528
+ }
1529
+ }
1530
+ const receipt = await recordContextUsageServing({
1516
1531
  pmRoot,
1517
1532
  author,
1518
1533
  surface: "context",
1519
1534
  profile: focusGroups.packing.profile,
1520
- rows: focusGroups.ranking.ranked.map((entry) => ({
1521
- id: entry.id,
1522
- rank: entry.rank,
1523
- included: included.has(entry.id),
1524
- })),
1535
+ rows,
1525
1536
  enabled: process.env.PM_CONTEXT_USAGE_DISABLED !== "1",
1526
1537
  });
1538
+ attachContextUsageServingReceipt(result, receipt);
1527
1539
  }
1528
1540
  catch {
1529
1541
  result.warnings = mergeSortedWarnings(result.warnings, [
@@ -1676,4 +1688,4 @@ export async function runContext(options, global) {
1676
1688
  return result;
1677
1689
  }
1678
1690
  //# sourceMappingURL=context.js.map
1679
- //# debugId=a870e3ac-f731-5cbf-a66e-38d1d5b1d0aa
1691
+ //# debugId=56205e5f-fc26-59ab-abdf-45bdcaf1c263