akm-cli 0.9.31 → 0.10.261002-beta

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 (270) hide show
  1. package/CHANGELOG.md +420 -0
  2. package/STABILITY.md +99 -57
  3. package/dist/api-node.mjs +20 -0
  4. package/dist/api.d.ts +23 -0
  5. package/dist/api.js +45 -0
  6. package/dist/assets/hints/cli-hints-full.md +15 -29
  7. package/dist/assets/hints/cli-hints-short.md +2 -2
  8. package/dist/assets/improve-strategies/default.json +3 -3
  9. package/dist/assets/prompts/consolidate-pair.md +9 -7
  10. package/dist/assets/stash-skeleton/README.md +1 -1
  11. package/dist/assets/tasks/improve/akm-improve-consolidate.yml +1 -1
  12. package/dist/cli/shared.js +31 -48
  13. package/dist/cli/unknown-flags.js +6 -43
  14. package/dist/cli.js +8 -34
  15. package/dist/commands/agent/contribute-cli.js +1 -1
  16. package/dist/commands/completions.js +1 -2
  17. package/dist/commands/config-cli.js +5 -28
  18. package/dist/commands/env/env-cli.js +5 -5
  19. package/dist/commands/env/secret-cli.js +2 -2
  20. package/dist/commands/feedback-cli.js +75 -49
  21. package/dist/commands/health/checks.js +41 -16
  22. package/dist/commands/health/plugin-staleness.js +52 -22
  23. package/dist/commands/improve/autonomy-gate.js +5 -3
  24. package/dist/commands/improve/consolidate/chunking.js +3 -10
  25. package/dist/commands/improve/consolidate/continuity-check.js +4 -4
  26. package/dist/commands/improve/consolidate/pair-pass.js +29 -10
  27. package/dist/commands/improve/consolidate.js +10 -65
  28. package/dist/commands/improve/distill/lesson-update.js +91 -0
  29. package/dist/commands/improve/distill-guards.js +1 -43
  30. package/dist/commands/improve/distill.js +148 -21
  31. package/dist/commands/improve/eligibility.js +14 -1
  32. package/dist/commands/improve/extract-cli.js +1 -1
  33. package/dist/commands/improve/extract-prompt.js +17 -7
  34. package/dist/commands/improve/extract.js +5 -7
  35. package/dist/commands/improve/improve-cli.js +92 -106
  36. package/dist/commands/improve/improve-strategies.js +8 -12
  37. package/dist/commands/improve/improve.js +134 -52
  38. package/dist/commands/improve/loop-stages.js +18 -5
  39. package/dist/commands/improve/planner.js +0 -1
  40. package/dist/commands/improve/preparation.js +40 -99
  41. package/dist/commands/improve/proactive-maintenance.js +2 -2
  42. package/dist/commands/improve/reflect-noise.js +3 -55
  43. package/dist/commands/improve/reflect.js +29 -34
  44. package/dist/commands/improve/retrieval-queries.js +37 -0
  45. package/dist/commands/improve/retrieval-scope.js +2 -2
  46. package/dist/commands/improve/salience.js +2 -9
  47. package/dist/commands/improve/session-asset.js +4 -1
  48. package/dist/commands/improve/stage.js +23 -12
  49. package/dist/commands/lint/index.js +5 -20
  50. package/dist/commands/observability-cli.js +3 -9
  51. package/dist/commands/proposal/drain.js +43 -6
  52. package/dist/commands/proposal/proposal-cli.js +11 -31
  53. package/dist/commands/proposal/repository.js +20 -14
  54. package/dist/commands/proposal/validators/proposal-quality-validators.js +0 -3
  55. package/dist/commands/read/knowledge.js +3 -3
  56. package/dist/commands/read/remember-cli.js +0 -13
  57. package/dist/commands/read/search-cli.js +29 -97
  58. package/dist/commands/read/search.js +0 -11
  59. package/dist/commands/read/show.js +0 -19
  60. package/dist/commands/sources/plugin-upgrade.js +164 -101
  61. package/dist/commands/sources/sources-cli.js +1 -15
  62. package/dist/commands/sources/stash-cli.js +6 -14
  63. package/dist/commands/tasks/tasks-cli.js +4 -115
  64. package/dist/commands/workflow/plan.js +11 -33
  65. package/dist/commands/workflow-cli.js +2 -7
  66. package/dist/core/adapter/adapter-ids.js +1 -2
  67. package/dist/core/adapter/adapters/agent-skills-adapter.js +0 -4
  68. package/dist/core/adapter/adapters/akm-adapter.js +9 -48
  69. package/dist/core/adapter/adapters/akm-lint.js +10 -26
  70. package/dist/core/adapter/adapters/akm-metadata.js +2 -72
  71. package/dist/core/adapter/adapters/akm-task-adapter.js +0 -5
  72. package/dist/core/adapter/adapters/akm-workflow-adapter.js +9 -28
  73. package/dist/core/adapter/adapters/dotenv-adapter.js +1 -20
  74. package/dist/core/adapter/adapters/generic-files-adapter.js +0 -10
  75. package/dist/core/adapter/adapters/llm-wiki-adapter.js +0 -4
  76. package/dist/core/adapter/adapters/tool-dir-shared.js +0 -24
  77. package/dist/core/adapter/adapters/website-snapshot-adapter.js +4 -4
  78. package/dist/core/adapter/recognize-match.js +3 -4
  79. package/dist/core/common.js +18 -2
  80. package/dist/core/config/config-schema.js +12 -15
  81. package/dist/core/config/config-types.js +6 -15
  82. package/dist/core/config/config-walker.js +6 -23
  83. package/dist/core/config/config.js +17 -80
  84. package/dist/core/config/engine-semantics.js +7 -3
  85. package/dist/core/config/schema/engines.js +7 -7
  86. package/dist/core/config/schema/improve-processes.js +10 -63
  87. package/dist/core/config/schema/improve.js +2 -21
  88. package/dist/core/config/schema/index-config.js +6 -39
  89. package/dist/core/env-secret-ref.js +2 -45
  90. package/dist/core/errors.js +1 -2
  91. package/dist/core/extra-params.js +2 -86
  92. package/dist/core/file-lock.js +2 -6
  93. package/dist/core/improve-result.js +13 -1
  94. package/dist/core/mutation-target.js +2 -2
  95. package/dist/core/paths.js +4 -0
  96. package/dist/core/recognition-util.js +4 -5
  97. package/dist/core/run-lock.js +1 -1
  98. package/dist/core/subprocess.js +49 -0
  99. package/dist/core/warn.js +21 -4
  100. package/dist/core/write-source.js +3 -3
  101. package/dist/execution/target-ref.js +1 -2
  102. package/dist/indexer/indexer.js +15 -6
  103. package/dist/indexer/passes/memory-inference.js +15 -6
  104. package/dist/indexer/search/db-search.js +49 -36
  105. package/dist/indexer/search/fts-query.js +0 -19
  106. package/dist/indexer/search/ranking.js +29 -1
  107. package/dist/indexer/usage/usage-events.js +21 -16
  108. package/dist/integrations/agent/engine-resolution.js +5 -2
  109. package/dist/integrations/agent/index.js +1 -1
  110. package/dist/integrations/agent/profiles.js +41 -98
  111. package/dist/integrations/agent/request-lowering.js +3 -3
  112. package/dist/integrations/harnesses/aider/index.js +5 -0
  113. package/dist/integrations/harnesses/amazonq/index.js +1 -0
  114. package/dist/integrations/harnesses/claude/config-import.js +2 -3
  115. package/dist/integrations/harnesses/claude/index.js +5 -0
  116. package/dist/integrations/harnesses/codex/index.js +1 -0
  117. package/dist/integrations/harnesses/copilot/index.js +1 -0
  118. package/dist/integrations/harnesses/gemini/index.js +1 -0
  119. package/dist/integrations/harnesses/index.js +18 -39
  120. package/dist/integrations/harnesses/opencode/agent-builder.js +60 -35
  121. package/dist/integrations/harnesses/opencode/config-import.js +121 -52
  122. package/dist/integrations/harnesses/opencode/index.js +22 -0
  123. package/dist/integrations/harnesses/opencode/plugin-layout.js +68 -0
  124. package/dist/integrations/harnesses/opencode/session-log-types.js +12 -0
  125. package/dist/integrations/harnesses/opencode/session-log-v1.js +72 -0
  126. package/dist/integrations/harnesses/opencode/session-log-v2.js +140 -0
  127. package/dist/integrations/harnesses/opencode/session-log.js +104 -115
  128. package/dist/integrations/harnesses/opencode/version.js +98 -0
  129. package/dist/integrations/harnesses/opencode-sdk/{harness.js → index.js} +5 -17
  130. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +105 -206
  131. package/dist/integrations/harnesses/opencode-sdk/v1-adapter.js +199 -0
  132. package/dist/integrations/harnesses/opencode-sdk/v2-adapter.js +254 -0
  133. package/dist/integrations/harnesses/opencode-sdk/wire.js +66 -0
  134. package/dist/integrations/harnesses/openhands/index.js +1 -0
  135. package/dist/integrations/harnesses/pi/index.js +1 -0
  136. package/dist/integrations/harnesses/shared.js +2 -0
  137. package/dist/integrations/harnesses/types.js +2 -0
  138. package/dist/llm/client.js +41 -11
  139. package/dist/output/cli-hints.js +1 -1
  140. package/dist/output/context.js +10 -19
  141. package/dist/output/format-exempt.js +5 -8
  142. package/dist/output/shapes/helpers.js +1 -22
  143. package/dist/output/shapes/passthrough.js +0 -1
  144. package/dist/output/shapes.js +3 -31
  145. package/dist/output/stdout.js +5 -1
  146. package/dist/output/text/command-format.js +6 -1
  147. package/dist/output/text/workflow-format.js +4 -22
  148. package/dist/scripts/akm-migrate-node.js +15745 -16711
  149. package/dist/scripts/akm-migrate.js +16318 -17280
  150. package/dist/setup/detect.js +10 -4
  151. package/dist/setup/harness-config-import.js +5 -14
  152. package/dist/setup/setup.js +1 -1
  153. package/dist/setup/steps/tasks.js +12 -13
  154. package/dist/sources/providers/git-stash.js +113 -16
  155. package/dist/storage/repositories/index-utility-repository.js +2 -32
  156. package/dist/storage/repositories/index-vec-repository.js +4 -0
  157. package/dist/storage/repositories/task-history-repository.js +0 -5
  158. package/dist/storage/repositories/workflow-runs-repository.js +5 -15
  159. package/dist/tasks/embedded.js +2 -1
  160. package/dist/tasks/run/run-command-task.js +9 -1
  161. package/dist/tasks/run/run-workflow-task.js +4 -3
  162. package/dist/tasks/run/task-history.js +0 -2
  163. package/dist/tasks/run/task-log.js +38 -1
  164. package/dist/tasks/schedule-gates.js +39 -0
  165. package/dist/tasks/scheduler-binding.js +0 -16
  166. package/dist/tasks/scheduler-sync.js +3 -97
  167. package/dist/tasks/source/bounded-document.js +1 -1
  168. package/dist/tasks/source/task-source-v4.js +2 -12
  169. package/dist/tasks/task-run-reserved-flags.js +2 -31
  170. package/dist/workflows/authoring/authoring.js +6 -6
  171. package/dist/workflows/compile.js +8 -65
  172. package/dist/workflows/exec/child-workflow.js +22 -3
  173. package/dist/workflows/exec/native-executor.js +12 -147
  174. package/dist/workflows/exec/run-workflow.js +31 -103
  175. package/dist/workflows/exec/step-work.js +9 -178
  176. package/dist/workflows/exec/unit-dispatch.js +0 -10
  177. package/dist/workflows/freeze/environment.js +3 -44
  178. package/dist/workflows/freeze/freeze.js +8 -41
  179. package/dist/workflows/freeze/targets/child-workflow.js +27 -63
  180. package/dist/workflows/freeze/targets/command.js +2 -16
  181. package/dist/workflows/freeze/targets/shell.js +0 -2
  182. package/dist/workflows/freeze/task-bindings.js +2 -28
  183. package/dist/workflows/ir/params.js +12 -2
  184. package/dist/workflows/parser.js +82 -232
  185. package/dist/workflows/plan.js +1 -1
  186. package/dist/workflows/renderer.js +10 -19
  187. package/dist/workflows/runtime/run-plan.js +11 -53
  188. package/dist/workflows/runtime/runs.js +4 -27
  189. package/dist/workflows/source-files.js +12 -16
  190. package/dist/workflows/source-semantics.js +1 -93
  191. package/docs/README.md +1 -1
  192. package/docs/agents/README.md +3 -3
  193. package/docs/agents/agent-install.md +1 -1
  194. package/docs/architecture/0.10-process-rules.md +827 -0
  195. package/docs/architecture/adapters.md +17 -31
  196. package/docs/architecture/architecture.md +4 -5
  197. package/docs/architecture/improvement.md +115 -76
  198. package/docs/architecture/internals/improve-workflow.md +22 -16
  199. package/docs/architecture/internals/indexing.md +1 -1
  200. package/docs/architecture/internals/search.md +1 -2
  201. package/docs/architecture/internals/storage-locations.md +6 -6
  202. package/docs/architecture/persisted-data-compat.md +13 -4
  203. package/docs/architecture/specs/0.9.0-decisions.md +6 -2
  204. package/docs/architecture/specs/improve-collapse-churn-detector-design.md +2 -2
  205. package/docs/architecture/specs/workflow-format-unification.md +1 -6
  206. package/docs/architecture/testing/manual-testing-checklist.md +21 -30
  207. package/docs/architecture/testing/task-workflow-manual-tests.md +106 -203
  208. package/docs/architecture/testing/testing-workflow.md +1 -1
  209. package/docs/architecture/workflow-engine.md +5 -56
  210. package/docs/guides/author-workflows.md +39 -69
  211. package/docs/guides/bundles.md +3 -1
  212. package/docs/guides/capture-knowledge.md +1 -1
  213. package/docs/guides/claude-code-vs-akm-workflows.md +1 -2
  214. package/docs/guides/discover-and-load.md +4 -4
  215. package/docs/guides/getting-started.md +1 -1
  216. package/docs/guides/improve-the-library.md +10 -9
  217. package/docs/guides/recipes/headless-install.md +2 -2
  218. package/docs/guides/run-workflows.md +1 -1
  219. package/docs/guides/scheduling.md +9 -1
  220. package/docs/guides/use-with-any-agent.md +49 -4
  221. package/docs/integration/bundling-akm.md +7 -6
  222. package/docs/maintainers/curate-workmap.md +1 -2
  223. package/docs/maintainers/release-checklist.md +33 -1
  224. package/docs/migration/README.md +2 -0
  225. package/docs/migration/release-notes/0.10.0.md +201 -0
  226. package/docs/migration/release-notes/README.md +1 -0
  227. package/docs/migration/v0.9-to-v0.10.md +507 -0
  228. package/docs/posts/akm-command-tour.md +3 -3
  229. package/docs/product-surface.md +1 -1
  230. package/docs/reference/README.md +1 -0
  231. package/docs/reference/api.md +65 -0
  232. package/docs/reference/asset-types.md +3 -5
  233. package/docs/reference/bundle-types.md +2 -4
  234. package/docs/reference/cli.md +160 -188
  235. package/docs/reference/configuration.md +101 -20
  236. package/docs/reference/data-and-telemetry.md +2 -2
  237. package/docs/reference/env-and-secrets.md +2 -2
  238. package/docs/reference/supported-formats.md +3 -4
  239. package/docs/reference/tasks.md +12 -19
  240. package/docs/reference/website-sources.md +1 -1
  241. package/docs/reference/workflow-schema.md +89 -317
  242. package/docs/reference/workflows.md +20 -48
  243. package/package.json +12 -2
  244. package/schemas/akm-config.json +2 -243
  245. package/schemas/akm-task.json +2 -3
  246. package/schemas/akm-workflow.json +47 -84
  247. package/dist/assets/improve-strategies/catchup.json +0 -41
  248. package/dist/assets/improve-strategies/proactive-maintenance.json +0 -24
  249. package/dist/assets/improve-strategies/quick.json +0 -17
  250. package/dist/assets/improve-strategies/reflect-distill.json +0 -29
  251. package/dist/assets/improve-strategies/thorough.json +0 -49
  252. package/dist/assets/prompts/retrieval-relevance-judge.md +0 -6
  253. package/dist/assets/tasks/improve/akm-improve-catchup.yml +0 -6
  254. package/dist/assets/tasks/improve/akm-improve-frequent.yml +0 -4
  255. package/dist/assets/tasks/improve/akm-improve-nightly.yml +0 -4
  256. package/dist/cli/retired-commands.js +0 -118
  257. package/dist/commands/improve/retrieval-gate.js +0 -132
  258. package/dist/commands/tasks/validate.js +0 -176
  259. package/dist/core/config/legacy-source-shape-shim.js +0 -117
  260. package/dist/indexer/feedback/utility-policy.js +0 -75
  261. package/dist/integrations/harnesses/ids.js +0 -28
  262. package/dist/tasks/source/task-source-v3-frozen.js +0 -424
  263. package/dist/tasks/source/task-to-v4.js +0 -863
  264. package/dist/workflows/exec/worktree.js +0 -372
  265. package/dist/workflows/freeze/child-output-references.js +0 -83
  266. package/dist/workflows/freeze/identity.js +0 -22
  267. package/dist/workflows/freeze/targets/script.js +0 -50
  268. package/dist/workflows/freeze/targets/task.js +0 -124
  269. package/dist/workflows/github-yaml.js +0 -506
  270. package/dist/workflows/runtime/run-outputs.js +0 -80
@@ -49,10 +49,14 @@ function htmlExcerpt(body) {
49
49
  export class LlmCallError extends Error {
50
50
  code;
51
51
  statusCode;
52
- constructor(message, code, statusCode) {
52
+ namesResponseFormat;
53
+ constructor(message, code, statusCode,
54
+ /** A 5xx whose body names the response format; set from the whole body, since the message keeps only its first 200 characters. */
55
+ namesResponseFormat = false) {
53
56
  super(message);
54
57
  this.code = code;
55
58
  this.statusCode = statusCode;
59
+ this.namesResponseFormat = namesResponseFormat;
56
60
  this.name = "LlmCallError";
57
61
  }
58
62
  }
@@ -275,6 +279,8 @@ const THINKING_FIELD_NAMED = /chat_template_kwargs|enable_thinking/;
275
279
  const TEMPERATURE_NAMED = /'temperature'/;
276
280
  /** `Unsupported parameter: 'max_tokens' is not supported with this model. Use 'max_completion_tokens' instead.` */
277
281
  const MAX_TOKENS_REPLACED = /max_completion_tokens/;
282
+ /** A gateway that cannot honour the schema may answer 5xx: `502 {"error":{"code":"format_ignored"}}` (#1150). */
283
+ const RESPONSE_FORMAT_NAMED = /format_ignored|response_format|json_schema/;
278
284
  /**
279
285
  * A single chat-completion attempt: one HTTP request/response cycle, with an
280
286
  * inline fallback-once when a schema was requested and the provider 4xx's
@@ -295,24 +301,41 @@ const MAX_TOKENS_REPLACED = /max_completion_tokens/;
295
301
  async function chatCompletionAttempt(config, messages, options, timeoutMs, includeThinking = (options?.enableThinking ?? config.enableThinking) !== undefined &&
296
302
  !isThinkingControlKnownRejected(config)) {
297
303
  const wantsSchema = Boolean(options?.responseSchema) && config.supportsJsonSchema !== false && !isJsonSchemaKnownUnsupported(config);
304
+ // The schema attempt's error record is held, not emitted: when the retry without the schema
305
+ // recovers the call, that call is ONE success, not a failure plus a success. Every other exit
306
+ // flushes the held record, so a failed call is still recorded once.
307
+ const held = wantsSchema ? {} : undefined;
308
+ const flushHeld = () => {
309
+ if (held?.record)
310
+ emitLlmUsage(held.record);
311
+ };
298
312
  try {
299
- return await chatCompletionAttemptOnce(config, messages, options, timeoutMs, wantsSchema, includeThinking);
313
+ return await chatCompletionAttemptOnce(config, messages, options, timeoutMs, wantsSchema, includeThinking, held);
300
314
  }
301
315
  catch (err) {
302
- if (!isRejectedRequest(err))
316
+ // A 5xx that names the response format is answered like the 4xx rejection below (#1150).
317
+ if (wantsSchema && err instanceof LlmCallError && err.namesResponseFormat) {
318
+ return retryWithoutSchema(config, messages, options, timeoutMs, includeThinking, err);
319
+ }
320
+ if (!isRejectedRequest(err)) {
321
+ flushHeld();
303
322
  throw err;
323
+ }
304
324
  const key = connectionKey(config);
305
325
  if (TEMPERATURE_NAMED.test(err.message) && !temperatureRejectedConnections.has(key)) {
326
+ flushHeld();
306
327
  warnVerbose(`[akm] LLM rejected temperature (${err.statusCode}); retrying once without it: ${err.message}`);
307
328
  temperatureRejectedConnections.add(key);
308
329
  return chatCompletionAttempt(config, messages, options, timeoutMs, includeThinking);
309
330
  }
310
331
  if (MAX_TOKENS_REPLACED.test(err.message) && !maxCompletionTokensConnections.has(key)) {
332
+ flushHeld();
311
333
  warnVerbose(`[akm] LLM rejected max_tokens (${err.statusCode}); retrying once with max_completion_tokens: ${err.message}`);
312
334
  maxCompletionTokensConnections.add(key);
313
335
  return chatCompletionAttempt(config, messages, options, timeoutMs, includeThinking);
314
336
  }
315
337
  if (includeThinking && THINKING_FIELD_NAMED.test(err.message)) {
338
+ flushHeld();
316
339
  warnVerbose(`[akm] LLM rejected chat_template_kwargs/enable_thinking (${err.statusCode}); retrying once without them: ${err.message}`);
317
340
  const fallback = await chatCompletionAttempt(config, messages, options, timeoutMs, false);
318
341
  thinkingControlRejectedConnections.add(connectionKey(config));
@@ -320,13 +343,16 @@ async function chatCompletionAttempt(config, messages, options, timeoutMs, inclu
320
343
  }
321
344
  if (!wantsSchema)
322
345
  throw err;
323
- warnVerbose(`[akm] LLM rejected response_format:json_schema (${err.statusCode}); retrying once without it: ${err.message}`);
324
- const fallback = await chatCompletionAttemptOnce(config, messages, options, timeoutMs, false, includeThinking);
325
- jsonSchemaUnsupportedConnections.add(connectionKey(config));
326
- return fallback;
346
+ return retryWithoutSchema(config, messages, options, timeoutMs, includeThinking, err);
327
347
  }
328
348
  }
329
- async function chatCompletionAttemptOnce(config, messages, options, timeoutMs, includeSchema, includeThinking) {
349
+ async function retryWithoutSchema(config, messages, options, timeoutMs, includeThinking, err) {
350
+ warnVerbose(`[akm] LLM rejected response_format:json_schema (${err.statusCode}); retrying once without it: ${err.message}`);
351
+ const fallback = await chatCompletionAttemptOnce(config, messages, options, timeoutMs, false, includeThinking);
352
+ jsonSchemaUnsupportedConnections.add(connectionKey(config));
353
+ return fallback;
354
+ }
355
+ async function chatCompletionAttemptOnce(config, messages, options, timeoutMs, includeSchema, includeThinking, held) {
330
356
  if (config.extraParams !== undefined) {
331
357
  const issue = validateExtraParams(config.extraParams)[0];
332
358
  if (issue)
@@ -440,7 +466,7 @@ async function chatCompletionAttemptOnce(config, messages, options, timeoutMs, i
440
466
  throw new LlmCallError(`LLM provider returned HTML instead of JSON (${status}) ${config.endpoint}: ${redactSensitiveText(htmlExcerpt(rawBody), resolvedKey ? [resolvedKey] : [])}`, "provider_html_error", status);
441
467
  }
442
468
  if (status >= 500) {
443
- throw new LlmCallError(`LLM provider error (${status}) ${config.endpoint}: ${safeBody}`, "provider_error", status);
469
+ throw new LlmCallError(`LLM provider error (${status}) ${config.endpoint}: ${safeBody}`, "provider_error", status, RESPONSE_FORMAT_NAMED.test(rawBody));
444
470
  }
445
471
  throw new LlmCallError(`LLM request failed (${status}) ${config.endpoint}: ${safeBody}`, "provider_error", status);
446
472
  }
@@ -499,12 +525,16 @@ async function chatCompletionAttemptOnce(config, messages, options, timeoutMs, i
499
525
  return result;
500
526
  }
501
527
  catch (err) {
502
- emitLlmUsage({
528
+ const errorRecord = {
503
529
  ...terminalFields,
504
530
  outcome: "error",
505
531
  durationMs: Date.now() - requestStartedAt,
506
532
  errorCode: err instanceof LlmCallError ? err.code : "unknown_error",
507
- });
533
+ };
534
+ if (held)
535
+ held.record = errorRecord;
536
+ else
537
+ emitLlmUsage(errorRecord);
508
538
  throw err;
509
539
  }
510
540
  }
@@ -2,7 +2,7 @@
2
2
  // License, v. 2.0. If a copy of the MPL was not distributed with this
3
3
  // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
4
  /**
5
- * Embedded agent CLI guide rendered by `akm hints` and `akm help agents`.
5
+ * Embedded agent CLI guide rendered by `akm hints`.
6
6
  *
7
7
  * Extracted from `src/cli.ts` so it does not bloat the CLI module and so
8
8
  * docs/CI tooling can re-use the same constants. Two flavors:
@@ -4,7 +4,8 @@
4
4
  /**
5
5
  * Process-level output mode singleton.
6
6
  *
7
- * Output mode (format + detail + shape) is parsed once at startup from
7
+ * Output mode (format + detail, and the agent projection `--detail agent`
8
+ * selects) is parsed once at startup from
8
9
  * `process.argv` and the persisted user config. All subsequent `output()`
9
10
  * calls read from this in-memory singleton instead of re-scanning argv and
10
11
  * re-loading config on every call.
@@ -15,8 +16,7 @@
15
16
  */
16
17
  import { UsageError } from "../core/errors.js";
17
18
  const OUTPUT_FORMATS = ["json", "yaml", "text", "jsonl", "md", "html"];
18
- const DETAIL_LEVELS = ["brief", "normal", "full"];
19
- const SHAPE_MODES = ["human", "agent", "summary"];
19
+ const DETAIL_VALUES = ["brief", "normal", "full", "agent"];
20
20
  function parseOutputFormat(value) {
21
21
  if (!value)
22
22
  return undefined;
@@ -27,16 +27,9 @@ function parseOutputFormat(value) {
27
27
  export function parseDetailLevel(value) {
28
28
  if (!value)
29
29
  return undefined;
30
- if (DETAIL_LEVELS.includes(value))
30
+ if (DETAIL_VALUES.includes(value))
31
31
  return value;
32
- throw new UsageError(`Invalid value for --detail: ${value}. Expected one of: ${DETAIL_LEVELS.join("|")}`, "INVALID_DETAIL_VALUE");
33
- }
34
- function parseShapeMode(value) {
35
- if (!value)
36
- return undefined;
37
- if (SHAPE_MODES.includes(value))
38
- return value;
39
- throw new UsageError(`Invalid value for --shape: ${value}. Expected one of: ${SHAPE_MODES.join("|")}`, "INVALID_SHAPE_VALUE");
32
+ throw new UsageError(`Invalid value for --detail: ${value}. Expected one of: ${DETAIL_VALUES.join("|")}`, "INVALID_DETAIL_VALUE");
40
33
  }
41
34
  export function parseFlagValue(argv, flag) {
42
35
  for (let i = 0; i < argv.length; i++) {
@@ -88,13 +81,11 @@ export function getHyphenatedBoolean(args, key) {
88
81
  */
89
82
  export function resolveOutputMode(argv, defaults = {}) {
90
83
  const format = parseOutputFormat(parseFlagValue(argv, "--format")) ?? defaults?.format ?? "json";
91
- const rawDetail = parseFlagValue(argv, "--detail");
92
- const rawShape = parseFlagValue(argv, "--shape");
93
- // `--detail` is verbosity only (brief|normal|full); the projection presets
94
- // (`summary`/`agent`) and the `--for-agent` boolean were removed in 0.9.0 —
95
- // use `--shape`. Unknown `--detail` values fall through to the default.
96
- const detail = parseDetailLevel(rawDetail) ?? defaults?.detail ?? "brief";
97
- const shape = parseShapeMode(rawShape) ?? "human";
84
+ // `--detail agent` selects the agent projection; the verbosity then stays at
85
+ // the configured default (the projection ignores it).
86
+ const parsedDetail = parseDetailLevel(parseFlagValue(argv, "--detail"));
87
+ const shape = parsedDetail === "agent" ? "agent" : "human";
88
+ const detail = (parsedDetail === "agent" ? undefined : parsedDetail) ?? defaults?.detail ?? "brief";
98
89
  const outputPath = parseFlagValue(argv, "--output");
99
90
  return { format, detail, shape, ...(outputPath ? { outputPath } : {}) };
100
91
  }
@@ -35,12 +35,11 @@ const EXEMPT_COMMANDS = new Set([
35
35
  // progress-event lines the child prints during a real `apply` still print
36
36
  // verbatim ahead of the formatted result — those are operational logging,
37
37
  // not part of the result envelope, the same way a progress spinner would be.
38
- // Document payload group: bare `help` prints the sectioned overview,
39
- // `help migrate <version>` prints release notes, and `help agents` prints
40
- // the embedded CLI-reference guide (`src/output/cli-hints.ts`) — none of
41
- // the three render a result envelope.
38
+ // Document payload group: bare `help` prints the sectioned overview and
39
+ // `help migrate <version>` prints release notes — neither renders a result
40
+ // envelope.
42
41
  "help",
43
- // Embedded agent guide document.
42
+ // Embedded agent guide document (`src/output/cli-hints.ts`).
44
43
  "hints",
45
44
  ]);
46
45
  /**
@@ -57,9 +56,7 @@ const EXEMPT_SUBCOMMANDS = new Set([
57
56
  // envelope. Wrapping it broke every existing substitution silently: the
58
57
  // CLI's default format is `json`, so an un-flagged `akm env path <ref>`
59
58
  // (exactly how the substitution is always written) started emitting
60
- // `{"path":"..."}` instead of the raw path. Unlike `config path`, this
61
- // command has no `--all`-style multi-field variant, so the whole surface
62
- // can be exempt without wrongly warning on a real envelope case.
59
+ // `{"path":"..."}` instead of the raw path.
63
60
  "env path",
64
61
  ]);
65
62
  /**
@@ -310,7 +310,7 @@ export function shapeSearchHit(hit, detail) {
310
310
  }
311
311
  // Stash hit (local or remote)
312
312
  // `ref` is included at `brief` so agents can run `akm show <ref>` without
313
- // needing --detail full or --shape agent (REC-03).
313
+ // needing --detail full or --detail agent (REC-03).
314
314
  if (detail === "brief") {
315
315
  return pickFields(hit, ["type", "name", "ref", "action", "estimatedTokens", "keys"]);
316
316
  }
@@ -393,27 +393,6 @@ export function shapeShowOutput(result, detail, shape = "human") {
393
393
  delete shaped.editHint;
394
394
  return shaped;
395
395
  }
396
- if (shape === "summary") {
397
- return pickFields(result, [
398
- "type",
399
- "name",
400
- // ref is present on every show shape (human/summary/agent) — it is the
401
- // canonical identity of the asset, not an agent-only convenience field
402
- // (R-020).
403
- "ref",
404
- "description",
405
- "tags",
406
- "parameters",
407
- "workflowTitle",
408
- "action",
409
- "run",
410
- "origin",
411
- "keys",
412
- "links",
413
- ...FRAGMENT_PROVENANCE_FIELDS,
414
- ...FRAGMENT_CONTEXT_FIELDS,
415
- ]);
416
- }
417
396
  const base = pickFields(result, [
418
397
  "type",
419
398
  "name",
@@ -75,7 +75,6 @@ const PASSTHROUGH_COMMANDS = [
75
75
  "task-run",
76
76
  "task-sync",
77
77
  "task-sync-dry-run",
78
- "task-validate",
79
78
  "update",
80
79
  "upgrade",
81
80
  "workflow-abandon",
@@ -1,23 +1,6 @@
1
1
  // This Source Code Form is subject to the terms of the Mozilla Public
2
2
  // License, v. 2.0. If a copy of the MPL was not distributed with this
3
3
  // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
- /**
5
- * Pure shaping functions that select and trim fields from command result
6
- * objects according to the active detail level / agent mode.
7
- *
8
- * Every function in this module is side-effect free and operates on plain
9
- * `Record<string, unknown>` shapes, which makes them trivial to unit test.
10
- *
11
- * Output shapes are assembled EXPLICITLY here: each per-command module under
12
- * `src/output/shapes/` EXPORTS a pure `OutputShapeEntry[]` (no top-level
13
- * side effect), and this barrel imports those exports and registers them in a
14
- * single deterministic, order-independent pass (`BUILT_IN_OUTPUT_SHAPES`).
15
- * Dropping a module from the assembly array is a COMPILE error, not a silent
16
- * runtime gap. The central `shapeForCommand` dispatcher looks up the registry
17
- * and throws for unknown commands (v1 spec §9 — exhaustive registry, no silent
18
- * fallback).
19
- */
20
- import { warnOnce } from "../core/warn.js";
21
4
  import { curateShapes } from "./shapes/curate.js";
22
5
  import { envListShapes } from "./shapes/env-list.js";
23
6
  import { eventsShapes } from "./shapes/events.js";
@@ -64,12 +47,6 @@ const BUILT_IN_OUTPUT_SHAPES = [
64
47
  ...passthroughShapes,
65
48
  ];
66
49
  registerOutputShapes(BUILT_IN_OUTPUT_SHAPES);
67
- /**
68
- * Commands whose shape handler implements the `summary` projection. For every
69
- * other command, `--shape summary` is a usage error (v1 §5 — honest rejection
70
- * for a soon-frozen contract, not a silent fallback to `human`).
71
- */
72
- const SHAPE_SUMMARY_COMMANDS = new Set(["show"]);
73
50
  // ── `results` collection alias ──────────────────────────────────────────────
74
51
  //
75
52
  // Every list-returning command names its collection differently (`hits`,
@@ -80,8 +57,8 @@ const SHAPE_SUMMARY_COMMANDS = new Set(["show"]);
80
57
  //
81
58
  // This maps each list-returning command to the field already holding its
82
59
  // collection, and `withResultsAlias` below adds a `results` key pointing at
83
- // that SAME array (not a copy) to the shaped output — in every `--shape` /
84
- // `--detail` combination, `human` included, so `--shape agent` needs no
60
+ // that SAME array (not a copy) to the shaped output — in every
61
+ // `--detail` combination, `agent` included, so `--detail agent` needs no
85
62
  // separate handling to "guarantee" it. A new list-returning command MUST add
86
63
  // an entry here; there is no way to detect a missed one automatically, so the
87
64
  // survey deliberately lives in this one place rather than scattered per
@@ -118,14 +95,9 @@ function withResultsAlias(command, shaped) {
118
95
  return { ...obj, results: collection };
119
96
  }
120
97
  export function shapeForCommand(command, result, detail, shape = "human") {
121
- let effectiveShape = shape;
122
- if (shape === "summary" && !SHAPE_SUMMARY_COMMANDS.has(command)) {
123
- warnOnce(`shape-summary-unsupported:${command}`, `[output] '--shape summary' is not supported for 'akm ${command}' (only 'akm show' has a summary projection); falling back to 'agent'.`);
124
- effectiveShape = "agent";
125
- }
126
98
  const handler = getOutputShapeHandler(command);
127
99
  if (handler) {
128
- return withResultsAlias(command, handler(result, detail, effectiveShape));
100
+ return withResultsAlias(command, handler(result, detail, shape));
129
101
  }
130
102
  // v1 spec §9 (output-shape registry exhaustive): no silent JSON.stringify
131
103
  // fallback. A missing case here is a registration bug — fail loudly so
@@ -20,5 +20,9 @@
20
20
  * `tests/integration/output-baseline.test.ts` pins the bytes either way.
21
21
  */
22
22
  export function writeStdout(document) {
23
- process.stdout.write(`${document}\n`);
23
+ process.stdout.write(stdoutText(document));
24
+ }
25
+ /** The exact bytes {@link writeStdout} writes for `document`; in-process callers return this instead of writing. */
26
+ export function stdoutText(document) {
27
+ return `${document}\n`;
24
28
  }
@@ -662,7 +662,12 @@ export function formatUpdatePlain(r) {
662
662
  }
663
663
  return lines.length > 0 ? lines.join("\n") : `update: nothing to update`;
664
664
  }
665
- const HARNESS_LABELS = { "claude-code": "Claude Code", codex: "Codex", opencode: "OpenCode" };
665
+ const HARNESS_LABELS = {
666
+ "claude-code": "Claude Code",
667
+ codex: "Codex",
668
+ opencode: "OpenCode",
669
+ "opencode-v2": "OpenCode 2",
670
+ };
666
671
  /** The plugin lines worth printing: nothing for a plugin that is current or has no plugin to update. */
667
672
  function formatUpgradePluginLines(r) {
668
673
  const lines = [];
@@ -235,8 +235,7 @@ function formatInputBindingsList(bindings) {
235
235
  * expansion's own steps, recursively, indented one level per composition
236
236
  * boundary. A child step also carries the child's `planHash` on the step
237
237
  * line itself, and (when present) a `with:` line for the step's own
238
- * `inputBindings[]` and an `exports:` line for the child's declared output
239
- * names — §4.6's worked example.
238
+ * `inputBindings[]` — §4.6's worked example.
240
239
  */
241
240
  function renderPlanStepLines(step, ordinal, lines, prefix = " ") {
242
241
  const stepId = typeof step.stepId === "string" ? step.stepId : "unknown";
@@ -244,11 +243,9 @@ function renderPlanStepLines(step, ordinal, lines, prefix = " ") {
244
243
  const expansion = typeof step.expansion === "object" && step.expansion !== null ? step.expansion : {};
245
244
  const via = typeof expansion.via === "string" ? expansion.via : "direct";
246
245
  const childPlanHash = typeof expansion.childPlanHash === "string" ? expansion.childPlanHash : undefined;
247
- const viaText = via === "task"
248
- ? `via ${String(expansion.taskRef ?? "")}`
249
- : via === "child"
250
- ? `-> ${String(expansion.childRef ?? "")}${childPlanHash ? ` (plan ${childPlanHash})` : ""}`
251
- : "direct";
246
+ const viaText = via === "child"
247
+ ? `-> ${String(expansion.childRef ?? "")}${childPlanHash ? ` (plan ${childPlanHash})` : ""}`
248
+ : "direct";
252
249
  lines.push(`${prefix}${ordinal}. ${stepId} ${targetKind} ${viaText}`.replace(/ +/g, " ").trimEnd());
253
250
  const detailPrefix = `${prefix} `;
254
251
  const inputBindings = Array.isArray(step.inputBindings) ? step.inputBindings : [];
@@ -256,10 +253,6 @@ function renderPlanStepLines(step, ordinal, lines, prefix = " ") {
256
253
  lines.push(`${detailPrefix}with: ${formatInputBindingsList(inputBindings)}`);
257
254
  }
258
255
  if (via === "child") {
259
- const childOutputs = Array.isArray(expansion.childOutputs) ? expansion.childOutputs : [];
260
- if (childOutputs.length > 0) {
261
- lines.push(`${detailPrefix}exports: ${childOutputs.map(String).join(", ")}`);
262
- }
263
256
  const childSteps = Array.isArray(expansion.steps) ? expansion.steps : [];
264
257
  for (const [index, childStep] of childSteps.entries()) {
265
258
  renderPlanStepLines(childStep, index + 1, lines, `${prefix} ${ordinal}.`);
@@ -304,17 +297,6 @@ export function formatWorkflowPlanPlain(result) {
304
297
  const params = Array.isArray(result.params) ? result.params : undefined;
305
298
  if (params && params.length > 0)
306
299
  lines.push(`params: ${params.join(", ")}`);
307
- const outputs = typeof result.outputs === "object" && result.outputs !== null
308
- ? result.outputs
309
- : undefined;
310
- if (outputs) {
311
- for (const [name, declaration] of Object.entries(outputs)) {
312
- const from = typeof declaration === "object" && declaration !== null
313
- ? String(declaration.from ?? "")
314
- : "";
315
- lines.push(`outputs: ${name} <- ${from}`);
316
- }
317
- }
318
300
  const steps = Array.isArray(result.steps) ? result.steps : [];
319
301
  lines.push("steps:");
320
302
  for (const [index, step] of steps.entries()) {