akm-cli 0.9.31 → 0.10.26101002-alpha

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 (236) hide show
  1. package/CHANGELOG.md +265 -0
  2. package/STABILITY.md +79 -53
  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 +13 -27
  7. package/dist/assets/hints/cli-hints-short.md +1 -1
  8. package/dist/assets/improve-strategies/default.json +3 -3
  9. package/dist/assets/prompts/consolidate-pair.md +9 -7
  10. package/dist/cli/shared.js +31 -48
  11. package/dist/cli/unknown-flags.js +6 -43
  12. package/dist/cli.js +8 -34
  13. package/dist/commands/agent/contribute-cli.js +1 -1
  14. package/dist/commands/completions.js +1 -2
  15. package/dist/commands/config-cli.js +5 -28
  16. package/dist/commands/env/env-cli.js +5 -5
  17. package/dist/commands/env/secret-cli.js +2 -2
  18. package/dist/commands/health/checks.js +41 -16
  19. package/dist/commands/health/plugin-staleness.js +52 -22
  20. package/dist/commands/improve/autonomy-gate.js +5 -3
  21. package/dist/commands/improve/consolidate/pair-pass.js +7 -1
  22. package/dist/commands/improve/consolidate.js +6 -35
  23. package/dist/commands/improve/distill/lesson-update.js +91 -0
  24. package/dist/commands/improve/distill-guards.js +1 -43
  25. package/dist/commands/improve/distill.js +140 -20
  26. package/dist/commands/improve/eligibility.js +14 -1
  27. package/dist/commands/improve/extract-cli.js +1 -1
  28. package/dist/commands/improve/improve-cli.js +87 -102
  29. package/dist/commands/improve/improve-strategies.js +8 -12
  30. package/dist/commands/improve/improve.js +2 -2
  31. package/dist/commands/improve/loop-stages.js +0 -1
  32. package/dist/commands/improve/planner.js +0 -1
  33. package/dist/commands/improve/preparation.js +40 -99
  34. package/dist/commands/improve/proactive-maintenance.js +2 -2
  35. package/dist/commands/improve/reflect-noise.js +3 -55
  36. package/dist/commands/improve/reflect.js +3 -8
  37. package/dist/commands/improve/retrieval-scope.js +2 -2
  38. package/dist/commands/improve/salience.js +2 -9
  39. package/dist/commands/improve/stage.js +14 -9
  40. package/dist/commands/lint/index.js +5 -20
  41. package/dist/commands/observability-cli.js +3 -9
  42. package/dist/commands/proposal/drain.js +39 -6
  43. package/dist/commands/proposal/proposal-cli.js +11 -31
  44. package/dist/commands/proposal/repository.js +3 -3
  45. package/dist/commands/read/knowledge.js +3 -3
  46. package/dist/commands/read/remember-cli.js +0 -13
  47. package/dist/commands/read/search-cli.js +29 -97
  48. package/dist/commands/read/search.js +0 -11
  49. package/dist/commands/read/show.js +0 -19
  50. package/dist/commands/sources/plugin-upgrade.js +164 -101
  51. package/dist/commands/sources/sources-cli.js +1 -15
  52. package/dist/commands/sources/stash-cli.js +4 -12
  53. package/dist/commands/tasks/tasks-cli.js +4 -115
  54. package/dist/commands/workflow/plan.js +11 -33
  55. package/dist/commands/workflow-cli.js +2 -7
  56. package/dist/core/adapter/adapter-ids.js +1 -2
  57. package/dist/core/adapter/adapters/agent-skills-adapter.js +0 -4
  58. package/dist/core/adapter/adapters/akm-adapter.js +9 -48
  59. package/dist/core/adapter/adapters/akm-lint.js +10 -26
  60. package/dist/core/adapter/adapters/akm-metadata.js +2 -72
  61. package/dist/core/adapter/adapters/akm-task-adapter.js +0 -5
  62. package/dist/core/adapter/adapters/akm-workflow-adapter.js +9 -28
  63. package/dist/core/adapter/adapters/dotenv-adapter.js +1 -20
  64. package/dist/core/adapter/adapters/generic-files-adapter.js +0 -10
  65. package/dist/core/adapter/adapters/llm-wiki-adapter.js +0 -4
  66. package/dist/core/adapter/adapters/tool-dir-shared.js +0 -24
  67. package/dist/core/adapter/adapters/website-snapshot-adapter.js +4 -4
  68. package/dist/core/adapter/recognize-match.js +3 -4
  69. package/dist/core/common.js +18 -2
  70. package/dist/core/config/config-schema.js +12 -15
  71. package/dist/core/config/config-types.js +6 -15
  72. package/dist/core/config/config-walker.js +6 -23
  73. package/dist/core/config/config.js +17 -80
  74. package/dist/core/config/engine-semantics.js +7 -3
  75. package/dist/core/config/schema/engines.js +7 -7
  76. package/dist/core/config/schema/improve-processes.js +8 -61
  77. package/dist/core/config/schema/improve.js +2 -21
  78. package/dist/core/config/schema/index-config.js +6 -39
  79. package/dist/core/env-secret-ref.js +2 -45
  80. package/dist/core/errors.js +1 -2
  81. package/dist/core/extra-params.js +2 -86
  82. package/dist/core/improve-result.js +11 -1
  83. package/dist/core/mutation-target.js +2 -2
  84. package/dist/core/recognition-util.js +4 -5
  85. package/dist/core/warn.js +21 -4
  86. package/dist/core/write-source.js +3 -3
  87. package/dist/execution/target-ref.js +1 -2
  88. package/dist/indexer/search/db-search.js +4 -19
  89. package/dist/indexer/search/fts-query.js +0 -19
  90. package/dist/integrations/agent/engine-resolution.js +5 -2
  91. package/dist/integrations/agent/index.js +1 -1
  92. package/dist/integrations/agent/profiles.js +41 -98
  93. package/dist/integrations/agent/request-lowering.js +3 -3
  94. package/dist/integrations/harnesses/aider/index.js +5 -0
  95. package/dist/integrations/harnesses/amazonq/index.js +1 -0
  96. package/dist/integrations/harnesses/claude/config-import.js +2 -3
  97. package/dist/integrations/harnesses/claude/index.js +5 -0
  98. package/dist/integrations/harnesses/codex/index.js +1 -0
  99. package/dist/integrations/harnesses/copilot/index.js +1 -0
  100. package/dist/integrations/harnesses/gemini/index.js +1 -0
  101. package/dist/integrations/harnesses/index.js +18 -39
  102. package/dist/integrations/harnesses/opencode/agent-builder.js +60 -35
  103. package/dist/integrations/harnesses/opencode/config-import.js +121 -52
  104. package/dist/integrations/harnesses/opencode/index.js +22 -0
  105. package/dist/integrations/harnesses/opencode/plugin-layout.js +68 -0
  106. package/dist/integrations/harnesses/opencode/session-log-types.js +12 -0
  107. package/dist/integrations/harnesses/opencode/session-log-v1.js +72 -0
  108. package/dist/integrations/harnesses/opencode/session-log-v2.js +140 -0
  109. package/dist/integrations/harnesses/opencode/session-log.js +104 -115
  110. package/dist/integrations/harnesses/opencode/version.js +98 -0
  111. package/dist/integrations/harnesses/opencode-sdk/{harness.js → index.js} +5 -17
  112. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +105 -206
  113. package/dist/integrations/harnesses/opencode-sdk/v1-adapter.js +199 -0
  114. package/dist/integrations/harnesses/opencode-sdk/v2-adapter.js +228 -0
  115. package/dist/integrations/harnesses/opencode-sdk/wire.js +66 -0
  116. package/dist/integrations/harnesses/openhands/index.js +1 -0
  117. package/dist/integrations/harnesses/pi/index.js +1 -0
  118. package/dist/integrations/harnesses/shared.js +2 -0
  119. package/dist/integrations/harnesses/types.js +2 -0
  120. package/dist/llm/client.js +41 -11
  121. package/dist/output/cli-hints.js +1 -1
  122. package/dist/output/context.js +10 -19
  123. package/dist/output/format-exempt.js +5 -8
  124. package/dist/output/shapes/helpers.js +1 -22
  125. package/dist/output/shapes/passthrough.js +0 -1
  126. package/dist/output/shapes.js +3 -31
  127. package/dist/output/stdout.js +5 -1
  128. package/dist/output/text/command-format.js +6 -1
  129. package/dist/output/text/workflow-format.js +4 -22
  130. package/dist/scripts/akm-migrate-node.js +13049 -14013
  131. package/dist/scripts/akm-migrate.js +16316 -17276
  132. package/dist/setup/detect.js +10 -4
  133. package/dist/setup/harness-config-import.js +5 -14
  134. package/dist/setup/setup.js +1 -1
  135. package/dist/setup/steps/tasks.js +12 -13
  136. package/dist/storage/repositories/task-history-repository.js +0 -5
  137. package/dist/storage/repositories/workflow-runs-repository.js +5 -15
  138. package/dist/tasks/embedded.js +2 -1
  139. package/dist/tasks/run/task-history.js +0 -2
  140. package/dist/tasks/schedule-gates.js +39 -0
  141. package/dist/tasks/scheduler-binding.js +0 -16
  142. package/dist/tasks/scheduler-sync.js +3 -97
  143. package/dist/tasks/source/bounded-document.js +1 -1
  144. package/dist/tasks/source/task-source-v4.js +2 -12
  145. package/dist/tasks/task-run-reserved-flags.js +2 -31
  146. package/dist/workflows/authoring/authoring.js +6 -6
  147. package/dist/workflows/compile.js +8 -65
  148. package/dist/workflows/exec/child-workflow.js +22 -3
  149. package/dist/workflows/exec/native-executor.js +12 -147
  150. package/dist/workflows/exec/run-workflow.js +31 -103
  151. package/dist/workflows/exec/step-work.js +9 -178
  152. package/dist/workflows/exec/unit-dispatch.js +0 -10
  153. package/dist/workflows/freeze/environment.js +3 -44
  154. package/dist/workflows/freeze/freeze.js +8 -41
  155. package/dist/workflows/freeze/targets/child-workflow.js +27 -63
  156. package/dist/workflows/freeze/targets/command.js +2 -16
  157. package/dist/workflows/freeze/targets/shell.js +0 -2
  158. package/dist/workflows/freeze/task-bindings.js +2 -28
  159. package/dist/workflows/ir/params.js +12 -2
  160. package/dist/workflows/parser.js +82 -232
  161. package/dist/workflows/plan.js +1 -1
  162. package/dist/workflows/renderer.js +10 -19
  163. package/dist/workflows/runtime/run-plan.js +11 -53
  164. package/dist/workflows/runtime/runs.js +4 -27
  165. package/dist/workflows/source-files.js +12 -16
  166. package/dist/workflows/source-semantics.js +1 -93
  167. package/docs/README.md +1 -1
  168. package/docs/agents/README.md +3 -3
  169. package/docs/agents/agent-install.md +1 -1
  170. package/docs/architecture/adapters.md +17 -31
  171. package/docs/architecture/architecture.md +4 -5
  172. package/docs/architecture/improvement.md +52 -21
  173. package/docs/architecture/internals/improve-workflow.md +15 -8
  174. package/docs/architecture/internals/indexing.md +1 -1
  175. package/docs/architecture/internals/search.md +1 -2
  176. package/docs/architecture/internals/storage-locations.md +5 -5
  177. package/docs/architecture/persisted-data-compat.md +13 -4
  178. package/docs/architecture/specs/0.9.0-decisions.md +6 -2
  179. package/docs/architecture/specs/improve-collapse-churn-detector-design.md +2 -2
  180. package/docs/architecture/specs/workflow-format-unification.md +1 -6
  181. package/docs/architecture/testing/manual-testing-checklist.md +20 -29
  182. package/docs/architecture/testing/task-workflow-manual-tests.md +106 -203
  183. package/docs/architecture/testing/testing-workflow.md +1 -1
  184. package/docs/architecture/workflow-engine.md +5 -56
  185. package/docs/guides/author-workflows.md +39 -69
  186. package/docs/guides/capture-knowledge.md +1 -1
  187. package/docs/guides/claude-code-vs-akm-workflows.md +1 -2
  188. package/docs/guides/discover-and-load.md +4 -4
  189. package/docs/guides/getting-started.md +1 -1
  190. package/docs/guides/improve-the-library.md +7 -7
  191. package/docs/guides/recipes/headless-install.md +2 -2
  192. package/docs/guides/run-workflows.md +1 -1
  193. package/docs/guides/use-with-any-agent.md +49 -4
  194. package/docs/integration/bundling-akm.md +7 -6
  195. package/docs/maintainers/curate-workmap.md +1 -2
  196. package/docs/maintainers/release-checklist.md +30 -1
  197. package/docs/posts/akm-command-tour.md +3 -3
  198. package/docs/product-surface.md +1 -1
  199. package/docs/reference/README.md +1 -0
  200. package/docs/reference/api.md +65 -0
  201. package/docs/reference/asset-types.md +3 -5
  202. package/docs/reference/bundle-types.md +2 -4
  203. package/docs/reference/cli.md +129 -171
  204. package/docs/reference/configuration.md +84 -19
  205. package/docs/reference/data-and-telemetry.md +1 -1
  206. package/docs/reference/env-and-secrets.md +2 -2
  207. package/docs/reference/supported-formats.md +3 -4
  208. package/docs/reference/tasks.md +12 -19
  209. package/docs/reference/website-sources.md +1 -1
  210. package/docs/reference/workflow-schema.md +89 -317
  211. package/docs/reference/workflows.md +20 -48
  212. package/package.json +12 -2
  213. package/schemas/akm-config.json +2 -243
  214. package/schemas/akm-task.json +2 -3
  215. package/schemas/akm-workflow.json +47 -84
  216. package/dist/assets/improve-strategies/catchup.json +0 -41
  217. package/dist/assets/improve-strategies/proactive-maintenance.json +0 -24
  218. package/dist/assets/improve-strategies/quick.json +0 -17
  219. package/dist/assets/improve-strategies/reflect-distill.json +0 -29
  220. package/dist/assets/improve-strategies/thorough.json +0 -49
  221. package/dist/assets/tasks/improve/akm-improve-catchup.yml +0 -6
  222. package/dist/assets/tasks/improve/akm-improve-frequent.yml +0 -4
  223. package/dist/assets/tasks/improve/akm-improve-nightly.yml +0 -4
  224. package/dist/cli/retired-commands.js +0 -118
  225. package/dist/commands/tasks/validate.js +0 -176
  226. package/dist/core/config/legacy-source-shape-shim.js +0 -117
  227. package/dist/integrations/harnesses/ids.js +0 -28
  228. package/dist/tasks/source/task-source-v3-frozen.js +0 -424
  229. package/dist/tasks/source/task-to-v4.js +0 -863
  230. package/dist/workflows/exec/worktree.js +0 -372
  231. package/dist/workflows/freeze/child-output-references.js +0 -83
  232. package/dist/workflows/freeze/identity.js +0 -22
  233. package/dist/workflows/freeze/targets/script.js +0 -50
  234. package/dist/workflows/freeze/targets/task.js +0 -124
  235. package/dist/workflows/github-yaml.js +0 -506
  236. package/dist/workflows/runtime/run-outputs.js +0 -80
@@ -10,7 +10,6 @@
10
10
  * reorders it.
11
11
  */
12
12
  import path from "node:path";
13
- import { stashDirFor } from "../../core/asset/asset-placement.js";
14
13
  import { displayRef } from "../../core/asset/resolve-ref.js";
15
14
  import { compareCodePoints } from "../../core/common.js";
16
15
  import { classifyPathAccess } from "../../core/path-access.js";
@@ -27,7 +26,7 @@ import { getMeta } from "../../storage/repositories/index-meta-repository.js";
27
26
  import { getEmbeddingCount, searchVec } from "../../storage/repositories/index-vec-repository.js";
28
27
  import { ensureIndex } from "../ensure-index.js";
29
28
  import { isProposedQuality } from "../passes/metadata.js";
30
- import { ftsQueryTokens, parseRefPrefixQuery, parseRetiredTypePrefixQuery } from "./fts-query.js";
29
+ import { ftsQueryTokens, parseRefPrefixQuery } from "./fts-query.js";
31
30
  import { reciprocalRankFusion } from "./ranking.js";
32
31
  import { attachSearchHitAttribution } from "./search-attribution.js";
33
32
  import { enrichSearchHit } from "./search-hit-enrichers.js";
@@ -96,7 +95,7 @@ function hasConfiguredEmbeddingProvider(config) {
96
95
  return Boolean(config.embedding?.endpoint && config.embedding?.model);
97
96
  }
98
97
  export async function searchLocal(input) {
99
- const { query, stashDir, config } = input;
98
+ const { stashDir, config } = input;
100
99
  const warnings = [];
101
100
  // Semantic search is attempted fresh on every query (see `startVectorChannel`);
102
101
  // there is no cached readiness verdict to consult here. The only thing
@@ -143,7 +142,7 @@ export async function searchLocal(input) {
143
142
  warnings.push(semanticWarning);
144
143
  return {
145
144
  hits,
146
- tip: hits.length === 0 ? emptyResultTip(query) : undefined,
145
+ tip: hits.length === 0 ? EMPTY_RESULT_TIP : undefined,
147
146
  warnings: warnings.length > 0 ? warnings : undefined,
148
147
  embedMs,
149
148
  rankMs,
@@ -294,21 +293,7 @@ function describeRanks(candidate) {
294
293
  ...(vectorRank !== undefined ? [`vector rank ${vectorRank}`] : []),
295
294
  ];
296
295
  }
297
- /**
298
- * The no-hits tip. A query in the retired `<type>:` / `<type>:<prefix>/` browse
299
- * grammar gets the conceptId spelling that replaces it: without this it comes
300
- * back empty and silent, which is the failure D4 removed the grammar to avoid.
301
- */
302
- function emptyResultTip(query) {
303
- const generic = "No matching stash assets were found. Try a different query or run 'akm index' to rebuild.";
304
- const retired = parseRetiredTypePrefixQuery(query);
305
- if (!retired)
306
- return generic;
307
- const root = stashDirFor(retired.type);
308
- if (!root)
309
- return generic;
310
- return `No matching stash assets were found. The '<type>:' browse grammar was removed in 0.9.0 — use the conceptId spelling: 'akm search "${root}/${retired.rest}"'.`;
311
- }
296
+ const EMPTY_RESULT_TIP = "No matching stash assets were found. Try a different query or run 'akm index' to rebuild.";
312
297
  // ── Enumeration (browse) path ────────────────────────────────────────────────
313
298
  /**
314
299
  * Enumerate index entries without FTS scoring — the browse path shared by
@@ -78,22 +78,3 @@ export function parseRefPrefixQuery(query) {
78
78
  return { bundle, conceptIdPrefix: rest };
79
79
  return null;
80
80
  }
81
- /**
82
- * Recognize the retired `<type>:` / `<type>:<prefix>/` browse grammar so the
83
- * caller can name the replacement spelling rather than letting the query
84
- * degrade silently into a keyword search — the exact silent failure D4 removes.
85
- * Shape recognition only; mapping the type to its conceptId root belongs to the
86
- * caller, which keeps this module dependency-free.
87
- */
88
- export function parseRetiredTypePrefixQuery(query) {
89
- const trimmed = query.trim();
90
- if (trimmed.length === 0 || /\s/.test(trimmed) || trimmed.includes("//"))
91
- return null;
92
- const colon = trimmed.indexOf(":");
93
- if (colon <= 0)
94
- return null;
95
- const rest = trimmed.slice(colon + 1);
96
- if (rest !== "" && !rest.endsWith("/"))
97
- return null;
98
- return { type: trimmed.slice(0, colon), rest };
99
- }
@@ -12,8 +12,9 @@ import { formatExtraParamsIssue, validateExtraParams } from "../../core/extra-pa
12
12
  import { collectSensitiveValues } from "../../core/redaction.js";
13
13
  import { resolveSecretFromStore } from "../../sources/snapshot-fetchers/secret-seam.js";
14
14
  import { getHarness } from "../harnesses/index.js";
15
+ import { resolveOpencodeBin } from "../harnesses/opencode/version.js";
15
16
  import { DEFAULT_LLM_TIMEOUT_MS } from "./config.js";
16
- import { getBuiltinAgentProfile, OPENCODE_SDK_SERVER_BIN } from "./profiles.js";
17
+ import { getBuiltinAgentProfile } from "./profiles.js";
17
18
  const LLM_CONNECTION_FIELDS = [
18
19
  "provider",
19
20
  "endpoint",
@@ -273,11 +274,13 @@ function lowerAgentEngine(name, engine, config) {
273
274
  const platform = harness.id;
274
275
  const sdk = platform === "opencode-sdk";
275
276
  const builtin = getBuiltinAgentProfile(platform);
277
+ // An OpenCode engine without its own `bin` runs the newest OpenCode on PATH.
278
+ const opencodeFamily = platform === "opencode" || sdk;
276
279
  const profile = {
277
280
  name,
278
281
  platform,
279
282
  personaChannel: sdk ? "native" : (harness.agentBuilder?.personaChannel ?? "prompt"),
280
- bin: engine.bin ?? builtin?.bin ?? (sdk ? OPENCODE_SDK_SERVER_BIN : platform),
283
+ bin: opencodeFamily ? resolveOpencodeBin(engine.bin) : (engine.bin ?? builtin?.bin ?? platform),
281
284
  args: engine.args ?? builtin?.args ?? [],
282
285
  stdio: "captured",
283
286
  ...(builtin?.env ? { env: builtin.env } : {}),
@@ -3,5 +3,5 @@
3
3
  // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
4
  export { DEFAULT_AGENT_TIMEOUT_MS } from "./config.js";
5
5
  export { _setAgentDetectForTests, defaultWhich, detectAgentCliProfiles, pickDefaultAgentProfile } from "./detect.js";
6
- export { BUILTIN_AGENT_PROFILE_NAMES, getBuiltinAgentProfile, listBuiltinAgentProfiles, } from "./profiles.js";
6
+ export { getBuiltinAgentProfile, listBuiltinAgentProfiles, } from "./profiles.js";
7
7
  export { buildProposePrompt, buildReflectPrompt, parseAgentProposalPayload } from "./prompts.js";
@@ -8,116 +8,59 @@
8
8
  * coding-agent CLI. Named engines lower canonical harness metadata into this
9
9
  * intentionally small internal shape. The wrapper is in `./spawn.ts`.
10
10
  */
11
- import { COMMON_SPAWN_ENV_PASSTHROUGH, OPENCODE_ENV_PASSTHROUGH } from "../../core/spawn-env.js";
12
- // AKM_EVENT_SOURCE carries usage-event provenance (improve/task) so that akm
13
- // invocations a spawned agent makes are recorded as machine traffic, not user
14
- // demand (DRIFT-6). Without it in the passthrough whitelist, buildChildEnv drops
15
- // the stamp at the agent boundary — e.g. `akm wiki ingest` spawns an agent whose
16
- // `akm curate/show/search` tool-calls then log source='user', silently inflating
17
- // every lane's read-back (GRR). It is a provenance tag, never a secret.
18
- const COMMON_PASSTHROUGH = COMMON_SPAWN_ENV_PASSTHROUGH;
11
+ import { COMMON_SPAWN_ENV_PASSTHROUGH } from "../../core/spawn-env.js";
12
+ import { HARNESS_REGISTRY } from "../harnesses/index.js";
19
13
  /**
20
- * Built-in profiles for the agent CLIs akm knows out of the box: the five the
21
- * v1 spec calls out explicitly, plus the P2 harness adapters (copilot, pi,
22
- * amazonq, openhands — plan §"Capability matrix"). The fields here are
23
- * conservative defaults — every value is overridable from user config.
14
+ * Built-in profiles for the agent CLIs akm knows out of the box, DERIVED from
15
+ * the harness registry: each harness that declares `profile` defaults gets one,
16
+ * named by its id. The fields are conservative defaults, overridable from user
17
+ * config. Engine lowering selects captured stdio for unattended dispatch.
24
18
  *
25
- * Engine lowering selects captured stdio for unattended dispatch.
19
+ * Built lazily: the registry transitively imports this module, so it cannot be
20
+ * read at module load.
26
21
  */
27
- const BUILTINS = {
28
- opencode: {
29
- name: "opencode",
30
- bin: "opencode",
31
- args: ["run"],
32
- stdio: "interactive",
33
- envPassthrough: [...COMMON_PASSTHROUGH, ...OPENCODE_ENV_PASSTHROUGH],
34
- parseOutput: "text",
35
- },
36
- claude: {
37
- name: "claude",
38
- bin: "claude",
39
- args: [],
40
- stdio: "interactive",
41
- envPassthrough: [...COMMON_PASSTHROUGH, "ANTHROPIC_API_KEY", "CLAUDE_CONFIG"],
42
- parseOutput: "text",
43
- },
44
- codex: {
45
- name: "codex",
46
- bin: "codex",
47
- args: [],
48
- stdio: "interactive",
49
- envPassthrough: [...COMMON_PASSTHROUGH, "OPENAI_API_KEY", "CODEX_CONFIG"],
50
- parseOutput: "text",
51
- },
52
- gemini: {
53
- name: "gemini",
54
- bin: "gemini",
55
- args: [],
56
- stdio: "interactive",
57
- envPassthrough: [...COMMON_PASSTHROUGH, "GEMINI_API_KEY", "GOOGLE_API_KEY"],
58
- parseOutput: "text",
59
- },
60
- aider: {
61
- name: "aider",
62
- bin: "aider",
63
- args: ["--no-auto-commits"],
64
- stdio: "interactive",
65
- envPassthrough: [...COMMON_PASSTHROUGH, "OPENAI_API_KEY", "ANTHROPIC_API_KEY"],
66
- parseOutput: "text",
67
- },
68
- // ── P2 harness-adapter profiles (plan §"Capability matrix") ────────────────
69
- copilot: {
70
- name: "copilot",
71
- bin: "copilot",
72
- args: [],
73
- stdio: "interactive",
74
- envPassthrough: [...COMMON_PASSTHROUGH, "GH_TOKEN", "GITHUB_TOKEN"],
75
- parseOutput: "text",
76
- },
77
- pi: {
78
- name: "pi",
79
- bin: "pi",
80
- args: [],
81
- stdio: "interactive",
82
- envPassthrough: [...COMMON_PASSTHROUGH, "PI_API_KEY", "ANTHROPIC_API_KEY", "OPENAI_API_KEY"],
83
- parseOutput: "text",
84
- },
85
- amazonq: {
86
- name: "amazonq",
87
- bin: "q",
88
- args: [],
89
- stdio: "interactive",
90
- envPassthrough: [...COMMON_PASSTHROUGH, "AWS_PROFILE", "AWS_REGION"],
91
- parseOutput: "text",
92
- },
93
- openhands: {
94
- name: "openhands",
95
- bin: "openhands",
96
- args: [],
97
- stdio: "interactive",
98
- envPassthrough: [...COMMON_PASSTHROUGH, "LLM_MODEL", "LLM_API_KEY", "LLM_BASE_URL"],
99
- parseOutput: "text",
100
- },
101
- };
22
+ let builtins;
23
+ function builtinProfiles() {
24
+ if (builtins)
25
+ return builtins;
26
+ builtins = {};
27
+ for (const harness of HARNESS_REGISTRY) {
28
+ if (!harness.profile)
29
+ continue;
30
+ builtins[harness.id] = {
31
+ name: harness.id,
32
+ bin: harness.profile.bin,
33
+ args: harness.profile.args,
34
+ stdio: "interactive",
35
+ // AKM_EVENT_SOURCE (in the common set) carries usage-event provenance so akm
36
+ // invocations a spawned agent makes are recorded as machine traffic; it is a
37
+ // provenance tag, never a secret.
38
+ envPassthrough: [...COMMON_SPAWN_ENV_PASSTHROUGH, ...harness.profile.envPassthrough],
39
+ parseOutput: "text",
40
+ };
41
+ }
42
+ return builtins;
43
+ }
102
44
  /**
103
45
  * Binary the `opencode-sdk` harness needs on PATH.
104
46
  *
105
- * The embedded SDK is not self-contained, and that is the SDK's own design
106
- * rather than a consequence of how akm drives it: `@opencode-ai/sdk` ships
107
- * with `"dependencies": {}` and its `createOpencodeServer` is itself a
108
- * `spawn("opencode", ["serve", ...])`. akm's runner spawns the same binary
109
- * directly and talks HTTP to it (see `harnesses/opencode-sdk/sdk-runner.ts`),
47
+ * The embedded client is not self-contained, and that is the SDK's own design
48
+ * rather than a consequence of how akm drives it: `@opencode-ai/sdk` (OpenCode 1)
49
+ * ships with `"dependencies": {}` and its `createOpencodeServer` is itself a
50
+ * `spawn("opencode", ["serve", ...])`; `@opencode/client` (OpenCode 2, the
51
+ * default) is an HTTP client only and starts nothing. akm's runner spawns a
52
+ * private `opencode serve` of the engine's detected major (
53
+ * never the v2 background service) and talks HTTP to it through that major's
54
+ * adapter (see `harnesses/opencode-sdk/sdk-runner.ts`, `v1-adapter.ts`, `v2-adapter.ts`),
110
55
  * so the `opencode` binary gates the SDK path exactly as it gates the CLI
111
56
  * path — a host with the npm package but no binary can dispatch neither.
112
- * `opencode-sdk` deliberately has no {@link BUILTINS} entry — it dispatches
57
+ * `opencode-sdk` deliberately declares no `profile` — it dispatches
113
58
  * without argv construction — so this is the one place that pairing lives.
114
59
  */
115
60
  export const OPENCODE_SDK_SERVER_BIN = "opencode";
116
- /** Names of the canonical built-in harness descriptors. Stable, sorted. */
117
- export const BUILTIN_AGENT_PROFILE_NAMES = Object.freeze(Object.keys(BUILTINS).sort());
118
61
  /** Returns the built-in descriptor for a canonical harness id. */
119
62
  export function getBuiltinAgentProfile(name) {
120
- return BUILTINS[name];
63
+ return builtinProfiles()[name];
121
64
  }
122
65
  /**
123
66
  * Return a copy of every canonical built-in descriptor keyed by harness id.
@@ -125,7 +68,7 @@ export function getBuiltinAgentProfile(name) {
125
68
  */
126
69
  export function listBuiltinAgentProfiles() {
127
70
  const out = {};
128
- for (const [name, profile] of Object.entries(BUILTINS)) {
71
+ for (const [name, profile] of Object.entries(builtinProfiles())) {
129
72
  out[name] = { ...profile };
130
73
  }
131
74
  return out;
@@ -4,7 +4,7 @@
4
4
  import { ConfigError } from "../../core/errors.js";
5
5
  import { withSchemaInstruction } from "../../core/structured.js";
6
6
  import { MODEL_WORK_POLICY_ID } from "../../execution/source.js";
7
- import { HARNESS_MODEL_WORK_IDS, HARNESS_NATIVE_AGENT_IDS } from "../harnesses/ids.js";
7
+ import { getHarness } from "../harnesses/index.js";
8
8
  import { composeConversationFallbackPrompt } from "./conversation-fallback.js";
9
9
  import { composePersonaFallbackPrompt } from "./persona-fallback.js";
10
10
  /** A tool selection that actually names tools (not omitted, null, or empty). */
@@ -77,7 +77,7 @@ export function createAgentRequestLowerer(options) {
77
77
  }
78
78
  }
79
79
  else if (typeof request.agent === "string") {
80
- if (!HARNESS_NATIVE_AGENT_IDS.has(options.adapter)) {
80
+ if (!getHarness(options.adapter)?.capabilities.nativeAgent) {
81
81
  throw new ConfigError(`The ${options.adapter} transport cannot consume native agent selector ${JSON.stringify(request.agent)}.`, "INVALID_CONFIG_FILE");
82
82
  }
83
83
  dispatch.agent = request.agent;
@@ -99,7 +99,7 @@ export function createAgentRequestLowerer(options) {
99
99
  }
100
100
  }
101
101
  if (modelWork) {
102
- if (!HARNESS_MODEL_WORK_IDS.has(options.adapter)) {
102
+ if (!getHarness(options.adapter)?.capabilities.modelWork) {
103
103
  throw new ConfigError(`The ${options.adapter} transport cannot enforce the model-work tool policy.`, "INVALID_CONFIG_FILE");
104
104
  }
105
105
  // The builder selects its own confined agent or flags; another agent would replace them.
@@ -27,6 +27,11 @@ export class AiderHarness extends BaseHarness {
27
27
  id = "aider";
28
28
  displayName = "Aider";
29
29
  agentBuilder = aiderBuilder;
30
+ profile = {
31
+ bin: "aider",
32
+ args: ["--no-auto-commits"],
33
+ envPassthrough: ["OPENAI_API_KEY", "ANTHROPIC_API_KEY"],
34
+ };
30
35
  resultExtractor = aiderResultExtractor;
31
36
  // ── Workflow-engine descriptor (plan §"Capability matrix", P2) ────────────
32
37
  // No flag-shaped resume: Aider persists context in chat-history files
@@ -28,6 +28,7 @@ export class AmazonqHarness extends BaseHarness {
28
28
  id = "amazonq";
29
29
  displayName = "Amazon Q Developer CLI";
30
30
  agentBuilder = amazonqBuilder;
31
+ profile = { bin: "q", args: [], envPassthrough: ["AWS_PROFILE", "AWS_REGION"] };
31
32
  resultExtractor = amazonqResultExtractor;
32
33
  // ── Workflow-engine descriptor (plan §"Capability matrix", P2) ────────────
33
34
  // No `identityEnv`: the matrix lists Q's identity markers as uncertain, and
@@ -7,9 +7,8 @@
7
7
  *
8
8
  * Detects a Claude Code installation (filesystem only, no network) and reads
9
9
  * its config to extract LLM connection details. API key VALUES are never
10
- * stored — only the env var name that holds them. The pluggable registry
11
- * (`HARNESS_CONFIG_IMPORTERS`) and the OpenCode importer stay in
12
- * `setup/harness-config-import.ts`, which imports this importer back.
10
+ * stored — only the env var name that holds them. `ClaudeHarness` declares it
11
+ * as its `configImporter`.
13
12
  *
14
13
  * Behaviour-preserving relocation.
15
14
  */
@@ -4,6 +4,7 @@
4
4
  import { caps } from "../shared.js";
5
5
  import { BaseHarness } from "../types.js";
6
6
  import { claudeBuilder } from "./agent-builder.js";
7
+ import { claudeCodeImporter } from "./config-import.js";
7
8
  import { claudeResultExtractor } from "./result-extractor.js";
8
9
  import { ClaudeCodeProvider } from "./session-log.js";
9
10
  export { claudeBuilder } from "./agent-builder.js";
@@ -22,7 +23,9 @@ export class ClaudeHarness extends BaseHarness {
22
23
  // session-log provider, so offering it as a stash source is functional.
23
24
  setupDetectionDir = ".claude";
24
25
  agentBuilder = claudeBuilder;
26
+ profile = { bin: "claude", args: [], envPassthrough: ["ANTHROPIC_API_KEY", "CLAUDE_CONFIG"] };
25
27
  resultExtractor = claudeResultExtractor;
28
+ configImporter = claudeCodeImporter;
26
29
  // ── Workflow-engine descriptor (plan §"Capability matrix", P2) ────────────
27
30
  // Session-id env marker: presence of a concrete session id (not the bare
28
31
  // "running under Claude Code" flag) attributes a run to this harness.
@@ -32,6 +35,8 @@ export class ClaudeHarness extends BaseHarness {
32
35
  sessionLogs: true,
33
36
  agentDispatch: true,
34
37
  detection: true,
38
+ modelWork: true,
39
+ nativeAgent: true,
35
40
  configImport: true,
36
41
  });
37
42
  }
@@ -20,6 +20,7 @@ export class CodexHarness extends BaseHarness {
20
20
  // No `setupDetectionDir`: `~/.codex` holds credentials and session rollouts,
21
21
  // not assets, so `akm setup` must not offer it as a stash source.
22
22
  agentBuilder = codexBuilder;
23
+ profile = { bin: "codex", args: [], envPassthrough: ["OPENAI_API_KEY", "CODEX_CONFIG"] };
23
24
  resultExtractor = codexResultExtractor;
24
25
  // ── Workflow-engine descriptor (plan §"Capability matrix", P2) ────────────
25
26
  // No flag-shaped resume: codex resume is the `exec resume <id>` SUBCOMMAND
@@ -28,6 +28,7 @@ export class CopilotHarness extends BaseHarness {
28
28
  id = "copilot";
29
29
  displayName = "GitHub Copilot CLI";
30
30
  agentBuilder = copilotBuilder;
31
+ profile = { bin: "copilot", args: [], envPassthrough: ["GH_TOKEN", "GITHUB_TOKEN"] };
31
32
  resultExtractor = copilotResultExtractor;
32
33
  // ── Workflow-engine descriptor (plan §"Capability matrix", P2) ────────────
33
34
  // Session-id env marker only. The matrix's other candidates (GH_TOKEN,
@@ -27,6 +27,7 @@ export class GeminiHarness extends BaseHarness {
27
27
  id = "gemini";
28
28
  displayName = "Gemini CLI";
29
29
  agentBuilder = geminiBuilder;
30
+ profile = { bin: "gemini", args: [], envPassthrough: ["GEMINI_API_KEY", "GOOGLE_API_KEY"] };
30
31
  resultExtractor = geminiResultExtractor;
31
32
  // ── Workflow-engine descriptor (plan §"Capability matrix", P2) ────────────
32
33
  // The matrix's identity marker: Gemini CLI stamps GEMINI_CLI=1 only on
@@ -2,16 +2,17 @@
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
- * Single registration point for every akm agent harness (#562).
5
+ * Single registration point for every akm agent harness (#562, #1095).
6
6
  *
7
- * `HARNESS_REGISTRY` is the ONE source of truth that replaces the three
8
- * previously-disconnected registries (session-logs index, agent profiles,
9
- * config/setup platform strings). All derived exports below are computed from
10
- * it, so adding a harness is a single entry here instead of ~16 scattered edits.
7
+ * `HARNESS_REGISTRY` is the ONE source of truth. Each harness is a
8
+ * self-contained folder (`./<id>/index.ts`) exporting its descriptor; the
9
+ * config schema's platform enum, the built-in agent profiles, command
10
+ * builders, setup choices and the docs table all derive from the array below.
11
11
  *
12
- * This module is a dependency-graph LEAF: it imports nothing from
13
- * `core/config`. `core/config/config-types.ts` derives `VALID_HARNESS_IDS`
14
- * from here, which keeps the import direction acyclic (config ← harnesses).
12
+ * Adding a harness: create `./<id>/index.ts`, add its class here. Removing:
13
+ * delete both. This module must stay free of runtime `core/config` imports
14
+ * (config derives `VALID_HARNESS_IDS` from here), so a harness's heavy runner
15
+ * (e.g. `opencode-sdk/sdk-runner.ts`) is imported by its caller, not here.
15
16
  */
16
17
  import { AiderHarness } from "./aider/index.js";
17
18
  import { AmazonqHarness } from "./amazonq/index.js";
@@ -19,26 +20,14 @@ import { ClaudeHarness } from "./claude/index.js";
19
20
  import { CodexHarness } from "./codex/index.js";
20
21
  import { CopilotHarness } from "./copilot/index.js";
21
22
  import { GeminiHarness } from "./gemini/index.js";
22
- import { HARNESS_ID_TABLE } from "./ids.js";
23
23
  import { OpencodeHarness } from "./opencode/index.js";
24
- // Import the descriptor from its LEAF module, not the per-harness barrel: the
25
- // barrel re-exports `./sdk-runner` (which imports `core/config`, and
26
- // `core/config/config-types` derives `VALID_HARNESS_IDS` back from this file).
27
- // Importing the class through the barrel would form an initialization cycle
28
- // that throws a TDZ error when the barrel is the first module loaded in a fresh
29
- // graph (see ./opencode-sdk/harness.ts). This keeps the registry a config-leaf.
30
- import { OpencodeSdkHarness } from "./opencode-sdk/harness.js";
24
+ import { OpencodeSdkHarness } from "./opencode-sdk/index.js";
31
25
  import { OpenhandsHarness } from "./openhands/index.js";
32
26
  import { PiHarness } from "./pi/index.js";
33
27
  import { isSessionLogHarness } from "./types.js";
34
28
  export { isSessionLogHarness } from "./types.js";
35
- // Each harness descriptor (ClaudeHarness, OpencodeHarness, OpencodeSdkHarness)
36
- // lives in its own per-harness directory alongside that harness's session-log
37
- // reader, agent builder, config importer, and/or SDK runner (#563/#564). They
38
- // are imported above and registered in HARNESS_REGISTRY below.
39
29
  /**
40
- * The single registration point. Add a harness here (and nothing else) to make
41
- * every derived registry pick it up.
30
+ * The single registration point.
42
31
  *
43
32
  * Typed `as const` (not `AkmHarness[]`) so each entry keeps its literal `id`,
44
33
  * which lets `VALID_HARNESS_IDS` stay a literal tuple — `HarnessId`,
@@ -62,23 +51,6 @@ export const HARNESS_REGISTRY = Object.freeze([
62
51
  new AmazonqHarness(),
63
52
  new OpenhandsHarness(),
64
53
  ]);
65
- // Construction-time drift guard (WI-9.8 KILL 3, D.3 edge B): ./ids.ts's
66
- // HARNESS_ID_TABLE is config's dependency-free mirror of (id, agentDispatch)
67
- // for every registered harness, kept out of core/config's import graph so
68
- // config doesn't have to import this (heavier) barrel. Assert they match so
69
- // the mirror can never silently drift — a new/reordered/recapability'd
70
- // harness that forgets to update ./ids.ts fails loudly here instead of
71
- // quietly breaking config's platform validation.
72
- if (HARNESS_REGISTRY.length !== HARNESS_ID_TABLE.length) {
73
- throw new Error(`harnesses/ids.ts HARNESS_ID_TABLE has ${HARNESS_ID_TABLE.length} entries but HARNESS_REGISTRY has ${HARNESS_REGISTRY.length} — update ids.ts to match.`);
74
- }
75
- HARNESS_REGISTRY.forEach((h, i) => {
76
- const expected = HARNESS_ID_TABLE[i];
77
- if (!expected || expected.id !== h.id || expected.agentDispatch !== h.capabilities.agentDispatch) {
78
- throw new Error(`harnesses/ids.ts HARNESS_ID_TABLE[${i}] (${expected?.id ?? "<missing>"}, agentDispatch=${expected?.agentDispatch}) ` +
79
- `does not match HARNESS_REGISTRY[${i}] (${h.id}, agentDispatch=${h.capabilities.agentDispatch}) — update ids.ts to match.`);
80
- }
81
- });
82
54
  /** Lookup by the one supported harness id. */
83
55
  export const HARNESS_BY_ID = new Map(HARNESS_REGISTRY.map((h) => [h.id, h]));
84
56
  /**
@@ -96,6 +68,13 @@ export const VALID_HARNESS_IDS = Object.freeze(HARNESS_REGISTRY.map((h) => h.id)
96
68
  export const SESSION_LOG_HARNESSES = HARNESS_REGISTRY.filter(isSessionLogHarness);
97
69
  /** Harnesses that can be dispatched as an agent CLI / SDK. */
98
70
  export const AGENT_DISPATCH_HARNESSES = HARNESS_REGISTRY.filter((h) => h.capabilities.agentDispatch);
71
+ const idsWith = (flag) => new Set(HARNESS_REGISTRY.filter(flag).map((h) => h.id));
72
+ /** Harness ids whose `capabilities.agentDispatch` is `true`. */
73
+ export const HARNESS_AGENT_DISPATCH_IDS = idsWith((h) => h.capabilities.agentDispatch);
74
+ /** Harness ids that confine the model-work tool policy, so unattended model work may run on them. */
75
+ export const HARNESS_MODEL_WORK_IDS = idsWith((h) => h.capabilities.modelWork);
76
+ /** Harness ids that can run a named native agent, so an engine may name a default `agent`. */
77
+ export const HARNESS_NATIVE_AGENT_IDS = idsWith((h) => h.capabilities.nativeAgent);
99
78
  /**
100
79
  * Resolve an exact harness id to its descriptor, or `undefined` if unknown.
101
80
  */
@@ -10,30 +10,75 @@
10
10
  * `getCommandBuilder`, the default builder, flag/tool helpers) stays in
11
11
  * `agent/builders.ts`, which imports this builder back into `BUILTIN_BUILDERS`.
12
12
  *
13
- * Behaviour-preserving relocation: the produced argv is byte-identical to the
14
- * pre-migration `opencodeBuilder`. The builder's `platform` stays `'opencode'`
15
- * (the canonical harness id).
13
+ * The builder's `platform` stays `'opencode'` (the canonical harness id). The
14
+ * one thing that depends on the OpenCode major (`./version.ts`, one cached
15
+ * `--version` run per binary) is `--standalone`, added on OpenCode 2 only; the
16
+ * OpenCode 1 argv is byte-identical to the pre-OpenCode-2 builder's.
16
17
  */
17
- import { modelFromArgs, resolveDispatchModel } from "../../agent/builder-shared.js";
18
+ import { modelFromArgs, resolveDispatchModel, } from "../../agent/builder-shared.js";
18
19
  import { createAgentRequestLowerer } from "../../agent/request-lowering.js";
19
20
  import { MODEL_WORK_AGENT_INFERENCE, opencodeInferenceConfig } from "./model-config.js";
20
21
  import { MODEL_WORK_OPENCODE_AGENT, modelWorkOpencodeConfig, modelWorkPluginEnv } from "./model-work-agent.js";
22
+ import { detectOpencodeMajor } from "./version.js";
21
23
  /**
22
- * OpenCode builder.
23
- * Command shape: opencode run [--agent <name>] [--model <m>] -- "<prompt>"
24
+ * OpenCode 2's `run` attaches to a shared background service by default,
25
+ * starting it when absent and leaving it running after the run; `--standalone`
26
+ * runs a private server that exits with the run (and reads the
27
+ * `OPENCODE_CONFIG_CONTENT` model work travels in, which the service ignores).
28
+ * OpenCode 1 has no such flag. An engine whose own `args` name `--server`
29
+ * chose that server, so it gets no `--standalone`. `--auto` is never passed:
30
+ * a non-interactive run rejects a permission request, on both majors.
31
+ */
32
+ const STANDALONE_FLAG = "--standalone";
33
+ function standalone(major, flags) {
34
+ if (major !== 2)
35
+ return [];
36
+ const chosen = flags.some((arg) => arg === STANDALONE_FLAG || arg === "--server" || arg.startsWith("--server="));
37
+ return chosen ? [] : [STANDALONE_FLAG];
38
+ }
39
+ /**
40
+ * The flags before the `--` separator of an ordinary dispatch: the engine's
41
+ * `args` (without any `--model` when the request names one), then `--agent`
42
+ * and `--model`. Both majors spell these the same way.
43
+ */
44
+ function ordinaryFlags(profile, req) {
45
+ const args = req.model ? [] : [...profile.args];
46
+ if (req.model) {
47
+ for (let index = 0; index < profile.args.length; index += 1) {
48
+ const arg = profile.args[index];
49
+ if (arg === undefined)
50
+ continue;
51
+ if (arg === "--model") {
52
+ index += 1;
53
+ }
54
+ else if (!arg.startsWith("--model=")) {
55
+ args.push(arg);
56
+ }
57
+ }
58
+ }
59
+ if (req.agent) {
60
+ args.push("--agent", req.agent);
61
+ }
62
+ if (req.model) {
63
+ args.push("--model", resolveDispatchModel(req, profile, "opencode"));
64
+ }
65
+ return args;
66
+ }
67
+ /**
68
+ * OpenCode builder: `opencode run [--standalone] [--agent <name>] [--model <m>] -- "<prompt>"`
69
+ * (`--standalone` on OpenCode 2 only).
24
70
  *
25
71
  * `opencode run` has no system-prompt option (1.18.25 prints its usage and
26
72
  * exits 1 on `--system-prompt`), so the shared lowerer composes a persona
27
73
  * into the prompt.
28
74
  *
29
- * Tool policy is omitted — opencode manages tool access through its own agent
75
+ * Tool policy is omitted: opencode manages tool access through its own agent
30
76
  * config files, not via CLI flags. The one exception is the model-work tool
31
77
  * policy: the builder injects its confined agent through
32
78
  * `OPENCODE_CONFIG_CONTENT` and selects it with `--agent`. That command is
33
- * akm's own (`opencode run --agent akm-model-work`): the engine's `args` are
34
- * left out, because one such as `--attach` or `--dir` would move the run out
35
- * of the injected config or the scratch working directory. Only the model they
36
- * name is kept.
79
+ * akm's own: the engine's `args` are left out, because one such as `--attach`
80
+ * or `--dir` would move the run out of the injected config or the scratch
81
+ * working directory. Only the model they name is kept.
37
82
  *
38
83
  * Model work's agent carries the request's inference options
39
84
  * (`./model-config.ts`). Any other dispatch injects nothing and carries none, so
@@ -49,6 +94,7 @@ export const opencodeBuilder = {
49
94
  inference: MODEL_WORK_AGENT_INFERENCE,
50
95
  }),
51
96
  build(profile, req) {
97
+ const major = detectOpencodeMajor(profile.bin).major;
52
98
  if (req.modelWork) {
53
99
  const model = req.model ?? modelFromArgs(profile.args);
54
100
  const { agentOptions } = opencodeInferenceConfig(req.inference, true);
@@ -56,6 +102,7 @@ export const opencodeBuilder = {
56
102
  argv: [
57
103
  profile.bin,
58
104
  "run",
105
+ ...standalone(major, []),
59
106
  "--agent",
60
107
  MODEL_WORK_OPENCODE_AGENT,
61
108
  ...(model ? ["--model", model] : []),
@@ -68,29 +115,7 @@ export const opencodeBuilder = {
68
115
  },
69
116
  };
70
117
  }
71
- const args = req.model ? [] : [...profile.args];
72
- if (req.model) {
73
- for (let index = 0; index < profile.args.length; index += 1) {
74
- const arg = profile.args[index];
75
- if (arg === undefined)
76
- continue;
77
- if (arg === "--model") {
78
- index += 1;
79
- }
80
- else if (!arg.startsWith("--model=")) {
81
- args.push(arg);
82
- }
83
- }
84
- }
85
- if (req.agent) {
86
- args.push("--agent", req.agent);
87
- }
88
- if (req.model) {
89
- const resolved = resolveDispatchModel(req, profile, "opencode");
90
- args.push("--model", resolved);
91
- }
92
- args.push("--");
93
- args.push(req.prompt);
94
- return { argv: [profile.bin, ...args] };
118
+ const flags = ordinaryFlags(profile, req);
119
+ return { argv: [profile.bin, ...flags, ...standalone(major, flags), "--", req.prompt] };
95
120
  },
96
121
  };