@unbrained/pm-cli 2026.8.27 → 2026.8.29

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 (148) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/CHANGELOG.md +49 -1
  3. package/dist/cli/error-guidance.js +61 -7
  4. package/dist/cli/register-annotations.js +58 -41
  5. package/dist/cli-bundle/bundle-manifest.json +142 -142
  6. package/dist/cli-bundle/chunks/{chunk-TNX6HC54.js → chunk-2MKLQL2E.js} +2 -2
  7. package/dist/cli-bundle/chunks/{chunk-SKXLJIEK.js → chunk-2UETTHY6.js} +49 -49
  8. package/dist/cli-bundle/chunks/{chunk-SHMDY36D.js → chunk-3TEF3HM5.js} +2 -2
  9. package/dist/cli-bundle/chunks/{chunk-SH6P7FXI.js → chunk-7SKMLSMX.js} +2 -2
  10. package/dist/cli-bundle/chunks/chunk-7X6OZEIV.js +34 -0
  11. package/dist/cli-bundle/chunks/{chunk-CVBBGWW5.js → chunk-CMIEDWAY.js} +54 -50
  12. package/dist/cli-bundle/chunks/{chunk-3OO3W6FW.js → chunk-GNWPTEBW.js} +64 -61
  13. package/dist/cli-bundle/chunks/chunk-MTTKAUBX.js +2 -0
  14. package/dist/cli-bundle/chunks/chunk-N6OR6FAR.js +13 -0
  15. package/dist/cli-bundle/chunks/{chunk-R4ETAOJC.js → chunk-TGNAA66W.js} +2 -2
  16. package/dist/cli-bundle/chunks/{chunk-52EKTW6V.js → chunk-WS2GJQVG.js} +2 -2
  17. package/dist/cli-bundle/chunks/{register-list-query-EUWM6VII.js → register-list-query-OMKJ5ONN.js} +2 -2
  18. package/dist/cli-bundle/chunks/{register-mutation-FD4HSAVU.js → register-mutation-QBSNCOY3.js} +4 -4
  19. package/dist/cli-bundle/chunks/{register-operations-HRMNFEC3.js → register-operations-UC77FQZC.js} +2 -2
  20. package/dist/cli-bundle/chunks/{register-setup-33GNICLX.js → register-setup-GLQ5SYV3.js} +2 -2
  21. package/dist/cli-bundle/focused-chunks/{chunk-HC7ODMH3.js → chunk-3AVAWVZA.js} +2 -2
  22. package/dist/cli-bundle/focused-chunks/chunk-3UMBF26I.js +158 -0
  23. package/dist/cli-bundle/focused-chunks/{chunk-YVVZ3LQ6.js → chunk-73EDXGYE.js} +16 -15
  24. package/dist/cli-bundle/focused-chunks/{chunk-4BR5UU52.js → chunk-AXZ5AJAF.js} +45 -45
  25. package/dist/cli-bundle/focused-chunks/{chunk-MXTYGECH.js → chunk-G6PATS5E.js} +3 -3
  26. package/dist/cli-bundle/focused-chunks/{chunk-SUBSWYW3.js → chunk-JZYPPMXF.js} +2 -2
  27. package/dist/cli-bundle/focused-chunks/chunk-LLNTHF5X.js +2 -0
  28. package/dist/cli-bundle/focused-chunks/chunk-LYFWQMVC.js +2 -0
  29. package/dist/cli-bundle/focused-chunks/chunk-RKOKG43G.js +2 -0
  30. package/dist/cli-bundle/focused-chunks/chunk-TDK3OCX3.js +2 -0
  31. package/dist/cli-bundle/focused-chunks/{chunk-EKX37ZHA.js → chunk-THEPQMLX.js} +2 -2
  32. package/dist/cli-bundle/focused-chunks/{chunk-FC2AXLB5.js → chunk-XXOWRJZ5.js} +2 -2
  33. package/dist/cli-bundle/main.js +14 -14
  34. package/dist/cli-bundle/sdk-authoring.js +1 -1
  35. package/dist/cli-bundle/sdk-contracts.js +2 -2
  36. package/dist/cli-bundle/sdk-core.js +31 -32
  37. package/dist/cli-bundle/sdk-governance.js +1 -1
  38. package/dist/cli-bundle/sdk-graph.js +1 -1
  39. package/dist/cli-bundle/sdk-merge.js +34 -32
  40. package/dist/cli-bundle/sdk-query.js +1 -1
  41. package/dist/cli-bundle/sdk-runtime.js +1 -1
  42. package/dist/cli-bundle/sdk-testing.js +1 -1
  43. package/dist/cli-bundle/sdk.js +10 -10
  44. package/dist/core/diagnostics/remediation.js +16 -6
  45. package/dist/core/history/drift-scan.d.ts +2 -0
  46. package/dist/core/history/drift-scan.js +72 -36
  47. package/dist/core/history/history.d.ts +3 -3
  48. package/dist/core/history/history.js +5 -5
  49. package/dist/core/history/replay.d.ts +1 -1
  50. package/dist/core/history/replay.js +50 -19
  51. package/dist/core/item/actionability.d.ts +7 -1
  52. package/dist/core/item/actionability.js +44 -11
  53. package/dist/core/item/dependency-reference.d.ts +10 -0
  54. package/dist/core/item/dependency-reference.js +21 -0
  55. package/dist/core/item/parse.d.ts +4 -0
  56. package/dist/core/item/parse.js +38 -16
  57. package/dist/core/shared/author.js +69 -17
  58. package/dist/mcp/tool-definitions.js +5 -5
  59. package/dist/sdk/agent/command-recovery.js +3 -3
  60. package/dist/sdk/agent/task-transcript-contracts.d.ts +52 -0
  61. package/dist/sdk/agent/task-transcript-contracts.js +198 -0
  62. package/dist/sdk/annotations.d.ts +5 -2
  63. package/dist/sdk/annotations.js +66 -36
  64. package/dist/sdk/cli-bootstrap.d.ts +2 -8
  65. package/dist/sdk/cli-bootstrap.js +7 -67
  66. package/dist/sdk/cli-contracts/bootstrap-command-scanner.d.ts +23 -0
  67. package/dist/sdk/cli-contracts/bootstrap-command-scanner.js +80 -0
  68. package/dist/sdk/cli-contracts/flag-contracts.js +5 -2
  69. package/dist/sdk/cli-contracts/flag-lexicon-contracts.js +5 -5
  70. package/dist/sdk/cli-contracts/tool-parameter-tables.js +15 -2
  71. package/dist/sdk/cli-contracts/tool-schema.d.ts +1 -1
  72. package/dist/sdk/cli-contracts/tool-schema.js +16 -3
  73. package/dist/sdk/comments.d.ts +4 -0
  74. package/dist/sdk/comments.js +2 -2
  75. package/dist/sdk/completion.js +9 -5
  76. package/dist/sdk/contracts.d.ts +1 -0
  77. package/dist/sdk/contracts.js +3 -2
  78. package/dist/sdk/dependency-provenance.d.ts +51 -2
  79. package/dist/sdk/dependency-provenance.js +91 -7
  80. package/dist/sdk/extension/install-sources.d.ts +8 -1
  81. package/dist/sdk/extension/install-sources.js +132 -39
  82. package/dist/sdk/generated/generated-error-code-catalog-part-1.js +74 -14
  83. package/dist/sdk/generated/generated-error-code-catalog-part-2.js +86 -38
  84. package/dist/sdk/governance/health.js +86 -22
  85. package/dist/sdk/governance/validate-history-drift.js +5 -2
  86. package/dist/sdk/graph/assembly.js +51 -22
  87. package/dist/sdk/history-repair.d.ts +1 -1
  88. package/dist/sdk/history-repair.js +20 -10
  89. package/dist/sdk/index.d.ts +1 -0
  90. package/dist/sdk/index.js +3 -2
  91. package/dist/sdk/learnings.d.ts +4 -0
  92. package/dist/sdk/learnings.js +7 -4
  93. package/dist/sdk/lifecycle/close.js +4 -3
  94. package/dist/sdk/lifecycle/create.js +43 -19
  95. package/dist/sdk/linked-artifacts.js +21 -31
  96. package/dist/sdk/merge/reconcile.js +3 -16
  97. package/dist/sdk/notes.d.ts +4 -0
  98. package/dist/sdk/notes.js +2 -2
  99. package/dist/sdk/query/next.d.ts +6 -0
  100. package/dist/sdk/query/next.js +7 -2
  101. package/dist/sdk/runtime-input.js +5 -2
  102. package/dist/sdk/runtime.d.ts +6 -6
  103. package/dist/sdk/runtime.js +8 -8
  104. package/dist/sdk/test/entry.d.ts +1 -1
  105. package/dist/sdk/test/entry.js +5 -9
  106. package/dist/sdk/test/execution.d.ts +3 -0
  107. package/dist/sdk/test/execution.js +54 -13
  108. package/docs/COMMANDS.md +4 -3
  109. package/docs/CONTEXT_RECOVERY_PRIMITIVES.md +67 -0
  110. package/docs/MERGE_SAFETY.md +23 -10
  111. package/docs/OUTPUT_TOKEN_ACCOUNTING.md +20 -7
  112. package/docs/README.md +1 -0
  113. package/docs/RELEASING.md +9 -1
  114. package/docs/SDK.md +12 -0
  115. package/docs/SDK_CONTEXT_INTEGRITY.md +12 -1
  116. package/docs/SDK_RUNTIME_BOUNDARIES.md +10 -0
  117. package/docs/TESTING.md +6 -2
  118. package/docs/agent-task-token-baseline.json +97 -11
  119. package/docs/agent-task-transcripts.json +211 -0
  120. package/docs/generated/FLAG_LEXICON_BUDGETS.md +3 -3
  121. package/docs/generated/REFUSAL_CLOSURE_CENSUS.md +13 -4
  122. package/docs/performance/cli-transport-overhead.md +10 -2
  123. package/marketplace.json +2 -2
  124. package/package.json +8 -8
  125. package/packages/pm-beads/package.json +1 -1
  126. package/packages/pm-calendar/package.json +1 -1
  127. package/packages/pm-command-kit/package.json +1 -1
  128. package/packages/pm-digital-twin/package.json +1 -1
  129. package/packages/pm-governance-audit/package.json +1 -1
  130. package/packages/pm-guide-shell/package.json +1 -1
  131. package/packages/pm-kanban/package.json +1 -1
  132. package/packages/pm-lifecycle-hooks/package.json +1 -1
  133. package/packages/pm-linked-test-adapters/package.json +1 -1
  134. package/packages/pm-search-advanced/package.json +1 -1
  135. package/packages/pm-templates/package.json +1 -1
  136. package/packages/pm-todos/package.json +1 -1
  137. package/packages/pm-vcs/package.json +1 -1
  138. package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
  139. package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
  140. package/sdk/public-surface.json +297 -36
  141. package/dist/cli-bundle/chunks/chunk-MFNTKMTI.js +0 -13
  142. package/dist/cli-bundle/chunks/chunk-OS27HHBN.js +0 -35
  143. package/dist/cli-bundle/chunks/chunk-QTO7USTH.js +0 -2
  144. package/dist/cli-bundle/focused-chunks/chunk-2AGZ5BRT.js +0 -2
  145. package/dist/cli-bundle/focused-chunks/chunk-AQ5IYEZZ.js +0 -2
  146. package/dist/cli-bundle/focused-chunks/chunk-XDPYBQCF.js +0 -156
  147. package/dist/cli-bundle/focused-chunks/chunk-Y5A7SJJ7.js +0 -2
  148. package/dist/cli-bundle/focused-chunks/chunk-YHWHX6YY.js +0 -2
@@ -4,7 +4,7 @@
4
4
  * Defines item parsing, formatting, and lifecycle helpers for Parse.
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]="2252cfb9-37ea-57f1-b7ea-2b0c0f87ecb2")}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]="80965b57-856c-5fef-a372-0c9f3766ada5")}catch(e){}}();
8
8
  import { PmCliError } from "../shared/errors.js";
9
9
  import { EXIT_CODE } from "../shared/constants.js";
10
10
  const STDIN_TOKEN = "-";
@@ -30,6 +30,16 @@ const CONTINUABLE_VALUE_KEYS = new Set([
30
30
  "value",
31
31
  "why_now",
32
32
  ]);
33
+ /** Classify plaintext as structured key/value input using an explicit accepted-key vocabulary. */
34
+ export function looksLikeStructuredKeyValueEntry(raw, keys) {
35
+ if (raw.startsWith("```") || raw.includes("\n")) {
36
+ return true;
37
+ }
38
+ const keyPattern = keys
39
+ .map((key) => key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"))
40
+ .join("|");
41
+ return new RegExp(`^(?:[-*+]\\s+)?(?:${keyPattern})\\s*[:=]`, "i").test(raw);
42
+ }
33
43
  /** Implements parse tags for the public runtime surface of this module. */
34
44
  export function parseTags(raw) {
35
45
  const trimmed = raw.trim();
@@ -188,16 +198,16 @@ function splitCsvSegments(raw) {
188
198
  const segments = [];
189
199
  let current = "";
190
200
  let inQuotes = false;
191
- let escaped = false;
192
- for (const char of raw) {
193
- if (escaped) {
194
- current += char;
195
- escaped = false;
201
+ for (let index = 0; index < raw.length; index += 1) {
202
+ const char = raw[index];
203
+ if (char === "\\" && raw[index + 1] === ",") {
204
+ current += ",";
205
+ index += 1;
196
206
  continue;
197
207
  }
198
- if (char === "\\") {
199
- current += char;
200
- escaped = true;
208
+ if (char === "\\" && raw[index + 1] !== undefined) {
209
+ current += `${char}${raw[index + 1]}`;
210
+ index += 1;
201
211
  continue;
202
212
  }
203
213
  if (char === '"') {
@@ -206,14 +216,14 @@ function splitCsvSegments(raw) {
206
216
  continue;
207
217
  }
208
218
  if (char === "," && !inQuotes) {
209
- segments.push(current.trim());
219
+ segments.push(current);
210
220
  current = "";
211
221
  continue;
212
222
  }
213
223
  current += char;
214
224
  }
215
225
  if (current.trim()) {
216
- segments.push(current.trim());
226
+ segments.push(current);
217
227
  }
218
228
  return segments;
219
229
  }
@@ -235,8 +245,8 @@ function unquoteValue(value) {
235
245
  function findKeyValueDelimiter(segment) {
236
246
  const equalsIndex = segment.indexOf("=");
237
247
  const colonIndex = segment.indexOf(":");
238
- const hasEquals = equalsIndex > 0;
239
- const hasColon = colonIndex > 0;
248
+ const hasEquals = equalsIndex >= 0;
249
+ const hasColon = colonIndex >= 0;
240
250
  if (hasEquals && hasColon) {
241
251
  return Math.min(equalsIndex, colonIndex);
242
252
  }
@@ -324,15 +334,18 @@ export function parseCsvKv(raw, optionName) {
324
334
  const segments = splitCsvSegments(trimmed);
325
335
  for (const segment of segments) {
326
336
  const delimiterIndex = findKeyValueDelimiter(segment);
327
- if (delimiterIndex > 0) {
337
+ if (delimiterIndex >= 0) {
328
338
  const key = segment.slice(0, delimiterIndex).trim();
339
+ if (!key) {
340
+ throw new PmCliError(buildInvalidKvMessage(raw, optionName), EXIT_CODE.USAGE);
341
+ }
329
342
  const value = unquoteValue(segment.slice(delimiterIndex + 1).trim());
330
343
  result[key] = value;
331
344
  activeKey = key;
332
345
  continue;
333
346
  }
334
347
  if (activeKey && CONTINUABLE_VALUE_KEYS.has(activeKey.toLowerCase())) {
335
- result[activeKey] = `${result[activeKey]},${segment.trim()}`;
348
+ result[activeKey] = `${result[activeKey]},${segment}`;
336
349
  continue;
337
350
  }
338
351
  throw new PmCliError(buildInvalidKvMessage(raw, optionName), EXIT_CODE.USAGE);
@@ -358,6 +371,15 @@ export function looksLikeGenericKeyValueEntry(raw) {
358
371
  }
359
372
  return GENERIC_LEADING_KV_KEY_PATTERN.test(trimmed);
360
373
  }
374
+ /** Refuse a comma-bearing bare value whose meaning depends on guessing whether the comma separates values or belongs to one value. */
375
+ export function assertNoAmbiguousBareCommaEntry(raw, optionName, noun, structured) {
376
+ if (structured || !raw.includes(","))
377
+ return;
378
+ throw new PmCliError(`${optionName} received an ambiguous comma-bearing bare ${noun}. Repeat ${optionName} for multiple values, or quote or escape literal commas in a structured key=<value> entry.`, EXIT_CODE.USAGE, {
379
+ code: "bare_comma_entry_ambiguous",
380
+ required: `Use one ${optionName} per bare ${noun}, or quote or escape literal commas in a structured key=<value> entry.`,
381
+ });
382
+ }
361
383
  /**
362
384
  * Reject any key in a parsed CSV/markdown key/value map that is not part of the
363
385
  * caller's allowed-key contract, mirroring the strict `test --add` behavior
@@ -579,4 +601,4 @@ export const _testOnly = {
579
601
  parseMarkdownKeyValueLines,
580
602
  };
581
603
  //# sourceMappingURL=parse.js.map
582
- //# debugId=2252cfb9-37ea-57f1-b7ea-2b0c0f87ecb2
604
+ //# debugId=80965b57-856c-5fef-a372-0c9f3766ada5
@@ -4,7 +4,7 @@
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]="8d89d381-5c6e-5f96-ba03-298fc6db9bba")}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]="cf550961-0040-5e04-9d8d-16e316027e43")}catch(e){}}();
8
8
  import { AsyncLocalStorage } from "node:async_hooks";
9
9
  import { createHash } from "node:crypto";
10
10
  import fs from "node:fs";
@@ -312,20 +312,50 @@ const MAX_PROVENANCE_PROBE_BYTES = 1_048_576;
312
312
  const MAX_CODEX_PROVENANCE_PROBE_BYTES = 4_194_304;
313
313
  const codexProvenanceSnapshotCache = new Map();
314
314
  const EMPTY_CODEX_PROVENANCE_SNAPSHOT = Object.freeze({});
315
- function parseClaudeProvenanceLine(line, dimension) {
315
+ const claudeProvenanceSnapshotCache = new Map();
316
+ const EMPTY_CLAUDE_PROVENANCE_SNAPSHOT = Object.freeze({});
317
+ function asProvenanceRecord(value) {
318
+ return typeof value === "object" && value !== null && !Array.isArray(value)
319
+ ? value
320
+ : undefined;
321
+ }
322
+ function parseClaudeProvenanceLine(line) {
316
323
  let parsed;
317
324
  try {
318
325
  parsed = JSON.parse(line);
319
326
  }
320
327
  catch {
321
- return undefined;
328
+ return EMPTY_CLAUDE_PROVENANCE_SNAPSHOT;
322
329
  }
323
- const record = parsed;
324
- const message = typeof record.message === "object" && record.message !== null
325
- ? record.message
326
- : undefined;
327
- const value = dimension === "model" ? message?.model : record.version;
328
- return nonBlank(value)?.slice(0, 256);
330
+ const record = asProvenanceRecord(parsed);
331
+ if (!record)
332
+ return EMPTY_CLAUDE_PROVENANCE_SNAPSHOT;
333
+ const queue = [
334
+ { value: record, depth: 0 },
335
+ ];
336
+ let visited = 0;
337
+ let model;
338
+ let version;
339
+ while (queue.length > 0 && visited < 64) {
340
+ const current = queue.shift();
341
+ visited += 1;
342
+ model ??= nonBlank(current.value.model)?.slice(0, 256);
343
+ version ??= nonBlank(current.value.version)?.slice(0, 256);
344
+ if (model && version)
345
+ break;
346
+ if (current.depth >= 4)
347
+ continue;
348
+ for (const value of Object.values(current.value)) {
349
+ const nested = asProvenanceRecord(value);
350
+ if (nested) {
351
+ queue.push({
352
+ value: nested,
353
+ depth: current.depth + 1,
354
+ });
355
+ }
356
+ }
357
+ }
358
+ return Object.freeze({ model, version });
329
359
  }
330
360
  function readClaudeSessionProvenance(dimension, signals, env) {
331
361
  const session = nonBlank(env.CLAUDE_CODE_SESSION_ID);
@@ -337,18 +367,40 @@ function readClaudeSessionProvenance(dimension, signals, env) {
337
367
  try {
338
368
  const file = fs.openSync(sessionPath, "r");
339
369
  try {
340
- const size = fs.fstatSync(file).size;
341
- const tailLength = Math.min(size, MAX_PROVENANCE_PROBE_BYTES);
370
+ const stats = fs.fstatSync(file);
371
+ const cacheKey = `${sessionPath}\u0000${stats.size}\u0000${stats.mtimeMs}`;
372
+ const cached = claudeProvenanceSnapshotCache.get(cacheKey);
373
+ if (cached)
374
+ return cached[dimension];
375
+ const probeLength = Math.min(stats.size, MAX_PROVENANCE_PROBE_BYTES);
376
+ const headLength = Math.min(probeLength, Math.ceil(probeLength / 2));
377
+ const tailLength = Math.min(probeLength - headLength, stats.size - headLength);
378
+ const head = Buffer.alloc(headLength);
379
+ fs.readSync(file, head, 0, headLength, 0);
342
380
  const tail = Buffer.alloc(tailLength);
343
- fs.readSync(file, tail, 0, tailLength, Math.max(0, size - tailLength));
344
- const lines = tail.toString("utf8").split("\n").reverse().slice(0, 4_096);
381
+ if (tailLength > 0) {
382
+ fs.readSync(file, tail, 0, tailLength, stats.size - tailLength);
383
+ }
384
+ const snapshot = {};
385
+ const lines = stats.size <= probeLength
386
+ ? Buffer.concat([head, tail]).toString("utf8").split("\n")
387
+ : [
388
+ ...head.toString("utf8").split("\n").slice(0, 4_096),
389
+ ...tail.toString("utf8").split("\n").slice(-4_096),
390
+ ];
345
391
  for (const line of lines) {
346
392
  if (line.length === 0 || line.length > 262_144)
347
393
  continue;
348
- const value = parseClaudeProvenanceLine(line, dimension);
349
- if (value)
350
- return value;
394
+ const parsed = parseClaudeProvenanceLine(line);
395
+ snapshot.model ??= parsed.model;
396
+ snapshot.version ??= parsed.version;
397
+ if (snapshot.model && snapshot.version)
398
+ break;
351
399
  }
400
+ const frozen = Object.freeze(snapshot);
401
+ claudeProvenanceSnapshotCache.clear();
402
+ claudeProvenanceSnapshotCache.set(cacheKey, frozen);
403
+ return frozen[dimension];
352
404
  }
353
405
  finally {
354
406
  fs.closeSync(file);
@@ -1030,4 +1082,4 @@ export function resolveAuthor(candidate, fallback) {
1030
1082
  return identity.author;
1031
1083
  }
1032
1084
  //# sourceMappingURL=author.js.map
1033
- //# debugId=8d89d381-5c6e-5f96-ba03-298fc6db9bba
1085
+ //# debugId=cf550961-0040-5e04-9d8d-16e316027e43
@@ -4,7 +4,7 @@
4
4
  * Defines the Tool Definitions module for pm CLI source.
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]="b69edba8-884f-521c-9bc3-451c7df755a9")}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]="41396cb5-7faf-5642-8746-8d94edb01d86")}catch(e){}}();
8
8
  import { GRAPH_SUBCOMMAND_VALUES, PM_DISCOVERABLE_TOOL_ACTIONS, } from "../sdk/cli-contracts/enum-contracts.js";
9
9
  import { buildPmActionToolInputSchema } from "../sdk/cli-contracts/tool-schema.js";
10
10
  import { RUNTIME_STATUS_ROLE_VALUES } from "../types.js";
@@ -413,7 +413,7 @@ const RAW_TOOLS = [
413
413
  },
414
414
  {
415
415
  name: "pm_comments",
416
- description: "List, add, edit, or delete comments on a pm item. Use options.add to append, options.edit=<1-based index> with replacement text to fix a comment, or options.delete=<1-based index> to remove one. " +
416
+ description: "List, add, edit, or delete comments on a pm item. Use options.add to append, options.edit=<1-based index> with replacement text to fix a comment, or options.delete=<1-based index> to remove one. options.ifAbsent applies only to options.add for retry-safe author-and-text deduplication and cannot combine with options.edit or options.delete. " +
417
417
  "List calls default to the most recent 20 comments with total_count/has_more metadata for token efficiency. " +
418
418
  "Pass options.limit=N to choose a page size, options.limit=0 for summary-only metadata, or options.full=true for full history.",
419
419
  inputSchema: objectSchema({ id: idSchema, options: { type: "object" } }, [
@@ -436,14 +436,14 @@ const RAW_TOOLS = [
436
436
  },
437
437
  {
438
438
  name: "pm_notes",
439
- description: "List or add structured notes on a pm item. Use options.add to append a note; omit it to list existing notes.",
439
+ description: "List, add, edit, or delete structured notes on a pm item. options.ifAbsent applies only to options.add for retry-safe author-and-text deduplication and cannot combine with options.edit or options.delete. Omit mutation options to list existing notes.",
440
440
  inputSchema: objectSchema({ id: idSchema, options: { type: "object" } }, [
441
441
  "id",
442
442
  ]),
443
443
  },
444
444
  {
445
445
  name: "pm_learnings",
446
- description: "List or add learnings on a pm item. Use options.add to capture a learning/insight; omit it to list existing learnings.",
446
+ description: "List, add, edit, or delete learnings on a pm item. options.ifAbsent applies only to options.add for retry-safe author-and-text deduplication and cannot combine with options.edit or options.delete. Omit mutation options to list existing learnings.",
447
447
  inputSchema: objectSchema({ id: idSchema, options: { type: "object" } }, [
448
448
  "id",
449
449
  ]),
@@ -748,4 +748,4 @@ export function buildMcpToolContracts() {
748
748
  }).sort((left, right) => left.name.localeCompare(right.name));
749
749
  }
750
750
  //# sourceMappingURL=tool-definitions.js.map
751
- //# debugId=b69edba8-884f-521c-9bc3-451c7df755a9
751
+ //# debugId=41396cb5-7faf-5642-8746-8d94edb01d86
@@ -6,10 +6,10 @@
6
6
  * supplied arguments are preserved byte-for-token.
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]="4973a995-60f0-5990-9927-371dc6716063")}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]="6084a18e-ccda-57f9-a7d8-45572799c021")}catch(e){}}();
10
10
  import { renderPmCommand } from "../command-line.js";
11
11
  import { resolveSubcommandFlagContractsForCommand } from "../cli-contracts.js";
12
- import { parseBootstrapCommandName } from "../cli-bootstrap.js";
12
+ import { parseBootstrapCommandName } from "../cli-contracts/bootstrap-command-scanner.js";
13
13
  function normalizeMissingFlag(label) {
14
14
  const match = label.trim().match(/--[a-z0-9][a-z0-9_-]*/i);
15
15
  return match?.[0]?.replaceAll("_", "-").toLowerCase();
@@ -57,4 +57,4 @@ export function renderMissingOptionRetry(invocationArgv, commandName, missingLab
57
57
  : renderPmCommand(retry);
58
58
  }
59
59
  //# sourceMappingURL=command-recovery.js.map
60
- //# debugId=4973a995-60f0-5990-9927-371dc6716063
60
+ //# debugId=6084a18e-ccda-57f9-a7d8-45572799c021
@@ -0,0 +1,52 @@
1
+ import { type PmOutputEnvelopeKind } from "../output-contracts.js";
2
+ /** Stable schema version for replayable agent-task transcript corpora. */
3
+ export declare const PM_AGENT_TASK_TRANSCRIPT_VERSION: 2;
4
+ /** Supported strategies for measuring one transcript step's output cost. */
5
+ export declare const PM_AGENT_TASK_ACCOUNTING_MODES: readonly ["self_reported", "independent_transport"];
6
+ /** Token-accounting strategy a replayed transcript step must preserve. */
7
+ export type PmAgentTaskAccountingMode = (typeof PM_AGENT_TASK_ACCOUNTING_MODES)[number];
8
+ /** Scalar value that a replayed transcript must observe at one output path. */
9
+ export type PmAgentTaskExpectedFieldValue = string | number | boolean | null;
10
+ /** Output family expected from one replayed agent-task step. */
11
+ export type PmAgentTaskStepOutputKind = PmOutputEnvelopeKind | "refusal";
12
+ /** Declares one real CLI invocation within a replayable agent task. */
13
+ export interface PmAgentTaskTranscriptStep {
14
+ /** Stable step identifier unique within its task. */
15
+ id: string;
16
+ /** Shell-free CLI arguments, excluding the pm executable, preserved exactly. */
17
+ args: readonly string[];
18
+ /** Process status required for the step to satisfy the transcript. */
19
+ expected_exit_code: number;
20
+ /** Semantic output family required from the transport. */
21
+ expected_output_kind: PmAgentTaskStepOutputKind;
22
+ /** Accounting strategy required from the measured transport. */
23
+ expected_accounting_mode: PmAgentTaskAccountingMode;
24
+ /** Dot-separated own-property paths proving required context was consumed. */
25
+ required_fields: readonly string[];
26
+ /** Exact terminal scalar values required at selected output paths. */
27
+ expected_field_values?: Readonly<Record<string, PmAgentTaskExpectedFieldValue>>;
28
+ /** Stable diagnostic code required from a refusal step. */
29
+ expected_error_code?: string;
30
+ /** Exact command or flag required in a refusal identity. */
31
+ expected_refusal_surface?: string;
32
+ /** Earlier refusal step whose recovery this successful step executes. */
33
+ recovery_for?: string;
34
+ }
35
+ /** Versioned multi-step agent workflow suitable for deterministic replay. */
36
+ export interface PmAgentTaskTranscript {
37
+ /** Stable task identifier used by baselines and reports. */
38
+ id: string;
39
+ /** Human-readable task outcome represented by the transcript. */
40
+ description: string;
41
+ /** Ordered invocations whose shared workspace state completes the task. */
42
+ steps: readonly PmAgentTaskTranscriptStep[];
43
+ }
44
+ /** Published collection of replayable agent workflows. */
45
+ export interface PmAgentTaskTranscriptCorpus {
46
+ /** Schema version governing every transcript in the corpus. */
47
+ version: typeof PM_AGENT_TASK_TRANSCRIPT_VERSION;
48
+ /** Complete ordered task set evaluated by the release gate. */
49
+ tasks: readonly PmAgentTaskTranscript[];
50
+ }
51
+ /** Parse and fail closed on malformed or internally inconsistent transcripts. */
52
+ export declare function parsePmAgentTaskTranscriptCorpus(value: unknown): PmAgentTaskTranscriptCorpus;
@@ -0,0 +1,198 @@
1
+ /**
2
+ * @module sdk/agent-task-transcript-contracts
3
+ *
4
+ * Defines and validates replayable agent-task transcripts without adding the
5
+ * SDK-only parser to ordinary CLI command bundles.
6
+ */
7
+
8
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="006109f8-cb09-5a91-8929-7ac7521753b6")}catch(e){}}();
9
+ import { isPmSuccessfulExitCode, resolvePmCommandExitContract, } from "../cli-contracts/command-exit-contracts.js";
10
+ import { parseBootstrapCommandName } from "../cli-contracts/bootstrap-command-scanner.js";
11
+ import { PM_OUTPUT_ENVELOPE_KINDS, resolvePmCommandOutputEnvelope, } from "../output-contracts.js";
12
+ /** Stable schema version for replayable agent-task transcript corpora. */
13
+ export const PM_AGENT_TASK_TRANSCRIPT_VERSION = 2;
14
+ /** Supported strategies for measuring one transcript step's output cost. */
15
+ export const PM_AGENT_TASK_ACCOUNTING_MODES = [
16
+ "self_reported",
17
+ "independent_transport",
18
+ ];
19
+ function isRecord(value) {
20
+ return typeof value === "object" && value !== null && !Array.isArray(value);
21
+ }
22
+ function readTranscriptString(value, field) {
23
+ if (typeof value !== "string" || value.trim().length === 0) {
24
+ throw new TypeError(`${field} must be a non-empty string`);
25
+ }
26
+ return value.trim();
27
+ }
28
+ function readTranscriptStringArray(value, field, preserveWhitespace = false) {
29
+ if (!Array.isArray(value) || value.length === 0) {
30
+ throw new TypeError(`${field} must be a non-empty string array`);
31
+ }
32
+ return value.map((entry, index) => {
33
+ const normalized = readTranscriptString(entry, `${field}[${String(index)}]`);
34
+ return preserveWhitespace ? entry : normalized;
35
+ });
36
+ }
37
+ function readOptionalTranscriptString(value, field) {
38
+ return value === undefined ? undefined : readTranscriptString(value, field);
39
+ }
40
+ function readExpectedFieldValues(value, field) {
41
+ if (value === undefined)
42
+ return undefined;
43
+ if (!isRecord(value) || Object.keys(value).length === 0) {
44
+ throw new TypeError(`${field} must be a non-empty object`);
45
+ }
46
+ const entries = Object.entries(value).map(([path, expectedValue]) => {
47
+ if (path.length === 0 ||
48
+ path.split(".").some((segment) => segment.length === 0)) {
49
+ throw new TypeError(`${field} must use dot-separated own-property paths`);
50
+ }
51
+ if (expectedValue !== null &&
52
+ typeof expectedValue !== "string" &&
53
+ typeof expectedValue !== "boolean" &&
54
+ (typeof expectedValue !== "number" || !Number.isFinite(expectedValue))) {
55
+ throw new TypeError(`${field} values must be JSON primitives`);
56
+ }
57
+ return [path, expectedValue];
58
+ });
59
+ return Object.fromEntries(entries);
60
+ }
61
+ function assertTranscriptStepOutputContract(field, args, exitCode, outputKind, errorCode, refusalSurface) {
62
+ if (outputKind === "refusal") {
63
+ if (isPmSuccessfulExitCode(exitCode) ||
64
+ errorCode === undefined ||
65
+ refusalSurface === undefined) {
66
+ throw new TypeError(`${field} refusal steps require a non-success exit, error code, and refusal surface`);
67
+ }
68
+ return;
69
+ }
70
+ if (errorCode !== undefined || refusalSurface !== undefined) {
71
+ throw new TypeError(`${field} only refusal steps may declare expected_error_code or expected_refusal_surface`);
72
+ }
73
+ const command = parseBootstrapCommandName(args);
74
+ if (command === undefined) {
75
+ throw new TypeError(`${field}.args must identify a command after global flags`);
76
+ }
77
+ const exitContract = resolvePmCommandExitContract(command);
78
+ if (!isPmSuccessfulExitCode(exitCode) ||
79
+ (exitCode !== 0 &&
80
+ !exitContract?.exit_codes.some((declaredExitCode) => declaredExitCode === exitCode))) {
81
+ throw new TypeError(`${field} successful output must use exit code 0 or a command-declared successful effect exit`);
82
+ }
83
+ const declaredKind = resolvePmCommandOutputEnvelope(command).kind;
84
+ if (outputKind !== declaredKind) {
85
+ throw new TypeError(`${field} expected ${outputKind}, but ${command} declares ${declaredKind}`);
86
+ }
87
+ }
88
+ function parseAgentTaskTranscriptStep(value, taskId, index) {
89
+ if (!isRecord(value)) {
90
+ throw new TypeError(`tasks.${taskId}.steps[${String(index)}] must be an object`);
91
+ }
92
+ const field = `tasks.${taskId}.steps[${String(index)}]`;
93
+ const id = readTranscriptString(value.id, `${field}.id`);
94
+ const args = readTranscriptStringArray(value.args, `${field}.args`, true);
95
+ const expectedExitCode = value.expected_exit_code;
96
+ if (typeof expectedExitCode !== "number" ||
97
+ !Number.isSafeInteger(expectedExitCode)) {
98
+ throw new TypeError(`${field}.expected_exit_code must be a safe integer`);
99
+ }
100
+ const rawOutputKind = value.expected_output_kind;
101
+ if (rawOutputKind !== "refusal" &&
102
+ !PM_OUTPUT_ENVELOPE_KINDS.includes(rawOutputKind)) {
103
+ throw new TypeError(`${field}.expected_output_kind is unsupported`);
104
+ }
105
+ const expectedOutputKind = rawOutputKind;
106
+ const rawAccountingMode = value.expected_accounting_mode;
107
+ if (!PM_AGENT_TASK_ACCOUNTING_MODES.includes(rawAccountingMode)) {
108
+ throw new TypeError(`${field}.expected_accounting_mode is unsupported`);
109
+ }
110
+ const expectedAccountingMode = rawAccountingMode;
111
+ if (expectedAccountingMode === "independent_transport" &&
112
+ expectedOutputKind !== "refusal") {
113
+ throw new TypeError(`${field} independent_transport accounting is supported only for refusal steps`);
114
+ }
115
+ const requiredFields = readTranscriptStringArray(value.required_fields, `${field}.required_fields`);
116
+ if (requiredFields.some((requiredField) => requiredField.split(".").some((segment) => segment.length === 0))) {
117
+ throw new TypeError(`${field}.required_fields must contain dot-separated own-property paths`);
118
+ }
119
+ const expectedFieldValues = readExpectedFieldValues(value.expected_field_values, `${field}.expected_field_values`);
120
+ const expectedErrorCode = readOptionalTranscriptString(value.expected_error_code, `${field}.expected_error_code`);
121
+ const expectedRefusalSurface = readOptionalTranscriptString(value.expected_refusal_surface, `${field}.expected_refusal_surface`);
122
+ const recoveryFor = readOptionalTranscriptString(value.recovery_for, `${field}.recovery_for`);
123
+ assertTranscriptStepOutputContract(field, args, expectedExitCode, expectedOutputKind, expectedErrorCode, expectedRefusalSurface);
124
+ return {
125
+ id,
126
+ args,
127
+ expected_exit_code: expectedExitCode,
128
+ expected_output_kind: expectedOutputKind,
129
+ expected_accounting_mode: expectedAccountingMode,
130
+ required_fields: requiredFields,
131
+ ...(expectedFieldValues
132
+ ? { expected_field_values: expectedFieldValues }
133
+ : {}),
134
+ ...(expectedErrorCode ? { expected_error_code: expectedErrorCode } : {}),
135
+ ...(expectedRefusalSurface
136
+ ? { expected_refusal_surface: expectedRefusalSurface }
137
+ : {}),
138
+ ...(recoveryFor ? { recovery_for: recoveryFor } : {}),
139
+ };
140
+ }
141
+ function parseAgentTaskTranscript(value, index) {
142
+ if (!isRecord(value)) {
143
+ throw new TypeError(`tasks[${String(index)}] must be an object`);
144
+ }
145
+ const id = readTranscriptString(value.id, `tasks[${String(index)}].id`);
146
+ const description = readTranscriptString(value.description, `tasks.${id}.description`);
147
+ if (!Array.isArray(value.steps) || value.steps.length === 0) {
148
+ throw new TypeError(`tasks.${id}.steps must be a non-empty array`);
149
+ }
150
+ const steps = value.steps.map((step, stepIndex) => parseAgentTaskTranscriptStep(step, id, stepIndex));
151
+ const priorSteps = new Map();
152
+ for (const step of steps) {
153
+ if (priorSteps.has(step.id)) {
154
+ throw new TypeError(`tasks.${id} contains duplicate step id ${step.id}`);
155
+ }
156
+ if (step.recovery_for !== undefined) {
157
+ const refusal = priorSteps.get(step.recovery_for);
158
+ if (refusal?.expected_output_kind !== "refusal" ||
159
+ step.expected_output_kind === "refusal") {
160
+ throw new TypeError(`tasks.${id}.${step.id} recovery_for must reference an earlier refusal and declare successful output`);
161
+ }
162
+ }
163
+ priorSteps.set(step.id, step);
164
+ }
165
+ if (steps.at(-1)?.expected_output_kind === "refusal") {
166
+ throw new TypeError(`tasks.${id} completed task must terminate with successful output`);
167
+ }
168
+ for (const step of steps) {
169
+ if (step.expected_output_kind === "refusal" &&
170
+ !steps.some((candidate) => candidate.recovery_for === step.id)) {
171
+ throw new TypeError(`tasks.${id}.${step.id} refusal must have a later successful recovery_for step`);
172
+ }
173
+ }
174
+ return { id, description, steps };
175
+ }
176
+ /** Parse and fail closed on malformed or internally inconsistent transcripts. */
177
+ export function parsePmAgentTaskTranscriptCorpus(value) {
178
+ if (!isRecord(value)) {
179
+ throw new TypeError("agent-task transcript corpus must be an object");
180
+ }
181
+ if (value.version !== PM_AGENT_TASK_TRANSCRIPT_VERSION) {
182
+ throw new TypeError(`agent-task transcript corpus version must be ${String(PM_AGENT_TASK_TRANSCRIPT_VERSION)}`);
183
+ }
184
+ if (!Array.isArray(value.tasks) || value.tasks.length === 0) {
185
+ throw new TypeError("agent-task transcript corpus tasks must be non-empty");
186
+ }
187
+ const tasks = value.tasks.map((task, index) => parseAgentTaskTranscript(task, index));
188
+ const taskIds = new Set();
189
+ for (const task of tasks) {
190
+ if (taskIds.has(task.id)) {
191
+ throw new TypeError(`duplicate agent-task transcript id ${task.id}`);
192
+ }
193
+ taskIds.add(task.id);
194
+ }
195
+ return { version: PM_AGENT_TASK_TRANSCRIPT_VERSION, tasks };
196
+ }
197
+ //# sourceMappingURL=task-transcript-contracts.js.map
198
+ //# debugId=006109f8-cb09-5a91-8929-7ac7521753b6
@@ -25,6 +25,8 @@ export interface AnnotationCommandOptions {
25
25
  includeMeta?: boolean;
26
26
  /** Return the complete collection after a mutation instead of the bounded mutation receipt. */
27
27
  fullHistory?: boolean;
28
+ /** Append only when no entry has the same resolved author and text. */
29
+ ifAbsent?: boolean;
28
30
  }
29
31
  /** Identifies the single annotation mutation represented by a bounded response. */
30
32
  export interface AnnotationMutationReceipt {
@@ -32,8 +34,8 @@ export interface AnnotationMutationReceipt {
32
34
  action: "add" | "edit" | "delete";
33
35
  /** Stable one-based collection position targeted by the mutation. */
34
36
  entry_index: number;
35
- /** Number of stored entries changed by the mutation. */
36
- changed_count: 1;
37
+ /** Number of stored entries changed by the mutation attempt. */
38
+ changed_count: 0 | 1;
37
39
  /** Whether the response includes the complete post-mutation collection. */
38
40
  full_history_included: boolean;
39
41
  }
@@ -133,6 +135,7 @@ export type AnnotationCommandResult<TKey extends string, TEntry extends Annotati
133
135
  limit?: number;
134
136
  mutation_receipt?: AnnotationMutationReceipt;
135
137
  omission_receipt?: AnnotationOmissionReceipt;
138
+ changed?: boolean;
136
139
  };
137
140
  /** Implements limit annotation entries for the public runtime surface of this module. */
138
141
  export declare function limitAnnotationEntries<TEntry>(values: TEntry[], limit: number | undefined): TEntry[];