akm-cli 0.9.0-rc.1 → 0.9.0-rc.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 +223 -9
- package/SECURITY.md +23 -24
- package/dist/assets/help/help-improve.md +10 -10
- package/dist/assets/hints/cli-hints-full.md +44 -10
- package/dist/assets/hints/cli-hints-short.md +6 -2
- package/dist/assets/{profiles → improve-strategies}/default.json +1 -0
- package/dist/assets/{profiles → improve-strategies}/graph-refresh.json +1 -1
- package/dist/assets/{profiles → improve-strategies}/proactive-maintenance.json +2 -3
- package/dist/assets/{profiles → improve-strategies}/reflect-distill.json +3 -4
- package/dist/assets/stash-skeleton/README.md +28 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +6 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +6 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +12 -1
- package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +11 -1
- package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +6 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +9 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +7 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +7 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +6 -0
- package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +98 -0
- package/dist/assets/stash-skeleton/facts/conventions/domains.md +63 -0
- package/dist/assets/stash-skeleton/facts/conventions/organization.md +127 -0
- package/dist/assets/tasks/core/backup.yml +1 -0
- package/dist/assets/tasks/core/extract.yml +1 -0
- package/dist/assets/tasks/core/improve.yml +1 -0
- package/dist/assets/tasks/core/index-refresh.yml +1 -0
- package/dist/assets/tasks/core/sync.yml +1 -0
- package/dist/assets/tasks/core/version-check.yml +1 -0
- package/dist/assets/tasks/graph-refresh-weekly.yml +4 -4
- package/dist/assets/templates/html/health.html +5 -1
- package/dist/cli/config-migrate.js +31 -138
- package/dist/cli/config-validate.js +10 -8
- package/dist/cli.js +48 -14
- package/dist/commands/agent/agent-dispatch.js +17 -16
- package/dist/commands/agent/agent-support.js +0 -24
- package/dist/commands/agent/contribute-cli.js +5 -15
- package/dist/commands/backup-cli.js +54 -0
- package/dist/commands/config-cli.js +45 -159
- package/dist/commands/env/secret.js +8 -5
- package/dist/commands/health/checks.js +130 -83
- package/dist/commands/health/html-report.js +4 -0
- package/dist/commands/health/improve-metrics.js +30 -32
- package/dist/commands/health/llm-usage.js +19 -19
- package/dist/commands/health/md-report.js +4 -0
- package/dist/commands/health/metrics.js +2 -1
- package/dist/commands/health/surfaces.js +5 -4
- package/dist/commands/health.js +1 -1
- package/dist/commands/improve/consolidate/chunking.js +2 -2
- package/dist/commands/improve/consolidate.js +28 -25
- package/dist/commands/improve/distill/promote-memory.js +5 -12
- package/dist/commands/improve/distill/quality-gate.js +5 -7
- package/dist/commands/improve/distill.js +16 -5
- package/dist/commands/improve/eligibility.js +22 -12
- package/dist/commands/improve/extract-cli.js +47 -19
- package/dist/commands/improve/extract.js +110 -62
- package/dist/commands/improve/improve-cli.js +38 -16
- package/dist/commands/improve/improve-result-file.js +30 -24
- package/dist/commands/improve/improve-strategies.js +137 -0
- package/dist/commands/improve/improve.js +60 -30
- package/dist/commands/improve/locks.js +66 -45
- package/dist/commands/improve/loop-stages.js +75 -33
- package/dist/commands/improve/memory/memory-belief.js +79 -7
- package/dist/commands/improve/memory/memory-contradiction-detect.js +12 -4
- package/dist/commands/improve/preparation.js +71 -73
- package/dist/commands/improve/procedural.js +3 -2
- package/dist/commands/improve/recombine.js +2 -1
- package/dist/commands/improve/reflect.js +119 -214
- package/dist/commands/improve/shared.js +11 -5
- package/dist/commands/lint/base-linter.js +152 -42
- package/dist/commands/mv-cli.js +809 -0
- package/dist/commands/proposal/proposal-cli.js +18 -8
- package/dist/commands/proposal/propose.js +64 -69
- package/dist/commands/read/knowledge.js +436 -4
- package/dist/commands/read/remember-cli.js +39 -2
- package/dist/commands/read/search-cli.js +6 -1
- package/dist/commands/registry-cli.js +29 -14
- package/dist/commands/remember.js +2 -0
- package/dist/commands/sources/init.js +13 -14
- package/dist/commands/sources/schema-repair.js +2 -4
- package/dist/commands/sources/source-add.js +62 -73
- package/dist/commands/sources/source-manage.js +50 -46
- package/dist/commands/sources/stash-cli.js +41 -4
- package/dist/commands/tasks/default-tasks.js +12 -12
- package/dist/commands/tasks/tasks-cli.js +7 -3
- package/dist/commands/tasks/tasks.js +113 -18
- package/dist/commands/wiki-cli.js +9 -10
- package/dist/core/asset/frontmatter.js +12 -2
- package/dist/core/common.js +5 -3
- package/dist/core/config/config-io.js +28 -17
- package/dist/core/config/config-schema.js +358 -66
- package/dist/core/config/config-types.js +3 -3
- package/dist/core/config/config-version.js +29 -0
- package/dist/core/config/config-walker.js +98 -27
- package/dist/core/config/config.js +132 -266
- package/dist/core/config/deep-merge.js +41 -0
- package/dist/core/config/engine-semantics.js +32 -0
- package/dist/core/errors.js +2 -2
- package/dist/core/extra-params.js +61 -0
- package/dist/core/file-lock.js +201 -56
- package/dist/core/improve-result.js +178 -0
- package/dist/core/maintenance-barrier.js +119 -0
- package/dist/core/migration-backup.js +416 -0
- package/dist/core/paths.js +3 -0
- package/dist/core/redaction.js +358 -0
- package/dist/core/state/migrations.js +17 -2
- package/dist/core/state-db.js +44 -1
- package/dist/indexer/db/db.js +116 -1
- package/dist/indexer/graph/graph-extraction.js +28 -16
- package/dist/indexer/index-writer-lock.js +31 -24
- package/dist/indexer/index-written-assets.js +15 -6
- package/dist/indexer/indexer.js +47 -2
- package/dist/indexer/passes/memory-inference.js +10 -6
- package/dist/indexer/passes/metadata.js +250 -0
- package/dist/indexer/search/db-search.js +111 -44
- package/dist/indexer/search/fts-query.js +41 -0
- package/dist/indexer/search/ranking-contributors.js +48 -0
- package/dist/indexer/search/ranking.js +36 -23
- package/dist/indexer/search/search-fields.js +11 -1
- package/dist/integrations/agent/builder-shared.js +7 -0
- package/dist/integrations/agent/builders.js +5 -52
- package/dist/integrations/agent/config.js +3 -143
- package/dist/integrations/agent/detect.js +17 -2
- package/dist/integrations/agent/engine-resolution.js +202 -0
- package/dist/integrations/agent/index.js +1 -2
- package/dist/integrations/agent/model-aliases.js +7 -2
- package/dist/integrations/agent/profiles.js +6 -99
- package/dist/integrations/agent/runner-dispatch.js +76 -13
- package/dist/integrations/agent/runner.js +76 -207
- package/dist/integrations/agent/spawn.js +4 -6
- package/dist/integrations/harnesses/aider/agent-builder.js +2 -3
- package/dist/integrations/harnesses/aider/index.js +0 -1
- package/dist/integrations/harnesses/amazonq/agent-builder.js +2 -3
- package/dist/integrations/harnesses/amazonq/index.js +0 -1
- package/dist/integrations/harnesses/claude/agent-builder.js +2 -3
- package/dist/integrations/harnesses/claude/index.js +0 -2
- package/dist/integrations/harnesses/codex/agent-builder.js +2 -3
- package/dist/integrations/harnesses/codex/index.js +0 -1
- package/dist/integrations/harnesses/copilot/agent-builder.js +2 -3
- package/dist/integrations/harnesses/copilot/index.js +0 -1
- package/dist/integrations/harnesses/gemini/agent-builder.js +2 -3
- package/dist/integrations/harnesses/gemini/index.js +0 -1
- package/dist/integrations/harnesses/index.js +1 -24
- package/dist/integrations/harnesses/opencode/agent-builder.js +2 -3
- package/dist/integrations/harnesses/opencode/index.js +0 -6
- package/dist/integrations/harnesses/opencode-sdk/harness.js +1 -6
- package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +274 -191
- package/dist/integrations/harnesses/openhands/agent-builder.js +2 -3
- package/dist/integrations/harnesses/openhands/index.js +0 -1
- package/dist/integrations/harnesses/pi/agent-builder.js +2 -3
- package/dist/integrations/harnesses/pi/index.js +0 -1
- package/dist/integrations/harnesses/types.js +1 -32
- package/dist/integrations/lockfile.js +32 -21
- package/dist/llm/client.js +48 -14
- package/dist/llm/feature-gate.js +15 -47
- package/dist/llm/graph-extract.js +1 -1
- package/dist/llm/index-passes.js +8 -42
- package/dist/llm/memory-infer-impl.js +1 -1
- package/dist/llm/usage-persist.js +4 -0
- package/dist/llm/usage-telemetry.js +35 -5
- package/dist/output/shapes/helpers.js +2 -1
- package/dist/output/shapes/passthrough.js +2 -0
- package/dist/output/text/helpers.js +3 -1
- package/dist/schemas/akm-config.json +11013 -8600
- package/dist/schemas/akm-task.json +87 -0
- package/dist/schemas/akm-workflow.json +57 -13
- package/dist/scripts/migrate-storage.js +8591 -509
- package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +8623 -484
- package/dist/setup/detected-engines.js +142 -0
- package/dist/setup/engine-config.js +89 -0
- package/dist/setup/setup.js +236 -132
- package/dist/setup/steps/connection.js +61 -32
- package/dist/setup/steps/platforms.js +4 -4
- package/dist/setup/steps.js +3 -2
- package/dist/storage/database.js +13 -1
- package/dist/storage/engines/sqlite-migrations.js +1 -0
- package/dist/storage/repositories/improve-runs-repository.js +5 -5
- package/dist/storage/repositories/task-history-repository.js +78 -0
- package/dist/storage/repositories/workflow-runs-repository.js +9 -8
- package/dist/tasks/parser.js +138 -52
- package/dist/tasks/runner.js +71 -75
- package/dist/tasks/schema.js +1 -1
- package/dist/tasks/validator.js +11 -6
- package/dist/wiki/wiki.js +9 -8
- package/dist/workflows/authoring/workflow-program-template.yaml +3 -3
- package/dist/workflows/concurrency-policy.js +15 -0
- package/dist/workflows/db.js +65 -13
- package/dist/workflows/exec/brief.js +18 -24
- package/dist/workflows/exec/frozen-judge.js +47 -0
- package/dist/workflows/exec/native-executor.js +124 -65
- package/dist/workflows/exec/report.js +93 -33
- package/dist/workflows/exec/run-workflow.js +38 -25
- package/dist/workflows/exec/scheduler.js +12 -41
- package/dist/workflows/exec/step-work.js +91 -35
- package/dist/workflows/ir/compile.js +13 -26
- package/dist/workflows/ir/freeze.js +243 -0
- package/dist/workflows/ir/plan-hash.js +40 -5
- package/dist/workflows/ir/schema.js +542 -1
- package/dist/workflows/parser.js +7 -0
- package/dist/workflows/program/parser.js +132 -23
- package/dist/workflows/program/project.js +3 -4
- package/dist/workflows/program/schema.js +2 -2
- package/dist/workflows/resource-limits.js +20 -0
- package/dist/workflows/runtime/plan-classifier.js +187 -0
- package/dist/workflows/runtime/runs.js +54 -47
- package/dist/workflows/runtime/workflow-asset-loader.js +0 -22
- package/dist/workflows/validator.js +25 -0
- package/docs/data-and-telemetry.md +2 -2
- package/docs/migration/release-notes/0.6.0.md +1 -1
- package/docs/migration/release-notes/0.7.0.md +5 -4
- package/docs/migration/v0.8-to-v0.9.md +401 -0
- package/package.json +4 -2
- package/schemas/akm-config.json +16638 -0
- package/schemas/akm-task.json +87 -0
- package/schemas/akm-workflow.json +372 -0
- package/dist/commands/improve/improve-profiles.js +0 -168
- package/dist/core/config/config-migration.js +0 -602
- package/dist/core/deep-merge.js +0 -38
- package/dist/llm/call-ai.js +0 -62
- package/dist/setup/legacy-config.js +0 -106
- package/docs/README.md +0 -104
- /package/dist/assets/{profiles → improve-strategies}/catchup.json +0 -0
- /package/dist/assets/{profiles → improve-strategies}/consolidate.json +0 -0
- /package/dist/assets/{profiles → improve-strategies}/frequent.json +0 -0
- /package/dist/assets/{profiles → improve-strategies}/memory-focus.json +0 -0
- /package/dist/assets/{profiles → improve-strategies}/quick.json +0 -0
- /package/dist/assets/{profiles → improve-strategies}/recombine-only.json +0 -0
- /package/dist/assets/{profiles → improve-strategies}/synthesize.json +0 -0
- /package/dist/assets/{profiles → improve-strategies}/thorough.json +0 -0
|
@@ -9,7 +9,8 @@ import * as p from "../../cli/clack.js";
|
|
|
9
9
|
import { detectAgentCliProfiles, pickDefaultAgentProfile } from "../../integrations/agent/index.js";
|
|
10
10
|
import { probeLlmCapabilities } from "../../llm/client.js";
|
|
11
11
|
import { detectLMStudio, detectOllama } from "../detect.js";
|
|
12
|
-
import {
|
|
12
|
+
import { verifyOpenAiCompatibleEndpoint } from "../detected-engines.js";
|
|
13
|
+
import { cloneLlmConnection, readAgentEngineSelection, readCurrentLlmEngine, } from "../engine-config.js";
|
|
13
14
|
import { prompt, promptOrBack } from "../prompt.js";
|
|
14
15
|
export async function stepOllama(current) {
|
|
15
16
|
const spin = p.spinner();
|
|
@@ -154,7 +155,7 @@ export async function stepLlm(current, ollamaEndpoint, ollamaChatModels, lmStudi
|
|
|
154
155
|
options.push({ value: "lmstudio", label: "LM Studio / local server", hint: lmStudioHint });
|
|
155
156
|
options.push({ value: "custom", label: "Custom OpenAI-compatible endpoint" });
|
|
156
157
|
options.push({ value: "none", label: "Skip LLM", hint: "no metadata enhancement during indexing" });
|
|
157
|
-
const currentLlm =
|
|
158
|
+
const currentLlm = readCurrentLlmEngine(current);
|
|
158
159
|
if (currentLlm) {
|
|
159
160
|
options.push({
|
|
160
161
|
value: "keep",
|
|
@@ -169,7 +170,7 @@ export async function stepLlm(current, ollamaEndpoint, ollamaChatModels, lmStudi
|
|
|
169
170
|
initialValue,
|
|
170
171
|
}));
|
|
171
172
|
if (choice === "keep")
|
|
172
|
-
return
|
|
173
|
+
return cloneLlmConnection(currentLlm);
|
|
173
174
|
if (choice === "none")
|
|
174
175
|
return undefined;
|
|
175
176
|
// Handle "Import from <Harness>" choices
|
|
@@ -180,16 +181,28 @@ export async function stepLlm(current, ollamaEndpoint, ollamaChatModels, lmStudi
|
|
|
180
181
|
// Show a summary before accepting
|
|
181
182
|
p.log.info(`Importing LLM config from ${harness.harnessName}: ` +
|
|
182
183
|
[harness.provider, harness.model, harness.baseUrl].filter(Boolean).join(", "));
|
|
184
|
+
if (!harness.baseUrl || !harness.model) {
|
|
185
|
+
p.log.warn(`Skipping ${harness.harnessName}: no complete endpoint/model was detected.`);
|
|
186
|
+
return undefined;
|
|
187
|
+
}
|
|
188
|
+
const verified = await verifyOpenAiCompatibleEndpoint({
|
|
189
|
+
endpoint: harness.baseUrl,
|
|
190
|
+
model: harness.model,
|
|
191
|
+
apiKeyEnvVar: harness.apiKeyEnvVar,
|
|
192
|
+
});
|
|
193
|
+
if (!verified.ok) {
|
|
194
|
+
p.log.warn(`Skipping ${harness.harnessName}: ${verified.reason}. Fix its endpoint/credential and retry setup.`);
|
|
195
|
+
return undefined;
|
|
196
|
+
}
|
|
183
197
|
const llmConfig = {
|
|
184
|
-
endpoint:
|
|
198
|
+
endpoint: verified.endpoint,
|
|
185
199
|
model: harness.model ?? "",
|
|
186
200
|
temperature: 0.3,
|
|
187
201
|
maxTokens: 1024,
|
|
202
|
+
...(harness.apiKeyEnvVar ? { apiKey: `\${${harness.apiKeyEnvVar}}` } : {}),
|
|
188
203
|
};
|
|
189
204
|
if (harness.provider)
|
|
190
205
|
llmConfig.provider = harness.provider;
|
|
191
|
-
if (harness.baseUrl)
|
|
192
|
-
llmConfig.endpoint = harness.baseUrl;
|
|
193
206
|
return llmConfig;
|
|
194
207
|
}
|
|
195
208
|
let llm;
|
|
@@ -376,7 +389,7 @@ export async function stepSmallModelConnection(current) {
|
|
|
376
389
|
? `${lmStudio.models.length} model${lmStudio.models.length === 1 ? "" : "s"} detected`
|
|
377
390
|
: "http://localhost:1234";
|
|
378
391
|
providerOptions.push({ value: "openai", label: "OpenAI", hint: "requires AKM_LLM_API_KEY" }, { value: "lmstudio", label: "LM Studio / local server", hint: lmStudioHint }, { value: "custom", label: "Custom OpenAI-compatible endpoint" }, { value: "skip", label: "Skip — disable enrichment features" });
|
|
379
|
-
const currentLlmSmall =
|
|
392
|
+
const currentLlmSmall = readCurrentLlmEngine(current);
|
|
380
393
|
if (currentLlmSmall) {
|
|
381
394
|
providerOptions.push({
|
|
382
395
|
value: "keep",
|
|
@@ -391,7 +404,7 @@ export async function stepSmallModelConnection(current) {
|
|
|
391
404
|
initialValue,
|
|
392
405
|
}));
|
|
393
406
|
if (providerChoice === "keep") {
|
|
394
|
-
return { llm:
|
|
407
|
+
return { llm: cloneLlmConnection(currentLlmSmall), skipped: false, ollamaEndpoint };
|
|
395
408
|
}
|
|
396
409
|
if (providerChoice === "skip") {
|
|
397
410
|
p.note([
|
|
@@ -539,8 +552,11 @@ export async function stepSmallModelConnection(current) {
|
|
|
539
552
|
validate: (v) => (!v?.trim() ? "Model name cannot be empty" : undefined),
|
|
540
553
|
}));
|
|
541
554
|
const apiKeyInput = await promptOrBack(() => p.text({
|
|
542
|
-
message: "API key
|
|
543
|
-
placeholder: "",
|
|
555
|
+
message: "API key environment variable name (optional):",
|
|
556
|
+
placeholder: "CUSTOM_LLM_API_KEY",
|
|
557
|
+
validate: (value) => value && !/^[A-Za-z_][A-Za-z0-9_]*$/.test(value)
|
|
558
|
+
? "Use an environment variable name, not a key value"
|
|
559
|
+
: undefined,
|
|
544
560
|
}));
|
|
545
561
|
llm = {
|
|
546
562
|
provider: "custom",
|
|
@@ -548,7 +564,7 @@ export async function stepSmallModelConnection(current) {
|
|
|
548
564
|
model: model.trim(),
|
|
549
565
|
temperature: 0.3,
|
|
550
566
|
maxTokens: 1024,
|
|
551
|
-
...(apiKeyInput?.trim() ? { apiKey: apiKeyInput.trim() } : {}),
|
|
567
|
+
...(apiKeyInput?.trim() ? { apiKey: `\${${apiKeyInput.trim()}}` } : {}),
|
|
552
568
|
};
|
|
553
569
|
}
|
|
554
570
|
// Best-effort probe — never blocks setup.
|
|
@@ -584,7 +600,7 @@ export async function stepAgentConnection(current, smallModel) {
|
|
|
584
600
|
].join("\n"));
|
|
585
601
|
// Detect available CLI agents.
|
|
586
602
|
const detections = detectAgentCliProfiles(current);
|
|
587
|
-
const currentAgentBlock =
|
|
603
|
+
const currentAgentBlock = readAgentEngineSelection(current);
|
|
588
604
|
const availableClis = detections.filter((d) => d.available);
|
|
589
605
|
const agentOptions = [];
|
|
590
606
|
if (!smallModel.skipped && smallModel.llm) {
|
|
@@ -606,8 +622,8 @@ export async function stepAgentConnection(current, smallModel) {
|
|
|
606
622
|
if (currentAgentBlock) {
|
|
607
623
|
const currentDesc = currentAgentBlock.default
|
|
608
624
|
? `CLI: ${currentAgentBlock.default}`
|
|
609
|
-
: currentAgentBlock.
|
|
610
|
-
? `SDK: ${currentAgentBlock.
|
|
625
|
+
: currentAgentBlock.engines?.default?.model
|
|
626
|
+
? `SDK: ${currentAgentBlock.engines.default.model}`
|
|
611
627
|
: "configured";
|
|
612
628
|
agentOptions.push({ value: "keep", label: `Keep current: ${currentDesc}` });
|
|
613
629
|
}
|
|
@@ -647,9 +663,9 @@ export async function stepAgentConnection(current, smallModel) {
|
|
|
647
663
|
else {
|
|
648
664
|
const baseEndpoint = smallModel.llm.endpoint.replace("/v1/chat/completions", "");
|
|
649
665
|
p.log.info(`Endpoint: ${baseEndpoint} (from Step 1)`);
|
|
650
|
-
const profileName = smallModel.llm.provider ?? "default"
|
|
666
|
+
const profileName = `${smallModel.llm.provider ?? "default"}-agent`;
|
|
651
667
|
// Pre-populate from existing agent profile for this provider, if any.
|
|
652
|
-
const existingAgentModel = currentAgentBlock?.
|
|
668
|
+
const existingAgentModel = currentAgentBlock?.engines?.[profileName]?.model ?? smallModel.llm.model ?? undefined;
|
|
653
669
|
const agentModel = await prompt(() => p.text({
|
|
654
670
|
message: "Model to use for agent tasks (same model is fine, larger models work better):",
|
|
655
671
|
placeholder: existingAgentModel ?? "qwen2.5-coder:32b",
|
|
@@ -658,13 +674,14 @@ export async function stepAgentConnection(current, smallModel) {
|
|
|
658
674
|
}));
|
|
659
675
|
return {
|
|
660
676
|
...(currentAgentBlock ?? {}),
|
|
661
|
-
|
|
662
|
-
...(currentAgentBlock?.
|
|
677
|
+
engines: {
|
|
678
|
+
...(currentAgentBlock?.engines ?? {}),
|
|
663
679
|
[profileName]: {
|
|
664
|
-
...(currentAgentBlock?.
|
|
665
|
-
|
|
680
|
+
...(currentAgentBlock?.engines?.[profileName] ?? {}),
|
|
681
|
+
kind: "agent",
|
|
682
|
+
platform: "opencode-sdk",
|
|
666
683
|
model: agentModel.trim(),
|
|
667
|
-
|
|
684
|
+
...(current.defaults?.llmEngine ? { llmEngine: current.defaults.llmEngine } : {}),
|
|
668
685
|
},
|
|
669
686
|
},
|
|
670
687
|
default: profileName,
|
|
@@ -693,8 +710,8 @@ export async function stepAgentConnection(current, smallModel) {
|
|
|
693
710
|
}
|
|
694
711
|
// "new-connection" (also fall-through from "same-provider" when Step 1 was skipped)
|
|
695
712
|
// Pre-populate from current "custom" agent profile if available.
|
|
696
|
-
const currentCustomAgentProfile = currentAgentBlock?.
|
|
697
|
-
const currentNewEndpoint =
|
|
713
|
+
const currentCustomAgentProfile = currentAgentBlock?.engines?.["custom-agent"];
|
|
714
|
+
const currentNewEndpoint = undefined;
|
|
698
715
|
const currentNewModel = currentCustomAgentProfile?.model ?? undefined;
|
|
699
716
|
const newEndpoint = await prompt(() => p.text({
|
|
700
717
|
message: "OpenAI-compatible chat completions endpoint:",
|
|
@@ -708,8 +725,11 @@ export async function stepAgentConnection(current, smallModel) {
|
|
|
708
725
|
},
|
|
709
726
|
}));
|
|
710
727
|
const newApiKeyInput = await promptOrBack(() => p.text({
|
|
711
|
-
message: "API key
|
|
712
|
-
placeholder: "",
|
|
728
|
+
message: "API key environment variable name (optional):",
|
|
729
|
+
placeholder: "CUSTOM_LLM_API_KEY",
|
|
730
|
+
validate: (value) => value && !/^[A-Za-z_][A-Za-z0-9_]*$/.test(value)
|
|
731
|
+
? "Use an environment variable name, not a key value"
|
|
732
|
+
: undefined,
|
|
713
733
|
}));
|
|
714
734
|
const newModel = await prompt(() => p.text({
|
|
715
735
|
message: "Model name (larger is better, e.g. gpt-4o):",
|
|
@@ -717,18 +737,27 @@ export async function stepAgentConnection(current, smallModel) {
|
|
|
717
737
|
...(currentNewModel ? { defaultValue: currentNewModel } : {}),
|
|
718
738
|
validate: (v) => (!v?.trim() ? "Model name cannot be empty" : undefined),
|
|
719
739
|
}));
|
|
740
|
+
const llmEngineName = "custom-llm";
|
|
741
|
+
const agentEngineName = "custom-agent";
|
|
720
742
|
const customProfile = {
|
|
721
|
-
|
|
722
|
-
|
|
743
|
+
kind: "agent",
|
|
744
|
+
platform: "opencode-sdk",
|
|
745
|
+
llmEngine: llmEngineName,
|
|
723
746
|
model: newModel.trim(),
|
|
724
|
-
...(newApiKeyInput?.trim() ? { apiKey: newApiKeyInput.trim() } : {}),
|
|
725
747
|
};
|
|
726
748
|
return {
|
|
727
749
|
...(currentAgentBlock ?? {}),
|
|
728
|
-
|
|
729
|
-
...(currentAgentBlock?.
|
|
730
|
-
|
|
750
|
+
engines: {
|
|
751
|
+
...(currentAgentBlock?.engines ?? {}),
|
|
752
|
+
[llmEngineName]: {
|
|
753
|
+
kind: "llm",
|
|
754
|
+
provider: "custom",
|
|
755
|
+
endpoint: newEndpoint.trim(),
|
|
756
|
+
model: newModel.trim(),
|
|
757
|
+
...(newApiKeyInput?.trim() ? { apiKey: `\${${newApiKeyInput.trim()}}` } : {}),
|
|
758
|
+
},
|
|
759
|
+
[agentEngineName]: customProfile,
|
|
731
760
|
},
|
|
732
|
-
default:
|
|
761
|
+
default: agentEngineName,
|
|
733
762
|
};
|
|
734
763
|
}
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
import * as p from "../../cli/clack.js";
|
|
9
9
|
import { detectAgentCliProfiles, pickDefaultAgentProfile } from "../../integrations/agent/index.js";
|
|
10
10
|
import { detectAgentPlatforms } from "../detect.js";
|
|
11
|
-
import {
|
|
11
|
+
import { readAgentEngineSelection } from "../engine-config.js";
|
|
12
12
|
import { prompt } from "../prompt.js";
|
|
13
13
|
export async function stepAgentPlatforms(current) {
|
|
14
14
|
const platforms = detectAgentPlatforms();
|
|
@@ -66,7 +66,7 @@ export function printCapabilitySummary(smallModelSkipped, agentConfigured) {
|
|
|
66
66
|
p.note(lines.join("\n"), "Feature Summary");
|
|
67
67
|
}
|
|
68
68
|
export async function stepAgentSelection(current, detections) {
|
|
69
|
-
const currentAgentBlock =
|
|
69
|
+
const currentAgentBlock = readAgentEngineSelection(current);
|
|
70
70
|
const available = detections.filter((d) => d.available);
|
|
71
71
|
if (available.length === 0) {
|
|
72
72
|
return currentAgentBlock;
|
|
@@ -85,7 +85,7 @@ export async function stepAgentSelection(current, detections) {
|
|
|
85
85
|
initialValue,
|
|
86
86
|
}));
|
|
87
87
|
if (selectedDefault === "disabled") {
|
|
88
|
-
if (!currentAgentBlock?.
|
|
88
|
+
if (!currentAgentBlock?.engines) {
|
|
89
89
|
return undefined;
|
|
90
90
|
}
|
|
91
91
|
return {
|
|
@@ -110,7 +110,7 @@ export async function stepAgentSelection(current, detections) {
|
|
|
110
110
|
*/
|
|
111
111
|
export function stepAgentCliDetection(current, detectFn = detectAgentCliProfiles) {
|
|
112
112
|
const detections = detectFn(current);
|
|
113
|
-
const currentAgentBlock =
|
|
113
|
+
const currentAgentBlock = readAgentEngineSelection(current);
|
|
114
114
|
const defaultName = pickDefaultAgentProfile(detections, currentAgentBlock?.default);
|
|
115
115
|
// No installed agents found and no existing config → leave block absent.
|
|
116
116
|
if (!defaultName && !currentAgentBlock) {
|
package/dist/setup/steps.js
CHANGED
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
2
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
3
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
+
import { deepMergeConfig } from "../core/config/deep-merge.js";
|
|
4
5
|
/**
|
|
5
6
|
* Build a fresh `SetupContext` over a starting config. The returned context
|
|
6
7
|
* applies deltas in-place onto an internal accumulator and exposes the
|
|
7
8
|
* latest snapshot via `ctx.config`.
|
|
8
9
|
*/
|
|
9
10
|
export function createSetupContext(initial, options) {
|
|
10
|
-
let acc = {
|
|
11
|
+
let acc = deepMergeConfig({}, initial);
|
|
11
12
|
return {
|
|
12
13
|
get config() {
|
|
13
14
|
return acc;
|
|
14
15
|
},
|
|
15
16
|
nonInteractive: options.nonInteractive,
|
|
16
17
|
apply(delta) {
|
|
17
|
-
acc =
|
|
18
|
+
acc = deepMergeConfig(acc, delta);
|
|
18
19
|
},
|
|
19
20
|
};
|
|
20
21
|
}
|
package/dist/storage/database.js
CHANGED
|
@@ -133,5 +133,17 @@ function openNodeDatabase(path, opts) {
|
|
|
133
133
|
if (opts?.create === false)
|
|
134
134
|
options.fileMustExist = true;
|
|
135
135
|
const db = opts ? new BetterSqlite3(path, options) : new BetterSqlite3(path);
|
|
136
|
-
return
|
|
136
|
+
return {
|
|
137
|
+
prepare: db.prepare.bind(db),
|
|
138
|
+
exec: db.exec.bind(db),
|
|
139
|
+
// better-sqlite3 exposes mutations on prepared statements, while
|
|
140
|
+
// bun:sqlite also provides db.run(). Normalize the latter at the provider
|
|
141
|
+
// boundary so callers and maintenance wrappers can rely on one contract.
|
|
142
|
+
run: (sql, ...params) => db.prepare(sql).run(...params),
|
|
143
|
+
transaction: db.transaction.bind(db),
|
|
144
|
+
get inTransaction() {
|
|
145
|
+
return db.inTransaction;
|
|
146
|
+
},
|
|
147
|
+
close: db.close.bind(db),
|
|
148
|
+
};
|
|
137
149
|
}
|
|
@@ -34,6 +34,7 @@ export function runMigrations(db, migrations, opts) {
|
|
|
34
34
|
for (const migration of migrations) {
|
|
35
35
|
if (applied.has(migration.id))
|
|
36
36
|
continue;
|
|
37
|
+
opts?.beforeMigration?.(migration);
|
|
37
38
|
db.transaction(() => {
|
|
38
39
|
db.exec(migration.up);
|
|
39
40
|
db.prepare("INSERT INTO schema_migrations (id) VALUES (?)").run(migration.id);
|
|
@@ -103,10 +103,10 @@ export function recordImproveRun(db, input) {
|
|
|
103
103
|
const metricsObj = input.metrics ?? computeImproveRunMetrics(input.result);
|
|
104
104
|
db.prepare(`
|
|
105
105
|
INSERT INTO improve_runs
|
|
106
|
-
|
|
106
|
+
(id, started_at, completed_at, stash_dir, dry_run, profile, strategy,
|
|
107
107
|
scope_mode, scope_value, guidance, ok, result_json, metrics_json, metadata_json)
|
|
108
|
-
|
|
109
|
-
`).run(input.id, input.startedAt, input.completedAt, input.stashDir, input.dryRun ? 1 : 0, input.
|
|
108
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
109
|
+
`).run(input.id, input.startedAt, input.completedAt, input.stashDir, input.dryRun ? 1 : 0, input.legacyProfile ?? null, input.strategy ?? null, input.scopeMode, input.scopeValue, input.guidance, input.ok ? 1 : 0, JSON.stringify(input.result), JSON.stringify(metricsObj), JSON.stringify(input.metadata ?? {}));
|
|
110
110
|
}
|
|
111
111
|
/**
|
|
112
112
|
* Read real (non-dry-run) improve_runs rows whose `started_at` falls in the
|
|
@@ -124,8 +124,8 @@ export function recordImproveRun(db, input) {
|
|
|
124
124
|
*/
|
|
125
125
|
export function queryImproveRuns(db, since, until) {
|
|
126
126
|
const sql = until
|
|
127
|
-
? "SELECT id, started_at, completed_at, ok, scope_mode, scope_value, result_json FROM improve_runs WHERE started_at >= ? AND started_at < ? AND dry_run = 0 ORDER BY started_at DESC"
|
|
128
|
-
: "SELECT id, started_at, completed_at, ok, scope_mode, scope_value, result_json FROM improve_runs WHERE started_at >= ? AND dry_run = 0 ORDER BY started_at DESC";
|
|
127
|
+
? "SELECT id, started_at, completed_at, ok, scope_mode, scope_value, profile AS legacyProfile, strategy, result_json FROM improve_runs WHERE started_at >= ? AND started_at < ? AND dry_run = 0 ORDER BY started_at DESC"
|
|
128
|
+
: "SELECT id, started_at, completed_at, ok, scope_mode, scope_value, profile AS legacyProfile, strategy, result_json FROM improve_runs WHERE started_at >= ? AND dry_run = 0 ORDER BY started_at DESC";
|
|
129
129
|
return (until ? db.prepare(sql).all(since, until) : db.prepare(sql).all(since));
|
|
130
130
|
}
|
|
131
131
|
/**
|
|
@@ -1,6 +1,84 @@
|
|
|
1
1
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
2
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
3
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
+
function metadataError(message) {
|
|
5
|
+
throw new Error(`invalid task_history metadata_json: ${message}`);
|
|
6
|
+
}
|
|
7
|
+
function isRecord(value) {
|
|
8
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
9
|
+
}
|
|
10
|
+
function validateDetail(value) {
|
|
11
|
+
if (value === undefined || value === null)
|
|
12
|
+
return;
|
|
13
|
+
if (!isRecord(value))
|
|
14
|
+
metadataError("detail must be an object or null");
|
|
15
|
+
const allowed = new Set(["runId", "reason", "error", "exitCode"]);
|
|
16
|
+
const unknown = Object.keys(value).filter((key) => !allowed.has(key));
|
|
17
|
+
if (unknown.length > 0)
|
|
18
|
+
metadataError(`unknown detail fields: ${unknown.sort().join(", ")}`);
|
|
19
|
+
for (const field of ["runId", "reason", "error"]) {
|
|
20
|
+
if (value[field] !== undefined && typeof value[field] !== "string")
|
|
21
|
+
metadataError(`detail.${field} must be a string`);
|
|
22
|
+
}
|
|
23
|
+
if (value.exitCode !== undefined && value.exitCode !== null && typeof value.exitCode !== "number") {
|
|
24
|
+
metadataError("detail.exitCode must be a number or null");
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/** Decode historical v1 and current v2 metadata without cross-version aliases. */
|
|
28
|
+
export function decodeTaskHistoryMetadata(input) {
|
|
29
|
+
let parsed = input;
|
|
30
|
+
if (typeof input === "string") {
|
|
31
|
+
try {
|
|
32
|
+
parsed = JSON.parse(input);
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
metadataError("not valid JSON");
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
if (!isRecord(parsed))
|
|
39
|
+
metadataError("root must be an object");
|
|
40
|
+
const version = parsed.metadataVersion ?? 1;
|
|
41
|
+
if (version === 1) {
|
|
42
|
+
const allowed = new Set(["metadataVersion", "durationMs", "detail", "profile"]);
|
|
43
|
+
const unknown = Object.keys(parsed).filter((key) => !allowed.has(key));
|
|
44
|
+
if (unknown.length > 0)
|
|
45
|
+
metadataError(`unknown v1 fields: ${unknown.sort().join(", ")}`);
|
|
46
|
+
if (parsed.metadataVersion !== undefined && parsed.metadataVersion !== 1)
|
|
47
|
+
metadataError("invalid v1 metadataVersion");
|
|
48
|
+
if (parsed.durationMs !== undefined && typeof parsed.durationMs !== "number")
|
|
49
|
+
metadataError("durationMs must be a number");
|
|
50
|
+
if (parsed.profile !== undefined && typeof parsed.profile !== "string")
|
|
51
|
+
metadataError("profile must be a string");
|
|
52
|
+
validateDetail(parsed.detail);
|
|
53
|
+
return {
|
|
54
|
+
metadataVersion: 1,
|
|
55
|
+
...(typeof parsed.durationMs === "number" ? { durationMs: parsed.durationMs } : {}),
|
|
56
|
+
...(parsed.detail !== undefined && parsed.detail !== null ? { detail: parsed.detail } : {}),
|
|
57
|
+
...(typeof parsed.profile === "string" ? { legacyProfile: parsed.profile } : {}),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
if (version === 2) {
|
|
61
|
+
const allowed = new Set(["metadataVersion", "durationMs", "detail", "engine"]);
|
|
62
|
+
const unknown = Object.keys(parsed).filter((key) => !allowed.has(key));
|
|
63
|
+
if (unknown.length > 0)
|
|
64
|
+
metadataError(`unknown v2 fields: ${unknown.sort().join(", ")}`);
|
|
65
|
+
if (typeof parsed.durationMs !== "number")
|
|
66
|
+
metadataError("durationMs must be a number");
|
|
67
|
+
if (!("detail" in parsed))
|
|
68
|
+
metadataError("detail is required in v2");
|
|
69
|
+
if (parsed.engine !== undefined && parsed.engine !== null && typeof parsed.engine !== "string") {
|
|
70
|
+
metadataError("engine must be a string or null");
|
|
71
|
+
}
|
|
72
|
+
validateDetail(parsed.detail);
|
|
73
|
+
return {
|
|
74
|
+
metadataVersion: 2,
|
|
75
|
+
durationMs: parsed.durationMs,
|
|
76
|
+
detail: parsed.detail ?? null,
|
|
77
|
+
...(parsed.engine !== undefined ? { engine: parsed.engine } : {}),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
metadataError(`unsupported metadataVersion: ${String(version)}`);
|
|
81
|
+
}
|
|
4
82
|
/**
|
|
5
83
|
* Upsert a task history row.
|
|
6
84
|
*/
|
|
@@ -137,10 +137,10 @@ export class WorkflowRunsRepository {
|
|
|
137
137
|
* `insertRun`, so a run row never exists without its frozen plan. Read back
|
|
138
138
|
* via {@link getRunById} (`plan_json` / `plan_hash` on the row).
|
|
139
139
|
*/
|
|
140
|
-
setRunPlan(runId, planJson, planHash) {
|
|
140
|
+
setRunPlan(runId, planJson, planHash, planIrVersion = 3) {
|
|
141
141
|
this.db
|
|
142
|
-
.prepare("UPDATE workflow_runs SET plan_json = ?, plan_hash = ? WHERE id = ?")
|
|
143
|
-
.run(planJson, planHash, runId);
|
|
142
|
+
.prepare("UPDATE workflow_runs SET plan_json = ?, plan_hash = ?, plan_ir_version = ? WHERE id = ?")
|
|
143
|
+
.run(planJson, planHash, planIrVersion, runId);
|
|
144
144
|
}
|
|
145
145
|
// ── engine run lease (migration 006 columns, R2 enforcement) ──────────────
|
|
146
146
|
//
|
|
@@ -238,15 +238,16 @@ export class WorkflowRunsRepository {
|
|
|
238
238
|
insertUnit(input) {
|
|
239
239
|
this.db
|
|
240
240
|
.prepare(`INSERT INTO workflow_run_units (
|
|
241
|
-
run_id, unit_id, step_id, node_id, parent_unit_id, phase, runner, model,
|
|
241
|
+
run_id, unit_id, step_id, node_id, parent_unit_id, phase, runner, engine, model,
|
|
242
242
|
status, input_hash, worktree_path, started_at, claim_holder, claim_expires_at
|
|
243
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'running', ?, ?, ?, ?, ?)
|
|
243
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 'running', ?, ?, ?, ?, ?)
|
|
244
244
|
ON CONFLICT(run_id, unit_id) DO UPDATE SET
|
|
245
245
|
step_id = excluded.step_id,
|
|
246
246
|
node_id = excluded.node_id,
|
|
247
247
|
parent_unit_id = excluded.parent_unit_id,
|
|
248
|
-
|
|
249
|
-
|
|
248
|
+
phase = excluded.phase,
|
|
249
|
+
runner = excluded.runner,
|
|
250
|
+
engine = excluded.engine,
|
|
250
251
|
model = excluded.model,
|
|
251
252
|
status = excluded.status,
|
|
252
253
|
input_hash = excluded.input_hash,
|
|
@@ -261,7 +262,7 @@ export class WorkflowRunsRepository {
|
|
|
261
262
|
finished_at = NULL,
|
|
262
263
|
last_checkin_at = NULL,
|
|
263
264
|
attempts = workflow_run_units.attempts + 1`)
|
|
264
|
-
.run(input.runId, input.unitId, input.stepId, input.nodeId, input.parentUnitId, input.phase, input.runner, input.model, input.inputHash, input.worktreePath ?? null, input.startedAt, input.claimHolder ?? null, input.claimExpiresAt ?? null);
|
|
265
|
+
.run(input.runId, input.unitId, input.stepId, input.nodeId, input.parentUnitId, input.phase, input.runner, input.engine ?? null, input.model, input.inputHash, input.worktreePath ?? null, input.startedAt, input.claimHolder ?? null, input.claimExpiresAt ?? null);
|
|
265
266
|
}
|
|
266
267
|
/**
|
|
267
268
|
* Stamp a `--status running` claim + heartbeat on a unit row (migration 009,
|