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.
- package/CHANGELOG.md +265 -0
- package/STABILITY.md +79 -53
- package/dist/api-node.mjs +20 -0
- package/dist/api.d.ts +23 -0
- package/dist/api.js +45 -0
- package/dist/assets/hints/cli-hints-full.md +13 -27
- package/dist/assets/hints/cli-hints-short.md +1 -1
- package/dist/assets/improve-strategies/default.json +3 -3
- package/dist/assets/prompts/consolidate-pair.md +9 -7
- package/dist/cli/shared.js +31 -48
- package/dist/cli/unknown-flags.js +6 -43
- package/dist/cli.js +8 -34
- package/dist/commands/agent/contribute-cli.js +1 -1
- package/dist/commands/completions.js +1 -2
- package/dist/commands/config-cli.js +5 -28
- package/dist/commands/env/env-cli.js +5 -5
- package/dist/commands/env/secret-cli.js +2 -2
- package/dist/commands/health/checks.js +41 -16
- package/dist/commands/health/plugin-staleness.js +52 -22
- package/dist/commands/improve/autonomy-gate.js +5 -3
- package/dist/commands/improve/consolidate/pair-pass.js +7 -1
- package/dist/commands/improve/consolidate.js +6 -35
- package/dist/commands/improve/distill/lesson-update.js +91 -0
- package/dist/commands/improve/distill-guards.js +1 -43
- package/dist/commands/improve/distill.js +140 -20
- package/dist/commands/improve/eligibility.js +14 -1
- package/dist/commands/improve/extract-cli.js +1 -1
- package/dist/commands/improve/improve-cli.js +87 -102
- package/dist/commands/improve/improve-strategies.js +8 -12
- package/dist/commands/improve/improve.js +2 -2
- package/dist/commands/improve/loop-stages.js +0 -1
- package/dist/commands/improve/planner.js +0 -1
- package/dist/commands/improve/preparation.js +40 -99
- package/dist/commands/improve/proactive-maintenance.js +2 -2
- package/dist/commands/improve/reflect-noise.js +3 -55
- package/dist/commands/improve/reflect.js +3 -8
- package/dist/commands/improve/retrieval-scope.js +2 -2
- package/dist/commands/improve/salience.js +2 -9
- package/dist/commands/improve/stage.js +14 -9
- package/dist/commands/lint/index.js +5 -20
- package/dist/commands/observability-cli.js +3 -9
- package/dist/commands/proposal/drain.js +39 -6
- package/dist/commands/proposal/proposal-cli.js +11 -31
- package/dist/commands/proposal/repository.js +3 -3
- package/dist/commands/read/knowledge.js +3 -3
- package/dist/commands/read/remember-cli.js +0 -13
- package/dist/commands/read/search-cli.js +29 -97
- package/dist/commands/read/search.js +0 -11
- package/dist/commands/read/show.js +0 -19
- package/dist/commands/sources/plugin-upgrade.js +164 -101
- package/dist/commands/sources/sources-cli.js +1 -15
- package/dist/commands/sources/stash-cli.js +4 -12
- package/dist/commands/tasks/tasks-cli.js +4 -115
- package/dist/commands/workflow/plan.js +11 -33
- package/dist/commands/workflow-cli.js +2 -7
- package/dist/core/adapter/adapter-ids.js +1 -2
- package/dist/core/adapter/adapters/agent-skills-adapter.js +0 -4
- package/dist/core/adapter/adapters/akm-adapter.js +9 -48
- package/dist/core/adapter/adapters/akm-lint.js +10 -26
- package/dist/core/adapter/adapters/akm-metadata.js +2 -72
- package/dist/core/adapter/adapters/akm-task-adapter.js +0 -5
- package/dist/core/adapter/adapters/akm-workflow-adapter.js +9 -28
- package/dist/core/adapter/adapters/dotenv-adapter.js +1 -20
- package/dist/core/adapter/adapters/generic-files-adapter.js +0 -10
- package/dist/core/adapter/adapters/llm-wiki-adapter.js +0 -4
- package/dist/core/adapter/adapters/tool-dir-shared.js +0 -24
- package/dist/core/adapter/adapters/website-snapshot-adapter.js +4 -4
- package/dist/core/adapter/recognize-match.js +3 -4
- package/dist/core/common.js +18 -2
- package/dist/core/config/config-schema.js +12 -15
- package/dist/core/config/config-types.js +6 -15
- package/dist/core/config/config-walker.js +6 -23
- package/dist/core/config/config.js +17 -80
- package/dist/core/config/engine-semantics.js +7 -3
- package/dist/core/config/schema/engines.js +7 -7
- package/dist/core/config/schema/improve-processes.js +8 -61
- package/dist/core/config/schema/improve.js +2 -21
- package/dist/core/config/schema/index-config.js +6 -39
- package/dist/core/env-secret-ref.js +2 -45
- package/dist/core/errors.js +1 -2
- package/dist/core/extra-params.js +2 -86
- package/dist/core/improve-result.js +11 -1
- package/dist/core/mutation-target.js +2 -2
- package/dist/core/recognition-util.js +4 -5
- package/dist/core/warn.js +21 -4
- package/dist/core/write-source.js +3 -3
- package/dist/execution/target-ref.js +1 -2
- package/dist/indexer/search/db-search.js +4 -19
- package/dist/indexer/search/fts-query.js +0 -19
- package/dist/integrations/agent/engine-resolution.js +5 -2
- package/dist/integrations/agent/index.js +1 -1
- package/dist/integrations/agent/profiles.js +41 -98
- package/dist/integrations/agent/request-lowering.js +3 -3
- package/dist/integrations/harnesses/aider/index.js +5 -0
- package/dist/integrations/harnesses/amazonq/index.js +1 -0
- package/dist/integrations/harnesses/claude/config-import.js +2 -3
- package/dist/integrations/harnesses/claude/index.js +5 -0
- package/dist/integrations/harnesses/codex/index.js +1 -0
- package/dist/integrations/harnesses/copilot/index.js +1 -0
- package/dist/integrations/harnesses/gemini/index.js +1 -0
- package/dist/integrations/harnesses/index.js +18 -39
- package/dist/integrations/harnesses/opencode/agent-builder.js +60 -35
- package/dist/integrations/harnesses/opencode/config-import.js +121 -52
- package/dist/integrations/harnesses/opencode/index.js +22 -0
- package/dist/integrations/harnesses/opencode/plugin-layout.js +68 -0
- package/dist/integrations/harnesses/opencode/session-log-types.js +12 -0
- package/dist/integrations/harnesses/opencode/session-log-v1.js +72 -0
- package/dist/integrations/harnesses/opencode/session-log-v2.js +140 -0
- package/dist/integrations/harnesses/opencode/session-log.js +104 -115
- package/dist/integrations/harnesses/opencode/version.js +98 -0
- package/dist/integrations/harnesses/opencode-sdk/{harness.js → index.js} +5 -17
- package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +105 -206
- package/dist/integrations/harnesses/opencode-sdk/v1-adapter.js +199 -0
- package/dist/integrations/harnesses/opencode-sdk/v2-adapter.js +228 -0
- package/dist/integrations/harnesses/opencode-sdk/wire.js +66 -0
- package/dist/integrations/harnesses/openhands/index.js +1 -0
- package/dist/integrations/harnesses/pi/index.js +1 -0
- package/dist/integrations/harnesses/shared.js +2 -0
- package/dist/integrations/harnesses/types.js +2 -0
- package/dist/llm/client.js +41 -11
- package/dist/output/cli-hints.js +1 -1
- package/dist/output/context.js +10 -19
- package/dist/output/format-exempt.js +5 -8
- package/dist/output/shapes/helpers.js +1 -22
- package/dist/output/shapes/passthrough.js +0 -1
- package/dist/output/shapes.js +3 -31
- package/dist/output/stdout.js +5 -1
- package/dist/output/text/command-format.js +6 -1
- package/dist/output/text/workflow-format.js +4 -22
- package/dist/scripts/akm-migrate-node.js +13049 -14013
- package/dist/scripts/akm-migrate.js +16316 -17276
- package/dist/setup/detect.js +10 -4
- package/dist/setup/harness-config-import.js +5 -14
- package/dist/setup/setup.js +1 -1
- package/dist/setup/steps/tasks.js +12 -13
- package/dist/storage/repositories/task-history-repository.js +0 -5
- package/dist/storage/repositories/workflow-runs-repository.js +5 -15
- package/dist/tasks/embedded.js +2 -1
- package/dist/tasks/run/task-history.js +0 -2
- package/dist/tasks/schedule-gates.js +39 -0
- package/dist/tasks/scheduler-binding.js +0 -16
- package/dist/tasks/scheduler-sync.js +3 -97
- package/dist/tasks/source/bounded-document.js +1 -1
- package/dist/tasks/source/task-source-v4.js +2 -12
- package/dist/tasks/task-run-reserved-flags.js +2 -31
- package/dist/workflows/authoring/authoring.js +6 -6
- package/dist/workflows/compile.js +8 -65
- package/dist/workflows/exec/child-workflow.js +22 -3
- package/dist/workflows/exec/native-executor.js +12 -147
- package/dist/workflows/exec/run-workflow.js +31 -103
- package/dist/workflows/exec/step-work.js +9 -178
- package/dist/workflows/exec/unit-dispatch.js +0 -10
- package/dist/workflows/freeze/environment.js +3 -44
- package/dist/workflows/freeze/freeze.js +8 -41
- package/dist/workflows/freeze/targets/child-workflow.js +27 -63
- package/dist/workflows/freeze/targets/command.js +2 -16
- package/dist/workflows/freeze/targets/shell.js +0 -2
- package/dist/workflows/freeze/task-bindings.js +2 -28
- package/dist/workflows/ir/params.js +12 -2
- package/dist/workflows/parser.js +82 -232
- package/dist/workflows/plan.js +1 -1
- package/dist/workflows/renderer.js +10 -19
- package/dist/workflows/runtime/run-plan.js +11 -53
- package/dist/workflows/runtime/runs.js +4 -27
- package/dist/workflows/source-files.js +12 -16
- package/dist/workflows/source-semantics.js +1 -93
- package/docs/README.md +1 -1
- package/docs/agents/README.md +3 -3
- package/docs/agents/agent-install.md +1 -1
- package/docs/architecture/adapters.md +17 -31
- package/docs/architecture/architecture.md +4 -5
- package/docs/architecture/improvement.md +52 -21
- package/docs/architecture/internals/improve-workflow.md +15 -8
- package/docs/architecture/internals/indexing.md +1 -1
- package/docs/architecture/internals/search.md +1 -2
- package/docs/architecture/internals/storage-locations.md +5 -5
- package/docs/architecture/persisted-data-compat.md +13 -4
- package/docs/architecture/specs/0.9.0-decisions.md +6 -2
- package/docs/architecture/specs/improve-collapse-churn-detector-design.md +2 -2
- package/docs/architecture/specs/workflow-format-unification.md +1 -6
- package/docs/architecture/testing/manual-testing-checklist.md +20 -29
- package/docs/architecture/testing/task-workflow-manual-tests.md +106 -203
- package/docs/architecture/testing/testing-workflow.md +1 -1
- package/docs/architecture/workflow-engine.md +5 -56
- package/docs/guides/author-workflows.md +39 -69
- package/docs/guides/capture-knowledge.md +1 -1
- package/docs/guides/claude-code-vs-akm-workflows.md +1 -2
- package/docs/guides/discover-and-load.md +4 -4
- package/docs/guides/getting-started.md +1 -1
- package/docs/guides/improve-the-library.md +7 -7
- package/docs/guides/recipes/headless-install.md +2 -2
- package/docs/guides/run-workflows.md +1 -1
- package/docs/guides/use-with-any-agent.md +49 -4
- package/docs/integration/bundling-akm.md +7 -6
- package/docs/maintainers/curate-workmap.md +1 -2
- package/docs/maintainers/release-checklist.md +30 -1
- package/docs/posts/akm-command-tour.md +3 -3
- package/docs/product-surface.md +1 -1
- package/docs/reference/README.md +1 -0
- package/docs/reference/api.md +65 -0
- package/docs/reference/asset-types.md +3 -5
- package/docs/reference/bundle-types.md +2 -4
- package/docs/reference/cli.md +129 -171
- package/docs/reference/configuration.md +84 -19
- package/docs/reference/data-and-telemetry.md +1 -1
- package/docs/reference/env-and-secrets.md +2 -2
- package/docs/reference/supported-formats.md +3 -4
- package/docs/reference/tasks.md +12 -19
- package/docs/reference/website-sources.md +1 -1
- package/docs/reference/workflow-schema.md +89 -317
- package/docs/reference/workflows.md +20 -48
- package/package.json +12 -2
- package/schemas/akm-config.json +2 -243
- package/schemas/akm-task.json +2 -3
- package/schemas/akm-workflow.json +47 -84
- package/dist/assets/improve-strategies/catchup.json +0 -41
- package/dist/assets/improve-strategies/proactive-maintenance.json +0 -24
- package/dist/assets/improve-strategies/quick.json +0 -17
- package/dist/assets/improve-strategies/reflect-distill.json +0 -29
- package/dist/assets/improve-strategies/thorough.json +0 -49
- package/dist/assets/tasks/improve/akm-improve-catchup.yml +0 -6
- package/dist/assets/tasks/improve/akm-improve-frequent.yml +0 -4
- package/dist/assets/tasks/improve/akm-improve-nightly.yml +0 -4
- package/dist/cli/retired-commands.js +0 -118
- package/dist/commands/tasks/validate.js +0 -176
- package/dist/core/config/legacy-source-shape-shim.js +0 -117
- package/dist/integrations/harnesses/ids.js +0 -28
- package/dist/tasks/source/task-source-v3-frozen.js +0 -424
- package/dist/tasks/source/task-to-v4.js +0 -863
- package/dist/workflows/exec/worktree.js +0 -372
- package/dist/workflows/freeze/child-output-references.js +0 -83
- package/dist/workflows/freeze/identity.js +0 -22
- package/dist/workflows/freeze/targets/script.js +0 -50
- package/dist/workflows/freeze/targets/task.js +0 -124
- package/dist/workflows/github-yaml.js +0 -506
- 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
|
|
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 {
|
|
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 ?
|
|
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
|
|
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 ??
|
|
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 {
|
|
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
|
|
12
|
-
|
|
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
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
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
|
-
*
|
|
19
|
+
* Built lazily: the registry transitively imports this module, so it cannot be
|
|
20
|
+
* read at module load.
|
|
26
21
|
*/
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
|
106
|
-
* rather than a consequence of how akm drives it: `@opencode-ai/sdk`
|
|
107
|
-
* with `"dependencies": {}` and its `createOpencodeServer` is itself a
|
|
108
|
-
* `spawn("opencode", ["serve", ...])
|
|
109
|
-
*
|
|
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
|
|
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
|
|
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(
|
|
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 {
|
|
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 (!
|
|
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 (!
|
|
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.
|
|
11
|
-
*
|
|
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
|
|
8
|
-
*
|
|
9
|
-
* config
|
|
10
|
-
*
|
|
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
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* from here,
|
|
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
|
-
|
|
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.
|
|
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
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
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
|
|
23
|
-
*
|
|
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
|
|
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
|
|
34
|
-
*
|
|
35
|
-
*
|
|
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
|
|
72
|
-
|
|
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
|
};
|