akm-cli 0.9.0-rc.0 → 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 +339 -2
- 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/prompts/workflow-unit-preamble.md +26 -0
- 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/env-binding.js +95 -0
- package/dist/commands/env/env-cli.js +8 -65
- 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/migration-help.js +7 -4
- 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/commands/workflow-cli.js +276 -12
- package/dist/core/asset/asset-spec.js +58 -1
- 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 +379 -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/json-schema.js +142 -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 +118 -2
- 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/indexer/walk/matchers.js +39 -0
- package/dist/integrations/agent/builder-shared.js +7 -0
- package/dist/integrations/agent/builders.js +5 -50
- 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 +16 -2
- package/dist/integrations/agent/profiles.js +36 -62
- package/dist/integrations/agent/runner-dispatch.js +91 -4
- package/dist/integrations/agent/runner.js +76 -207
- package/dist/integrations/agent/spawn.js +141 -20
- package/dist/integrations/harnesses/aider/agent-builder.js +112 -0
- package/dist/integrations/harnesses/aider/index.js +57 -0
- package/dist/integrations/harnesses/aider/result-extractor.js +53 -0
- package/dist/integrations/harnesses/amazonq/agent-builder.js +152 -0
- package/dist/integrations/harnesses/amazonq/index.js +58 -0
- package/dist/integrations/harnesses/amazonq/result-extractor.js +48 -0
- package/dist/integrations/harnesses/claude/agent-builder.js +46 -8
- package/dist/integrations/harnesses/claude/index.js +25 -25
- package/dist/integrations/harnesses/claude/result-extractor.js +52 -0
- package/dist/integrations/harnesses/codex/agent-builder.js +136 -0
- package/dist/integrations/harnesses/codex/index.js +62 -0
- package/dist/integrations/harnesses/codex/result-extractor.js +73 -0
- package/dist/integrations/harnesses/copilot/agent-builder.js +121 -0
- package/dist/integrations/harnesses/copilot/index.js +59 -0
- package/dist/integrations/harnesses/copilot/result-extractor.js +151 -0
- package/dist/integrations/harnesses/gemini/agent-builder.js +120 -0
- package/dist/integrations/harnesses/gemini/index.js +59 -0
- package/dist/integrations/harnesses/gemini/result-extractor.js +121 -0
- package/dist/integrations/harnesses/index.js +27 -28
- package/dist/integrations/harnesses/opencode/agent-builder.js +2 -3
- package/dist/integrations/harnesses/opencode/index.js +15 -22
- package/dist/integrations/harnesses/opencode-sdk/harness.js +60 -0
- package/dist/integrations/harnesses/opencode-sdk/index.js +8 -32
- package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +681 -108
- package/dist/integrations/harnesses/openhands/agent-builder.js +125 -0
- package/dist/integrations/harnesses/openhands/index.js +57 -0
- package/dist/integrations/harnesses/openhands/result-extractor.js +103 -0
- package/dist/integrations/harnesses/pi/agent-builder.js +103 -0
- package/dist/integrations/harnesses/pi/index.js +57 -0
- package/dist/integrations/harnesses/pi/result-extractor.js +135 -0
- package/dist/integrations/harnesses/types.js +8 -32
- package/dist/integrations/lockfile.js +32 -21
- package/dist/integrations/session-logs/index.js +24 -11
- 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/renderers.js +3 -2
- package/dist/output/shapes/helpers.js +2 -1
- package/dist/output/shapes/passthrough.js +6 -0
- package/dist/output/text/helpers.js +215 -2
- package/dist/output/text/workflow.js +3 -1
- package/dist/schemas/akm-config.json +16638 -0
- package/dist/schemas/akm-task.json +87 -0
- package/dist/schemas/akm-workflow.json +372 -0
- package/dist/scripts/migrate-storage.js +10944 -8801
- package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +9247 -350
- 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 +190 -1
- 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/text-import-hook.mjs +1 -1
- package/dist/wiki/wiki.js +9 -8
- package/dist/workflows/authoring/authoring.js +123 -10
- package/dist/workflows/authoring/workflow-program-template.yaml +31 -0
- package/dist/workflows/cli.js +4 -0
- package/dist/workflows/concurrency-policy.js +15 -0
- package/dist/workflows/db.js +200 -13
- package/dist/workflows/exec/brief.js +478 -0
- package/dist/workflows/exec/frozen-judge.js +47 -0
- package/dist/workflows/exec/native-executor.js +1034 -0
- package/dist/workflows/exec/param-secrets.js +115 -0
- package/dist/workflows/exec/report.js +1355 -0
- package/dist/workflows/exec/run-workflow.js +609 -0
- package/dist/workflows/exec/scheduler.js +71 -0
- package/dist/workflows/exec/step-work.js +1212 -0
- package/dist/workflows/exec/unit-writer.js +23 -0
- package/dist/workflows/exec/watch.js +116 -0
- package/dist/workflows/exec/worktree.js +171 -0
- package/dist/workflows/ir/compile.js +375 -0
- package/dist/workflows/ir/freeze.js +243 -0
- package/dist/workflows/ir/params.js +54 -0
- package/dist/workflows/ir/plan-hash.js +68 -0
- package/dist/workflows/ir/schema.js +545 -0
- package/dist/workflows/parser.js +10 -1
- package/dist/workflows/program/expressions.js +369 -0
- package/dist/workflows/program/parser.js +869 -0
- package/dist/workflows/program/project.js +104 -0
- package/dist/workflows/program/schema.js +54 -0
- package/dist/workflows/renderer.js +82 -5
- package/dist/workflows/resource-limits.js +20 -0
- package/dist/workflows/runtime/agent-identity.js +59 -14
- package/dist/workflows/runtime/plan-classifier.js +187 -0
- package/dist/workflows/runtime/runs.js +246 -69
- package/dist/workflows/runtime/unit-checkin.js +45 -0
- package/dist/workflows/runtime/workflow-asset-loader.js +42 -1
- package/dist/workflows/validate-summary.js +24 -3
- package/dist/workflows/validator.js +26 -1
- package/docs/data-and-telemetry.md +4 -3
- 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/release-notes/0.9.0-beta.60.md +19 -0
- 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
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* content. Pull recent feedback (`feedback` events for that ref) and
|
|
12
12
|
* lesson-lint findings to surface as schema hints.
|
|
13
13
|
* 3. Build the prompt via {@link buildReflectPrompt}.
|
|
14
|
-
* 4.
|
|
14
|
+
* 4. Dispatch the selected named engine via {@link executeRunner}.
|
|
15
15
|
* 5. Parse the agent's stdout into a {@link AgentProposalPayload}.
|
|
16
16
|
* 6. Insert into the proposal queue via {@link createProposal} with
|
|
17
17
|
* `source: "reflect"`.
|
|
@@ -30,22 +30,19 @@ import { parseFrontmatter } from "../../core/asset/frontmatter.js";
|
|
|
30
30
|
import { stripMarkdownFences } from "../../core/asset/markdown.js";
|
|
31
31
|
import { DESCRIPTION_MAX_CHARS, requiresDescription } from "../../core/authoring-rules.js";
|
|
32
32
|
import { resolveStashDir } from "../../core/common.js";
|
|
33
|
-
import {
|
|
34
|
-
import { ConfigError
|
|
33
|
+
import { loadConfig } from "../../core/config/config.js";
|
|
34
|
+
import { ConfigError } from "../../core/errors.js";
|
|
35
35
|
import { appendEvent, readEvents } from "../../core/events.js";
|
|
36
36
|
import { lintLessonContent } from "../../core/lesson-lint.js";
|
|
37
|
+
import { redactSensitiveText } from "../../core/redaction.js";
|
|
37
38
|
import { resolveStandardsContext } from "../../core/standards/resolve-standards-context.js";
|
|
38
|
-
import { warn } from "../../core/warn.js";
|
|
39
39
|
import { lookup } from "../../indexer/indexer.js";
|
|
40
|
-
import {
|
|
41
|
-
import { resolveProcessAgentProfile } from "../../integrations/agent/config.js";
|
|
40
|
+
import { resolveEngine } from "../../integrations/agent/engine-resolution.js";
|
|
42
41
|
import { buildReflectPrompt, extractDraftConfidence, parseAgentProposalPayload, } from "../../integrations/agent/prompts.js";
|
|
43
|
-
import {
|
|
44
|
-
import { executeRunner } from "../../integrations/agent/runner-dispatch.js";
|
|
45
|
-
import { runOpencodeSdk } from "../../integrations/harnesses/opencode-sdk/index.js";
|
|
42
|
+
import { materializeLlmRunnerConnection, resolveImproveProcessRunner, runnerIsLlm, runnerSupportsFileWrite, } from "../../integrations/agent/runner.js";
|
|
43
|
+
import { collectDispatchSensitiveValues, executeRunner } from "../../integrations/agent/runner-dispatch.js";
|
|
46
44
|
import { chatCompletion } from "../../llm/client.js";
|
|
47
|
-
import {
|
|
48
|
-
import { baseFailureFields, enoentHintMessage, isEnoentFailure, loadAgentConfigFromDisk, resolveAgentProfile, } from "../agent/agent-support.js";
|
|
45
|
+
import { baseFailureFields, enoentHintMessage, isEnoentFailure } from "../agent/agent-support.js";
|
|
49
46
|
import { createProposal, isProposalSkipped, listProposals, } from "../proposal/repository.js";
|
|
50
47
|
import { checkReflectSize, isValidDescription } from "../proposal/validators/proposal-quality-validators.js";
|
|
51
48
|
import { deriveLessonRef, runLessonQualityJudge } from "./distill.js";
|
|
@@ -153,8 +150,8 @@ function readRejectedProposals(stash, ref) {
|
|
|
153
150
|
* The path lives under {@link os.tmpdir} and embeds the (sanitized) ref +
|
|
154
151
|
* timestamp + random suffix so concurrent reflect calls cannot collide.
|
|
155
152
|
*
|
|
156
|
-
*
|
|
157
|
-
* has no filesystem access
|
|
153
|
+
* The LLM HTTP runner cannot use this path because chat-completion transport
|
|
154
|
+
* has no filesystem access.
|
|
158
155
|
*/
|
|
159
156
|
function synthesizeReflectDraftPath(ref) {
|
|
160
157
|
const safeRef = (ref ?? "no-ref").replace(/[^a-z0-9_-]/gi, "_");
|
|
@@ -321,19 +318,19 @@ function isStructuredCooldownSignal(stdout) {
|
|
|
321
318
|
* as valid proposals. Anthropic agent best practices recommend structured
|
|
322
319
|
* output when the SDK supports it; this tighter fallback is the safety net.
|
|
323
320
|
*
|
|
324
|
-
*
|
|
321
|
+
* For SDK runners, structured output (tool-call schema) should be used
|
|
325
322
|
* instead of this fallback. That wiring is tracked separately (full SDK
|
|
326
323
|
* structured-output integration); for now this tighter parser applies to all
|
|
327
324
|
* modes and is the primary R-6 deliverable.
|
|
328
325
|
*/
|
|
329
|
-
function fallbackPayloadFromRawContent(stdout, ref,
|
|
326
|
+
function fallbackPayloadFromRawContent(stdout, ref, sdkRunner = false) {
|
|
330
327
|
if (!ref)
|
|
331
328
|
return undefined;
|
|
332
329
|
const trimmed = stripMarkdownFences(stdout).trim();
|
|
333
330
|
if (!trimmed)
|
|
334
331
|
return undefined;
|
|
335
332
|
const targetType = ref.split(":")[0];
|
|
336
|
-
if (!looksLikeAssetContent(trimmed,
|
|
333
|
+
if (!looksLikeAssetContent(trimmed, sdkRunner, targetType))
|
|
337
334
|
return undefined;
|
|
338
335
|
return { ref, content: trimmed };
|
|
339
336
|
}
|
|
@@ -347,10 +344,10 @@ function fallbackPayloadFromRawContent(stdout, ref, sdkMode = false) {
|
|
|
347
344
|
* blocks and pure delimiter sequences as valid payloads.
|
|
348
345
|
* - Heading start (`#`): must have at least 3 non-blank lines after the heading,
|
|
349
346
|
* to ensure there is actual body content and not just a title stub.
|
|
350
|
-
* -
|
|
347
|
+
* - For SDK runners: additionally requires `when_to_use:` for
|
|
351
348
|
* lesson types (full structured output will replace this in a future PR).
|
|
352
349
|
*/
|
|
353
|
-
function looksLikeAssetContent(value,
|
|
350
|
+
function looksLikeAssetContent(value, sdkRunner = false, targetType) {
|
|
354
351
|
if (value.startsWith("---")) {
|
|
355
352
|
// YAML frontmatter must contain at least a description field.
|
|
356
353
|
const fmEnd = value.indexOf("\n---", 4);
|
|
@@ -362,7 +359,7 @@ function looksLikeAssetContent(value, sdkMode = false, targetType) {
|
|
|
362
359
|
return false;
|
|
363
360
|
// In SDK mode, lesson assets additionally require a when_to_use field.
|
|
364
361
|
// Use the target ref type rather than frontmatter type: (which is non-standard).
|
|
365
|
-
if (
|
|
362
|
+
if (sdkRunner && targetType === "lesson") {
|
|
366
363
|
return /^when_to_use\s*:/m.test(fmBlock);
|
|
367
364
|
}
|
|
368
365
|
return true;
|
|
@@ -661,19 +658,11 @@ export async function runReflectViaLlm(opts) {
|
|
|
661
658
|
messages.push({ role: "user", content: REFLECT_CRITIQUE_PROMPT });
|
|
662
659
|
}
|
|
663
660
|
try {
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
}
|
|
669
|
-
else {
|
|
670
|
-
// Production path: full chatCompletion with optional structured-output schema
|
|
671
|
-
// and optional hard max_tokens cap (derived from source body size).
|
|
672
|
-
stdout = await chatCompletion(opts.connection, messages, {
|
|
673
|
-
...(opts.responseSchema !== undefined ? { responseSchema: opts.responseSchema } : {}),
|
|
674
|
-
...(opts.maxTokens !== undefined ? { maxTokens: opts.maxTokens } : {}),
|
|
675
|
-
});
|
|
676
|
-
}
|
|
661
|
+
const stdout = await (opts.chat ?? chatCompletion)(opts.connection, messages, {
|
|
662
|
+
...(Object.hasOwn(opts, "timeoutMs") ? { timeoutMs: opts.timeoutMs } : {}),
|
|
663
|
+
...(opts.responseSchema !== undefined ? { responseSchema: opts.responseSchema } : {}),
|
|
664
|
+
...(opts.maxTokens !== undefined ? { maxTokens: opts.maxTokens } : {}),
|
|
665
|
+
});
|
|
677
666
|
return {
|
|
678
667
|
ok: true,
|
|
679
668
|
stdout,
|
|
@@ -695,10 +684,12 @@ export async function runReflectViaLlm(opts) {
|
|
|
695
684
|
};
|
|
696
685
|
}
|
|
697
686
|
}
|
|
698
|
-
function failureEnvelope(result, ref, fallbackReason = "non_zero_exit") {
|
|
687
|
+
function failureEnvelope(result, ref, engine, fallbackReason = "non_zero_exit") {
|
|
699
688
|
return {
|
|
700
689
|
...baseFailureFields(result, fallbackReason),
|
|
690
|
+
schemaVersion: 2,
|
|
701
691
|
...(ref ? { ref } : {}),
|
|
692
|
+
...(engine ? { engine } : {}),
|
|
702
693
|
};
|
|
703
694
|
}
|
|
704
695
|
export async function akmReflect(options = {}) {
|
|
@@ -710,7 +701,7 @@ export async function akmReflect(options = {}) {
|
|
|
710
701
|
...(options.ref ? { ref: options.ref } : {}),
|
|
711
702
|
metadata: {
|
|
712
703
|
...(options.task ? { task: options.task } : {}),
|
|
713
|
-
...(options.
|
|
704
|
+
...(options.engine ? { engine: options.engine } : {}),
|
|
714
705
|
// Attribution tagging: stamp the eligibility lane so reflect_invoked can be
|
|
715
706
|
// sliced by lane downstream. See EligibilitySource.
|
|
716
707
|
...(options.eligibilitySource ? { eligibilitySource: options.eligibilitySource } : {}),
|
|
@@ -755,7 +746,7 @@ export async function akmReflect(options = {}) {
|
|
|
755
746
|
// ("Reflect refused asset type" — ~9% of reflect-failed events).
|
|
756
747
|
emitReflectFailed("unsupported_type", "unsupported_type", options.ref, { type: parsedRef.type });
|
|
757
748
|
return {
|
|
758
|
-
schemaVersion:
|
|
749
|
+
schemaVersion: 2,
|
|
759
750
|
ok: false,
|
|
760
751
|
reason: "unsupported_type",
|
|
761
752
|
error: `Reflect refused: asset type "${parsedRef.type}" is not supported by reflect (only markdown-canonical types are allowed: ${[...REFLECT_ALLOWED_TYPES].sort().join(", ")}). Use \`akm propose\` or edit the file directly.`,
|
|
@@ -779,92 +770,42 @@ export async function akmReflect(options = {}) {
|
|
|
779
770
|
}
|
|
780
771
|
}
|
|
781
772
|
}
|
|
782
|
-
// 3. Resolve
|
|
783
|
-
//
|
|
784
|
-
//
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
// agent config (agent.processes["reflect"]) is picked up automatically.
|
|
788
|
-
let profile;
|
|
789
|
-
let resolvedTimeoutMs = options.timeoutMs;
|
|
773
|
+
// 3. Resolve exactly one named engine. Standalone reflect uses --engine or
|
|
774
|
+
// defaults.engine; improve resolves its LLM-only strategy/process overlay.
|
|
775
|
+
// An incompatible explicit engine is an error and never falls through.
|
|
776
|
+
const config = options.config ?? loadConfig();
|
|
777
|
+
const activeStrategy = options.improveProfile ?? config.improve?.strategies?.[config.defaults?.improveStrategy ?? "default"];
|
|
790
778
|
let runnerSpec;
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
// Test seam: injected profile bypasses all config.
|
|
794
|
-
profile = options.agentProfile;
|
|
795
|
-
}
|
|
796
|
-
else if (options.runner) {
|
|
797
|
-
// Caller-provided RunnerSpec (used in tests and --dry-run-resolve).
|
|
798
|
-
runnerSpec = options.runner;
|
|
799
|
-
}
|
|
800
|
-
else {
|
|
801
|
-
const cfg = options.config ?? loadConfig();
|
|
802
|
-
const reflectProcess = getImproveProcessConfig(cfg, "reflect", options.improveProfile);
|
|
803
|
-
// Resolve the runner from the improve profile's reflect entry when present.
|
|
804
|
-
runnerSpec = resolveImproveProcessRunnerFromProfile(reflectProcess, cfg) ?? undefined;
|
|
805
|
-
if (runnerSpec) {
|
|
806
|
-
if (resolvedTimeoutMs === undefined && runnerSpec.timeoutMs !== undefined) {
|
|
807
|
-
resolvedTimeoutMs = runnerSpec.timeoutMs;
|
|
808
|
-
}
|
|
809
|
-
}
|
|
810
|
-
else {
|
|
811
|
-
if (options.profile) {
|
|
812
|
-
// Explicit --profile flag wins over process config.
|
|
813
|
-
profile = resolveAgentProfile(options);
|
|
814
|
-
}
|
|
815
|
-
else {
|
|
816
|
-
// Use per-process config resolution (falls back to defaults.agent).
|
|
817
|
-
const agent = options.agentConfig ?? loadAgentConfigFromDisk();
|
|
818
|
-
const processName = options.agentProcess ?? "reflect";
|
|
819
|
-
const resolved = resolveProcessAgentProfile(processName, agent);
|
|
820
|
-
profile = resolved.profile;
|
|
821
|
-
// Only apply process-resolved timeoutMs when caller didn't supply one.
|
|
822
|
-
if (resolvedTimeoutMs === undefined) {
|
|
823
|
-
resolvedTimeoutMs = resolved.timeoutMs;
|
|
824
|
-
}
|
|
825
|
-
}
|
|
826
|
-
}
|
|
827
|
-
}
|
|
779
|
+
if (options.runner) {
|
|
780
|
+
runnerSpec = options.runner;
|
|
828
781
|
}
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
782
|
+
else if (Object.hasOwn(options, "runner")) {
|
|
783
|
+
throw new ConfigError("Reflect requires an LLM engine for the active improve invocation.", "LLM_NOT_CONFIGURED", "Set defaults.llmEngine or improve.strategies.<name>.processes.reflect.engine.");
|
|
784
|
+
}
|
|
785
|
+
else if (options.engine) {
|
|
786
|
+
runnerSpec = resolveEngine(options.engine, config);
|
|
833
787
|
}
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
// corrupts output text at worst (src/llm/call-ai.ts). An llm RunnerSpec
|
|
839
|
-
// resolved from the process block is honored; anything else — an agent/sdk
|
|
840
|
-
// spec, a resolved agent profile, or the default-agent fallback below — is
|
|
841
|
-
// pinned to `defaults.llm`, failing CLOSED when that is unset.
|
|
842
|
-
// `options.agentProfile` stays exempt: it is a direct-injection test seam
|
|
843
|
-
// the cron path (loop-stages) never sets.
|
|
844
|
-
if (options.eventSource === "improve" && !options.agentProfile && (!runnerSpec || !runnerIsLlm(runnerSpec))) {
|
|
845
|
-
const cfg = options.config ?? loadConfig();
|
|
846
|
-
const pinned = resolveDefaultLlmRunner(cfg, resolvedTimeoutMs);
|
|
847
|
-
if (!pinned) {
|
|
848
|
-
throw new ConfigError("Unattended improve pins reflect to the tool-less LLM runner, but the config resolves a tool-capable runner and no defaults.llm profile exists to pin to.", "LLM_NOT_CONFIGURED", 'Set processes.reflect.mode to "llm" on the active improve profile, or configure defaults.llm.');
|
|
788
|
+
else if (options.improveProfile) {
|
|
789
|
+
const processRunner = resolveImproveProcessRunner(activeStrategy, "reflect", config);
|
|
790
|
+
if (!processRunner) {
|
|
791
|
+
throw new ConfigError("Reflect requires an LLM engine for the active improve strategy.", "LLM_NOT_CONFIGURED", "Set defaults.llmEngine or improve.strategies.<name>.processes.reflect.engine.");
|
|
849
792
|
}
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
793
|
+
runnerSpec = processRunner;
|
|
794
|
+
}
|
|
795
|
+
else {
|
|
796
|
+
const defaultEngine = config.defaults?.engine;
|
|
797
|
+
if (!defaultEngine) {
|
|
798
|
+
throw new ConfigError("reflect requires --engine or defaults.engine.", "INVALID_CONFIG_FILE");
|
|
799
|
+
}
|
|
800
|
+
runnerSpec = resolveEngine(defaultEngine, config);
|
|
801
|
+
}
|
|
802
|
+
if (options.eventSource === "improve" && !runnerIsLlm(runnerSpec)) {
|
|
803
|
+
throw new ConfigError(`Unattended improve requires an LLM engine for reflect; engine "${runnerSpec.engine ?? options.engine ?? "unknown"}" is tool-capable.`, "INVALID_CONFIG_FILE", "Set defaults.llmEngine or improve.strategies.<name>.processes.reflect.engine to an LLM engine.");
|
|
855
804
|
}
|
|
856
|
-
|
|
857
|
-
if (!
|
|
858
|
-
|
|
859
|
-
profile = resolveAgentProfile({ ...options, agentConfig: agent });
|
|
805
|
+
const engineName = runnerSpec.engine ?? options.engine;
|
|
806
|
+
if (!engineName) {
|
|
807
|
+
throw new ConfigError("Reflect requires a named engine.", "INVALID_CONFIG_FILE");
|
|
860
808
|
}
|
|
861
|
-
// Derive a display name for logging — either from the resolved profile or the runnerSpec.
|
|
862
|
-
const resolvedProfileName = profile?.name ??
|
|
863
|
-
(runnerSpec && runnerIsLlm(runnerSpec)
|
|
864
|
-
? `llm:${runnerSpec.connection.model}`
|
|
865
|
-
: runnerSpec
|
|
866
|
-
? `${runnerSpec.kind}:${runnerSpec.profile.name ?? "unknown"}`
|
|
867
|
-
: "unknown");
|
|
868
809
|
// 4. Build the shared prompt inputs — feedback, hints, lessons, rejected
|
|
869
810
|
// proposals. These are stable across refinement iterations; only the
|
|
870
811
|
// `priorDraft` field changes per-iteration (R-1 / #372).
|
|
@@ -889,13 +830,16 @@ export async function akmReflect(options = {}) {
|
|
|
889
830
|
const MAX_REFINE_ITERS = 3;
|
|
890
831
|
const maxRefineIters = Math.min(Math.max(1, options.maxRefineIters ?? 1), MAX_REFINE_ITERS);
|
|
891
832
|
const agentEnv = options.eventSource === "improve" ? { AKM_EVENT_SOURCE: "improve" } : {};
|
|
833
|
+
const sensitiveValues = collectDispatchSensitiveValues(runnerSpec, {
|
|
834
|
+
...(Object.keys(agentEnv).length > 0 ? { env: agentEnv } : {}),
|
|
835
|
+
...(options.runAgentOptions ?? {}),
|
|
836
|
+
});
|
|
892
837
|
// Determine whether this dispatch can honour the file-write contract.
|
|
893
838
|
// Agent CLI + OpenCode SDK runners both have filesystem access; the direct
|
|
894
|
-
// LLM HTTP runner does NOT
|
|
895
|
-
// `profile.sdkMode` fallback also runs the SDK so it counts as file-writable.
|
|
839
|
+
// LLM HTTP runner does NOT.
|
|
896
840
|
// Test seams (`options.runAgentOptions.spawn`) emulate agent CLI behaviour so
|
|
897
841
|
// they participate as well — tests opt out by simply not writing the file.
|
|
898
|
-
const canRunnerWriteFile =
|
|
842
|
+
const canRunnerWriteFile = runnerSupportsFileWrite(runnerSpec);
|
|
899
843
|
// Initialized to a sentinel; always overwritten in the first loop iteration
|
|
900
844
|
// (maxRefineIters is clamped to >= 1 above). TypeScript cannot prove a
|
|
901
845
|
// for-loop always runs at least once, so we use a type assertion here.
|
|
@@ -960,76 +904,30 @@ export async function akmReflect(options = {}) {
|
|
|
960
904
|
// (conservative — most models are 3.5–4) and add 500-char overhead for the
|
|
961
905
|
// JSON wrapper and frontmatter block that surround the body in the response.
|
|
962
906
|
const maxTokensForLlm = maxOutputChars !== undefined ? Math.ceil((maxOutputChars + 500) / 3) : undefined;
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
}
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
...(Object.keys(agentEnv).length > 0 ? { env: agentEnv } : {}),
|
|
988
|
-
};
|
|
989
|
-
iterResult = await executeRunner(runnerSpec, prompt ?? "", runOptions, {
|
|
990
|
-
llm: async (spec) =>
|
|
991
|
-
// LLM HTTP path — `draftFilePath` is accepted for type symmetry
|
|
992
|
-
// (see `RunReflectViaLlmOptions.draftFilePath` docstring) but is
|
|
993
|
-
// intentionally a no-op. The prompt builder above also did not
|
|
994
|
-
// include the file-write contract for this kind, so the LLM is
|
|
995
|
-
// still asked for JSON via stdout.
|
|
996
|
-
runReflectViaLlm({
|
|
997
|
-
prompt,
|
|
998
|
-
connection: spec.connection,
|
|
999
|
-
timeoutMs: spec.timeoutMs ?? (typeof resolvedTimeoutMs === "number" ? resolvedTimeoutMs : undefined),
|
|
1000
|
-
priorDraft,
|
|
1001
|
-
iteration: iter,
|
|
1002
|
-
responseSchema: REFLECT_JSON_SCHEMA,
|
|
1003
|
-
chat: options.chat,
|
|
1004
|
-
...(maxTokensForLlm !== undefined ? { maxTokens: maxTokensForLlm } : {}),
|
|
1005
|
-
}),
|
|
1006
|
-
// The `agent` arm (and only the agent arm — preserving prior behavior)
|
|
1007
|
-
// overlays `spec.timeoutMs` onto the base run options.
|
|
1008
|
-
runAgent: (profile, p, opts) => runAgent(profile, p, {
|
|
1009
|
-
...opts,
|
|
1010
|
-
...(runnerSpec.timeoutMs !== undefined ? { timeoutMs: runnerSpec.timeoutMs } : {}),
|
|
1011
|
-
}),
|
|
1012
|
-
});
|
|
1013
|
-
}
|
|
1014
|
-
else {
|
|
1015
|
-
// Production path (v1): dispatch directly to the appropriate runner.
|
|
1016
|
-
// The fallback at the end of step 3 guarantees `profile` is set whenever
|
|
1017
|
-
// `runnerSpec` is undefined, but TS can't prove that across the loop +
|
|
1018
|
-
// await boundary — narrow into a const.
|
|
1019
|
-
const resolvedProfile = profile;
|
|
1020
|
-
if (!resolvedProfile) {
|
|
1021
|
-
throw new Error("internal: reflect v1 dispatch reached without a resolved agent profile or runnerSpec");
|
|
1022
|
-
}
|
|
1023
|
-
const runOptions = {
|
|
1024
|
-
stdio: "captured",
|
|
1025
|
-
parseOutput: "text",
|
|
1026
|
-
...(resolvedTimeoutMs !== undefined ? { timeoutMs: resolvedTimeoutMs } : {}),
|
|
1027
|
-
...(Object.keys(agentEnv).length > 0 ? { env: agentEnv } : {}),
|
|
1028
|
-
};
|
|
1029
|
-
iterResult = resolvedProfile.sdkMode
|
|
1030
|
-
? await runOpencodeSdk(resolvedProfile, prompt ?? "", runOptions)
|
|
1031
|
-
: await runAgent(resolvedProfile, prompt, runOptions);
|
|
1032
|
-
}
|
|
907
|
+
// Every engine kind crosses the same dispatch seam. Injected spawn/timer
|
|
908
|
+
// functions remain ordinary run options for deterministic tests.
|
|
909
|
+
const runOptions = {
|
|
910
|
+
stdio: "captured",
|
|
911
|
+
parseOutput: "text",
|
|
912
|
+
...(options.timeoutMs !== undefined ? { timeoutMs: options.timeoutMs } : {}),
|
|
913
|
+
...(Object.keys(agentEnv).length > 0 ? { env: agentEnv } : {}),
|
|
914
|
+
...(options.runAgentOptions ?? {}),
|
|
915
|
+
};
|
|
916
|
+
const iterResult = await executeRunner(runnerSpec, prompt ?? "", runOptions, {
|
|
917
|
+
llm: async (spec, _prompt, opts) =>
|
|
918
|
+
// LLM HTTP runners cannot honor the file-write contract, so they
|
|
919
|
+
// return structured JSON through stdout.
|
|
920
|
+
runReflectViaLlm({
|
|
921
|
+
prompt,
|
|
922
|
+
connection: spec.connection,
|
|
923
|
+
...(Object.hasOwn(opts, "timeoutMs") ? { timeoutMs: opts.timeoutMs } : {}),
|
|
924
|
+
priorDraft,
|
|
925
|
+
iteration: iter,
|
|
926
|
+
responseSchema: REFLECT_JSON_SCHEMA,
|
|
927
|
+
chat: options.chat,
|
|
928
|
+
...(maxTokensForLlm !== undefined ? { maxTokens: maxTokensForLlm } : {}),
|
|
929
|
+
}),
|
|
930
|
+
});
|
|
1033
931
|
result = iterResult;
|
|
1034
932
|
if (!iterResult.ok)
|
|
1035
933
|
break; // surface failure after loop
|
|
@@ -1051,11 +949,11 @@ export async function akmReflect(options = {}) {
|
|
|
1051
949
|
...(finalResult.exitCode !== undefined ? { exitCode: finalResult.exitCode } : {}),
|
|
1052
950
|
});
|
|
1053
951
|
return {
|
|
1054
|
-
...failureEnvelope(finalResult, options.ref),
|
|
1055
|
-
error: enoentHintMessage(profile
|
|
952
|
+
...failureEnvelope(finalResult, options.ref, engineName),
|
|
953
|
+
error: enoentHintMessage(runnerIsLlm(runnerSpec) ? engineName : runnerSpec.profile.bin),
|
|
1056
954
|
};
|
|
1057
955
|
}
|
|
1058
|
-
const envelope = failureEnvelope(finalResult, options.ref);
|
|
956
|
+
const envelope = failureEnvelope(finalResult, options.ref, engineName);
|
|
1059
957
|
emitReflectFailed(envelope.reason, "agent_crash", options.ref, {
|
|
1060
958
|
...(envelope.exitCode !== null ? { exitCode: envelope.exitCode } : {}),
|
|
1061
959
|
});
|
|
@@ -1085,11 +983,12 @@ export async function akmReflect(options = {}) {
|
|
|
1085
983
|
...(result.exitCode !== null ? { exitCode: result.exitCode } : {}),
|
|
1086
984
|
});
|
|
1087
985
|
return {
|
|
1088
|
-
schemaVersion:
|
|
986
|
+
schemaVersion: 2,
|
|
1089
987
|
ok: false,
|
|
1090
988
|
reason: "parse_error",
|
|
1091
989
|
error: `Agent emitted DRAFT_WRITTEN but draft file is missing or empty (${lastDraftPath}). The file-write contract failed; either the agent's file tools are broken or the path was unwritable.`,
|
|
1092
990
|
...(options.ref ? { ref: options.ref } : {}),
|
|
991
|
+
engine: engineName,
|
|
1093
992
|
exitCode: result.exitCode,
|
|
1094
993
|
stdout: result.stdout,
|
|
1095
994
|
...(result.stderr ? { stderr: result.stderr } : {}),
|
|
@@ -1099,7 +998,7 @@ export async function akmReflect(options = {}) {
|
|
|
1099
998
|
// Happy path: agent wrote the body to disk. Use the ref the caller
|
|
1100
999
|
// supplied (or a placeholder when omitted — the R-3 ref-mismatch guard
|
|
1101
1000
|
// below has no effect when there is no expected ref).
|
|
1102
|
-
const fileContent = fs.readFileSync(lastDraftPath, "utf8");
|
|
1001
|
+
const fileContent = redactSensitiveText(fs.readFileSync(lastDraftPath, "utf8"), sensitiveValues);
|
|
1103
1002
|
// Phase 6A: file-write contract carries self-rated confidence on the
|
|
1104
1003
|
// `DRAFT_WRITTEN confidence=<n>` sentinel line. Extract it so the
|
|
1105
1004
|
// file-write path is on equal footing with the JSON-stdout path for
|
|
@@ -1120,7 +1019,7 @@ export async function akmReflect(options = {}) {
|
|
|
1120
1019
|
payload = parseAgentProposalPayload(result.stdout ?? "");
|
|
1121
1020
|
}
|
|
1122
1021
|
catch (err) {
|
|
1123
|
-
const fallback = fallbackPayloadFromRawContent(result.stdout ?? "", options.ref,
|
|
1022
|
+
const fallback = fallbackPayloadFromRawContent(result.stdout ?? "", options.ref, runnerSpec.kind === "sdk");
|
|
1124
1023
|
if (fallback) {
|
|
1125
1024
|
payload = fallback;
|
|
1126
1025
|
}
|
|
@@ -1135,11 +1034,12 @@ export async function akmReflect(options = {}) {
|
|
|
1135
1034
|
...(result.exitCode !== null ? { exitCode: result.exitCode } : {}),
|
|
1136
1035
|
});
|
|
1137
1036
|
return {
|
|
1138
|
-
schemaVersion:
|
|
1037
|
+
schemaVersion: 2,
|
|
1139
1038
|
ok: false,
|
|
1140
1039
|
reason,
|
|
1141
1040
|
error: err instanceof Error ? err.message : String(err),
|
|
1142
1041
|
...(options.ref ? { ref: options.ref } : {}),
|
|
1042
|
+
engine: engineName,
|
|
1143
1043
|
exitCode: result.exitCode,
|
|
1144
1044
|
stdout: result.stdout,
|
|
1145
1045
|
...(result.stderr ? { stderr: result.stderr } : {}),
|
|
@@ -1155,6 +1055,7 @@ export async function akmReflect(options = {}) {
|
|
|
1155
1055
|
// draft paths.
|
|
1156
1056
|
cleanupDrafts();
|
|
1157
1057
|
}
|
|
1058
|
+
payload = { ...payload, content: redactSensitiveText(payload.content, sensitiveValues) };
|
|
1158
1059
|
// 6b. Validate payload.ref === options.ref (R-3 / #366).
|
|
1159
1060
|
// A hallucinating agent can silently retarget proposals to a different ref.
|
|
1160
1061
|
// This guard normalises both refs through parseAssetRef so origin-prefix
|
|
@@ -1172,11 +1073,12 @@ export async function akmReflect(options = {}) {
|
|
|
1172
1073
|
...(result.exitCode !== null ? { exitCode: result.exitCode } : {}),
|
|
1173
1074
|
});
|
|
1174
1075
|
return {
|
|
1175
|
-
schemaVersion:
|
|
1076
|
+
schemaVersion: 2,
|
|
1176
1077
|
ok: false,
|
|
1177
1078
|
reason: "parse_error",
|
|
1178
1079
|
error: `Agent retargeted proposal: expected ref "${options.ref}" but got "${payload.ref}". Proposal rejected to prevent silent ref hallucination.`,
|
|
1179
1080
|
ref: options.ref,
|
|
1081
|
+
engine: engineName,
|
|
1180
1082
|
exitCode: result.exitCode,
|
|
1181
1083
|
stdout: result.stdout,
|
|
1182
1084
|
...(result.stderr ? { stderr: result.stderr } : {}),
|
|
@@ -1191,10 +1093,9 @@ export async function akmReflect(options = {}) {
|
|
|
1191
1093
|
// 7. R-5 / #374: Apply the proposal quality gate when enabled.
|
|
1192
1094
|
// Mirrors the lesson quality gate on distill proposals. The gate uses
|
|
1193
1095
|
// `runLessonQualityJudge` from distill.ts and is gated behind either
|
|
1194
|
-
// `
|
|
1195
|
-
// `
|
|
1196
|
-
//
|
|
1197
|
-
// `isLlmFeatureEnabled`). Fail-CLOSED (07 P0-2): a judge error / no-LLM /
|
|
1096
|
+
// `processes.reflect.qualityGate.enabled` or
|
|
1097
|
+
// `processes.distill.qualityGate.enabled` on the selected strategy.
|
|
1098
|
+
// Fail-CLOSED (07 P0-2): a judge error / no-LLM /
|
|
1198
1099
|
// parse failure rejects the proposal rather than passing it through.
|
|
1199
1100
|
// G-Eval (arXiv:2303.16634) — quality judgment before admission.
|
|
1200
1101
|
const runtimeConfig = options.config ??
|
|
@@ -1207,8 +1108,8 @@ export async function akmReflect(options = {}) {
|
|
|
1207
1108
|
}
|
|
1208
1109
|
})();
|
|
1209
1110
|
const chatFn = options.chat ?? chatCompletion;
|
|
1210
|
-
const qualityGateEnabled =
|
|
1211
|
-
|
|
1111
|
+
const qualityGateEnabled = (activeStrategy?.processes?.reflect?.qualityGate?.enabled ?? false) ||
|
|
1112
|
+
(activeStrategy?.processes?.distill?.qualityGate?.enabled ?? true);
|
|
1212
1113
|
if (qualityGateEnabled && runtimeConfig) {
|
|
1213
1114
|
const assetContent = (() => {
|
|
1214
1115
|
if (!options.ref)
|
|
@@ -1229,7 +1130,7 @@ export async function akmReflect(options = {}) {
|
|
|
1229
1130
|
return null;
|
|
1230
1131
|
}
|
|
1231
1132
|
})();
|
|
1232
|
-
const judgeResult = await runLessonQualityJudge(runtimeConfig, payload.content, assetContent ?? "", chatFn);
|
|
1133
|
+
const judgeResult = await runLessonQualityJudge(runtimeConfig, payload.content, assetContent ?? "", chatFn, undefined, runnerIsLlm(runnerSpec) ? materializeLlmRunnerConnection(runnerSpec) : undefined);
|
|
1233
1134
|
if (!judgeResult.pass) {
|
|
1234
1135
|
// Quality gate rejected the proposal — surface as parse_error so the
|
|
1235
1136
|
// improve orchestrator can log it and move on without crashing.
|
|
@@ -1244,11 +1145,12 @@ export async function akmReflect(options = {}) {
|
|
|
1244
1145
|
},
|
|
1245
1146
|
});
|
|
1246
1147
|
return {
|
|
1247
|
-
schemaVersion:
|
|
1148
|
+
schemaVersion: 2,
|
|
1248
1149
|
ok: false,
|
|
1249
1150
|
reason: "parse_error",
|
|
1250
1151
|
error: `Reflect proposal quality gate rejected: score=${judgeResult.score}, reason="${judgeResult.reason}"`,
|
|
1251
1152
|
...(options.ref ? { ref: options.ref } : {}),
|
|
1153
|
+
engine: engineName,
|
|
1252
1154
|
exitCode: result.exitCode,
|
|
1253
1155
|
};
|
|
1254
1156
|
}
|
|
@@ -1277,11 +1179,12 @@ export async function akmReflect(options = {}) {
|
|
|
1277
1179
|
},
|
|
1278
1180
|
});
|
|
1279
1181
|
return {
|
|
1280
|
-
schemaVersion:
|
|
1182
|
+
schemaVersion: 2,
|
|
1281
1183
|
ok: false,
|
|
1282
1184
|
reason: sanitizeOutcome.reject.reason,
|
|
1283
1185
|
error: sanitizeOutcome.reject.error,
|
|
1284
1186
|
...(options.ref ? { ref: options.ref } : {}),
|
|
1187
|
+
engine: engineName,
|
|
1285
1188
|
exitCode: result.exitCode,
|
|
1286
1189
|
};
|
|
1287
1190
|
}
|
|
@@ -1301,9 +1204,9 @@ export async function akmReflect(options = {}) {
|
|
|
1301
1204
|
const changeKind = classifyReflectChange(assetContent, payload.content);
|
|
1302
1205
|
// 'low-value' is config-gated (#639). DEFAULT OFF — absent = byte-identical
|
|
1303
1206
|
// pre-#639 behaviour (low-value treated the same as substantive). Resolved
|
|
1304
|
-
// by the caller from the
|
|
1207
|
+
// by the caller from the active improve strategy's
|
|
1305
1208
|
// `processes.reflect.lowValueFilter.enabled` and passed via options, so the
|
|
1306
|
-
// running
|
|
1209
|
+
// running strategy decides.
|
|
1307
1210
|
const lowValueFilterEnabled = options.lowValueFilter === true;
|
|
1308
1211
|
const isDeferred = changeKind === "noop" || changeKind === "cosmetic" || (changeKind === "low-value" && lowValueFilterEnabled);
|
|
1309
1212
|
if (isDeferred) {
|
|
@@ -1314,7 +1217,7 @@ export async function akmReflect(options = {}) {
|
|
|
1314
1217
|
: "reflect_skipped_cosmetic";
|
|
1315
1218
|
emitReflectFailed("no_change", subreason, options.ref, { changeKind });
|
|
1316
1219
|
return {
|
|
1317
|
-
schemaVersion:
|
|
1220
|
+
schemaVersion: 2,
|
|
1318
1221
|
ok: false,
|
|
1319
1222
|
reason: "no_change",
|
|
1320
1223
|
error: changeKind === "noop"
|
|
@@ -1323,6 +1226,7 @@ export async function akmReflect(options = {}) {
|
|
|
1323
1226
|
? `Reflect skipped: proposed content for ${payload.ref} is a low-value prose micro-rewrite (few changed tokens, no structural changes); no proposal created.`
|
|
1324
1227
|
: `Reflect skipped: proposed content for ${payload.ref} is a cosmetic-only reformat of the current asset (whitespace/fence/YAML-folding changes); no proposal created.`,
|
|
1325
1228
|
...(options.ref ? { ref: options.ref } : {}),
|
|
1229
|
+
engine: engineName,
|
|
1326
1230
|
exitCode: result.exitCode,
|
|
1327
1231
|
};
|
|
1328
1232
|
}
|
|
@@ -1370,11 +1274,11 @@ export async function akmReflect(options = {}) {
|
|
|
1370
1274
|
...(typeof payload.confidence === "number" ? { confidence: payload.confidence } : {}),
|
|
1371
1275
|
};
|
|
1372
1276
|
return {
|
|
1373
|
-
schemaVersion:
|
|
1277
|
+
schemaVersion: 2,
|
|
1374
1278
|
ok: true,
|
|
1375
1279
|
proposal: draftProposal,
|
|
1376
1280
|
ref: draftProposal.ref,
|
|
1377
|
-
|
|
1281
|
+
engine: engineName,
|
|
1378
1282
|
durationMs: result.durationMs,
|
|
1379
1283
|
};
|
|
1380
1284
|
}
|
|
@@ -1405,11 +1309,12 @@ export async function akmReflect(options = {}) {
|
|
|
1405
1309
|
proposalSkipReason: proposalResult.reason,
|
|
1406
1310
|
});
|
|
1407
1311
|
return {
|
|
1408
|
-
schemaVersion:
|
|
1312
|
+
schemaVersion: 2,
|
|
1409
1313
|
ok: false,
|
|
1410
1314
|
reason: "cooldown",
|
|
1411
1315
|
error: `Proposal skipped (${proposalResult.reason}): ${proposalResult.message}`,
|
|
1412
1316
|
...(options.ref ? { ref: options.ref } : {}),
|
|
1317
|
+
engine: engineName,
|
|
1413
1318
|
exitCode: null,
|
|
1414
1319
|
};
|
|
1415
1320
|
}
|
|
@@ -1420,15 +1325,15 @@ export async function akmReflect(options = {}) {
|
|
|
1420
1325
|
metadata: {
|
|
1421
1326
|
proposalId: proposal.id,
|
|
1422
1327
|
source: "reflect",
|
|
1423
|
-
|
|
1328
|
+
engine: engineName,
|
|
1424
1329
|
},
|
|
1425
1330
|
});
|
|
1426
1331
|
return {
|
|
1427
|
-
schemaVersion:
|
|
1332
|
+
schemaVersion: 2,
|
|
1428
1333
|
ok: true,
|
|
1429
1334
|
proposal,
|
|
1430
1335
|
ref: proposal.ref,
|
|
1431
|
-
|
|
1336
|
+
engine: engineName,
|
|
1432
1337
|
durationMs: result.durationMs,
|
|
1433
1338
|
};
|
|
1434
1339
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
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 { getDefaultLlmConfig
|
|
4
|
+
import { getDefaultLlmConfig } from "../../core/config/config.js";
|
|
5
5
|
import { warn } from "../../core/warn.js";
|
|
6
|
-
import {
|
|
6
|
+
import { materializeLlmRunnerConnection, resolveImproveProcessRunner } from "../../integrations/agent/runner.js";
|
|
7
7
|
import { chatCompletion } from "../../llm/client.js";
|
|
8
8
|
/** Normalize an unknown thrown value to a human-readable message string. */
|
|
9
9
|
export function errMessage(e) {
|
|
@@ -30,9 +30,15 @@ export function refSlug(ref) {
|
|
|
30
30
|
* profile's model; absent falls back to `default`.
|
|
31
31
|
*/
|
|
32
32
|
export function resolveImproveLlmFn(config, opts) {
|
|
33
|
-
const
|
|
34
|
-
const runnerSpec =
|
|
35
|
-
|
|
33
|
+
const planOwnsResolution = Object.hasOwn(opts, "llmConfig");
|
|
34
|
+
const runnerSpec = planOwnsResolution
|
|
35
|
+
? undefined
|
|
36
|
+
: resolveImproveProcessRunner(opts.activeProfile, opts.processKey, config);
|
|
37
|
+
const llmConfig = planOwnsResolution
|
|
38
|
+
? (opts.llmConfig ?? undefined)
|
|
39
|
+
: runnerSpec
|
|
40
|
+
? materializeLlmRunnerConnection(runnerSpec)
|
|
41
|
+
: getDefaultLlmConfig(config);
|
|
36
42
|
if (!llmConfig)
|
|
37
43
|
return undefined;
|
|
38
44
|
return async (prompt) => {
|