akm-cli 0.9.24 → 0.9.25-alpha.2
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 +173 -0
- package/dist/cli.js +1 -1
- package/dist/commands/health/checks.js +10 -11
- package/dist/commands/improve/consolidate/pair-pass.js +4 -2
- package/dist/commands/improve/consolidate.js +10 -4
- package/dist/commands/improve/execution.js +4 -11
- package/dist/commands/improve/extract-prompt.js +4 -4
- package/dist/commands/improve/extract.js +11 -13
- package/dist/commands/improve/improve-cli.js +65 -34
- package/dist/commands/improve/improve-strategies.js +49 -43
- package/dist/commands/improve/improve-usage-report.js +8 -17
- package/dist/commands/improve/loop-stages.js +3 -0
- package/dist/commands/improve/preparation.js +3 -1
- package/dist/commands/improve/reflect-noise.js +125 -0
- package/dist/commands/improve/reflect.js +105 -172
- package/dist/commands/improve/retrieval-gate.js +7 -2
- package/dist/commands/improve/stage.js +67 -24
- package/dist/commands/proposal/drain.js +11 -2
- package/dist/commands/proposal/proposal-cli.js +1 -5
- package/dist/commands/proposal/propose-cli.js +2 -2
- package/dist/commands/proposal/propose.js +72 -84
- package/dist/commands/proposal/validators/proposal-quality-validators.js +4 -2
- package/dist/commands/read/search-cli.js +0 -38
- package/dist/commands/remember.js +3 -3
- package/dist/commands/sources/schema-repair.js +1 -1
- package/dist/core/config/config-schema.js +37 -60
- package/dist/core/config/engine-semantics.js +15 -11
- package/dist/core/config/schema/improve-processes.js +18 -2
- package/dist/core/improve-result.js +3 -3
- package/dist/core/redaction.js +4 -0
- package/dist/core/spawn-env.js +25 -0
- package/dist/core/structured.js +11 -1
- package/dist/execution/source.js +10 -0
- package/dist/indexer/passes/memory-inference.js +2 -1
- package/dist/integrations/agent/builder-shared.js +15 -0
- package/dist/integrations/agent/config.js +1 -1
- package/dist/integrations/agent/engine-resolution.js +13 -31
- package/dist/integrations/agent/execution.js +48 -22
- package/dist/integrations/agent/index.js +1 -1
- package/dist/integrations/agent/profiles.js +2 -2
- package/dist/integrations/agent/prompts.js +55 -114
- package/dist/integrations/agent/request-lowering.js +21 -8
- package/dist/integrations/agent/runner-dispatch.js +96 -3
- package/dist/integrations/agent/runner.js +8 -2
- package/dist/integrations/harnesses/aider/agent-builder.js +10 -23
- package/dist/integrations/harnesses/aider/index.js +0 -5
- package/dist/integrations/harnesses/amazonq/agent-builder.js +9 -21
- package/dist/integrations/harnesses/amazonq/index.js +0 -5
- package/dist/integrations/harnesses/claude/agent-builder.js +47 -36
- package/dist/integrations/harnesses/claude/index.js +0 -14
- package/dist/integrations/harnesses/codex/agent-builder.js +3 -4
- package/dist/integrations/harnesses/codex/index.js +0 -4
- package/dist/integrations/harnesses/copilot/agent-builder.js +4 -13
- package/dist/integrations/harnesses/copilot/index.js +2 -7
- package/dist/integrations/harnesses/gemini/agent-builder.js +4 -13
- package/dist/integrations/harnesses/gemini/index.js +0 -5
- package/dist/integrations/harnesses/ids.js +12 -10
- package/dist/integrations/harnesses/opencode/agent-builder.js +41 -9
- package/dist/integrations/harnesses/opencode/index.js +0 -8
- package/dist/integrations/harnesses/opencode/model-config.js +33 -0
- package/dist/integrations/harnesses/opencode/model-work-agent.js +115 -0
- package/dist/integrations/harnesses/opencode-sdk/harness.js +2 -7
- package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +114 -28
- package/dist/integrations/harnesses/openhands/agent-builder.js +7 -21
- package/dist/integrations/harnesses/openhands/index.js +0 -5
- package/dist/integrations/harnesses/pi/agent-builder.js +7 -21
- package/dist/integrations/harnesses/pi/index.js +0 -5
- package/dist/llm/client.js +5 -0
- package/dist/llm/feature-gate.js +12 -9
- package/dist/llm/index-passes.js +2 -5
- package/dist/llm/memory-infer.js +6 -5
- package/dist/llm/structured-call.js +33 -11
- package/dist/output/shapes/passthrough.js +1 -0
- package/dist/scripts/akm-migrate-node.js +298 -228
- package/dist/scripts/akm-migrate.js +298 -228
- package/dist/workflows/exec/step-work.js +6 -5
- package/dist/workflows/exec/unit-dispatch.js +4 -13
- package/dist/workflows/freeze/step-values.js +1 -1
- package/docs/reference/cli.md +41 -18
- package/docs/reference/configuration.md +165 -12
- package/docs/reference/data-and-telemetry.md +2 -3
- package/docs/reference/workflow-schema.md +10 -9
- package/package.json +1 -1
- package/schemas/akm-config.json +108 -0
|
@@ -39,13 +39,11 @@
|
|
|
39
39
|
* - **schema** — the matrix places Aider in the "via prompt+validate" tier
|
|
40
40
|
* with *no* structured output mode at all (plan §"Structured-output
|
|
41
41
|
* normalization", tier "none"): there is no schema flag and no JSON output
|
|
42
|
-
* flag, so the JSON Schema
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
* lacks. No temp schema file is written — that is Codex's native-schema
|
|
48
|
-
* mechanism (`--output-schema`), which Aider does not have.
|
|
42
|
+
* flag, so the JSON Schema reaches it only as the instruction the shared
|
|
43
|
+
* request lowering appends to the prompt. Downstream, embedded-JSON
|
|
44
|
+
* extraction + the engine's shared retry-until-valid loop supply the
|
|
45
|
+
* validation Aider lacks. No temp schema file is written — that is Codex's
|
|
46
|
+
* native-schema mechanism (`--output-schema`), which Aider does not have.
|
|
49
47
|
* - **tools** — deliberately unconsumed. Aider has no per-tool allowlist
|
|
50
48
|
* flag; tool-ish behaviour is governed by its own switches (`--yes-always`,
|
|
51
49
|
* git integration, shell-command confirmation). A restrictive policy is
|
|
@@ -57,41 +55,31 @@
|
|
|
57
55
|
* durable source of truth; resume works even against a harness with no
|
|
58
56
|
* session model (plan §"Session, MCP, and identity across harnesses" —
|
|
59
57
|
* Aider is the plan's named example).
|
|
60
|
-
* - **
|
|
61
|
-
*
|
|
58
|
+
* - **inference** — not translated: the shared lowering reports each field of
|
|
59
|
+
* the request's inference as untranslated.
|
|
62
60
|
*
|
|
63
61
|
* Registered: `aiderBuilder` is `AiderHarness.agentBuilder` (`./index.ts`),
|
|
64
62
|
* one of the ten harnesses `HARNESS_REGISTRY` constructs
|
|
65
63
|
* (`harnesses/index.ts`); `agent/builders.ts` derives `BUILTIN_BUILDERS` from
|
|
66
64
|
* that registry, so this builder is reachable under the `"aider"` platform
|
|
67
|
-
* name without any further wiring.
|
|
68
|
-
* `structuredOutput: "none"` alongside it (`./index.ts`).
|
|
65
|
+
* name without any further wiring.
|
|
69
66
|
*/
|
|
70
67
|
import { resolveDispatchModel } from "../../agent/builder-shared.js";
|
|
71
68
|
import { createAgentRequestLowerer } from "../../agent/request-lowering.js";
|
|
72
69
|
/** Canonical harness/platform id used for model-alias resolution. */
|
|
73
70
|
export const AIDER_PLATFORM = "aider";
|
|
74
|
-
/**
|
|
75
|
-
* Assemble the `--message` payload: optional system text, the task prompt,
|
|
76
|
-
* and — when a schema is requested — the same schema directive the workflow
|
|
77
|
-
* engine's prompt assembly uses (Aider has no native structured output, so
|
|
78
|
-
* the prompt is the only channel; plan §"Structured-output normalization",
|
|
79
|
-
* tier "none").
|
|
80
|
-
*/
|
|
71
|
+
/** Assemble the `--message` payload: optional system text, then the task prompt. */
|
|
81
72
|
function buildMessagePayload(req) {
|
|
82
73
|
const sections = [];
|
|
83
74
|
if (req.systemPrompt)
|
|
84
75
|
sections.push(req.systemPrompt);
|
|
85
76
|
sections.push(req.prompt);
|
|
86
|
-
if (req.schema) {
|
|
87
|
-
sections.push(`Respond with ONLY a JSON value matching this JSON Schema (no prose, no code fences):\n${JSON.stringify(req.schema)}`);
|
|
88
|
-
}
|
|
89
77
|
return sections.join("\n\n");
|
|
90
78
|
}
|
|
91
79
|
/**
|
|
92
80
|
* Aider builder.
|
|
93
81
|
* Command shape:
|
|
94
|
-
* aider [--model <m>] --yes-always --no-pretty --message=<[system\n\n]prompt
|
|
82
|
+
* aider [--model <m>] --yes-always --no-pretty --message=<[system\n\n]prompt>
|
|
95
83
|
*/
|
|
96
84
|
export const aiderBuilder = {
|
|
97
85
|
platform: AIDER_PLATFORM,
|
|
@@ -100,7 +88,6 @@ export const aiderBuilder = {
|
|
|
100
88
|
adapter: AIDER_PLATFORM,
|
|
101
89
|
personaChannel: "prompt",
|
|
102
90
|
tools: "none",
|
|
103
|
-
outputSchema: true,
|
|
104
91
|
}),
|
|
105
92
|
build(profile, req) {
|
|
106
93
|
const args = [...profile.args];
|
|
@@ -29,11 +29,6 @@ export class AiderHarness extends BaseHarness {
|
|
|
29
29
|
agentBuilder = aiderBuilder;
|
|
30
30
|
resultExtractor = aiderResultExtractor;
|
|
31
31
|
// ── Workflow-engine descriptor (plan §"Capability matrix", P2) ────────────
|
|
32
|
-
// akm spawns the `aider` CLI locally per unit ⇒ local-runner.
|
|
33
|
-
pattern = "local-runner";
|
|
34
|
-
// No structured-output mode at all (the matrix's "none — parse output"):
|
|
35
|
-
// akm injects the schema into the prompt and extracts embedded JSON.
|
|
36
|
-
structuredOutput = "none";
|
|
37
32
|
// No flag-shaped resume: Aider persists context in chat-history files
|
|
38
33
|
// (`.aider.chat.history.md`), not session ids — the plan's named example of
|
|
39
34
|
// a harness with no session model. akm's `workflow_run_units` remains the
|
|
@@ -35,9 +35,8 @@
|
|
|
35
35
|
* blank line.
|
|
36
36
|
* - **schema** — the matrix places Q in the NO-structured-output tier
|
|
37
37
|
* ("via prompt+validate": *(none documented)* — there is no `--json` or
|
|
38
|
-
* `--output-format` to ask for). The JSON Schema
|
|
39
|
-
*
|
|
40
|
-
* (`step-work.ts` `buildUnitPrompt`) is appended to the payload.
|
|
38
|
+
* `--output-format` to ask for). The JSON Schema therefore reaches it only
|
|
39
|
+
* as the instruction the shared request lowering appends to the prompt.
|
|
41
40
|
* Stdout stays plain text; `./result-extractor.ts` strips terminal framing
|
|
42
41
|
* and the engine's shared embedded-JSON parse + retry-until-valid loop does
|
|
43
42
|
* the rest. No schema temp file is written — that seam is codex-only
|
|
@@ -51,16 +50,14 @@
|
|
|
51
50
|
* falling back to `--trust-all-tools` (never silently widen a restriction)
|
|
52
51
|
* — Q then refuses untrusted tool actions in non-interactive mode, which is
|
|
53
52
|
* the conservative failure mode.
|
|
54
|
-
* - **
|
|
55
|
-
*
|
|
53
|
+
* - **inference** — not translated: the shared lowering reports each field of
|
|
54
|
+
* the request's inference as untranslated.
|
|
56
55
|
*
|
|
57
56
|
* Registered: `amazonqBuilder` is `AmazonqHarness.agentBuilder`
|
|
58
57
|
* (`./index.ts`), one of the ten harnesses `HARNESS_REGISTRY` constructs
|
|
59
58
|
* (`harnesses/index.ts`); `agent/builders.ts` derives `BUILTIN_BUILDERS` from
|
|
60
59
|
* that registry, so this builder is reachable under the `"amazonq"` platform
|
|
61
|
-
* name without any further wiring.
|
|
62
|
-
* pattern `local-runner`, structuredOutput `none` — is declared alongside it
|
|
63
|
-
* (`./index.ts`).
|
|
60
|
+
* name without any further wiring.
|
|
64
61
|
*/
|
|
65
62
|
import { resolveDispatchModel } from "../../agent/builder-shared.js";
|
|
66
63
|
import { createAgentRequestLowerer } from "../../agent/request-lowering.js";
|
|
@@ -84,27 +81,19 @@ function toolPolicyEntries(tools) {
|
|
|
84
81
|
}
|
|
85
82
|
return undefined;
|
|
86
83
|
}
|
|
87
|
-
/**
|
|
88
|
-
* Assemble the positional prompt payload: optional system prompt, the task
|
|
89
|
-
* prompt, and — when a schema is requested — the same schema directive the
|
|
90
|
-
* workflow engine's prompt assembly uses, so both dispatch paths speak one
|
|
91
|
-
* dialect.
|
|
92
|
-
*/
|
|
84
|
+
/** Assemble the positional prompt payload: optional system prompt, then the task prompt. */
|
|
93
85
|
function buildPromptPayload(req) {
|
|
94
86
|
const sections = [];
|
|
95
87
|
if (req.systemPrompt)
|
|
96
88
|
sections.push(req.systemPrompt);
|
|
97
89
|
sections.push(req.prompt);
|
|
98
|
-
if (req.schema) {
|
|
99
|
-
sections.push(`Respond with ONLY a JSON value matching this JSON Schema (no prose, no code fences):\n${JSON.stringify(req.schema)}`);
|
|
100
|
-
}
|
|
101
90
|
return sections.join("\n\n");
|
|
102
91
|
}
|
|
103
92
|
/**
|
|
104
93
|
* Amazon Q Developer CLI builder.
|
|
105
94
|
* Command shape:
|
|
106
95
|
* q chat --no-interactive (--trust-all-tools | --trust-tools=<t1,t2>)
|
|
107
|
-
* [--model <m>] -- "<systemPrompt?\n\nprompt
|
|
96
|
+
* [--model <m>] -- "<systemPrompt?\n\nprompt>"
|
|
108
97
|
*/
|
|
109
98
|
export const amazonqBuilder = {
|
|
110
99
|
platform: AMAZONQ_PLATFORM,
|
|
@@ -113,7 +102,6 @@ export const amazonqBuilder = {
|
|
|
113
102
|
adapter: AMAZONQ_PLATFORM,
|
|
114
103
|
personaChannel: "prompt",
|
|
115
104
|
tools: "flat",
|
|
116
|
-
outputSchema: true,
|
|
117
105
|
}),
|
|
118
106
|
build(profile, req) {
|
|
119
107
|
// Built-in q profiles would ship `args: []`; headless dispatch is the
|
|
@@ -141,8 +129,8 @@ export const amazonqBuilder = {
|
|
|
141
129
|
const resolved = resolveDispatchModel(req, profile, AMAZONQ_PLATFORM);
|
|
142
130
|
args.push("--model", resolved);
|
|
143
131
|
}
|
|
144
|
-
// No system-prompt
|
|
145
|
-
//
|
|
132
|
+
// No system-prompt flag exists on `q chat` — it travels in the positional
|
|
133
|
+
// payload, after the end-of-options separator.
|
|
146
134
|
args.push("--");
|
|
147
135
|
args.push(buildPromptPayload(req));
|
|
148
136
|
return { argv: [profile.bin, ...args] };
|
|
@@ -30,11 +30,6 @@ export class AmazonqHarness extends BaseHarness {
|
|
|
30
30
|
agentBuilder = amazonqBuilder;
|
|
31
31
|
resultExtractor = amazonqResultExtractor;
|
|
32
32
|
// ── Workflow-engine descriptor (plan §"Capability matrix", P2) ────────────
|
|
33
|
-
// akm spawns `q chat` locally per unit ⇒ local-runner.
|
|
34
|
-
pattern = "local-runner";
|
|
35
|
-
// No documented structured output: akm injects the schema into the prompt
|
|
36
|
-
// and extracts embedded JSON from plain-text stdout.
|
|
37
|
-
structuredOutput = "none";
|
|
38
33
|
// No `identityEnv`: the matrix lists Q's identity markers as uncertain, and
|
|
39
34
|
// Q stamps no session var onto child processes.
|
|
40
35
|
capabilities = caps({
|
|
@@ -11,51 +11,57 @@
|
|
|
11
11
|
* in `agent/builders.ts`, which imports this builder back into
|
|
12
12
|
* `BUILTIN_BUILDERS`.
|
|
13
13
|
*
|
|
14
|
-
* ## Structured output
|
|
14
|
+
* ## Structured output
|
|
15
15
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* `
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* `native-json` to match this builder honestly.)
|
|
16
|
+
* For a schema-bearing request this builder emits `--output-format json`,
|
|
17
|
+
* which wraps the run in a RESULT ENVELOPE
|
|
18
|
+
* (`{"type":"result","result":"<final answer>","session_id":"…", …}`); the
|
|
19
|
+
* shared request lowering has already appended the schema instruction to the
|
|
20
|
+
* prompt. The envelope is unwrapped by `./result-extractor.ts`, and the
|
|
21
|
+
* engine's shared `runStructured` retry-until-valid loop validates the
|
|
22
|
+
* extracted text against the schema (hinted output is trusted but verified).
|
|
23
|
+
* Without a schema the argv carries no output flag.
|
|
25
24
|
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* self-sufficient — matching the copilot/gemini native-json builders. The
|
|
30
|
-
* result envelope is unwrapped by `./result-extractor.ts`, and the engine's
|
|
31
|
-
* shared `runStructured` retry-until-valid loop still validates the extracted
|
|
32
|
-
* text against the node schema (constrained/hinted output is trusted but
|
|
33
|
-
* verified). Without a schema the argv is byte-identical to the pre-fix shape.
|
|
25
|
+
* Claude Code 2.1.283 also has `--json-schema <schema>` (JSON Schema for
|
|
26
|
+
* structured output validation, with `--print`). akm does not pass it; the
|
|
27
|
+
* instruction and the validation loop above are what enforce a schema.
|
|
34
28
|
*
|
|
35
29
|
* The builder's `platform` stays `'claude'` (the canonical harness id).
|
|
36
30
|
*/
|
|
37
|
-
import { normalizeTools, resolveDispatchModel, } from "../../agent/builder-shared.js";
|
|
31
|
+
import { modelFromArgs, normalizeTools, resolveDispatchModel, } from "../../agent/builder-shared.js";
|
|
38
32
|
import { createAgentRequestLowerer } from "../../agent/request-lowering.js";
|
|
39
|
-
/**
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
return `${req.prompt}\n\nRespond with ONLY a JSON value matching this JSON Schema (no prose, no code fences):\n${JSON.stringify(req.schema)}`;
|
|
51
|
-
}
|
|
33
|
+
/** The model-work tool policy on Claude Code: read, edit in the working directory, `akm search`, `akm show`. */
|
|
34
|
+
export const MODEL_WORK_CLAUDE_FLAGS = Object.freeze([
|
|
35
|
+
"--restricted",
|
|
36
|
+
"--strict-mcp-config",
|
|
37
|
+
"--tools",
|
|
38
|
+
"Read,Edit,Bash",
|
|
39
|
+
"--allowedTools",
|
|
40
|
+
"Read,Edit,Bash(akm search *),Bash(akm show *)",
|
|
41
|
+
"--permission-mode",
|
|
42
|
+
"dontAsk",
|
|
43
|
+
]);
|
|
52
44
|
/**
|
|
53
45
|
* Claude Code builder.
|
|
54
46
|
* Command shape:
|
|
55
47
|
* claude [--agent <name>] [--system-prompt "..."] [--model <m>] [--allowedTools <t>]
|
|
56
|
-
* [--output-format json] --print -- "<prompt
|
|
48
|
+
* [--output-format json] --print -- "<prompt>"
|
|
57
49
|
*
|
|
58
50
|
* --print switches Claude Code to non-interactive captured output mode.
|
|
51
|
+
*
|
|
52
|
+
* The model-work tool policy lowers to {@link MODEL_WORK_CLAUDE_FLAGS} in place
|
|
53
|
+
* of the engine's `args` and `--allowedTools`, verified against Claude Code
|
|
54
|
+
* 2.1.283 and a local stub:
|
|
55
|
+
* - `--restricted` ignores the user, project and local settings files (whose
|
|
56
|
+
* allow rules would otherwise pre-approve any command or path) and confines
|
|
57
|
+
* the file tools to the working directory;
|
|
58
|
+
* - `--strict-mcp-config` starts no MCP server, and `--tools` offers only
|
|
59
|
+
* Read, Edit and Bash;
|
|
60
|
+
* - `--allowedTools` pre-approves Read, Edit, `akm search` and `akm show`,
|
|
61
|
+
* and `--permission-mode dontAsk` denies everything else instead of
|
|
62
|
+
* prompting, including a compound, redirected or substituted command.
|
|
63
|
+
* The engine's `args` are left out because `--add-dir`, `--settings` or a
|
|
64
|
+
* second `--allowedTools` would widen that. Only the model they name is kept.
|
|
59
65
|
*/
|
|
60
66
|
export const claudeBuilder = {
|
|
61
67
|
platform: "claude",
|
|
@@ -65,10 +71,10 @@ export const claudeBuilder = {
|
|
|
65
71
|
personaChannel: "native",
|
|
66
72
|
nativeAgentSelector: true,
|
|
67
73
|
tools: "all",
|
|
68
|
-
outputSchema: true,
|
|
69
74
|
}),
|
|
70
75
|
build(profile, req) {
|
|
71
|
-
const
|
|
76
|
+
const modelWork = req.modelWork === true;
|
|
77
|
+
const args = modelWork ? [...MODEL_WORK_CLAUDE_FLAGS] : [...profile.args];
|
|
72
78
|
if (req.agent) {
|
|
73
79
|
args.push("--agent", req.agent);
|
|
74
80
|
}
|
|
@@ -79,6 +85,11 @@ export const claudeBuilder = {
|
|
|
79
85
|
const resolved = resolveDispatchModel(req, profile, "claude");
|
|
80
86
|
args.push("--model", resolved);
|
|
81
87
|
}
|
|
88
|
+
else if (modelWork) {
|
|
89
|
+
const model = modelFromArgs(profile.args);
|
|
90
|
+
if (model)
|
|
91
|
+
args.push("--model", model);
|
|
92
|
+
}
|
|
82
93
|
if (req.tools) {
|
|
83
94
|
args.push("--allowedTools", normalizeTools(req.tools));
|
|
84
95
|
}
|
|
@@ -90,7 +101,7 @@ export const claudeBuilder = {
|
|
|
90
101
|
// --print = non-interactive, outputs to stdout — required for captured mode
|
|
91
102
|
args.push("--print");
|
|
92
103
|
args.push("--");
|
|
93
|
-
args.push(
|
|
104
|
+
args.push(req.prompt);
|
|
94
105
|
return { argv: [profile.bin, ...args] };
|
|
95
106
|
},
|
|
96
107
|
};
|
|
@@ -24,20 +24,6 @@ export class ClaudeHarness extends BaseHarness {
|
|
|
24
24
|
agentBuilder = claudeBuilder;
|
|
25
25
|
resultExtractor = claudeResultExtractor;
|
|
26
26
|
// ── Workflow-engine descriptor (plan §"Capability matrix", P2) ────────────
|
|
27
|
-
// Claude Code is the in-harness pattern: the orchestrating session itself
|
|
28
|
-
// drives units via the `akm workflow` gate spine (`claude -p` headless
|
|
29
|
-
// dispatch also exists via `agentBuilder`, but the pattern classification
|
|
30
|
-
// follows the matrix row).
|
|
31
|
-
pattern = "in-harness";
|
|
32
|
-
// Structured output tier for the AGENT-DISPATCH (`claude -p`) path akm's
|
|
33
|
-
// local runner uses (Codex round-3 finding A). The headless CLI has NO
|
|
34
|
-
// output-schema flag — its documented structured path is `--output-format
|
|
35
|
-
// json`, a RESULT ENVELOPE akm parses (`./result-extractor.ts`) and then
|
|
36
|
-
// validates against the node schema ⇒ the "native-json" tier. (Claude Code's
|
|
37
|
-
// in-harness `Workflow`/`agent()` tool-input-schema path IS native-schema,
|
|
38
|
-
// but that is a different surface than the dispatch builder — the descriptor
|
|
39
|
-
// is aligned to what the builder honestly does.)
|
|
40
|
-
structuredOutput = "native-json";
|
|
41
27
|
// Session-id env marker: presence of a concrete session id (not the bare
|
|
42
28
|
// "running under Claude Code" flag) attributes a run to this harness.
|
|
43
29
|
identityEnv = ["CLAUDE_SESSION_ID"];
|
|
@@ -42,9 +42,9 @@
|
|
|
42
42
|
* not expressible through `AgentDispatchRequest` (which has no session
|
|
43
43
|
* field yet); {@link codexResumeArgs} exposes the argv prefix for the
|
|
44
44
|
* integration task that wires session-id reuse from `workflow_run_units`.
|
|
45
|
-
* -
|
|
46
|
-
*
|
|
47
|
-
*
|
|
45
|
+
* - The request's inference is not translated: the shared lowering reports
|
|
46
|
+
* each field as untranslated. codex would take `reasoningEffort` as
|
|
47
|
+
* `-c model_reasoning_effort=<v>`, which is left to the integration task.
|
|
48
48
|
*
|
|
49
49
|
* Registered: `codexBuilder` is `CodexHarness.agentBuilder` (`./index.ts`),
|
|
50
50
|
* one of the ten harnesses `HARNESS_REGISTRY` constructs
|
|
@@ -110,7 +110,6 @@ export const codexBuilder = {
|
|
|
110
110
|
adapter: "codex",
|
|
111
111
|
personaChannel: "prompt",
|
|
112
112
|
tools: "none",
|
|
113
|
-
outputSchema: true,
|
|
114
113
|
}),
|
|
115
114
|
build(profile, req) {
|
|
116
115
|
// Built-in codex profiles ship `args: []`; headless dispatch is the `exec`
|
|
@@ -29,10 +29,6 @@ export class CodexHarness extends BaseHarness {
|
|
|
29
29
|
agentBuilder = codexBuilder;
|
|
30
30
|
resultExtractor = codexResultExtractor;
|
|
31
31
|
// ── Workflow-engine descriptor (plan §"Capability matrix", P2) ────────────
|
|
32
|
-
// akm spawns `codex exec` locally per unit ⇒ local-runner.
|
|
33
|
-
pattern = "local-runner";
|
|
34
|
-
// `--output-schema <file>` enforces a caller-supplied JSON schema natively.
|
|
35
|
-
structuredOutput = "native-schema";
|
|
36
32
|
// No flag-shaped resume: codex resume is the `exec resume <id>` SUBCOMMAND
|
|
37
33
|
// chain (see `codexResumeArgs` in ./agent-builder.ts).
|
|
38
34
|
// Presence flag: CODEX_SANDBOX is stamped only on processes codex itself
|
|
@@ -24,9 +24,8 @@
|
|
|
24
24
|
* blank line.
|
|
25
25
|
* - **schema** — the matrix places Copilot in the "via prompt+validate" tier
|
|
26
26
|
* (no native `--output-schema` equivalent, unlike Codex), so the JSON
|
|
27
|
-
* Schema
|
|
28
|
-
*
|
|
29
|
-
* payload, and `--output-format json` is emitted so stdout is the
|
|
27
|
+
* Schema reaches it as the instruction the shared request lowering appends
|
|
28
|
+
* to the prompt, and `--output-format json` is emitted so stdout is the
|
|
30
29
|
* documented JSON envelope the copilot result extractor normalizes. The
|
|
31
30
|
* engine's shared retry-until-valid loop performs the actual validation.
|
|
32
31
|
* - **tools** — a string/array tool policy maps to repeated
|
|
@@ -66,26 +65,19 @@ function toolPolicyEntries(tools) {
|
|
|
66
65
|
}
|
|
67
66
|
return undefined;
|
|
68
67
|
}
|
|
69
|
-
/**
|
|
70
|
-
* Assemble the `-p` payload: optional system prompt, the task prompt, and —
|
|
71
|
-
* when a schema is requested — the same schema directive the workflow
|
|
72
|
-
* engine's prompt assembly uses, so both dispatch paths speak one dialect.
|
|
73
|
-
*/
|
|
68
|
+
/** Assemble the `-p` payload: optional system prompt, then the task prompt. */
|
|
74
69
|
function buildPromptPayload(req) {
|
|
75
70
|
const sections = [];
|
|
76
71
|
if (req.systemPrompt)
|
|
77
72
|
sections.push(req.systemPrompt);
|
|
78
73
|
sections.push(req.prompt);
|
|
79
|
-
if (req.schema) {
|
|
80
|
-
sections.push(`Respond with ONLY a JSON value matching this JSON Schema (no prose, no code fences):\n${JSON.stringify(req.schema)}`);
|
|
81
|
-
}
|
|
82
74
|
return sections.join("\n\n");
|
|
83
75
|
}
|
|
84
76
|
/**
|
|
85
77
|
* GitHub Copilot CLI builder.
|
|
86
78
|
* Command shape:
|
|
87
79
|
* copilot [--model <m>] (--allow-all-tools | --allow-tool <t> ...)
|
|
88
|
-
* [--output-format json] -p "<systemPrompt?\n\nprompt
|
|
80
|
+
* [--output-format json] -p "<systemPrompt?\n\nprompt>"
|
|
89
81
|
*/
|
|
90
82
|
export const copilotBuilder = {
|
|
91
83
|
platform: COPILOT_PLATFORM,
|
|
@@ -94,7 +86,6 @@ export const copilotBuilder = {
|
|
|
94
86
|
adapter: COPILOT_PLATFORM,
|
|
95
87
|
personaChannel: "prompt",
|
|
96
88
|
tools: "flat",
|
|
97
|
-
outputSchema: true,
|
|
98
89
|
}),
|
|
99
90
|
build(profile, req) {
|
|
100
91
|
const args = [...profile.args];
|
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
*
|
|
11
11
|
* It also defines {@link CopilotHarness}, the {@link AkmHarness} descriptor
|
|
12
12
|
* that `HARNESS_REGISTRY` registers. This is the LOCAL Copilot CLI
|
|
13
|
-
* (`copilot -p …`); the cloud "Copilot coding agent" is
|
|
14
|
-
*
|
|
13
|
+
* (`copilot -p …`); the cloud "Copilot coding agent" is a separate, future
|
|
14
|
+
* descriptor.
|
|
15
15
|
*/
|
|
16
16
|
import { caps } from "../shared.js";
|
|
17
17
|
import { BaseHarness } from "../types.js";
|
|
@@ -30,11 +30,6 @@ export class CopilotHarness extends BaseHarness {
|
|
|
30
30
|
agentBuilder = copilotBuilder;
|
|
31
31
|
resultExtractor = copilotResultExtractor;
|
|
32
32
|
// ── Workflow-engine descriptor (plan §"Capability matrix", P2) ────────────
|
|
33
|
-
// akm spawns the `copilot` CLI locally per unit ⇒ local-runner.
|
|
34
|
-
pattern = "local-runner";
|
|
35
|
-
// `--output-format json` emits a documented JSON envelope akm parses, then
|
|
36
|
-
// validates against the node schema ⇒ native-json tier.
|
|
37
|
-
structuredOutput = "native-json";
|
|
38
33
|
// Session-id env marker only. The matrix's other candidates (GH_TOKEN,
|
|
39
34
|
// bare COPILOT_* presence vars) are credential/presence flags that would
|
|
40
35
|
// stamp identity onto manual runs, so they are deliberately NOT registered
|
|
@@ -26,9 +26,8 @@
|
|
|
26
26
|
* prompt, separated by a blank line.
|
|
27
27
|
* - **schema** — the matrix places Gemini in the "via prompt+validate" tier
|
|
28
28
|
* (no native `--output-schema` equivalent, unlike Codex — so no temp-file
|
|
29
|
-
* plumbing here), so the JSON Schema
|
|
30
|
-
*
|
|
31
|
-
* `buildUnitPrompt`) is appended to the `-p` payload, and
|
|
29
|
+
* plumbing here), so the JSON Schema reaches it as the instruction the
|
|
30
|
+
* shared request lowering appends to the prompt, and
|
|
32
31
|
* `--output-format json` is emitted so stdout is the documented JSON
|
|
33
32
|
* envelope the gemini result extractor normalizes. The engine's shared
|
|
34
33
|
* retry-until-valid loop performs the actual validation.
|
|
@@ -69,26 +68,19 @@ function toolPolicyEntries(tools) {
|
|
|
69
68
|
}
|
|
70
69
|
return undefined;
|
|
71
70
|
}
|
|
72
|
-
/**
|
|
73
|
-
* Assemble the `-p` payload: optional system prompt, the task prompt, and —
|
|
74
|
-
* when a schema is requested — the same schema directive the workflow
|
|
75
|
-
* engine's prompt assembly uses, so both dispatch paths speak one dialect.
|
|
76
|
-
*/
|
|
71
|
+
/** Assemble the `-p` payload: optional system prompt, then the task prompt. */
|
|
77
72
|
function buildPromptPayload(req) {
|
|
78
73
|
const sections = [];
|
|
79
74
|
if (req.systemPrompt)
|
|
80
75
|
sections.push(req.systemPrompt);
|
|
81
76
|
sections.push(req.prompt);
|
|
82
|
-
if (req.schema) {
|
|
83
|
-
sections.push(`Respond with ONLY a JSON value matching this JSON Schema (no prose, no code fences):\n${JSON.stringify(req.schema)}`);
|
|
84
|
-
}
|
|
85
77
|
return sections.join("\n\n");
|
|
86
78
|
}
|
|
87
79
|
/**
|
|
88
80
|
* Gemini CLI builder.
|
|
89
81
|
* Command shape:
|
|
90
82
|
* gemini [--model <m>] [--allowed-tools <t> ...]
|
|
91
|
-
* [--output-format json] -p "<systemPrompt?\n\nprompt
|
|
83
|
+
* [--output-format json] -p "<systemPrompt?\n\nprompt>"
|
|
92
84
|
*/
|
|
93
85
|
export const geminiBuilder = {
|
|
94
86
|
platform: GEMINI_PLATFORM,
|
|
@@ -97,7 +89,6 @@ export const geminiBuilder = {
|
|
|
97
89
|
adapter: GEMINI_PLATFORM,
|
|
98
90
|
personaChannel: "prompt",
|
|
99
91
|
tools: "flat",
|
|
100
|
-
outputSchema: true,
|
|
101
92
|
}),
|
|
102
93
|
build(profile, req) {
|
|
103
94
|
const args = [...profile.args];
|
|
@@ -29,11 +29,6 @@ export class GeminiHarness extends BaseHarness {
|
|
|
29
29
|
agentBuilder = geminiBuilder;
|
|
30
30
|
resultExtractor = geminiResultExtractor;
|
|
31
31
|
// ── Workflow-engine descriptor (plan §"Capability matrix", P2) ────────────
|
|
32
|
-
// akm spawns the `gemini` CLI locally per unit ⇒ local-runner.
|
|
33
|
-
pattern = "local-runner";
|
|
34
|
-
// `--output-format json` emits a documented JSON envelope akm parses, then
|
|
35
|
-
// validates against the node schema ⇒ native-json tier.
|
|
36
|
-
structuredOutput = "native-json";
|
|
37
32
|
// The matrix's identity marker: Gemini CLI stamps GEMINI_CLI=1 only on
|
|
38
33
|
// processes it spawns, so it genuinely means "running under gemini" (it is
|
|
39
34
|
// not a user-profile config var) — but its VALUE ("1") is a bare flag, not
|
|
@@ -2,16 +2,16 @@
|
|
|
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
|
export const HARNESS_ID_TABLE = [
|
|
5
|
-
{ id: "opencode", agentDispatch: true },
|
|
6
|
-
{ id: "claude", agentDispatch: true },
|
|
7
|
-
{ id: "opencode-sdk", agentDispatch: true },
|
|
8
|
-
{ id: "codex", agentDispatch: true },
|
|
9
|
-
{ id: "copilot", agentDispatch: true },
|
|
10
|
-
{ id: "pi", agentDispatch: true },
|
|
11
|
-
{ id: "gemini", agentDispatch: true },
|
|
12
|
-
{ id: "aider", agentDispatch: true },
|
|
13
|
-
{ id: "amazonq", agentDispatch: true },
|
|
14
|
-
{ id: "openhands", agentDispatch: true },
|
|
5
|
+
{ id: "opencode", agentDispatch: true, enforcesModelWorkTools: true },
|
|
6
|
+
{ id: "claude", agentDispatch: true, enforcesModelWorkTools: true },
|
|
7
|
+
{ id: "opencode-sdk", agentDispatch: true, enforcesModelWorkTools: true },
|
|
8
|
+
{ id: "codex", agentDispatch: true, enforcesModelWorkTools: false },
|
|
9
|
+
{ id: "copilot", agentDispatch: true, enforcesModelWorkTools: false },
|
|
10
|
+
{ id: "pi", agentDispatch: true, enforcesModelWorkTools: false },
|
|
11
|
+
{ id: "gemini", agentDispatch: true, enforcesModelWorkTools: false },
|
|
12
|
+
{ id: "aider", agentDispatch: true, enforcesModelWorkTools: false },
|
|
13
|
+
{ id: "amazonq", agentDispatch: true, enforcesModelWorkTools: false },
|
|
14
|
+
{ id: "openhands", agentDispatch: true, enforcesModelWorkTools: false },
|
|
15
15
|
];
|
|
16
16
|
/**
|
|
17
17
|
* Canonical, ordered list of valid harness / platform ids — the
|
|
@@ -22,3 +22,5 @@ export const HARNESS_ID_TABLE = [
|
|
|
22
22
|
export const VALID_HARNESS_IDS = Object.freeze(HARNESS_ID_TABLE.map((h) => h.id));
|
|
23
23
|
/** Harness ids whose `capabilities.agentDispatch` is `true`. */
|
|
24
24
|
export const HARNESS_AGENT_DISPATCH_IDS = new Set(HARNESS_ID_TABLE.filter((h) => h.agentDispatch).map((h) => h.id));
|
|
25
|
+
/** Harness ids that confine the model-work tool policy, so unattended model work may run on them. */
|
|
26
|
+
export const HARNESS_MODEL_WORK_IDS = new Set(HARNESS_ID_TABLE.filter((h) => h.enforcesModelWorkTools).map((h) => h.id));
|
|
@@ -14,26 +14,61 @@
|
|
|
14
14
|
* pre-migration `opencodeBuilder`. The builder's `platform` stays `'opencode'`
|
|
15
15
|
* (the canonical harness id).
|
|
16
16
|
*/
|
|
17
|
-
import { resolveDispatchModel } from "../../agent/builder-shared.js";
|
|
17
|
+
import { modelFromArgs, resolveDispatchModel } from "../../agent/builder-shared.js";
|
|
18
18
|
import { createAgentRequestLowerer } from "../../agent/request-lowering.js";
|
|
19
|
+
import { MODEL_WORK_AGENT_INFERENCE, opencodeInferenceConfig } from "./model-config.js";
|
|
20
|
+
import { MODEL_WORK_OPENCODE_AGENT, modelWorkOpencodeConfig, modelWorkPluginEnv } from "./model-work-agent.js";
|
|
19
21
|
/**
|
|
20
22
|
* OpenCode builder.
|
|
21
|
-
* Command shape: opencode run [--
|
|
23
|
+
* Command shape: opencode run [--agent <name>] [--model <m>] -- "<prompt>"
|
|
24
|
+
*
|
|
25
|
+
* `opencode run` has no system-prompt option (1.18.25 prints its usage and
|
|
26
|
+
* exits 1 on `--system-prompt`), so the shared lowerer composes a persona
|
|
27
|
+
* into the prompt.
|
|
22
28
|
*
|
|
23
29
|
* Tool policy is omitted — opencode manages tool access through its own agent
|
|
24
|
-
* config files, not via CLI flags.
|
|
30
|
+
* config files, not via CLI flags. The one exception is the model-work tool
|
|
31
|
+
* policy: the builder injects its confined agent through
|
|
32
|
+
* `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.
|
|
37
|
+
*
|
|
38
|
+
* Model work's agent carries the request's inference options
|
|
39
|
+
* (`./model-config.ts`). Any other dispatch injects nothing and carries none, so
|
|
40
|
+
* the model's own opencode config applies: set inference there.
|
|
25
41
|
*/
|
|
26
42
|
export const opencodeBuilder = {
|
|
27
43
|
platform: "opencode",
|
|
28
|
-
personaChannel: "
|
|
44
|
+
personaChannel: "prompt",
|
|
29
45
|
lower: createAgentRequestLowerer({
|
|
30
46
|
adapter: "opencode",
|
|
31
|
-
personaChannel: "
|
|
47
|
+
personaChannel: "prompt",
|
|
32
48
|
nativeAgentSelector: true,
|
|
33
49
|
tools: "none",
|
|
34
|
-
|
|
50
|
+
inference: MODEL_WORK_AGENT_INFERENCE,
|
|
35
51
|
}),
|
|
36
52
|
build(profile, req) {
|
|
53
|
+
if (req.modelWork) {
|
|
54
|
+
const model = req.model ?? modelFromArgs(profile.args);
|
|
55
|
+
const { agentOptions } = opencodeInferenceConfig(req.inference, true);
|
|
56
|
+
return {
|
|
57
|
+
argv: [
|
|
58
|
+
profile.bin,
|
|
59
|
+
"run",
|
|
60
|
+
"--agent",
|
|
61
|
+
MODEL_WORK_OPENCODE_AGENT,
|
|
62
|
+
...(model ? ["--model", model] : []),
|
|
63
|
+
"--",
|
|
64
|
+
req.prompt,
|
|
65
|
+
],
|
|
66
|
+
env: {
|
|
67
|
+
...modelWorkPluginEnv(),
|
|
68
|
+
OPENCODE_CONFIG_CONTENT: JSON.stringify(modelWorkOpencodeConfig(agentOptions)),
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
}
|
|
37
72
|
const args = req.model ? [] : [...profile.args];
|
|
38
73
|
if (req.model) {
|
|
39
74
|
for (let index = 0; index < profile.args.length; index += 1) {
|
|
@@ -48,9 +83,6 @@ export const opencodeBuilder = {
|
|
|
48
83
|
}
|
|
49
84
|
}
|
|
50
85
|
}
|
|
51
|
-
if (req.systemPrompt) {
|
|
52
|
-
args.push("--system-prompt", req.systemPrompt);
|
|
53
|
-
}
|
|
54
86
|
if (req.agent) {
|
|
55
87
|
args.push("--agent", req.agent);
|
|
56
88
|
}
|
|
@@ -21,14 +21,6 @@ export class OpencodeHarness extends BaseHarness {
|
|
|
21
21
|
setupDetectionDir = ".config/opencode";
|
|
22
22
|
agentBuilder = opencodeBuilder;
|
|
23
23
|
// ── Workflow-engine descriptor (plan §"Capability matrix", P2) ────────────
|
|
24
|
-
// This entry is the CLI spawn path (`opencode run …`): akm launches the
|
|
25
|
-
// harness locally per unit ⇒ local-runner. (The SDK path is the separate
|
|
26
|
-
// `opencode-sdk` harness.)
|
|
27
|
-
pattern = "local-runner";
|
|
28
|
-
// The CLI path emits plain text — no JSON stream akm consumes — so the
|
|
29
|
-
// engine uses the prompt-injected schema + embedded-JSON extraction tier
|
|
30
|
-
// (the matrix's "via prompt+validate"). The SDK entry is native-json.
|
|
31
|
-
structuredOutput = "none";
|
|
32
24
|
// Session-id env marker for run attribution.
|
|
33
25
|
identityEnv = ["OPENCODE_SESSION_ID"];
|
|
34
26
|
sessionLogProvider = () => new OpenCodeProvider();
|