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
package/dist/tasks/parser.js
CHANGED
|
@@ -29,13 +29,14 @@
|
|
|
29
29
|
* tags: [scheduled, backup]
|
|
30
30
|
* ```
|
|
31
31
|
*
|
|
32
|
-
* Validation lives in {@link validateTaskDocument}. The parser
|
|
33
|
-
* shape; cron syntax
|
|
32
|
+
* Validation lives in {@link validateTaskDocument}. The parser enforces the
|
|
33
|
+
* strict source shape; cron syntax and target reachability are
|
|
34
34
|
* checked separately so callers can choose how strictly to surface errors.
|
|
35
35
|
*/
|
|
36
36
|
import path from "node:path";
|
|
37
37
|
import { parse as parseYaml } from "yaml";
|
|
38
38
|
import { UsageError } from "../core/errors.js";
|
|
39
|
+
import { formatExtraParamsIssue, validateExtraParams } from "../core/extra-params.js";
|
|
39
40
|
import { TASK_SCHEMA_VERSION } from "./schema.js";
|
|
40
41
|
export function parseTaskDocument(input) {
|
|
41
42
|
const { yaml, filePath, id } = input;
|
|
@@ -52,15 +53,17 @@ export function parseTaskDocument(input) {
|
|
|
52
53
|
throw err;
|
|
53
54
|
throw new UsageError(`Task "${id}" has invalid YAML: ${err instanceof Error ? err.message : String(err)}. File: ${filePath}`, "INVALID_FLAG_VALUE");
|
|
54
55
|
}
|
|
55
|
-
|
|
56
|
+
requireVersion(data, id, filePath);
|
|
57
|
+
rejectUnknownKeys(data, id, filePath);
|
|
58
|
+
const schedule = requireString(data.schedule, "schedule", filePath);
|
|
56
59
|
if (!schedule) {
|
|
57
60
|
throw new UsageError(`Task "${id}" is missing a schedule (YAML key "schedule"). File: ${filePath}`, "MISSING_REQUIRED_ARGUMENT");
|
|
58
61
|
}
|
|
59
|
-
const enabled = data.enabled
|
|
60
|
-
const name =
|
|
61
|
-
const description =
|
|
62
|
-
const when_to_use =
|
|
63
|
-
const tags =
|
|
62
|
+
const enabled = readEnabled(data.enabled, filePath);
|
|
63
|
+
const name = optionalString(data.name, "name", filePath);
|
|
64
|
+
const description = optionalString(data.description, "description", filePath);
|
|
65
|
+
const when_to_use = optionalString(data.when_to_use, "when_to_use", filePath);
|
|
66
|
+
const tags = readTags(data.tags, filePath);
|
|
64
67
|
const hasWorkflow = "workflow" in data && data.workflow !== "" && data.workflow != null;
|
|
65
68
|
const hasPrompt = "prompt" in data && data.prompt !== "" && data.prompt != null;
|
|
66
69
|
const hasCommand = "command" in data && data.command !== "" && data.command != null;
|
|
@@ -73,7 +76,8 @@ export function parseTaskDocument(input) {
|
|
|
73
76
|
}
|
|
74
77
|
let target;
|
|
75
78
|
if (hasWorkflow) {
|
|
76
|
-
|
|
79
|
+
rejectTargetFields(data, ["params"], id, filePath);
|
|
80
|
+
const ref = requireString(data.workflow, "workflow", filePath);
|
|
77
81
|
if (!ref) {
|
|
78
82
|
throw new UsageError(`Task "${id}" has empty \`workflow\`. File: ${filePath}`, "INVALID_FLAG_VALUE");
|
|
79
83
|
}
|
|
@@ -84,48 +88,81 @@ export function parseTaskDocument(input) {
|
|
|
84
88
|
};
|
|
85
89
|
}
|
|
86
90
|
else if (hasCommand) {
|
|
91
|
+
rejectTargetFields(data, ["timeoutMs"], id, filePath);
|
|
87
92
|
const cmd = readCommand(data.command, filePath, id);
|
|
88
93
|
target = { kind: "command", cmd };
|
|
89
94
|
}
|
|
90
95
|
else {
|
|
91
|
-
|
|
96
|
+
rejectTargetFields(data, ["engine", "model", "timeoutMs", "llm"], id, filePath);
|
|
97
|
+
const promptRaw = requireString(data.prompt, "prompt", filePath);
|
|
92
98
|
if (!promptRaw) {
|
|
93
99
|
throw new UsageError(`Task "${id}" has empty \`prompt\`. File: ${filePath}`, "INVALID_FLAG_VALUE");
|
|
94
100
|
}
|
|
95
|
-
const
|
|
101
|
+
const engine = optionalString(data.engine, "engine", filePath);
|
|
102
|
+
const model = optionalString(data.model, "model", filePath);
|
|
103
|
+
const timeoutMs = readTimeout(data.timeoutMs, filePath);
|
|
104
|
+
const llm = readLlmOverrides(data.llm, filePath);
|
|
96
105
|
target = {
|
|
97
106
|
kind: "prompt",
|
|
98
107
|
source: resolvePromptSource(promptRaw, filePath, id),
|
|
99
|
-
|
|
108
|
+
...(engine ? { engine } : {}),
|
|
109
|
+
...(model ? { model } : {}),
|
|
110
|
+
...(timeoutMs !== undefined ? { timeoutMs } : {}),
|
|
111
|
+
...(llm ? { llm } : {}),
|
|
100
112
|
};
|
|
101
113
|
}
|
|
102
|
-
|
|
103
|
-
// Omitted → undefined (inherits config.agent.timeoutMs).
|
|
104
|
-
let timeoutMs;
|
|
105
|
-
if ("timeoutMs" in data) {
|
|
106
|
-
const raw = data.timeoutMs;
|
|
107
|
-
if (raw === null || raw === "null" || raw === 0 || (typeof raw === "number" && raw < 0)) {
|
|
108
|
-
timeoutMs = null;
|
|
109
|
-
}
|
|
110
|
-
else if (typeof raw === "number" && raw > 0) {
|
|
111
|
-
timeoutMs = raw;
|
|
112
|
-
}
|
|
113
|
-
// non-numeric / unrecognised → leave as undefined (inherit)
|
|
114
|
-
}
|
|
114
|
+
const timeoutMs = hasCommand ? readTimeout(data.timeoutMs, filePath) : undefined;
|
|
115
115
|
return {
|
|
116
|
+
version: TASK_SCHEMA_VERSION,
|
|
116
117
|
schemaVersion: TASK_SCHEMA_VERSION,
|
|
117
118
|
id,
|
|
118
119
|
schedule,
|
|
119
120
|
enabled,
|
|
120
121
|
target,
|
|
121
|
-
name
|
|
122
|
-
description
|
|
123
|
-
when_to_use
|
|
124
|
-
tags
|
|
122
|
+
...(name ? { name } : {}),
|
|
123
|
+
...(description ? { description } : {}),
|
|
124
|
+
...(when_to_use ? { when_to_use } : {}),
|
|
125
|
+
...(tags ? { tags } : {}),
|
|
125
126
|
source: { path: filePath },
|
|
126
127
|
timeoutMs,
|
|
127
128
|
};
|
|
128
129
|
}
|
|
130
|
+
const TASK_KEYS = new Set([
|
|
131
|
+
"version",
|
|
132
|
+
"name",
|
|
133
|
+
"description",
|
|
134
|
+
"when_to_use",
|
|
135
|
+
"tags",
|
|
136
|
+
"schedule",
|
|
137
|
+
"enabled",
|
|
138
|
+
"workflow",
|
|
139
|
+
"prompt",
|
|
140
|
+
"command",
|
|
141
|
+
"params",
|
|
142
|
+
"engine",
|
|
143
|
+
"model",
|
|
144
|
+
"timeoutMs",
|
|
145
|
+
"llm",
|
|
146
|
+
]);
|
|
147
|
+
const SHARED_KEYS = new Set(["version", "name", "description", "when_to_use", "tags", "schedule", "enabled"]);
|
|
148
|
+
function requireVersion(data, id, filePath) {
|
|
149
|
+
if (data.version === TASK_SCHEMA_VERSION)
|
|
150
|
+
return;
|
|
151
|
+
const actual = data.version === undefined ? "missing" : JSON.stringify(data.version);
|
|
152
|
+
throw new UsageError(`TASK_SCHEMA_VERSION_UNSUPPORTED: Task "${id}" uses task schema version ${actual}; version: 2 is required. File: ${filePath}`, "TASK_SCHEMA_VERSION_UNSUPPORTED", "Rewrite the task using version: 2 and replace profile with engine.");
|
|
153
|
+
}
|
|
154
|
+
function rejectUnknownKeys(data, id, filePath) {
|
|
155
|
+
const unknown = Object.keys(data).filter((key) => !TASK_KEYS.has(key));
|
|
156
|
+
if (unknown.length > 0) {
|
|
157
|
+
throw new UsageError(`Task "${id}" has unknown key(s): ${unknown.join(", ")}. File: ${filePath}`, "INVALID_FLAG_VALUE");
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
function rejectTargetFields(data, allowed, id, filePath) {
|
|
161
|
+
const forbidden = Object.keys(data).filter((key) => !SHARED_KEYS.has(key) && !allowed.includes(key) && !["workflow", "prompt", "command"].includes(key));
|
|
162
|
+
if (forbidden.length > 0) {
|
|
163
|
+
throw new UsageError(`Task "${id}" has field(s) not valid for this target: ${forbidden.join(", ")}. File: ${filePath}`, "INVALID_FLAG_VALUE");
|
|
164
|
+
}
|
|
165
|
+
}
|
|
129
166
|
/**
|
|
130
167
|
* Resolve a `prompt:` value into a {@link TaskPromptSource} variant.
|
|
131
168
|
*
|
|
@@ -150,32 +187,44 @@ function resolvePromptSource(raw, filePath, id) {
|
|
|
150
187
|
}
|
|
151
188
|
return { kind: "inline", text: trimmed };
|
|
152
189
|
}
|
|
153
|
-
function
|
|
190
|
+
function optionalString(value, key, filePath) {
|
|
154
191
|
if (value === undefined || value === null)
|
|
155
192
|
return undefined;
|
|
156
193
|
if (typeof value === "string")
|
|
157
|
-
return value
|
|
158
|
-
if (typeof value === "number" || typeof value === "boolean")
|
|
159
|
-
return String(value);
|
|
194
|
+
return value;
|
|
160
195
|
throw new UsageError(`Key "${key}" must be a string. File: ${filePath}`, "INVALID_FLAG_VALUE");
|
|
161
196
|
}
|
|
162
|
-
function
|
|
197
|
+
function requireString(value, key, filePath) {
|
|
198
|
+
const result = optionalString(value, key, filePath);
|
|
199
|
+
if (result === undefined || result.length === 0) {
|
|
200
|
+
throw new UsageError(`Key "${key}" must be a non-empty string. File: ${filePath}`, "INVALID_FLAG_VALUE");
|
|
201
|
+
}
|
|
202
|
+
return result;
|
|
203
|
+
}
|
|
204
|
+
function readEnabled(value, filePath) {
|
|
205
|
+
if (value === undefined)
|
|
206
|
+
return true;
|
|
207
|
+
if (typeof value !== "boolean")
|
|
208
|
+
throw new UsageError(`Key "enabled" must be a boolean. File: ${filePath}`, "INVALID_FLAG_VALUE");
|
|
209
|
+
return value;
|
|
210
|
+
}
|
|
211
|
+
function readTags(value, filePath) {
|
|
163
212
|
if (value === undefined || value === null)
|
|
164
213
|
return undefined;
|
|
165
|
-
if (typeof value === "string") {
|
|
166
|
-
return value
|
|
167
|
-
.split(/[\s,]+/)
|
|
168
|
-
.map((s) => s.trim())
|
|
169
|
-
.filter(Boolean);
|
|
170
|
-
}
|
|
171
214
|
if (Array.isArray(value)) {
|
|
172
|
-
|
|
215
|
+
if (!value.every((entry) => typeof entry === "string" && entry.length > 0)) {
|
|
216
|
+
throw new UsageError(`Key "tags" must be an array of non-empty strings. File: ${filePath}`, "INVALID_FLAG_VALUE");
|
|
217
|
+
}
|
|
218
|
+
return value;
|
|
173
219
|
}
|
|
174
|
-
|
|
220
|
+
throw new UsageError(`Key "tags" must be an array of strings. File: ${filePath}`, "INVALID_FLAG_VALUE");
|
|
175
221
|
}
|
|
176
222
|
function readCommand(value, filePath, id) {
|
|
177
223
|
if (Array.isArray(value)) {
|
|
178
|
-
|
|
224
|
+
if (!value.every((entry) => typeof entry === "string" && entry.length > 0)) {
|
|
225
|
+
throw new UsageError(`Task "${id}" command array must contain non-empty strings. File: ${filePath}`, "INVALID_FLAG_VALUE");
|
|
226
|
+
}
|
|
227
|
+
const parts = value;
|
|
179
228
|
if (parts.length === 0) {
|
|
180
229
|
throw new UsageError(`Task "${id}" has empty \`command\` array. File: ${filePath}`, "INVALID_FLAG_VALUE");
|
|
181
230
|
}
|
|
@@ -196,16 +245,53 @@ function readParams(value, filePath) {
|
|
|
196
245
|
if (typeof value === "object" && !Array.isArray(value)) {
|
|
197
246
|
return value;
|
|
198
247
|
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
248
|
+
throw new UsageError(`Key "params" must be a mapping. File: ${filePath}`, "INVALID_FLAG_VALUE");
|
|
249
|
+
}
|
|
250
|
+
function readTimeout(value, filePath) {
|
|
251
|
+
if (value === undefined)
|
|
252
|
+
return undefined;
|
|
253
|
+
if (value === null)
|
|
254
|
+
return null;
|
|
255
|
+
if (typeof value === "number" && Number.isInteger(value) && value > 0)
|
|
256
|
+
return value;
|
|
257
|
+
throw new UsageError(`Key "timeoutMs" must be a positive integer or null. File: ${filePath}`, "INVALID_FLAG_VALUE");
|
|
258
|
+
}
|
|
259
|
+
function readLlmOverrides(value, filePath) {
|
|
260
|
+
if (value === undefined)
|
|
261
|
+
return undefined;
|
|
262
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
263
|
+
throw new UsageError(`Key "llm" must be a mapping. File: ${filePath}`, "INVALID_FLAG_VALUE");
|
|
264
|
+
}
|
|
265
|
+
const data = value;
|
|
266
|
+
const allowed = new Set([
|
|
267
|
+
"temperature",
|
|
268
|
+
"maxTokens",
|
|
269
|
+
"supportsJsonSchema",
|
|
270
|
+
"extraParams",
|
|
271
|
+
"contextLength",
|
|
272
|
+
"enableThinking",
|
|
273
|
+
]);
|
|
274
|
+
const unknown = Object.keys(data).filter((key) => !allowed.has(key));
|
|
275
|
+
if (unknown.length)
|
|
276
|
+
throw new UsageError(`Key "llm" has unknown field(s): ${unknown.join(", ")}. File: ${filePath}`, "INVALID_FLAG_VALUE");
|
|
277
|
+
if (data.temperature !== undefined && (typeof data.temperature !== "number" || !Number.isFinite(data.temperature))) {
|
|
278
|
+
throw new UsageError(`Key "llm.temperature" must be a finite number. File: ${filePath}`, "INVALID_FLAG_VALUE");
|
|
279
|
+
}
|
|
280
|
+
for (const key of ["maxTokens", "contextLength"]) {
|
|
281
|
+
if (data[key] !== undefined && (!Number.isInteger(data[key]) || data[key] <= 0)) {
|
|
282
|
+
throw new UsageError(`Key "llm.${key}" must be a positive integer. File: ${filePath}`, "INVALID_FLAG_VALUE");
|
|
205
283
|
}
|
|
206
|
-
|
|
207
|
-
|
|
284
|
+
}
|
|
285
|
+
for (const key of ["supportsJsonSchema", "enableThinking"]) {
|
|
286
|
+
if (data[key] !== undefined && typeof data[key] !== "boolean") {
|
|
287
|
+
throw new UsageError(`Key "llm.${key}" must be a boolean. File: ${filePath}`, "INVALID_FLAG_VALUE");
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
if (data.extraParams !== undefined) {
|
|
291
|
+
const issue = validateExtraParams(data.extraParams)[0];
|
|
292
|
+
if (issue) {
|
|
293
|
+
throw new UsageError(`${formatExtraParamsIssue('Key "llm.extraParams"', issue)}. File: ${filePath}`, "INVALID_FLAG_VALUE");
|
|
208
294
|
}
|
|
209
295
|
}
|
|
210
|
-
|
|
296
|
+
return data;
|
|
211
297
|
}
|
package/dist/tasks/runner.js
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* 3. Refuse to run when `enabled === false` (defense-in-depth).
|
|
16
16
|
* 4. Dispatch by target kind:
|
|
17
17
|
* • workflow → `startWorkflowRun(ref, params)`
|
|
18
|
-
* • prompt → `
|
|
18
|
+
* • prompt → `executeRunner(engine, prompt, { stdio: "captured" })`
|
|
19
19
|
* 5. Capture stdout / stderr as structured rows in logs.db (task_logs) and,
|
|
20
20
|
* transitionally, as a flat text tail at `<cacheDir>/tasks/logs/<id>/<ts>.log`
|
|
21
21
|
* (see docs/technical/logs-audit.md).
|
|
@@ -35,17 +35,18 @@ import { buildTaskRunId, insertTaskLogLines, openLogsDatabase, } from "../core/l
|
|
|
35
35
|
import { getTaskLogDir } from "../core/paths.js";
|
|
36
36
|
import { withStateDb } from "../core/state-db.js";
|
|
37
37
|
import { error } from "../core/warn.js";
|
|
38
|
-
import {
|
|
39
|
-
import {
|
|
40
|
-
import {
|
|
38
|
+
import { resolveEngine, resolveLlmEngineUse } from "../integrations/agent/engine-resolution.js";
|
|
39
|
+
import { resolveModel } from "../integrations/agent/model-aliases.js";
|
|
40
|
+
import { executeRunner } from "../integrations/agent/runner-dispatch.js";
|
|
41
|
+
import { chatCompletion } from "../llm/client.js";
|
|
41
42
|
import { spawn } from "../runtime.js";
|
|
42
43
|
import { resolveAssetPath } from "../sources/resolve.js";
|
|
43
|
-
import { getTaskHistory, queryTaskHistory, upsertTaskHistory } from "../storage/repositories/task-history-repository.js";
|
|
44
|
+
import { decodeTaskHistoryMetadata, getTaskHistory, queryTaskHistory, upsertTaskHistory, } from "../storage/repositories/task-history-repository.js";
|
|
44
45
|
import { startWorkflowRun } from "../workflows/runtime/runs.js";
|
|
45
46
|
import { parseTaskDocument } from "./parser.js";
|
|
46
47
|
export async function runTask(id, options = {}) {
|
|
47
48
|
const stashDir = options.stashDir ?? resolveStashDir();
|
|
48
|
-
const runAgentImpl = options.runAgentImpl
|
|
49
|
+
const runAgentImpl = options.runAgentImpl;
|
|
49
50
|
const startWorkflowRunImpl = options.startWorkflowRunImpl ?? startWorkflowRun;
|
|
50
51
|
const now = options.now ?? (() => new Date());
|
|
51
52
|
const logDir = options.logDir ?? getTaskLogDir();
|
|
@@ -63,8 +64,8 @@ export async function runTask(id, options = {}) {
|
|
|
63
64
|
const disabledTarget = task.target.kind === "workflow"
|
|
64
65
|
? { kind: "workflow", ref: task.target.ref }
|
|
65
66
|
: task.target.kind === "command"
|
|
66
|
-
? { kind: "
|
|
67
|
-
: { kind: "prompt",
|
|
67
|
+
? { kind: "command", cmd: task.target.cmd }
|
|
68
|
+
: { kind: "prompt", engine: task.target.engine ?? null };
|
|
68
69
|
const result = {
|
|
69
70
|
id,
|
|
70
71
|
status: "disabled",
|
|
@@ -98,9 +99,6 @@ export async function runTask(id, options = {}) {
|
|
|
98
99
|
if (task.target.kind === "command") {
|
|
99
100
|
return await runCommandTask({ task, logPath, startedAt, now });
|
|
100
101
|
}
|
|
101
|
-
// Resolve config once here so runPromptTask does not call loadConfig()
|
|
102
|
-
// on every dispatch in a batch run (Fix C6).
|
|
103
|
-
const config = loadConfig();
|
|
104
102
|
return await runPromptTask({
|
|
105
103
|
task,
|
|
106
104
|
stashDir,
|
|
@@ -109,8 +107,7 @@ export async function runTask(id, options = {}) {
|
|
|
109
107
|
now,
|
|
110
108
|
runAgentImpl,
|
|
111
109
|
agentOptions: options.agentOptions,
|
|
112
|
-
|
|
113
|
-
agentTimeoutMs: undefined,
|
|
110
|
+
chatCompletionImpl: options.chatCompletionImpl ?? chatCompletion,
|
|
114
111
|
});
|
|
115
112
|
}
|
|
116
113
|
// ── command target ──────────────────────────────────────────────────────────
|
|
@@ -201,7 +198,7 @@ async function runCommandTask(input) {
|
|
|
201
198
|
finishedAt: finishedAt.toISOString(),
|
|
202
199
|
durationMs: finishedAt.getTime() - startedAt.getTime(),
|
|
203
200
|
log: logPath,
|
|
204
|
-
target: { kind: "
|
|
201
|
+
target: { kind: "command", cmd },
|
|
205
202
|
detail: { exitCode },
|
|
206
203
|
};
|
|
207
204
|
appendHistory(result);
|
|
@@ -300,70 +297,70 @@ function renderWorkflowLog(input) {
|
|
|
300
297
|
}
|
|
301
298
|
// ── prompt target ───────────────────────────────────────────────────────────
|
|
302
299
|
async function runPromptTask(input) {
|
|
303
|
-
const { task, stashDir, logPath, startedAt, now,
|
|
300
|
+
const { task, stashDir, logPath, startedAt, now, agentOptions } = input;
|
|
304
301
|
if (task.target.kind !== "prompt")
|
|
305
302
|
throw new Error("invariant: prompt target");
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
else {
|
|
331
|
-
profile = requireAgentProfile(agentCfg, task.target.profile);
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
else {
|
|
335
|
-
// v1: Task doc explicitly names a profile — honour it directly.
|
|
336
|
-
profile = requireAgentProfile(agentCfg, task.target.profile);
|
|
337
|
-
}
|
|
303
|
+
const promptTarget = task.target;
|
|
304
|
+
const config = loadConfig();
|
|
305
|
+
const engineName = promptTarget.engine ?? config.defaults?.engine;
|
|
306
|
+
if (!engineName)
|
|
307
|
+
throw new NotFoundError(`Task "${task.id}" has no selected engine.`, "ASSET_NOT_FOUND");
|
|
308
|
+
let runner = resolveEngine(engineName, config);
|
|
309
|
+
if (runner.kind === "llm") {
|
|
310
|
+
const resolved = resolveLlmEngineUse(config, [
|
|
311
|
+
{
|
|
312
|
+
engine: engineName,
|
|
313
|
+
...(promptTarget.model !== undefined ? { model: promptTarget.model } : {}),
|
|
314
|
+
...(promptTarget.timeoutMs !== undefined ? { timeoutMs: promptTarget.timeoutMs } : {}),
|
|
315
|
+
...(promptTarget.llm !== undefined ? { llm: promptTarget.llm } : {}),
|
|
316
|
+
},
|
|
317
|
+
]);
|
|
318
|
+
runner = {
|
|
319
|
+
kind: "llm",
|
|
320
|
+
engine: resolved.engine,
|
|
321
|
+
connection: resolved.connection,
|
|
322
|
+
...(resolved.credential ? { credential: resolved.credential } : {}),
|
|
323
|
+
timeoutMs: resolved.timeoutMs,
|
|
324
|
+
};
|
|
338
325
|
}
|
|
339
326
|
else {
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
327
|
+
if (promptTarget.llm !== undefined) {
|
|
328
|
+
throw new NotFoundError(`Task "${task.id}" uses llm overrides with non-LLM engine "${engineName}".`, "ASSET_NOT_FOUND");
|
|
329
|
+
}
|
|
330
|
+
const requestedModel = promptTarget.model;
|
|
331
|
+
const platform = runner.profile.platform;
|
|
332
|
+
if (!platform)
|
|
333
|
+
throw new Error(`Engine "${engineName}" resolved without a platform.`);
|
|
334
|
+
const model = requestedModel
|
|
335
|
+
? resolveModel(requestedModel, platform, runner.profile.modelAliases, runner.profile.globalModelAliases)
|
|
336
|
+
: runner.profile.model;
|
|
337
|
+
runner = {
|
|
338
|
+
...runner,
|
|
339
|
+
profile: { ...runner.profile, ...(model ? { model, modelIsExact: true } : {}) },
|
|
340
|
+
...(promptTarget.timeoutMs !== undefined ? { timeoutMs: promptTarget.timeoutMs } : {}),
|
|
341
|
+
};
|
|
344
342
|
}
|
|
345
|
-
// Task-level timeoutMs (including null = disabled) wins over global config.
|
|
346
|
-
// Resolution: task.timeoutMs → process entry timeoutMs → input.agentTimeoutMs → agentCfg.timeoutMs.
|
|
347
|
-
const agentTimeoutMs = task.timeoutMs !== undefined
|
|
348
|
-
? task.timeoutMs
|
|
349
|
-
: processTimeoutMs !== undefined
|
|
350
|
-
? processTimeoutMs
|
|
351
|
-
: input.agentTimeoutMs !== undefined
|
|
352
|
-
? input.agentTimeoutMs
|
|
353
|
-
: undefined;
|
|
354
343
|
const promptText = await resolvePromptText(task, stashDir);
|
|
355
|
-
const result = await
|
|
344
|
+
const result = await executeRunner(runner, promptText, {
|
|
356
345
|
stdio: "captured",
|
|
357
|
-
timeoutMs: agentTimeoutMs,
|
|
358
346
|
cwd: stashDir,
|
|
359
347
|
...agentOptions,
|
|
360
348
|
// Stamp task-runner provenance for any akm invocation the agent makes
|
|
361
349
|
// (DRIFT-6: agent-task traffic must not be recorded as user demand).
|
|
362
350
|
// Caller-supplied env still wins on conflicts.
|
|
363
351
|
env: { AKM_EVENT_SOURCE: "task", ...agentOptions?.env },
|
|
352
|
+
}, {
|
|
353
|
+
...(input.runAgentImpl ? { runAgent: input.runAgentImpl } : {}),
|
|
354
|
+
llm: async (spec, prompt, options) => {
|
|
355
|
+
const started = Date.now();
|
|
356
|
+
const stdout = await input.chatCompletionImpl(spec.connection, [{ role: "user", content: prompt }], {
|
|
357
|
+
...(options.timeoutMs !== undefined ? { timeoutMs: options.timeoutMs } : {}),
|
|
358
|
+
});
|
|
359
|
+
return { ok: true, exitCode: 0, stdout, stderr: "", durationMs: Date.now() - started };
|
|
360
|
+
},
|
|
364
361
|
});
|
|
365
362
|
const finishedAt = now();
|
|
366
|
-
const log = renderPromptLog({ task,
|
|
363
|
+
const log = renderPromptLog({ task, engineName, result });
|
|
367
364
|
persistRunLog({
|
|
368
365
|
taskId: task.id,
|
|
369
366
|
startedAtIso: startedAt.toISOString(),
|
|
@@ -380,7 +377,7 @@ async function runPromptTask(input) {
|
|
|
380
377
|
finishedAt: finishedAt.toISOString(),
|
|
381
378
|
durationMs: finishedAt.getTime() - startedAt.getTime(),
|
|
382
379
|
log: logPath,
|
|
383
|
-
target: { kind: "prompt",
|
|
380
|
+
target: { kind: "prompt", engine: engineName },
|
|
384
381
|
detail: result.ok
|
|
385
382
|
? { exitCode: result.exitCode }
|
|
386
383
|
: { reason: result.reason, error: result.error, exitCode: result.exitCode },
|
|
@@ -410,7 +407,7 @@ async function resolvePromptText(task, stashDir) {
|
|
|
410
407
|
function renderPromptLog(input) {
|
|
411
408
|
const lines = [];
|
|
412
409
|
const dbLines = [];
|
|
413
|
-
const header = `[akm tasks] task=${input.task.id} kind=prompt
|
|
410
|
+
const header = `[akm tasks] task=${input.task.id} kind=prompt engine=${input.engineName}`;
|
|
414
411
|
const summary = `ok=${input.result.ok} exit_code=${input.result.exitCode ?? "null"} duration_ms=${input.result.durationMs}`;
|
|
415
412
|
lines.push(header, summary);
|
|
416
413
|
dbLines.push({ line: header }, { level: input.result.ok ? "info" : "error", line: summary });
|
|
@@ -481,9 +478,10 @@ function appendHistory(result) {
|
|
|
481
478
|
target_kind: result.target.kind,
|
|
482
479
|
target_ref: result.target.kind === "workflow" ? result.target.ref : null,
|
|
483
480
|
metadata_json: JSON.stringify({
|
|
481
|
+
metadataVersion: 2,
|
|
484
482
|
durationMs: result.durationMs,
|
|
485
483
|
detail: result.detail ?? null,
|
|
486
|
-
|
|
484
|
+
...(result.target.kind === "prompt" ? { engine: result.target.engine } : {}),
|
|
487
485
|
}),
|
|
488
486
|
});
|
|
489
487
|
});
|
|
@@ -515,16 +513,14 @@ export function readTaskHistory(options = {}) {
|
|
|
515
513
|
* that callers of `readTaskHistory()` expect.
|
|
516
514
|
*/
|
|
517
515
|
function taskHistoryRowToResult(row) {
|
|
518
|
-
|
|
519
|
-
try {
|
|
520
|
-
meta = JSON.parse(row.metadata_json);
|
|
521
|
-
}
|
|
522
|
-
catch {
|
|
523
|
-
// ignore corrupt JSON
|
|
524
|
-
}
|
|
516
|
+
const meta = decodeTaskHistoryMetadata(row.metadata_json);
|
|
525
517
|
const target = row.target_kind === "workflow"
|
|
526
518
|
? { kind: "workflow", ref: row.target_ref ?? "" }
|
|
527
|
-
:
|
|
519
|
+
: row.target_kind === "command"
|
|
520
|
+
? { kind: "command" }
|
|
521
|
+
: meta.metadataVersion === 2
|
|
522
|
+
? { kind: "prompt", engine: meta.engine ?? null }
|
|
523
|
+
: { kind: "prompt", engine: null, ...(meta.legacyProfile ? { legacyProfile: meta.legacyProfile } : {}) };
|
|
528
524
|
return {
|
|
529
525
|
id: row.task_id,
|
|
530
526
|
status: row.status,
|
|
@@ -533,7 +529,7 @@ function taskHistoryRowToResult(row) {
|
|
|
533
529
|
durationMs: meta.durationMs ?? 0,
|
|
534
530
|
log: row.log_path ?? "",
|
|
535
531
|
target,
|
|
536
|
-
...(meta.detail !== undefined ? { detail: meta.detail } : {}),
|
|
532
|
+
...(meta.detail !== undefined && meta.detail !== null ? { detail: meta.detail } : {}),
|
|
537
533
|
};
|
|
538
534
|
}
|
|
539
535
|
/**
|
package/dist/tasks/schema.js
CHANGED
package/dist/tasks/validator.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* • the schedule is parseable and translates to the active backend
|
|
9
9
|
* • the workflow ref resolves (workflow targets)
|
|
10
10
|
* • the asset/file source exists (prompt targets)
|
|
11
|
-
* • the
|
|
11
|
+
* • the selected engine resolves (prompt targets)
|
|
12
12
|
*
|
|
13
13
|
* Validation is deliberately split from parsing: callers that only want to
|
|
14
14
|
* read frontmatter (e.g. `tasks list`) can skip these checks, while
|
|
@@ -20,7 +20,7 @@ import { parseAssetRef } from "../core/asset/asset-ref.js";
|
|
|
20
20
|
import { resolveStashDir } from "../core/common.js";
|
|
21
21
|
import { loadConfig } from "../core/config/config.js";
|
|
22
22
|
import { NotFoundError } from "../core/errors.js";
|
|
23
|
-
import {
|
|
23
|
+
import { resolveEngine } from "../integrations/agent/engine-resolution.js";
|
|
24
24
|
import { resolveAssetPath } from "../sources/resolve.js";
|
|
25
25
|
import { parseSchedule } from "./schedule.js";
|
|
26
26
|
export async function validateTaskDocument(task, options) {
|
|
@@ -38,13 +38,18 @@ export async function validateTaskDocument(task, options) {
|
|
|
38
38
|
if (task.target.kind !== "prompt") {
|
|
39
39
|
return;
|
|
40
40
|
}
|
|
41
|
-
// Prompt target. Resolve the
|
|
42
|
-
// set on the task,
|
|
43
|
-
// throws a clear error if neither is configured. Catching this at
|
|
41
|
+
// Prompt target. Resolve the engine unconditionally — when no engine is
|
|
42
|
+
// set on the task, defaults.engine is required. Catching this at
|
|
44
43
|
// `tasks add` / `tasks sync` time is much more useful than failing only
|
|
45
44
|
// when the OS scheduler fires.
|
|
46
45
|
const config = loadConfig();
|
|
47
|
-
|
|
46
|
+
const engine = task.target.engine ?? config.defaults?.engine;
|
|
47
|
+
if (!engine)
|
|
48
|
+
throw new NotFoundError(`Task "${task.id}" has no selected engine.`, "ASSET_NOT_FOUND");
|
|
49
|
+
const resolved = resolveEngine(engine, config);
|
|
50
|
+
if (task.target.llm !== undefined && resolved.kind !== "llm") {
|
|
51
|
+
throw new NotFoundError(`Task "${task.id}" uses llm overrides with non-LLM engine "${engine}".`, "ASSET_NOT_FOUND");
|
|
52
|
+
}
|
|
48
53
|
const src = task.target.source;
|
|
49
54
|
if (src.kind === "asset") {
|
|
50
55
|
const stashDir = options.stashDir ?? resolveStashDir();
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
import { readFile } from "node:fs/promises";
|
|
17
17
|
import { fileURLToPath } from "node:url";
|
|
18
18
|
|
|
19
|
-
const TEXT_EXTENSIONS = new Set([".md", ".xml", ".txt", ".sql"]);
|
|
19
|
+
const TEXT_EXTENSIONS = new Set([".md", ".xml", ".txt", ".sql", ".yaml", ".yml"]);
|
|
20
20
|
|
|
21
21
|
function isTextImport(url, importAttributes) {
|
|
22
22
|
if (importAttributes && importAttributes.type === "text") return true;
|
package/dist/wiki/wiki.js
CHANGED
|
@@ -48,7 +48,7 @@ import ingestWorkflowTemplate from "../assets/wiki/ingest-workflow-template.md"
|
|
|
48
48
|
import { akmSearch } from "../commands/read/search.js";
|
|
49
49
|
import { parseFrontmatter, parseFrontmatterBlock } from "../core/asset/frontmatter.js";
|
|
50
50
|
import { isWithin, todayIso } from "../core/common.js";
|
|
51
|
-
import { getSources,
|
|
51
|
+
import { getSources, mutateConfig } from "../core/config/config.js";
|
|
52
52
|
import { NotFoundError, UsageError } from "../core/errors.js";
|
|
53
53
|
import { resolveSourceEntries } from "../indexer/search/search-source.js";
|
|
54
54
|
import { buildIndexMd, buildLogMd, buildSchemaMd } from "./wiki-templates.js";
|
|
@@ -443,13 +443,14 @@ export function removeWiki(stashDir, name, options = {}) {
|
|
|
443
443
|
const external = registeredWikiSources(stashDir).find((source) => source.name === name);
|
|
444
444
|
const isStashWiki = fs.existsSync(wikiDir) && isRecognizedStashWiki(wikiDir);
|
|
445
445
|
if (!isStashWiki && external) {
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
446
|
+
mutateConfig((config) => {
|
|
447
|
+
const filteredSources = getSources(config).filter((entry) => entry.wikiName !== name);
|
|
448
|
+
const installed = (config.installed ?? []).filter((entry) => entry.wikiName !== name);
|
|
449
|
+
return {
|
|
450
|
+
...config,
|
|
451
|
+
sources: filteredSources.length > 0 ? filteredSources : undefined,
|
|
452
|
+
installed: installed.length > 0 ? installed : undefined,
|
|
453
|
+
};
|
|
453
454
|
});
|
|
454
455
|
return {
|
|
455
456
|
name,
|