akm-cli 0.9.31 → 0.10.26101002-alpha
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +265 -0
- package/STABILITY.md +79 -53
- package/dist/api-node.mjs +20 -0
- package/dist/api.d.ts +23 -0
- package/dist/api.js +45 -0
- package/dist/assets/hints/cli-hints-full.md +13 -27
- package/dist/assets/hints/cli-hints-short.md +1 -1
- package/dist/assets/improve-strategies/default.json +3 -3
- package/dist/assets/prompts/consolidate-pair.md +9 -7
- package/dist/cli/shared.js +31 -48
- package/dist/cli/unknown-flags.js +6 -43
- package/dist/cli.js +8 -34
- package/dist/commands/agent/contribute-cli.js +1 -1
- package/dist/commands/completions.js +1 -2
- package/dist/commands/config-cli.js +5 -28
- package/dist/commands/env/env-cli.js +5 -5
- package/dist/commands/env/secret-cli.js +2 -2
- package/dist/commands/health/checks.js +41 -16
- package/dist/commands/health/plugin-staleness.js +52 -22
- package/dist/commands/improve/autonomy-gate.js +5 -3
- package/dist/commands/improve/consolidate/pair-pass.js +7 -1
- package/dist/commands/improve/consolidate.js +6 -35
- package/dist/commands/improve/distill/lesson-update.js +91 -0
- package/dist/commands/improve/distill-guards.js +1 -43
- package/dist/commands/improve/distill.js +140 -20
- package/dist/commands/improve/eligibility.js +14 -1
- package/dist/commands/improve/extract-cli.js +1 -1
- package/dist/commands/improve/improve-cli.js +87 -102
- package/dist/commands/improve/improve-strategies.js +8 -12
- package/dist/commands/improve/improve.js +2 -2
- package/dist/commands/improve/loop-stages.js +0 -1
- package/dist/commands/improve/planner.js +0 -1
- package/dist/commands/improve/preparation.js +40 -99
- package/dist/commands/improve/proactive-maintenance.js +2 -2
- package/dist/commands/improve/reflect-noise.js +3 -55
- package/dist/commands/improve/reflect.js +3 -8
- package/dist/commands/improve/retrieval-scope.js +2 -2
- package/dist/commands/improve/salience.js +2 -9
- package/dist/commands/improve/stage.js +14 -9
- package/dist/commands/lint/index.js +5 -20
- package/dist/commands/observability-cli.js +3 -9
- package/dist/commands/proposal/drain.js +39 -6
- package/dist/commands/proposal/proposal-cli.js +11 -31
- package/dist/commands/proposal/repository.js +3 -3
- package/dist/commands/read/knowledge.js +3 -3
- package/dist/commands/read/remember-cli.js +0 -13
- package/dist/commands/read/search-cli.js +29 -97
- package/dist/commands/read/search.js +0 -11
- package/dist/commands/read/show.js +0 -19
- package/dist/commands/sources/plugin-upgrade.js +164 -101
- package/dist/commands/sources/sources-cli.js +1 -15
- package/dist/commands/sources/stash-cli.js +4 -12
- package/dist/commands/tasks/tasks-cli.js +4 -115
- package/dist/commands/workflow/plan.js +11 -33
- package/dist/commands/workflow-cli.js +2 -7
- package/dist/core/adapter/adapter-ids.js +1 -2
- package/dist/core/adapter/adapters/agent-skills-adapter.js +0 -4
- package/dist/core/adapter/adapters/akm-adapter.js +9 -48
- package/dist/core/adapter/adapters/akm-lint.js +10 -26
- package/dist/core/adapter/adapters/akm-metadata.js +2 -72
- package/dist/core/adapter/adapters/akm-task-adapter.js +0 -5
- package/dist/core/adapter/adapters/akm-workflow-adapter.js +9 -28
- package/dist/core/adapter/adapters/dotenv-adapter.js +1 -20
- package/dist/core/adapter/adapters/generic-files-adapter.js +0 -10
- package/dist/core/adapter/adapters/llm-wiki-adapter.js +0 -4
- package/dist/core/adapter/adapters/tool-dir-shared.js +0 -24
- package/dist/core/adapter/adapters/website-snapshot-adapter.js +4 -4
- package/dist/core/adapter/recognize-match.js +3 -4
- package/dist/core/common.js +18 -2
- package/dist/core/config/config-schema.js +12 -15
- package/dist/core/config/config-types.js +6 -15
- package/dist/core/config/config-walker.js +6 -23
- package/dist/core/config/config.js +17 -80
- package/dist/core/config/engine-semantics.js +7 -3
- package/dist/core/config/schema/engines.js +7 -7
- package/dist/core/config/schema/improve-processes.js +8 -61
- package/dist/core/config/schema/improve.js +2 -21
- package/dist/core/config/schema/index-config.js +6 -39
- package/dist/core/env-secret-ref.js +2 -45
- package/dist/core/errors.js +1 -2
- package/dist/core/extra-params.js +2 -86
- package/dist/core/improve-result.js +11 -1
- package/dist/core/mutation-target.js +2 -2
- package/dist/core/recognition-util.js +4 -5
- package/dist/core/warn.js +21 -4
- package/dist/core/write-source.js +3 -3
- package/dist/execution/target-ref.js +1 -2
- package/dist/indexer/search/db-search.js +4 -19
- package/dist/indexer/search/fts-query.js +0 -19
- package/dist/integrations/agent/engine-resolution.js +5 -2
- package/dist/integrations/agent/index.js +1 -1
- package/dist/integrations/agent/profiles.js +41 -98
- package/dist/integrations/agent/request-lowering.js +3 -3
- package/dist/integrations/harnesses/aider/index.js +5 -0
- package/dist/integrations/harnesses/amazonq/index.js +1 -0
- package/dist/integrations/harnesses/claude/config-import.js +2 -3
- package/dist/integrations/harnesses/claude/index.js +5 -0
- package/dist/integrations/harnesses/codex/index.js +1 -0
- package/dist/integrations/harnesses/copilot/index.js +1 -0
- package/dist/integrations/harnesses/gemini/index.js +1 -0
- package/dist/integrations/harnesses/index.js +18 -39
- package/dist/integrations/harnesses/opencode/agent-builder.js +60 -35
- package/dist/integrations/harnesses/opencode/config-import.js +121 -52
- package/dist/integrations/harnesses/opencode/index.js +22 -0
- package/dist/integrations/harnesses/opencode/plugin-layout.js +68 -0
- package/dist/integrations/harnesses/opencode/session-log-types.js +12 -0
- package/dist/integrations/harnesses/opencode/session-log-v1.js +72 -0
- package/dist/integrations/harnesses/opencode/session-log-v2.js +140 -0
- package/dist/integrations/harnesses/opencode/session-log.js +104 -115
- package/dist/integrations/harnesses/opencode/version.js +98 -0
- package/dist/integrations/harnesses/opencode-sdk/{harness.js → index.js} +5 -17
- package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +105 -206
- package/dist/integrations/harnesses/opencode-sdk/v1-adapter.js +199 -0
- package/dist/integrations/harnesses/opencode-sdk/v2-adapter.js +228 -0
- package/dist/integrations/harnesses/opencode-sdk/wire.js +66 -0
- package/dist/integrations/harnesses/openhands/index.js +1 -0
- package/dist/integrations/harnesses/pi/index.js +1 -0
- package/dist/integrations/harnesses/shared.js +2 -0
- package/dist/integrations/harnesses/types.js +2 -0
- package/dist/llm/client.js +41 -11
- package/dist/output/cli-hints.js +1 -1
- package/dist/output/context.js +10 -19
- package/dist/output/format-exempt.js +5 -8
- package/dist/output/shapes/helpers.js +1 -22
- package/dist/output/shapes/passthrough.js +0 -1
- package/dist/output/shapes.js +3 -31
- package/dist/output/stdout.js +5 -1
- package/dist/output/text/command-format.js +6 -1
- package/dist/output/text/workflow-format.js +4 -22
- package/dist/scripts/akm-migrate-node.js +13049 -14013
- package/dist/scripts/akm-migrate.js +16316 -17276
- package/dist/setup/detect.js +10 -4
- package/dist/setup/harness-config-import.js +5 -14
- package/dist/setup/setup.js +1 -1
- package/dist/setup/steps/tasks.js +12 -13
- package/dist/storage/repositories/task-history-repository.js +0 -5
- package/dist/storage/repositories/workflow-runs-repository.js +5 -15
- package/dist/tasks/embedded.js +2 -1
- package/dist/tasks/run/task-history.js +0 -2
- package/dist/tasks/schedule-gates.js +39 -0
- package/dist/tasks/scheduler-binding.js +0 -16
- package/dist/tasks/scheduler-sync.js +3 -97
- package/dist/tasks/source/bounded-document.js +1 -1
- package/dist/tasks/source/task-source-v4.js +2 -12
- package/dist/tasks/task-run-reserved-flags.js +2 -31
- package/dist/workflows/authoring/authoring.js +6 -6
- package/dist/workflows/compile.js +8 -65
- package/dist/workflows/exec/child-workflow.js +22 -3
- package/dist/workflows/exec/native-executor.js +12 -147
- package/dist/workflows/exec/run-workflow.js +31 -103
- package/dist/workflows/exec/step-work.js +9 -178
- package/dist/workflows/exec/unit-dispatch.js +0 -10
- package/dist/workflows/freeze/environment.js +3 -44
- package/dist/workflows/freeze/freeze.js +8 -41
- package/dist/workflows/freeze/targets/child-workflow.js +27 -63
- package/dist/workflows/freeze/targets/command.js +2 -16
- package/dist/workflows/freeze/targets/shell.js +0 -2
- package/dist/workflows/freeze/task-bindings.js +2 -28
- package/dist/workflows/ir/params.js +12 -2
- package/dist/workflows/parser.js +82 -232
- package/dist/workflows/plan.js +1 -1
- package/dist/workflows/renderer.js +10 -19
- package/dist/workflows/runtime/run-plan.js +11 -53
- package/dist/workflows/runtime/runs.js +4 -27
- package/dist/workflows/source-files.js +12 -16
- package/dist/workflows/source-semantics.js +1 -93
- package/docs/README.md +1 -1
- package/docs/agents/README.md +3 -3
- package/docs/agents/agent-install.md +1 -1
- package/docs/architecture/adapters.md +17 -31
- package/docs/architecture/architecture.md +4 -5
- package/docs/architecture/improvement.md +52 -21
- package/docs/architecture/internals/improve-workflow.md +15 -8
- package/docs/architecture/internals/indexing.md +1 -1
- package/docs/architecture/internals/search.md +1 -2
- package/docs/architecture/internals/storage-locations.md +5 -5
- package/docs/architecture/persisted-data-compat.md +13 -4
- package/docs/architecture/specs/0.9.0-decisions.md +6 -2
- package/docs/architecture/specs/improve-collapse-churn-detector-design.md +2 -2
- package/docs/architecture/specs/workflow-format-unification.md +1 -6
- package/docs/architecture/testing/manual-testing-checklist.md +20 -29
- package/docs/architecture/testing/task-workflow-manual-tests.md +106 -203
- package/docs/architecture/testing/testing-workflow.md +1 -1
- package/docs/architecture/workflow-engine.md +5 -56
- package/docs/guides/author-workflows.md +39 -69
- package/docs/guides/capture-knowledge.md +1 -1
- package/docs/guides/claude-code-vs-akm-workflows.md +1 -2
- package/docs/guides/discover-and-load.md +4 -4
- package/docs/guides/getting-started.md +1 -1
- package/docs/guides/improve-the-library.md +7 -7
- package/docs/guides/recipes/headless-install.md +2 -2
- package/docs/guides/run-workflows.md +1 -1
- package/docs/guides/use-with-any-agent.md +49 -4
- package/docs/integration/bundling-akm.md +7 -6
- package/docs/maintainers/curate-workmap.md +1 -2
- package/docs/maintainers/release-checklist.md +30 -1
- package/docs/posts/akm-command-tour.md +3 -3
- package/docs/product-surface.md +1 -1
- package/docs/reference/README.md +1 -0
- package/docs/reference/api.md +65 -0
- package/docs/reference/asset-types.md +3 -5
- package/docs/reference/bundle-types.md +2 -4
- package/docs/reference/cli.md +129 -171
- package/docs/reference/configuration.md +84 -19
- package/docs/reference/data-and-telemetry.md +1 -1
- package/docs/reference/env-and-secrets.md +2 -2
- package/docs/reference/supported-formats.md +3 -4
- package/docs/reference/tasks.md +12 -19
- package/docs/reference/website-sources.md +1 -1
- package/docs/reference/workflow-schema.md +89 -317
- package/docs/reference/workflows.md +20 -48
- package/package.json +12 -2
- package/schemas/akm-config.json +2 -243
- package/schemas/akm-task.json +2 -3
- package/schemas/akm-workflow.json +47 -84
- package/dist/assets/improve-strategies/catchup.json +0 -41
- package/dist/assets/improve-strategies/proactive-maintenance.json +0 -24
- package/dist/assets/improve-strategies/quick.json +0 -17
- package/dist/assets/improve-strategies/reflect-distill.json +0 -29
- package/dist/assets/improve-strategies/thorough.json +0 -49
- package/dist/assets/tasks/improve/akm-improve-catchup.yml +0 -6
- package/dist/assets/tasks/improve/akm-improve-frequent.yml +0 -4
- package/dist/assets/tasks/improve/akm-improve-nightly.yml +0 -4
- package/dist/cli/retired-commands.js +0 -118
- package/dist/commands/tasks/validate.js +0 -176
- package/dist/core/config/legacy-source-shape-shim.js +0 -117
- package/dist/integrations/harnesses/ids.js +0 -28
- package/dist/tasks/source/task-source-v3-frozen.js +0 -424
- package/dist/tasks/source/task-to-v4.js +0 -863
- package/dist/workflows/exec/worktree.js +0 -372
- package/dist/workflows/freeze/child-output-references.js +0 -83
- package/dist/workflows/freeze/identity.js +0 -22
- package/dist/workflows/freeze/targets/script.js +0 -50
- package/dist/workflows/freeze/targets/task.js +0 -124
- package/dist/workflows/github-yaml.js +0 -506
- package/dist/workflows/runtime/run-outputs.js +0 -80
|
@@ -5,9 +5,10 @@
|
|
|
5
5
|
* Reading a run row's frozen plan: the ONE plan decoder and its tolerant
|
|
6
6
|
* row-level wrapper.
|
|
7
7
|
*
|
|
8
|
-
* {@link decodeWorkflowPlan} accepts every plan shape a release
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* {@link decodeWorkflowPlan} accepts every plan shape a release at or above
|
|
9
|
+
* the 0.9.15 floor has stored — irVersion 5 (with its `sourceReadSet` and
|
|
10
|
+
* host-identity fields, which 0.9.15 and 0.9.16 wrote) as well as the current
|
|
11
|
+
* one — and keeps keys it does not know rather than
|
|
11
12
|
* refusing them. It rejects only a plan it cannot run: no steps, a unit with
|
|
12
13
|
* no frozen target, a request that does not decode. `plan_ir_version` and
|
|
13
14
|
* `plan_hash` are informational columns: neither gates execution. A plan that
|
|
@@ -62,30 +63,17 @@ export function frozenStepRows(plan) {
|
|
|
62
63
|
? step.root.kind === "map"
|
|
63
64
|
? step.root.template.instructions
|
|
64
65
|
: step.root.instructions
|
|
65
|
-
:
|
|
66
|
+
: "",
|
|
66
67
|
completionJson: step.gate.criteria.length > 0 ? JSON.stringify(step.gate.criteria) : null,
|
|
67
68
|
sequenceIndex: step.sequenceIndex,
|
|
68
69
|
}));
|
|
69
70
|
}
|
|
70
|
-
function routeInstructions(route) {
|
|
71
|
-
const branches = Object.entries(route.when)
|
|
72
|
-
.sort(([left], [right]) => (left < right ? -1 : left > right ? 1 : 0))
|
|
73
|
-
.map(([match, stepId]) => `"${match}" -> ${stepId}`);
|
|
74
|
-
if (route.defaultStepId !== undefined)
|
|
75
|
-
branches.push(`default -> ${route.defaultStepId}`);
|
|
76
|
-
return `Route on ${route.input}: ${branches.join(", ")}.`;
|
|
77
|
-
}
|
|
78
71
|
/** Decode a stored plan (any release's) into the one plan type. */
|
|
79
72
|
export function decodeWorkflowPlan(input, options = {}) {
|
|
80
73
|
const raw = record(input, "plan");
|
|
81
74
|
if (!Array.isArray(raw.steps) || raw.steps.length === 0)
|
|
82
75
|
fail("steps must be a non-empty array");
|
|
83
76
|
const steps = raw.steps.map((value, index) => decodeStep(value, index));
|
|
84
|
-
if (raw.outputs !== undefined) {
|
|
85
|
-
for (const [name, entry] of Object.entries(record(raw.outputs, "outputs"))) {
|
|
86
|
-
string(record(entry, `outputs.${name}`).from, `outputs.${name}.from`);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
77
|
const sourceHash = typeof raw.sourceHash === "string" ? raw.sourceHash : legacySourceHash(raw.sourceReadSet, options.workflowRef);
|
|
90
78
|
return {
|
|
91
79
|
...raw,
|
|
@@ -109,28 +97,18 @@ function legacySourceHash(readSet, workflowRef) {
|
|
|
109
97
|
function decodeStep(value, index) {
|
|
110
98
|
const step = record(value, `step ${index}`);
|
|
111
99
|
const stepId = string(step.stepId, `step ${index} stepId`);
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
fail(`step ${stepId} has neither a frozen root nor a route`);
|
|
100
|
+
if (step.root === undefined)
|
|
101
|
+
fail(`step ${stepId} has no frozen root`);
|
|
102
|
+
const root = decodeNode(step.root, stepId);
|
|
116
103
|
return {
|
|
117
104
|
...step,
|
|
118
105
|
stepId,
|
|
119
106
|
title: typeof step.title === "string" ? step.title : stepId,
|
|
120
107
|
sequenceIndex: index,
|
|
121
|
-
|
|
122
|
-
...(route ? { route } : {}),
|
|
108
|
+
root,
|
|
123
109
|
gate: decodeGate(step.gate, stepId),
|
|
124
110
|
};
|
|
125
111
|
}
|
|
126
|
-
function decodeRoute(value, stepId) {
|
|
127
|
-
const route = record(value, `step ${stepId} route`);
|
|
128
|
-
string(route.input, `step ${stepId} route.input`);
|
|
129
|
-
const when = record(route.when, `step ${stepId} route.when`);
|
|
130
|
-
for (const target of Object.values(when))
|
|
131
|
-
string(target, `step ${stepId} route target`);
|
|
132
|
-
return route;
|
|
133
|
-
}
|
|
134
112
|
function decodeNode(value, stepId) {
|
|
135
113
|
const node = record(value, `step ${stepId} root`);
|
|
136
114
|
if (node.kind !== "map")
|
|
@@ -154,7 +132,6 @@ function decodeUnit(node, stepId) {
|
|
|
154
132
|
id,
|
|
155
133
|
instructions: typeof node.instructions === "string" ? node.instructions : "",
|
|
156
134
|
onError: node.onError === "continue" ? "continue" : "fail",
|
|
157
|
-
isolation: node.isolation === "worktree" ? "worktree" : "none",
|
|
158
135
|
frozenTarget: decodeTarget(node.frozenTarget, label),
|
|
159
136
|
environment: Array.isArray(node.environment)
|
|
160
137
|
? node.environment.map((binding, index) => decodeEnvironmentBinding(binding, `${label} environment[${index}]`))
|
|
@@ -180,11 +157,7 @@ function decodeGate(value, stepId) {
|
|
|
180
157
|
function decodeTarget(value, label) {
|
|
181
158
|
const target = record(value, `${label} frozenTarget`);
|
|
182
159
|
const inputBindings = decodeInputBindings(target.inputBindings, label);
|
|
183
|
-
const
|
|
184
|
-
const optional = {
|
|
185
|
-
...(gitCommitOid ? { gitCommitOid } : {}),
|
|
186
|
-
...(inputBindings ? { inputBindings } : {}),
|
|
187
|
-
};
|
|
160
|
+
const optional = inputBindings ? { inputBindings } : {};
|
|
188
161
|
switch (target.kind) {
|
|
189
162
|
case "command":
|
|
190
163
|
return decodeCommandTarget(target, label);
|
|
@@ -196,20 +169,6 @@ function decodeTarget(value, label) {
|
|
|
196
169
|
cwdIdentity: record(target.cwdIdentity, `${label} cwdIdentity`),
|
|
197
170
|
...optional,
|
|
198
171
|
};
|
|
199
|
-
case "script":
|
|
200
|
-
return {
|
|
201
|
-
kind: "script",
|
|
202
|
-
ref: string(target.ref, `${label} script ref`),
|
|
203
|
-
contentHash: string(target.contentHash, `${label} contentHash`),
|
|
204
|
-
exec: decodeExec(target.exec, label),
|
|
205
|
-
interpreter: string(target.interpreter, `${label} interpreter`),
|
|
206
|
-
extension: string(target.extension, `${label} extension`),
|
|
207
|
-
bytesBase64: typeof target.bytesBase64 === "string" ? target.bytesBase64 : fail(`${label} has no script bytes`),
|
|
208
|
-
byteLength: typeof target.byteLength === "number" ? target.byteLength : 0,
|
|
209
|
-
cwdIdentity: record(target.cwdIdentity, `${label} cwdIdentity`),
|
|
210
|
-
materialization: "ephemeral-0700-delete",
|
|
211
|
-
...optional,
|
|
212
|
-
};
|
|
213
172
|
case "child-workflow":
|
|
214
173
|
return {
|
|
215
174
|
kind: "child-workflow",
|
|
@@ -217,7 +176,7 @@ function decodeTarget(value, label) {
|
|
|
217
176
|
planHash: string(target.planHash, `${label} planHash`),
|
|
218
177
|
frozenPlan: decodeWorkflowPlan(target.frozenPlan),
|
|
219
178
|
contentHash: string(target.contentHash, `${label} contentHash`),
|
|
220
|
-
|
|
179
|
+
...(target.via === "task" || target.via === "direct" ? { via: target.via } : {}),
|
|
221
180
|
...(typeof target.taskRef === "string" ? { taskRef: target.taskRef } : {}),
|
|
222
181
|
...(inputBindings ? { inputBindings } : {}),
|
|
223
182
|
};
|
|
@@ -247,7 +206,6 @@ function decodeCommandTarget(target, label) {
|
|
|
247
206
|
...(target.cwdIdentity && typeof target.cwdIdentity === "object"
|
|
248
207
|
? { cwdIdentity: target.cwdIdentity }
|
|
249
208
|
: {}),
|
|
250
|
-
...(typeof target.gitCommitOid === "string" ? { gitCommitOid: target.gitCommitOid } : {}),
|
|
251
209
|
...(inputBindings ? { inputBindings } : {}),
|
|
252
210
|
};
|
|
253
211
|
}
|
|
@@ -13,12 +13,11 @@ import { getCurrentWorkflowScopeKey } from "../authoring/scope-key.js";
|
|
|
13
13
|
import { frozenSummaryJudge } from "../exec/frozen-judge.js";
|
|
14
14
|
import { detectSecretShapedParams } from "../exec/param-secrets.js";
|
|
15
15
|
import { freezeWorkflow } from "../freeze/freeze.js";
|
|
16
|
-
import { materializeWorkflowParameterFlags, validateWorkflowParams } from "../ir/params.js";
|
|
16
|
+
import { applyWorkflowParamDefaults, materializeWorkflowParameterFlags, validateWorkflowParams, } from "../ir/params.js";
|
|
17
17
|
import { canonicalPlanJson, computePlanHash } from "../ir/plan-hash.js";
|
|
18
18
|
import { clip, WORKFLOW_UNIT_DIAGNOSTIC_CLIP } from "../resource-limits.js";
|
|
19
19
|
import { validateStepSummary } from "../validate-summary.js";
|
|
20
20
|
import { resolveAgentIdentity } from "./agent-identity.js";
|
|
21
|
-
import { resolveWorkflowRunOutputs } from "./run-outputs.js";
|
|
22
21
|
import { frozenStepRows, readRunPlan } from "./run-plan.js";
|
|
23
22
|
import { canonicalizeWorkflowRefInput, loadWorkflowAsset, resolveWorkflowEntryId } from "./workflow-asset-loader.js";
|
|
24
23
|
/**
|
|
@@ -76,9 +75,7 @@ export async function startWorkflowRun(ref, params = {}, options) {
|
|
|
76
75
|
if (options?.parameterFlags?.length && Object.keys(params).length > 0) {
|
|
77
76
|
throw new UsageError("Workflow parameters must use either an object or per-parameter flags, not both.");
|
|
78
77
|
}
|
|
79
|
-
const effectiveParams = options?.parameterFlags?.length
|
|
80
|
-
? materializeWorkflowParameterFlags(plan, options.parameterFlags)
|
|
81
|
-
: params;
|
|
78
|
+
const effectiveParams = applyWorkflowParamDefaults(plan, options?.parameterFlags?.length ? materializeWorkflowParameterFlags(plan, options.parameterFlags) : params);
|
|
82
79
|
// Non-fatal WARNINGS: untyped-step and undeclared-param advisories surface
|
|
83
80
|
// as `warn()` lines at start (stderr, consistent with the repo's other
|
|
84
81
|
// author-facing warnings) without blocking the run.
|
|
@@ -394,10 +391,9 @@ export async function completeWorkflowStep(input) {
|
|
|
394
391
|
return withWorkflowRunsRepo((repo) => {
|
|
395
392
|
let updatedRun;
|
|
396
393
|
let refreshedSteps = [];
|
|
397
|
-
let outputWarnings;
|
|
398
394
|
repo.transaction(() => {
|
|
399
395
|
const run = readWorkflowRun(repo, input.runId);
|
|
400
|
-
const
|
|
396
|
+
const _plan = requireRunPlan(run);
|
|
401
397
|
if (run.status !== "active") {
|
|
402
398
|
throw new UsageError(`Workflow run ${run.id} is ${run.status} and cannot be updated.`);
|
|
403
399
|
}
|
|
@@ -428,15 +424,6 @@ export async function completeWorkflowStep(input) {
|
|
|
428
424
|
});
|
|
429
425
|
refreshedSteps = readWorkflowRunSteps(repo, run.id);
|
|
430
426
|
const state = deriveRunState(refreshedSteps);
|
|
431
|
-
// Declared outputs resolve inside this transaction once the run
|
|
432
|
-
// completes; a failure rolls the step completion back whole.
|
|
433
|
-
let outputsJson; // undefined = untouched, keep the row's existing value
|
|
434
|
-
if (state.status === "completed" && plan.outputs) {
|
|
435
|
-
const resolved = resolveWorkflowRunOutputs(plan, refreshedSteps);
|
|
436
|
-
outputsJson = JSON.stringify(resolved.outputs);
|
|
437
|
-
repo.setRunOutputs(run.id, outputsJson);
|
|
438
|
-
outputWarnings = resolved.errors;
|
|
439
|
-
}
|
|
440
427
|
repo.updateRunState({
|
|
441
428
|
status: state.status,
|
|
442
429
|
currentStepId: state.currentStepId,
|
|
@@ -450,16 +437,9 @@ export async function completeWorkflowStep(input) {
|
|
|
450
437
|
current_step_id: state.currentStepId,
|
|
451
438
|
updated_at: completedAt,
|
|
452
439
|
completed_at: state.completedAt,
|
|
453
|
-
...(outputsJson !== undefined ? { outputs_json: outputsJson } : {}),
|
|
454
440
|
};
|
|
455
441
|
});
|
|
456
442
|
const detail = buildWorkflowRunDetail(repo, updatedRun, refreshedSteps);
|
|
457
|
-
if (outputWarnings?.length) {
|
|
458
|
-
const messages = outputWarnings.map((e) => `Workflow run ${input.runId} declared ${e}`);
|
|
459
|
-
for (const message of messages)
|
|
460
|
-
warn(message);
|
|
461
|
-
detail.warnings = [...(detail.warnings ?? []), ...messages];
|
|
462
|
-
}
|
|
463
443
|
// #11: emit `workflow_step_completed` ONLY for a genuine `completed`
|
|
464
444
|
// transition; every other non-pending status (failed/skipped/blocked)
|
|
465
445
|
// carries the honest `workflow_step_updated` name. The status is ALWAYS
|
|
@@ -680,10 +660,7 @@ function toWorkflowRunSummary(run) {
|
|
|
680
660
|
agentHarness: run.agent_harness ?? null,
|
|
681
661
|
agentSessionId: run.agent_session_id ?? null,
|
|
682
662
|
planIrVersion: run.plan_ir_version ?? null,
|
|
683
|
-
//
|
|
684
|
-
// pre-existing (non-child, no-outputs-declared) run's envelope is
|
|
685
|
-
// byte-identical.
|
|
686
|
-
...(run.outputs_json ? { outputs: parseJsonObject(run.outputs_json) ?? {} } : {}),
|
|
663
|
+
// Optional and conditionally spread, so a non-child run's envelope is unchanged.
|
|
687
664
|
...(run.parent_run_id ? { parentRunId: run.parent_run_id } : {}),
|
|
688
665
|
...(run.parent_unit_id ? { spawnedByUnitId: run.parent_unit_id } : {}),
|
|
689
666
|
};
|
|
@@ -4,11 +4,9 @@
|
|
|
4
4
|
/**
|
|
5
5
|
* Authoritative workflow-source ownership.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* cache reuse, lookup/show, and runtime load/start. It never chooses one
|
|
11
|
-
* extension by priority: two recognized siblings are a hard collision.
|
|
7
|
+
* One canonical workflow ref must have exactly one source file. This module
|
|
8
|
+
* is the shared filesystem arbitration point used before indexing, cache
|
|
9
|
+
* reuse, lookup/show, and runtime load/start.
|
|
12
10
|
*/
|
|
13
11
|
import fs from "node:fs";
|
|
14
12
|
import path from "node:path";
|
|
@@ -29,7 +27,7 @@ export class WorkflowSourceCollisionError extends WorkflowSourceRejectionError {
|
|
|
29
27
|
constructor(canonicalName, sourcePaths) {
|
|
30
28
|
const sorted = [...sourcePaths].sort(compareCodePoints);
|
|
31
29
|
super(`Workflow "${canonicalName}" resolves to multiple workflow source files: ${sorted.join(", ")}. ` +
|
|
32
|
-
"A canonical workflow ref must be owned by exactly one recognized .md
|
|
30
|
+
"A canonical workflow ref must be owned by exactly one recognized .md source; remove or rename the duplicate.", "RESOURCE_ALREADY_EXISTS", sorted);
|
|
33
31
|
this.name = "WorkflowSourceCollisionError";
|
|
34
32
|
this.canonicalName = canonicalName;
|
|
35
33
|
Object.setPrototypeOf(this, new.target.prototype);
|
|
@@ -46,15 +44,15 @@ export class WorkflowSourceIdentityError extends UsageError {
|
|
|
46
44
|
export class WorkflowSourceNameError extends WorkflowSourceRejectionError {
|
|
47
45
|
constructor(sourcePath, nestedSuffix) {
|
|
48
46
|
super(`Workflow source filename ${sourcePath} has an extensionless stem ending in recognized workflow suffix "${nestedSuffix}". ` +
|
|
49
|
-
"Nested workflow suffixes are invalid; remove the inner .md
|
|
47
|
+
"Nested workflow suffixes are invalid; remove the inner .md suffix instead of relying on repeated stripping.", "WORKFLOW_SOURCE_INVALID", [sourcePath]);
|
|
50
48
|
this.name = "WorkflowSourceNameError";
|
|
51
49
|
Object.setPrototypeOf(this, new.target.prototype);
|
|
52
50
|
}
|
|
53
51
|
}
|
|
54
52
|
export class WorkflowSourceLinkIdentityError extends WorkflowSourceRejectionError {
|
|
55
53
|
constructor(sourcePath, targetPath) {
|
|
56
|
-
super(`Workflow source ${sourcePath} resolves through a symlink to ${targetPath} with a different
|
|
57
|
-
"The authored workflow path and resolved source must
|
|
54
|
+
super(`Workflow source ${sourcePath} resolves through a symlink to ${targetPath} with a different extension. ` +
|
|
55
|
+
"The authored workflow path and resolved source must both be .md.", "WORKFLOW_SOURCE_INVALID", [sourcePath]);
|
|
58
56
|
this.name = "WorkflowSourceLinkIdentityError";
|
|
59
57
|
Object.setPrototypeOf(this, new.target.prototype);
|
|
60
58
|
}
|
|
@@ -240,13 +238,13 @@ function inspectWorkflowSourceDomain(candidates, canonicalName, realRoot) {
|
|
|
240
238
|
function pickWorkflowSource(adapterId, canonicalName, sources) {
|
|
241
239
|
if (sources.length <= 1)
|
|
242
240
|
return sources[0];
|
|
243
|
-
const winner =
|
|
241
|
+
const winner = sources[0];
|
|
244
242
|
const shadowed = sources.filter((source) => source !== winner);
|
|
245
243
|
const displayName = adapterId === "akm" ? `workflows/${canonicalName}` : canonicalName;
|
|
246
|
-
warnOnce(`workflow-source-collision:${displayName}`, `Workflow "${displayName}" has
|
|
244
|
+
warnOnce(`workflow-source-collision:${displayName}`, `Workflow "${displayName}" has several sources (${shadowed
|
|
247
245
|
.map((source) => source.relativePath)
|
|
248
|
-
.join(", ")} shadowed by ${winner?.relativePath}); using
|
|
249
|
-
"silence this warning.");
|
|
246
|
+
.join(", ")} shadowed by ${winner?.relativePath}); using ${winner?.relativePath}. Remove the shadowed ` +
|
|
247
|
+
"sibling to silence this warning.");
|
|
250
248
|
return winner;
|
|
251
249
|
}
|
|
252
250
|
function inspectWorkflowSourceCandidate(candidate, canonicalName, realRoot) {
|
|
@@ -297,7 +295,6 @@ function inspectWorkflowSourceCandidate(candidate, canonicalName, realRoot) {
|
|
|
297
295
|
realPath,
|
|
298
296
|
relativePath: candidate.relativePath,
|
|
299
297
|
canonicalName,
|
|
300
|
-
format: candidate.lowerExtension === ".md" ? "markdown" : "github-yaml",
|
|
301
298
|
},
|
|
302
299
|
};
|
|
303
300
|
}
|
|
@@ -321,8 +318,7 @@ export function assertIndexedWorkflowSourceIdentity(ref, indexedPath, authoritat
|
|
|
321
318
|
throw new WorkflowSourceIdentityError(ref, indexedPath, authoritative.path);
|
|
322
319
|
}
|
|
323
320
|
const indexedExtension = path.extname(indexedPath).toLowerCase();
|
|
324
|
-
|
|
325
|
-
if (indexedExtension !== authoritativeExtension) {
|
|
321
|
+
if (indexedExtension !== ".md") {
|
|
326
322
|
throw new WorkflowSourceIdentityError(ref, indexedPath, authoritative.path);
|
|
327
323
|
}
|
|
328
324
|
}
|
|
@@ -1,12 +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
|
-
/** Semantic checks
|
|
4
|
+
/** Semantic checks for the Markdown workflow grammar (`parser.ts`). */
|
|
5
5
|
import fs from "node:fs";
|
|
6
6
|
import path from "node:path";
|
|
7
|
-
import { parseBuiltinCommandAction } from "../commands/command/builtin-action.js";
|
|
8
|
-
import { classifyTargetRef } from "../execution/target-ref.js";
|
|
9
|
-
import { parseSchedule } from "../tasks/schedule.js";
|
|
10
7
|
export class WorkflowSourceSemanticError extends Error {
|
|
11
8
|
code;
|
|
12
9
|
constructor(code, message) {
|
|
@@ -15,36 +12,6 @@ export class WorkflowSourceSemanticError extends Error {
|
|
|
15
12
|
this.name = "WorkflowSourceSemanticError";
|
|
16
13
|
}
|
|
17
14
|
}
|
|
18
|
-
/** The argv a `run:` string executes under `shell`. */
|
|
19
|
-
export function workflowShellCommand(shell, content) {
|
|
20
|
-
if (shell === "cmd")
|
|
21
|
-
return ["cmd", "/d", "/s", "/c", content];
|
|
22
|
-
if (shell === "pwsh" || shell === "powershell")
|
|
23
|
-
return [shell, "-Command", content];
|
|
24
|
-
return [shell, "-c", content];
|
|
25
|
-
}
|
|
26
|
-
export function canonicalizeWorkflowCron(value) {
|
|
27
|
-
const canonical = value.trim().split(/\s+/).join(" ");
|
|
28
|
-
if (canonical.startsWith("@") || canonical.split(" ").length !== 5) {
|
|
29
|
-
throw new WorkflowSourceSemanticError("invalid-cron", "GitHub schedule cron must use exactly five fields.");
|
|
30
|
-
}
|
|
31
|
-
try {
|
|
32
|
-
parseSchedule(canonical, "cron");
|
|
33
|
-
}
|
|
34
|
-
catch (cause) {
|
|
35
|
-
throw new WorkflowSourceSemanticError("invalid-cron", cause instanceof Error ? cause.message : "Invalid cron schedule.");
|
|
36
|
-
}
|
|
37
|
-
return canonical;
|
|
38
|
-
}
|
|
39
|
-
export function canonicalizeWorkflowRun(value) {
|
|
40
|
-
if (value.includes("${{")) {
|
|
41
|
-
throw new WorkflowSourceSemanticError("unsupported-github-expression", "GitHub expressions and contexts are not supported.");
|
|
42
|
-
}
|
|
43
|
-
if (value.includes("\0")) {
|
|
44
|
-
throw new WorkflowSourceSemanticError("invalid-exec-argv", "Local run may not contain NUL bytes.");
|
|
45
|
-
}
|
|
46
|
-
return value;
|
|
47
|
-
}
|
|
48
15
|
export function canonicalizeWorkflowWorkingDirectory(value, workspaceRoot) {
|
|
49
16
|
if (hasControlCharacter(value)) {
|
|
50
17
|
throw new WorkflowSourceSemanticError("working-directory-control-character", "working-directory may not contain NUL or control characters.");
|
|
@@ -74,65 +41,6 @@ function hasControlCharacter(value) {
|
|
|
74
41
|
}
|
|
75
42
|
return false;
|
|
76
43
|
}
|
|
77
|
-
export function classifyWorkflowStepUses(value) {
|
|
78
|
-
if (value.includes("${{")) {
|
|
79
|
-
throw new WorkflowSourceSemanticError("unsupported-github-expression", "GitHub expressions are unsupported in uses.");
|
|
80
|
-
}
|
|
81
|
-
if (value.length === 0 || value.trim() !== value || /\s/.test(value)) {
|
|
82
|
-
throw new WorkflowSourceSemanticError("unsupported-uses-target", "uses must be one exact, non-empty executable ref");
|
|
83
|
-
}
|
|
84
|
-
if (value === "akm/command")
|
|
85
|
-
return { kind: "builtin-command", ref: "akm/command" };
|
|
86
|
-
try {
|
|
87
|
-
return classifyTargetRef(value);
|
|
88
|
-
}
|
|
89
|
-
catch (cause) {
|
|
90
|
-
throw usesFailure(value, cause);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
/**
|
|
94
|
-
* Validate AKM's built-in command action at the shared source/decoder boundary.
|
|
95
|
-
*
|
|
96
|
-
* Inline YAML actions are portable templates and therefore use WP4's one
|
|
97
|
-
* authoritative template validator. Markdown prose is explicitly `literal`,
|
|
98
|
-
* while a stored ref remains resolution-owned because its template bytes are
|
|
99
|
-
* not available until the later resolver loads the command asset.
|
|
100
|
-
*/
|
|
101
|
-
export function validateWorkflowBuiltinCommand(value, mode) {
|
|
102
|
-
let action;
|
|
103
|
-
try {
|
|
104
|
-
action = parseBuiltinCommandAction(value);
|
|
105
|
-
}
|
|
106
|
-
catch (cause) {
|
|
107
|
-
throw new WorkflowSourceSemanticError("builtin-command-inputs", cause instanceof Error ? cause.message : "Invalid akm/command inputs.");
|
|
108
|
-
}
|
|
109
|
-
const expectedMode = action.kind === "stored" ? "stored-ref" : "portable-template";
|
|
110
|
-
const effectiveMode = mode ?? expectedMode;
|
|
111
|
-
if (action.kind === "stored") {
|
|
112
|
-
if (effectiveMode !== "stored-ref") {
|
|
113
|
-
throw new WorkflowSourceSemanticError("builtin-command-inputs", "Stored akm/command refs require commandMode stored-ref.");
|
|
114
|
-
}
|
|
115
|
-
return action;
|
|
116
|
-
}
|
|
117
|
-
if (effectiveMode === "stored-ref") {
|
|
118
|
-
throw new WorkflowSourceSemanticError("builtin-command-inputs", "Inline akm/command content cannot use commandMode stored-ref.");
|
|
119
|
-
}
|
|
120
|
-
if (effectiveMode === "literal" && action.arguments !== undefined) {
|
|
121
|
-
throw new WorkflowSourceSemanticError("builtin-command-inputs", "Literal akm/command content cannot declare arguments because no substitution occurs.");
|
|
122
|
-
}
|
|
123
|
-
return action;
|
|
124
|
-
}
|
|
125
|
-
function usesFailure(value, cause) {
|
|
126
|
-
const message = cause instanceof Error ? cause.message : String(cause);
|
|
127
|
-
const code = value.startsWith("docker://")
|
|
128
|
-
? "docker-action-unsupported"
|
|
129
|
-
: value.startsWith("./") || value.startsWith("../") || value.startsWith("/")
|
|
130
|
-
? "local-action-path-unsupported"
|
|
131
|
-
: /^(?:[A-Za-z0-9][A-Za-z0-9._-]*\/\/)?agents\//.test(value)
|
|
132
|
-
? "non-executable-asset-ref"
|
|
133
|
-
: "unsupported-uses-target";
|
|
134
|
-
return new WorkflowSourceSemanticError(code, message);
|
|
135
|
-
}
|
|
136
44
|
function verifyPhysicalContainment(workspaceRoot, relative) {
|
|
137
45
|
let root;
|
|
138
46
|
try {
|
package/docs/README.md
CHANGED
|
@@ -17,7 +17,7 @@ Full per-directory indexes: [Guides](guides/README.md),
|
|
|
17
17
|
- [Getting Started](guides/getting-started.md) -- Install akm, connect a source, and pull a curated shortlist in five to seven minutes
|
|
18
18
|
- [Concepts](guides/concepts.md) -- Capabilities, bundles, adapters, asset types, and refs -- the mental model in one page
|
|
19
19
|
- [Agent Install Guide](agents/agent-install.md) -- Step-by-step automated (non-interactive) install for agents
|
|
20
|
-
- `akm
|
|
20
|
+
- `akm hints` (complete guide by default; `akm hints --detail brief` for the short one) -- The CLI reference agents load to use akm; always the embedded corpus at `src/assets/hints/cli-hints-{full,short}.md`
|
|
21
21
|
|
|
22
22
|
## Use
|
|
23
23
|
|
package/docs/agents/README.md
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
Documentation for wiring AI agents into akm.
|
|
4
4
|
|
|
5
5
|
Run `akm hints` for the complete CLI reference agents should load into a
|
|
6
|
-
system prompt, or `akm
|
|
7
|
-
|
|
6
|
+
system prompt, or `akm hints --detail brief` for the compact guide.
|
|
7
|
+
It always prints the embedded
|
|
8
8
|
corpus at `src/assets/hints/cli-hints-full.md` / `cli-hints-short.md` --
|
|
9
9
|
browse those files directly if you want to read the reference without running
|
|
10
10
|
the CLI. There used to be a separate `docs/agents/AGENTS.md` /
|
|
@@ -12,4 +12,4 @@ the CLI. There used to be a separate `docs/agents/AGENTS.md` /
|
|
|
12
12
|
re-diverging from the embedded copy with no test to catch it, so it was
|
|
13
13
|
deleted and its still-true unique content was merged into the embedded files.
|
|
14
14
|
|
|
15
|
-
- [Agent Install Guide](agent-install.md) -- Short redirect covering the install shape (binary, `akm setup --yes`, a source, an index, `akm
|
|
15
|
+
- [Agent Install Guide](agent-install.md) -- Short redirect covering the install shape (binary, `akm setup --yes`, a source, an index, `akm hints`); for the exact no-prompts command sequence, see the [headless install recipe](../guides/recipes/headless-install.md)
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
`akm` is a portable capability library agents install and configure without
|
|
4
4
|
interactive prompts: install the binary, run `akm setup --yes`, add a source,
|
|
5
|
-
build the index, then expose `akm
|
|
5
|
+
build the index, then expose `akm hints` output to the agent so it
|
|
6
6
|
knows how to search and load bundle content. Configuration lives in
|
|
7
7
|
top-level keys validated against the config schema, and semantic search can
|
|
8
8
|
run on local embeddings or a remote endpoint depending on what the host
|
|
@@ -101,23 +101,21 @@ interface BundleAdapter {
|
|
|
101
101
|
// reading authored bytes or walking the bundle.
|
|
102
102
|
readCandidates?(c: BundleComponent, conceptId: string): Array<{ path: string; conceptId: string }>;
|
|
103
103
|
|
|
104
|
-
// OPTIONAL —
|
|
105
|
-
placeNew?(c: BundleComponent, conceptId: string): string; // where a new item would live
|
|
104
|
+
// OPTIONAL — discovery.
|
|
106
105
|
directoryList?(c: BundleComponent): string[]; // owned dirs; feeds git exact-path staging
|
|
107
106
|
looksLikeRoot?(root: string): boolean; // install-time probe
|
|
108
107
|
}
|
|
109
108
|
```
|
|
110
109
|
|
|
111
110
|
`recognize` and `validate` are required on every adapter; `index`,
|
|
112
|
-
`affectedItems`, `readCandidates`, `
|
|
111
|
+
`affectedItems`, `readCandidates`, `directoryList`, and
|
|
113
112
|
`looksLikeRoot` are optional capability methods. An adapter overriding `index()` must keep
|
|
114
113
|
`recognize()` coherent with it (conformance: `index()` == fold of
|
|
115
114
|
`recognize()` over the core walk) or declare component-level
|
|
116
115
|
incrementality.
|
|
117
116
|
|
|
118
|
-
`readCandidates` is intentionally distinct from
|
|
119
|
-
|
|
120
|
-
write-normalization policy. Read candidates preserve canonical directory
|
|
117
|
+
`readCandidates` is intentionally distinct from `extensions`: extensions are
|
|
118
|
+
non-exhaustive walk hints. Read candidates preserve canonical directory
|
|
121
119
|
manifests (for example `skills/<name>/SKILL.md`) so lookup, index-backed show, and workflow runtime share one
|
|
122
120
|
physical-owner decision. Each candidate carries its canonical concept identity,
|
|
123
121
|
so a byte-denying ownership probe never has to infer identity from the query.
|
|
@@ -145,30 +143,21 @@ concern.
|
|
|
145
143
|
|
|
146
144
|
---
|
|
147
145
|
|
|
148
|
-
##
|
|
146
|
+
## Placement
|
|
149
147
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
`
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
Placement for every existing bundle is already correct today — this is a
|
|
159
|
-
deliberately sequenced routing change, not unfinished behavior. It's
|
|
160
|
-
scoped out of 0.9.0 by
|
|
161
|
-
[D12](../architecture/specs/0.9.0-decisions.md#d12--bundleadapterplacenew-stays-unwired-until-010)
|
|
162
|
-
and deferred to 0.10; nothing user-visible changes in 0.10 for this alone.
|
|
163
|
-
See [STABILITY.md](../../STABILITY.md) ("On the horizon") for the
|
|
164
|
-
up-to-date status if this changes.
|
|
148
|
+
Adapters do not place new items. AKM-native writes resolve through AKM's own
|
|
149
|
+
flat type→directory placement table (`src/core/asset/asset-placement.ts`'s
|
|
150
|
+
`PLACEMENT_SPECS`), which is correct for akm-layout bundles — the only bundles
|
|
151
|
+
the write commands touch in practice. A per-adapter `placeNew()` existed
|
|
152
|
+
through 0.9 but nothing called it, and it was removed in 0.10. Supporting
|
|
153
|
+
writable bundles in another layout would need it back: see
|
|
154
|
+
[#1163](https://github.com/itlackey/akm/issues/1163).
|
|
165
155
|
|
|
166
156
|
---
|
|
167
157
|
|
|
168
158
|
## Write allowlists
|
|
169
159
|
|
|
170
|
-
|
|
171
|
-
write commands are allowed to touch at all** — that's decided today by a
|
|
160
|
+
Which bundles AKM-native write commands are allowed to touch at all is decided by a
|
|
172
161
|
small allowlist check, `assertAkmAssetWrite` (`src/core/write-source.ts`),
|
|
173
162
|
run before any write command touches the filesystem. It compares the target
|
|
174
163
|
bundle's detected (or configured) adapter id against an allowlist and throws
|
|
@@ -181,12 +170,10 @@ The default allowlist is `["akm"]`. Two command families widen it explicitly:
|
|
|
181
170
|
| `akm env create` / `env remove` / `secret set` | `dotenv` |
|
|
182
171
|
| `akm workflow create` | `akm-workflow` |
|
|
183
172
|
|
|
184
|
-
Every other adapter
|
|
185
|
-
`
|
|
186
|
-
`generic-files`) — is rejected by every AKM-native write command in 0.9.0.
|
|
173
|
+
Every other adapter (`agent-skills`, `claude`, `opencode`, `akm-task`,
|
|
174
|
+
`generic-files`, and so on) is rejected by every AKM-native write command.
|
|
187
175
|
`akm task add` in particular uses the *default* allowlist (`akm` only), so a
|
|
188
|
-
standalone `akm-task` bundle is read-only through AKM-native write commands
|
|
189
|
-
even though `akm-task`'s own `placeNew()` is implemented.
|
|
176
|
+
standalone `akm-task` bundle is read-only through AKM-native write commands.
|
|
190
177
|
|
|
191
178
|
Reading, searching, and `akm lint` are unaffected by this allowlist — only
|
|
192
179
|
creating, editing, or deleting through AKM's own commands is restricted.
|
|
@@ -197,8 +184,7 @@ This allowlist boundary is also where AKM's [execution
|
|
|
197
184
|
boundary](../guides/concepts.md) principle shows up on the write side: AKM
|
|
198
185
|
retrieves and validates every supported format, but it only lets its own
|
|
199
186
|
write commands mutate a narrow, explicitly-declared set of targets — it
|
|
200
|
-
does not widen write access to a format just because
|
|
201
|
-
happens to expose a `placeNew()` implementation.
|
|
187
|
+
does not widen write access to a format just because it can read it.
|
|
202
188
|
|
|
203
189
|
---
|
|
204
190
|
|
|
@@ -260,8 +260,7 @@ schema (`src/core/config/schema/sources-bundles.ts`).
|
|
|
260
260
|
refresh.
|
|
261
261
|
|
|
262
262
|
Write-target resolution (`resolveWriteTarget`) follows: an explicit
|
|
263
|
-
destination flag (`--bundle`
|
|
264
|
-
`import`/env/secret mutations) -> `config.defaultWriteTarget` -> working
|
|
263
|
+
destination flag (`--bundle`) -> `config.defaultWriteTarget` -> working
|
|
265
264
|
bundle (`defaultBundle`) -> `ConfigError`. The resolved target keeps the
|
|
266
265
|
optional configured selector separate from the stable `source.name`: APIs
|
|
267
266
|
that must re-resolve a destination use the selector, while durable refs and
|
|
@@ -299,8 +298,8 @@ whose proposal queue is read or adjudicated; it does not override the proposal's
|
|
|
299
298
|
write destination. Qualified proposals and unqualified proposals created in a
|
|
300
299
|
configured secondary queue record the destination source name and materialized
|
|
301
300
|
root. Diff, accept, and revert use that binding by default and reject an explicit
|
|
302
|
-
`--
|
|
303
|
-
an explicit `--
|
|
301
|
+
`--bundle` that resolves elsewhere. An unbound short proposal requires either
|
|
302
|
+
an explicit `--bundle` or an authenticated `--queue` context; it does not
|
|
304
303
|
inherit a default write target.
|
|
305
304
|
|
|
306
305
|
---
|
|
@@ -442,7 +441,7 @@ An explicit missing or incompatible engine is an error and never falls
|
|
|
442
441
|
through to another configured engine.
|
|
443
442
|
|
|
444
443
|
Task-v3 execution and durable workflow-v4 dispatch use this runtime boundary.
|
|
445
|
-
Markdown
|
|
444
|
+
Markdown workflows compile through source IR v1; new starts freeze
|
|
446
445
|
v4-family `irVersion: 5`. A stored plan that decodes runs whatever release
|
|
447
446
|
froze it; one that does not is marked abandoned and `akm workflow run <ref>`
|
|
448
447
|
starts afresh. Only a plan a newer akm froze is refused, naming the upgrade.
|