akm-cli 0.9.7 → 0.9.8-beta.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 +310 -0
- package/dist/commands/env/env.js +2 -21
- package/dist/commands/health/checks.js +0 -25
- package/dist/commands/health/data-dir-usage.js +165 -0
- package/dist/commands/health/improve-metrics.js +8 -34
- package/dist/commands/health/windows.js +0 -4
- package/dist/commands/health.js +18 -37
- package/dist/commands/improve/consolidate/eligibility.js +11 -5
- package/dist/commands/improve/extract.js +36 -32
- package/dist/commands/improve/memory/memory-belief.js +15 -5
- package/dist/commands/improve/memory/memory-contradiction-detect.js +16 -25
- package/dist/commands/improve/memory/memory-improve.js +7 -19
- package/dist/commands/improve/preparation.js +52 -36
- package/dist/commands/improve/reflect.js +13 -17
- package/dist/commands/lint/base-linter.js +42 -2
- package/dist/commands/lint/index.js +4 -5
- package/dist/commands/migrate/config-extra-params.js +61 -0
- package/dist/commands/migrate/dead-residue.js +113 -0
- package/dist/commands/migrate/stale-txn.js +49 -0
- package/dist/commands/migrate-cli.js +42 -1
- package/dist/commands/proposal/proposal.js +1 -21
- package/dist/commands/proposal/repository.js +0 -4
- package/dist/commands/proposal/validators/proposal-quality-validators.js +0 -6
- package/dist/commands/read/curate.js +4 -18
- package/dist/commands/read/show.js +2 -1
- package/dist/commands/sources/installed-stashes.js +5 -1
- package/dist/commands/sources/self-update.js +38 -1
- package/dist/commands/sources/sources-cli.js +17 -1
- package/dist/commands/tasks/tasks.js +0 -8
- package/dist/commands/url-checker.js +20 -28
- package/dist/commands/workflow-cli.js +11 -61
- package/dist/core/adapter/adapters/agent-skills-adapter.js +2 -6
- package/dist/core/adapter/adapters/akm-adapter.js +2 -3
- package/dist/core/adapter/adapters/akm-lint.js +2 -20
- package/dist/core/adapter/adapters/akm-task-adapter.js +2 -6
- package/dist/core/adapter/adapters/akm-workflow-adapter.js +2 -6
- package/dist/core/adapter/adapters/dotenv-adapter.js +2 -22
- package/dist/core/adapter/adapters/generic-files-adapter.js +3 -9
- package/dist/core/adapter/adapters/llm-wiki-adapter.js +2 -6
- package/dist/core/adapter/adapters/okf-adapter.js +7 -18
- package/dist/core/adapter/adapters/shared.js +2 -0
- package/dist/core/adapter/adapters/tool-dir-shared.js +3 -9
- package/dist/core/adapter/adapters/website-snapshot-adapter.js +2 -6
- package/dist/core/adapter/validate-context.js +1 -3
- package/dist/core/asset/asset-placement.js +1 -3
- package/dist/core/asset/frontmatter.js +96 -0
- package/dist/core/common.js +66 -2
- package/dist/core/config/config-io.js +21 -8
- package/dist/core/config/config-walker.js +6 -10
- package/dist/core/config/config.js +11 -8
- package/dist/core/extra-params.js +17 -13
- package/dist/core/improve-result.js +1 -3
- package/dist/core/json-schema.js +9 -11
- package/dist/core/state/migrations.js +74 -2
- package/dist/core/state-db.js +2 -1
- package/dist/core/warn.js +15 -0
- package/dist/execution/executable-identity.js +1 -3
- package/dist/execution/guarded-source.js +1 -6
- package/dist/indexer/bundle-identity-guard.js +6 -1
- package/dist/indexer/db/graph-db.js +139 -154
- package/dist/indexer/ensure-index.js +11 -19
- package/dist/indexer/graph/graph-boost.js +23 -34
- package/dist/indexer/graph/graph-extraction.js +12 -2
- package/dist/indexer/indexer.js +43 -22
- package/dist/indexer/lookup/adapter-concept-owner.js +12 -9
- package/dist/indexer/passes/dir-staleness.js +30 -9
- package/dist/indexer/passes/memory-inference.js +7 -2
- package/dist/indexer/scan/drain-dir.js +2 -4
- package/dist/indexer/search/db-search.js +3 -3
- package/dist/indexer/search/fts-query.js +10 -15
- package/dist/indexer/search/search-source.js +0 -13
- package/dist/indexer/usage/usage-events.js +9 -1
- package/dist/indexer/walk/walker.js +11 -6
- package/dist/integrations/harnesses/claude/result-extractor.js +30 -15
- package/dist/integrations/harnesses/codex/result-extractor.js +43 -6
- package/dist/integrations/harnesses/copilot/result-extractor.js +39 -12
- package/dist/integrations/harnesses/gemini/result-extractor.js +40 -12
- package/dist/integrations/harnesses/index.js +0 -4
- package/dist/integrations/harnesses/openhands/result-extractor.js +51 -19
- package/dist/integrations/harnesses/pi/result-extractor.js +45 -12
- package/dist/integrations/lockfile.js +0 -14
- package/dist/integrations/session-logs/index.js +0 -81
- package/dist/llm/client.js +0 -12
- package/dist/llm/memory-infer.js +1 -3
- package/dist/llm/usage-telemetry.js +1 -3
- package/dist/registry/create-provider-registry.js +4 -0
- package/dist/registry/factory.js +4 -0
- package/dist/registry/resolve.js +23 -8
- package/dist/runtime.js +0 -12
- package/dist/scripts/akm-migrate-node.js +179 -248
- package/dist/scripts/akm-migrate.js +179 -248
- package/dist/setup/setup.js +6 -12
- package/dist/sources/providers/git-install.js +7 -2
- package/dist/sources/providers/tar-utils.js +1 -7
- package/dist/sources/snapshot-fetchers/content-extract.js +0 -17
- package/dist/storage/managed-db.js +20 -7
- package/dist/storage/repositories/events-repository.js +0 -81
- package/dist/storage/repositories/index-entries-repository.js +24 -28
- package/dist/storage/repositories/index-fts-repository.js +24 -30
- package/dist/storage/repositories/index-meta-repository.js +6 -4
- package/dist/storage/repositories/index-schema.js +16 -1
- package/dist/storage/repositories/index-utility-repository.js +0 -57
- package/dist/storage/repositories/index-vec-repository.js +25 -27
- package/dist/storage/repositories/proposals-repository.js +4 -1
- package/dist/storage/repositories/task-history-repository.js +9 -3
- package/dist/tasks/backends/launchd.js +8 -18
- package/dist/tasks/resolve-akm-bin.js +15 -0
- package/dist/tasks/run/task-history.js +21 -31
- package/dist/tasks/scheduler-sync.js +1 -6
- package/dist/tasks/source/bounded-document.js +1 -14
- package/dist/tasks/source/task-to-v3.js +10 -2
- package/dist/workflows/exec/child-workflow.js +1 -1
- package/dist/workflows/exec/native-executor.js +2 -2
- package/dist/workflows/exec/step-work.js +5 -17
- package/dist/workflows/exec/worktree.js +40 -6
- package/dist/workflows/freeze/task-bindings.js +2 -4
- package/dist/workflows/ir/compile.js +3 -14
- package/dist/workflows/ir/schema-v4.js +4 -6
- package/dist/workflows/ir/schema.js +2 -5
- package/dist/workflows/parser.js +23 -26
- package/dist/workflows/source-files.js +8 -13
- package/dist/workflows/source-ir/schema.js +1 -19
- package/docs/migration/v0.9.0-troubleshooting.md +12 -2
- package/docs/migration/v0.9.1-to-v0.9.2.md +18 -4
- package/docs/reference/cli.md +3 -1
- package/docs/reference/data-and-telemetry.md +1 -0
- package/docs/reference/tasks.md +11 -0
- package/docs/reference/workflow-schema.md +24 -0
- package/package.json +2 -9
- package/dist/workflows/source-ir/compare.js +0 -17
package/dist/workflows/parser.js
CHANGED
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
import { LineCounter, parseDocument } from "yaml";
|
|
34
34
|
import { parseFrontmatterBlock } from "../core/asset/frontmatter.js";
|
|
35
35
|
import { parseMarkdownToc } from "../core/asset/markdown.js";
|
|
36
|
-
import { isContainedRelativePath } from "../core/common.js";
|
|
36
|
+
import { isContainedRelativePath, isRecord } from "../core/common.js";
|
|
37
37
|
import { formatExtraParamsIssue, validateExtraParams } from "../core/extra-params.js";
|
|
38
38
|
import { checkJsonSchemaDefinition, JSON_SCHEMA_SUBSET_SUPPORTED_KEYWORDS } from "../core/json-schema.js";
|
|
39
39
|
import { parseReference } from "./program/expressions.js";
|
|
@@ -165,7 +165,7 @@ export function parseWorkflow(markdown, source) {
|
|
|
165
165
|
};
|
|
166
166
|
if (root === null || root === undefined)
|
|
167
167
|
root = {};
|
|
168
|
-
if (!
|
|
168
|
+
if (!isRecord(root)) {
|
|
169
169
|
return {
|
|
170
170
|
ok: false,
|
|
171
171
|
errors: [{ line: 2, message: `Workflow frontmatter must be a YAML mapping (key: value pairs).` }],
|
|
@@ -355,7 +355,7 @@ function checkEnvelopeFields(ctx, root, fmEndLine) {
|
|
|
355
355
|
checkActorStamp(ctx, root.verified, ["verified"], `"verified"`);
|
|
356
356
|
}
|
|
357
357
|
}
|
|
358
|
-
if (root.provenance !== undefined && !
|
|
358
|
+
if (root.provenance !== undefined && !isRecord(root.provenance)) {
|
|
359
359
|
ctx.err(["provenance"], `Workflow frontmatter "provenance" must be a mapping.`);
|
|
360
360
|
}
|
|
361
361
|
if (root.status !== undefined && (typeof root.status !== "string" || !LIFECYCLE_STATUSES.has(root.status))) {
|
|
@@ -368,7 +368,7 @@ function checkEnvelopeFields(ctx, root, fmEndLine) {
|
|
|
368
368
|
function checkActorStamp(ctx, value, path, label) {
|
|
369
369
|
if (value === undefined)
|
|
370
370
|
return;
|
|
371
|
-
if (!
|
|
371
|
+
if (!isRecord(value)) {
|
|
372
372
|
ctx.err(path, `Workflow frontmatter ${label} must be a mapping with a non-empty "by".`);
|
|
373
373
|
return;
|
|
374
374
|
}
|
|
@@ -402,7 +402,7 @@ function checkXrefs(ctx, value, fmEndLine) {
|
|
|
402
402
|
function parseParams(ctx, raw) {
|
|
403
403
|
if (raw === undefined)
|
|
404
404
|
return undefined;
|
|
405
|
-
if (!
|
|
405
|
+
if (!isRecord(raw)) {
|
|
406
406
|
ctx.err(["params"], `"params" must be a mapping of param name to a JSON Schema object (e.g. changed_files: { type: array }).`);
|
|
407
407
|
return undefined;
|
|
408
408
|
}
|
|
@@ -412,7 +412,7 @@ function parseParams(ctx, raw) {
|
|
|
412
412
|
ctx.err(["params", paramName], `Param name "${paramName}" is invalid. Use letters, digits, and underscores, starting with a letter or underscore, so "params.${paramName}" can address it.`);
|
|
413
413
|
continue;
|
|
414
414
|
}
|
|
415
|
-
if (!
|
|
415
|
+
if (!isRecord(value)) {
|
|
416
416
|
ctx.err(["params", paramName], `Param "${paramName}" must be a JSON Schema object (e.g. { type: string }).`);
|
|
417
417
|
continue;
|
|
418
418
|
}
|
|
@@ -441,7 +441,7 @@ function parseParams(ctx, raw) {
|
|
|
441
441
|
function parseOutputs(ctx, raw) {
|
|
442
442
|
if (raw === undefined)
|
|
443
443
|
return undefined;
|
|
444
|
-
if (!
|
|
444
|
+
if (!isRecord(raw)) {
|
|
445
445
|
ctx.err(["outputs"], `"outputs" must be a mapping of output name to { from, schema? } (e.g. report: { from: steps.summarize.output }).`);
|
|
446
446
|
return undefined;
|
|
447
447
|
}
|
|
@@ -453,7 +453,7 @@ function parseOutputs(ctx, raw) {
|
|
|
453
453
|
`underscore, so "steps.<child>.output.${outputName}" can address it.`);
|
|
454
454
|
continue;
|
|
455
455
|
}
|
|
456
|
-
if (!
|
|
456
|
+
if (!isRecord(value)) {
|
|
457
457
|
ctx.err(path, `Output "${outputName}" must be a mapping with "from" (and optional "schema").`);
|
|
458
458
|
continue;
|
|
459
459
|
}
|
|
@@ -474,7 +474,7 @@ function parseOutputs(ctx, raw) {
|
|
|
474
474
|
}
|
|
475
475
|
const entry = { from: value.from };
|
|
476
476
|
if (value.schema !== undefined) {
|
|
477
|
-
if (!
|
|
477
|
+
if (!isRecord(value.schema)) {
|
|
478
478
|
ctx.err([...path, "schema"], `Output "${outputName}" "schema" must be a JSON Schema object.`);
|
|
479
479
|
}
|
|
480
480
|
else {
|
|
@@ -493,7 +493,7 @@ function parseDefaults(ctx, raw) {
|
|
|
493
493
|
if (raw === undefined)
|
|
494
494
|
return undefined;
|
|
495
495
|
const path = ["defaults"];
|
|
496
|
-
if (!
|
|
496
|
+
if (!isRecord(raw)) {
|
|
497
497
|
ctx.err(path, `"defaults" must be a mapping with any of: ${DEFAULTS_KEYS.join(", ")}.`);
|
|
498
498
|
return undefined;
|
|
499
499
|
}
|
|
@@ -525,7 +525,7 @@ function parseBudget(ctx, raw) {
|
|
|
525
525
|
if (raw === undefined)
|
|
526
526
|
return undefined;
|
|
527
527
|
const path = ["budget"];
|
|
528
|
-
if (!
|
|
528
|
+
if (!isRecord(raw)) {
|
|
529
529
|
ctx.err(path, `"budget" must be a mapping with any of: ${BUDGET_KEYS.join(", ")}.`);
|
|
530
530
|
return undefined;
|
|
531
531
|
}
|
|
@@ -558,7 +558,7 @@ function parseSteps(ctx, raw) {
|
|
|
558
558
|
// (including ones that fail their own validation).
|
|
559
559
|
const idIndex = new Map();
|
|
560
560
|
raw.forEach((rawStep, index) => {
|
|
561
|
-
if (
|
|
561
|
+
if (isRecord(rawStep) && typeof rawStep.id === "string" && !idIndex.has(rawStep.id)) {
|
|
562
562
|
idIndex.set(rawStep.id, index);
|
|
563
563
|
}
|
|
564
564
|
});
|
|
@@ -567,7 +567,7 @@ function parseSteps(ctx, raw) {
|
|
|
567
567
|
const routeChecks = [];
|
|
568
568
|
raw.forEach((rawStep, index) => {
|
|
569
569
|
const path = ["steps", index];
|
|
570
|
-
if (!
|
|
570
|
+
if (!isRecord(rawStep)) {
|
|
571
571
|
ctx.err(path, `Step ${index + 1} must be a mapping with an "id".`);
|
|
572
572
|
return;
|
|
573
573
|
}
|
|
@@ -655,7 +655,7 @@ function parseSteps(ctx, raw) {
|
|
|
655
655
|
// Step blocks
|
|
656
656
|
// ---------------------------------------------------------------------------
|
|
657
657
|
function parseUnit(ctx, raw, path, stepLabel) {
|
|
658
|
-
if (!
|
|
658
|
+
if (!isRecord(raw)) {
|
|
659
659
|
ctx.err(path, `${stepLabel} "unit" must be a mapping (a dispatch-override bag).`);
|
|
660
660
|
return undefined;
|
|
661
661
|
}
|
|
@@ -733,7 +733,7 @@ function parseUnit(ctx, raw, path, stepLabel) {
|
|
|
733
733
|
* keeps that decision visible in the frontmatter diff.
|
|
734
734
|
*/
|
|
735
735
|
function parseExec(ctx, raw, path, stepLabel) {
|
|
736
|
-
if (!
|
|
736
|
+
if (!isRecord(raw)) {
|
|
737
737
|
ctx.err(path, `${stepLabel} "exec" must be a mapping with a "command" argv list.`);
|
|
738
738
|
return undefined;
|
|
739
739
|
}
|
|
@@ -832,7 +832,7 @@ function parseExecCwd(ctx, raw, path, stepLabel) {
|
|
|
832
832
|
return value;
|
|
833
833
|
}
|
|
834
834
|
function parseMap(ctx, raw, path, stepLabel) {
|
|
835
|
-
if (!
|
|
835
|
+
if (!isRecord(raw)) {
|
|
836
836
|
ctx.err(path, `${stepLabel} "map" must be a mapping with an "over" key.`);
|
|
837
837
|
return undefined;
|
|
838
838
|
}
|
|
@@ -866,7 +866,7 @@ function parseMap(ctx, raw, path, stepLabel) {
|
|
|
866
866
|
return map;
|
|
867
867
|
}
|
|
868
868
|
function parseRoute(ctx, raw, path, stepLabel, stepIndex, routeChecks) {
|
|
869
|
-
if (!
|
|
869
|
+
if (!isRecord(raw)) {
|
|
870
870
|
ctx.err(path, `${stepLabel} "route" must be a mapping with "input" and "when" keys.`);
|
|
871
871
|
return undefined;
|
|
872
872
|
}
|
|
@@ -888,7 +888,7 @@ function parseRoute(ctx, raw, path, stepLabel, stepIndex, routeChecks) {
|
|
|
888
888
|
const seenMatches = new Map();
|
|
889
889
|
raw.when.forEach((branch, i) => {
|
|
890
890
|
const branchPath = [...whenPath, i];
|
|
891
|
-
if (!
|
|
891
|
+
if (!isRecord(branch)) {
|
|
892
892
|
ctx.err(branchPath, `${stepLabel} "when[${i}]" must be a mapping: { match, step }.`);
|
|
893
893
|
return;
|
|
894
894
|
}
|
|
@@ -967,7 +967,7 @@ function parseInputs(ctx, raw, path, stepLabel) {
|
|
|
967
967
|
return out.length > 0 ? out : undefined;
|
|
968
968
|
}
|
|
969
969
|
function parseGate(ctx, raw, path, stepLabel) {
|
|
970
|
-
if (!
|
|
970
|
+
if (!isRecord(raw)) {
|
|
971
971
|
ctx.err(path, `${stepLabel} "gate" must be a mapping with any of: ${GATE_KEYS.join(", ")}.`);
|
|
972
972
|
return undefined;
|
|
973
973
|
}
|
|
@@ -1009,7 +1009,7 @@ function parseEngineName(ctx, raw, path, label) {
|
|
|
1009
1009
|
function parseRetry(ctx, raw, path, stepLabel) {
|
|
1010
1010
|
if (raw === undefined)
|
|
1011
1011
|
return undefined;
|
|
1012
|
-
if (!
|
|
1012
|
+
if (!isRecord(raw)) {
|
|
1013
1013
|
ctx.err(path, `${stepLabel} "retry" must be a mapping: { max: <n>, on: [<failure_reason>, …] }.`);
|
|
1014
1014
|
return undefined;
|
|
1015
1015
|
}
|
|
@@ -1092,7 +1092,7 @@ function parseEnumField(ctx, raw, path, label, allowed) {
|
|
|
1092
1092
|
function parseLlmOverrides(ctx, raw, path, label) {
|
|
1093
1093
|
if (raw === undefined)
|
|
1094
1094
|
return undefined;
|
|
1095
|
-
if (!
|
|
1095
|
+
if (!isRecord(raw)) {
|
|
1096
1096
|
ctx.err(path, `${label} must be a mapping of LLM invocation overrides.`);
|
|
1097
1097
|
return undefined;
|
|
1098
1098
|
}
|
|
@@ -1127,7 +1127,7 @@ function parseLlmOverrides(ctx, raw, path, label) {
|
|
|
1127
1127
|
ctx.err([...path, "supports_json_schema"], `${label}.supports_json_schema must be a boolean.`);
|
|
1128
1128
|
}
|
|
1129
1129
|
if (raw.extra_params !== undefined) {
|
|
1130
|
-
if (!
|
|
1130
|
+
if (!isRecord(raw.extra_params)) {
|
|
1131
1131
|
ctx.err([...path, "extra_params"], `${label}.extra_params must be a JSON object.`);
|
|
1132
1132
|
}
|
|
1133
1133
|
else {
|
|
@@ -1168,7 +1168,7 @@ function parseLlmOverrides(ctx, raw, path, label) {
|
|
|
1168
1168
|
function parseSchemaObject(ctx, raw, path, label) {
|
|
1169
1169
|
if (raw === undefined)
|
|
1170
1170
|
return undefined;
|
|
1171
|
-
if (!
|
|
1171
|
+
if (!isRecord(raw)) {
|
|
1172
1172
|
ctx.err(path, `${label} must be a JSON Schema object (e.g. { type: object, properties: { … } }).`);
|
|
1173
1173
|
return undefined;
|
|
1174
1174
|
}
|
|
@@ -1212,9 +1212,6 @@ function checkUnknownKeys(ctx, obj, path, allowed, label) {
|
|
|
1212
1212
|
// ---------------------------------------------------------------------------
|
|
1213
1213
|
// Utilities
|
|
1214
1214
|
// ---------------------------------------------------------------------------
|
|
1215
|
-
function isPlainRecord(value) {
|
|
1216
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1217
|
-
}
|
|
1218
1215
|
function describeError(cause) {
|
|
1219
1216
|
return cause instanceof Error ? cause.message : String(cause);
|
|
1220
1217
|
}
|
|
@@ -12,20 +12,21 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import fs from "node:fs";
|
|
14
14
|
import path from "node:path";
|
|
15
|
+
import { compareCodePoints, toPosix } from "../core/common.js";
|
|
15
16
|
import { UsageError } from "../core/errors.js";
|
|
16
17
|
import { canonicalizeWorkflowName, WORKFLOW_EXTENSIONS } from "../core/recognition-util.js";
|
|
17
18
|
export class WorkflowSourceRejectionError extends UsageError {
|
|
18
19
|
sourcePaths;
|
|
19
20
|
constructor(message, code, sourcePaths) {
|
|
20
21
|
super(message, code);
|
|
21
|
-
this.sourcePaths = [...sourcePaths].sort(
|
|
22
|
+
this.sourcePaths = [...sourcePaths].sort(compareCodePoints);
|
|
22
23
|
Object.setPrototypeOf(this, new.target.prototype);
|
|
23
24
|
}
|
|
24
25
|
}
|
|
25
26
|
export class WorkflowSourceCollisionError extends WorkflowSourceRejectionError {
|
|
26
27
|
canonicalName;
|
|
27
28
|
constructor(canonicalName, sourcePaths) {
|
|
28
|
-
const sorted = [...sourcePaths].sort(
|
|
29
|
+
const sorted = [...sourcePaths].sort(compareCodePoints);
|
|
29
30
|
super(`Workflow "${canonicalName}" resolves to multiple workflow source files: ${sorted.join(", ")}. ` +
|
|
30
31
|
"A canonical workflow ref must be owned by exactly one recognized .md or .yml source; remove or rename the duplicate.", "RESOURCE_ALREADY_EXISTS", sorted);
|
|
31
32
|
this.name = "WorkflowSourceCollisionError";
|
|
@@ -36,8 +37,8 @@ export class WorkflowSourceCollisionError extends WorkflowSourceRejectionError {
|
|
|
36
37
|
export class WorkflowSourceDomainError extends WorkflowSourceRejectionError {
|
|
37
38
|
canonicalName;
|
|
38
39
|
constructor(canonicalName, sourcePaths, issues, collidingSourcePaths) {
|
|
39
|
-
const sortedPaths = [...sourcePaths].sort(
|
|
40
|
-
const sortedCollisions = [...collidingSourcePaths].sort(
|
|
40
|
+
const sortedPaths = [...sourcePaths].sort(compareCodePoints);
|
|
41
|
+
const sortedCollisions = [...collidingSourcePaths].sort(compareCodePoints);
|
|
41
42
|
const code = issues.some((issue) => issue.code === "PATH_ESCAPE_VIOLATION")
|
|
42
43
|
? "PATH_ESCAPE_VIOLATION"
|
|
43
44
|
: "WORKFLOW_SOURCE_INVALID";
|
|
@@ -169,7 +170,7 @@ export function listWorkflowSourceFiles(sourceRoot, adapterId, name) {
|
|
|
169
170
|
extensionlessStem: entry.name.slice(0, -extension.length),
|
|
170
171
|
});
|
|
171
172
|
}
|
|
172
|
-
candidates.sort((left, right) =>
|
|
173
|
+
candidates.sort((left, right) => compareCodePoints(left.relativePath, right.relativePath));
|
|
173
174
|
const { sources, issues } = inspectWorkflowSourceDomain(candidates, canonicalName, realRoot);
|
|
174
175
|
if (issues.length > 0) {
|
|
175
176
|
throw workflowSourceDomainError(adapterId, canonicalName, candidates, sources, issues);
|
|
@@ -229,9 +230,9 @@ export function resolveWorkflowSourceDomains(sourceRoot, adapterId, sourcePaths)
|
|
|
229
230
|
candidatesByName.set(canonicalName, domain);
|
|
230
231
|
}
|
|
231
232
|
const resolutions = [];
|
|
232
|
-
for (const canonicalName of [...candidatesByName.keys()].sort(
|
|
233
|
+
for (const canonicalName of [...candidatesByName.keys()].sort(compareCodePoints)) {
|
|
233
234
|
const candidates = candidatesByName.get(canonicalName) ?? [];
|
|
234
|
-
candidates.sort((left, right) =>
|
|
235
|
+
candidates.sort((left, right) => compareCodePoints(left.relativePath, right.relativePath));
|
|
235
236
|
const { sources, issues } = inspectWorkflowSourceDomain(candidates, canonicalName, realRoot);
|
|
236
237
|
const sourcePaths = candidates.map((candidate) => candidate.relativePath);
|
|
237
238
|
if (issues.length > 0) {
|
|
@@ -365,9 +366,3 @@ function isWithinResolved(candidate, root) {
|
|
|
365
366
|
const relative = path.relative(root, path.resolve(candidate));
|
|
366
367
|
return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative));
|
|
367
368
|
}
|
|
368
|
-
function toPosix(value) {
|
|
369
|
-
return value.replaceAll("\\", "/");
|
|
370
|
-
}
|
|
371
|
-
function comparePaths(left, right) {
|
|
372
|
-
return left < right ? -1 : left > right ? 1 : 0;
|
|
373
|
-
}
|
|
@@ -10,12 +10,12 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { types as utilTypes } from "node:util";
|
|
12
12
|
import { bundleRefToString, parseBundleRef } from "../../core/asset/asset-ref.js";
|
|
13
|
+
import { compareCodePoints, wellFormedUnicode } from "../../core/common.js";
|
|
13
14
|
import { validateExtraParams } from "../../core/extra-params.js";
|
|
14
15
|
import { checkJsonSchemaDefinition } from "../../core/json-schema.js";
|
|
15
16
|
import { parseReference } from "../program/expressions.js";
|
|
16
17
|
import { PROGRAM_RETRY_REASONS } from "../program/schema.js";
|
|
17
18
|
import { jsonBytes, WORKFLOW_ENGINE_NAME_PATTERN, WORKFLOW_ENV_VAR_NAME_PATTERN, WORKFLOW_MAX_CONCURRENCY, WORKFLOW_MAX_ENGINE_NAME_LENGTH, WORKFLOW_MAX_EXTRA_PARAMS_BYTES, WORKFLOW_MAX_SCHEMA_BYTES, WORKFLOW_MAX_TIMEOUT_MS, } from "../resource-limits.js";
|
|
18
|
-
import { compareWorkflowSourceCodePoints } from "./compare.js";
|
|
19
19
|
import { canonicalizeWorkflowCron, canonicalizeWorkflowRun, canonicalizeWorkflowWorkingDirectory, classifyWorkflowStepUses, rejectNulInArgv, validateWorkflowBuiltinCommand, WorkflowSourceSemanticError, } from "./semantics.js";
|
|
20
20
|
export const WORKFLOW_SOURCE_IR_VERSION = 1;
|
|
21
21
|
export const WORKFLOW_SOURCE_IR_MAX_BYTES = 2 * 1024 * 1024;
|
|
@@ -269,9 +269,6 @@ function validateStep(value, jobId, index, stepIds, options) {
|
|
|
269
269
|
extensions(step.extensions, `step ${id} extensions`);
|
|
270
270
|
span(step.source, `step ${id} source`);
|
|
271
271
|
}
|
|
272
|
-
function compareCodePoints(left, right) {
|
|
273
|
-
return compareWorkflowSourceCodePoints(left, right);
|
|
274
|
-
}
|
|
275
272
|
function validateExec(value, location, options) {
|
|
276
273
|
if (value === undefined)
|
|
277
274
|
return;
|
|
@@ -692,21 +689,6 @@ function countJsonBytes(state, token) {
|
|
|
692
689
|
if (state.bytes > WORKFLOW_SOURCE_IR_MAX_BYTES)
|
|
693
690
|
fail("source IR exceeds the byte limit");
|
|
694
691
|
}
|
|
695
|
-
function wellFormedUnicode(value) {
|
|
696
|
-
for (let index = 0; index < value.length; index++) {
|
|
697
|
-
const code = value.charCodeAt(index);
|
|
698
|
-
if (code >= 0xd800 && code <= 0xdbff) {
|
|
699
|
-
const next = value.charCodeAt(index + 1);
|
|
700
|
-
if (!(next >= 0xdc00 && next <= 0xdfff))
|
|
701
|
-
return false;
|
|
702
|
-
index++;
|
|
703
|
-
}
|
|
704
|
-
else if (code >= 0xdc00 && code <= 0xdfff) {
|
|
705
|
-
return false;
|
|
706
|
-
}
|
|
707
|
-
}
|
|
708
|
-
return true;
|
|
709
|
-
}
|
|
710
692
|
function record(value, location) {
|
|
711
693
|
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
712
694
|
fail(`${location} must be an object`);
|
|
@@ -24,7 +24,17 @@ before that migration and tells you to run:
|
|
|
24
24
|
akm upgrade --force
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
Where akm cannot reinstall itself — a container that ships the CLI globally,
|
|
28
|
+
an unprivileged runtime user — that command fails at its install step
|
|
29
|
+
(`EACCES` on the global module directory) before it ever reaches the
|
|
30
|
+
migration. Use the install-free route instead:
|
|
31
|
+
|
|
32
|
+
```sh
|
|
33
|
+
akm upgrade --state-only
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Both admit migration 018 and both take the same verified safety copy described
|
|
37
|
+
below; `--state-only` simply skips the executable replacement. Immediately before
|
|
28
38
|
the migration, AKM takes a SQLite writer-exclusion lock, rechecks the exact
|
|
29
39
|
ledger, and writes a consistent SQLite snapshot beside the database as
|
|
30
40
|
`state.db.pre-018-drop-dead-lane-schema.<UTC-digits>.<UUID>.bak`. The randomized
|
|
@@ -41,7 +51,7 @@ general storage migrator.
|
|
|
41
51
|
|
|
42
52
|
An existing database with no applied migration IDs is never treated as a fresh
|
|
43
53
|
install, whether its `schema_migrations` table is absent or empty. Ordinary
|
|
44
|
-
commands reject it without writing. The explicit `akm upgrade --force` path
|
|
54
|
+
commands reject it without writing. The explicit `akm upgrade --force` (or `--state-only`) path
|
|
45
55
|
takes and verifies a descriptor-bound snapshot named
|
|
46
56
|
`state.db.pre-001-initial-schema.<UTC-digits>.<UUID>.bak` before it creates the
|
|
47
57
|
ledger or applies any migration from 001 through 022. A truly new database is
|
|
@@ -212,10 +212,24 @@ command: [node, scripts/release.js, "--exact value"]
|
|
|
212
212
|
|
|
213
213
|
Shell-sensitive strings — assignments, shell builtins, reserved words, and
|
|
214
214
|
similar constructs — are also blocked when translating them would invent
|
|
215
|
-
shell semantics. The original bytes remain untouched
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
215
|
+
shell semantics. The original bytes remain untouched, and `akm migrate
|
|
216
|
+
status`/`apply` names the block as `argv-array-has-no-portable-shell-string`
|
|
217
|
+
(or a sibling shell-safety reason) rather than guessing: this is manual
|
|
218
|
+
conversion, not a case the migrator can be re-run to fix. Rewrite the file
|
|
219
|
+
by hand using this field mapping:
|
|
220
|
+
|
|
221
|
+
| v2 | v4 |
|
|
222
|
+
|---|---|
|
|
223
|
+
| `command:` (array, argv style) | `run:` (string) plus `shell:` |
|
|
224
|
+
| `timeoutMs:` | `timeout:` |
|
|
225
|
+
| `enabled:` (document level) | removed — use `schedule:` as a list of `{cron, enabled}` entries |
|
|
226
|
+
| `schedule:` (cron string) | still accepted as a bare string, or as the list form above |
|
|
227
|
+
|
|
228
|
+
validate it, and rerun the preview. You can either write the replacement
|
|
229
|
+
directly as `version: 4` (generation 1 then reports it `already-v4` and
|
|
230
|
+
leaves it alone) or as a `version: 3` `run:` string, letting the second
|
|
231
|
+
generation carry it the rest of the way to v4 in the same `akm migrate
|
|
232
|
+
apply` run.
|
|
219
233
|
|
|
220
234
|
### Migrating task v3 to task source v4
|
|
221
235
|
|
package/docs/reference/cli.md
CHANGED
|
@@ -582,7 +582,7 @@ Subcommands:
|
|
|
582
582
|
| `list` | List workflow runs (optionally filtered by `--ref`; `--active` shows only `status=active` runs, excluding `blocked`/`failed`/`completed`). Child workflow runs are excluded unless `--children` is passed |
|
|
583
583
|
| `resume <run-id>` | Flip a `blocked` or `failed` run back to `active`. Completed runs cannot be resumed |
|
|
584
584
|
| `abandon <run-id>` | Mark a run failed so it stops counting as active (`resume` can reopen it) |
|
|
585
|
-
| `plan <ref>` | **Evolving.** Compile and freeze a workflow WITHOUT publishing a run: the canonical step graph, per-step frozen target kinds, task/child expansion, input bindings, source read set, and lowering notices — zero durable writes.
|
|
585
|
+
| `plan <ref>` | **Evolving.** Compile and freeze a workflow WITHOUT publishing a run: the canonical step graph, per-step frozen target kinds, task/child expansion, input bindings, source read set, and lowering notices — zero durable writes. Returns the full JSON envelope by default, like every other command; pass `--format text` for a human-readable summary |
|
|
586
586
|
|
|
587
587
|
The public `workflow start`, `next`, and `complete` lifecycle was removed in
|
|
588
588
|
0.9, along with the experimental `brief`/`report` external-driver protocol.
|
|
@@ -1022,12 +1022,14 @@ computed, with a 256 MiB binary limit. Release/checksum metadata is capped at
|
|
|
1022
1022
|
akm upgrade # Download and replace the running binary
|
|
1023
1023
|
akm upgrade --check # Check for updates without installing
|
|
1024
1024
|
akm upgrade --force # Force upgrade even if already on latest
|
|
1025
|
+
akm upgrade --state-only # Apply pending state.db migrations; install nothing
|
|
1025
1026
|
```
|
|
1026
1027
|
|
|
1027
1028
|
| Flag | Description |
|
|
1028
1029
|
| --- | --- |
|
|
1029
1030
|
| `--check` | Check for updates without installing |
|
|
1030
1031
|
| `--force` | Force upgrade even if on latest version |
|
|
1032
|
+
| `--state-only` | Apply pending `state.db` migrations without installing a new akm. For installs that cannot rewrite their own binary — a container shipping akm globally, an unprivileged runtime user — where the install step would fail `EACCES` before the migration could run. Takes the same verified safety copy as `--force`; it changes who may request the migration, not what it does. |
|
|
1031
1033
|
| `--skip-post-upgrade` | Skip the post-upgrade index rebuild |
|
|
1032
1034
|
|
|
1033
1035
|
Checksum verification is not optional and has no flag. If a release's
|
|
@@ -60,6 +60,7 @@ Override: set `AKM_CONFIG_DIR` or `XDG_CONFIG_HOME`.
|
|
|
60
60
|
| `state.db` | Events, local usage telemetry, proposals, task history, improve run results, and workflow run state/history (the former `workflow.db` was folded in during the 0.9.0 cutover) | **No** — deletes event/usage logs, proposal queue, improve history, and workflow run history |
|
|
61
61
|
| `logs.db` | Structured, high-volume task/run log lines (`{ts, task_id, run_id, stream, level, line}`), joined to `state.db`'s `task_history` rows by `task_id@started_at`. Kept separate from `state.db` because log lines are append-only and freely purgeable, unlike durable state | Yes — log lines are regenerable per run; deleting loses historical run output only |
|
|
62
62
|
| `akm.lock` | Inter-process write lock | Yes — recreated automatically |
|
|
63
|
+
| `backups/task-v3/`, `backups/task-v4/` | Copies of task files taken by `akm migrate apply` before it rewrites them, one timestamped directory per run; the five most recent per generation are kept (#897) | Yes — once the migrated tasks are verified |
|
|
63
64
|
| `akm.lock.lck` | Lock write sentinel | Yes — recreated automatically |
|
|
64
65
|
|
|
65
66
|
Override: set `AKM_DATA_DIR` or `XDG_DATA_HOME`.
|
package/docs/reference/tasks.md
CHANGED
|
@@ -325,6 +325,17 @@ for both generations combined. Resolve every blocked file manually, then
|
|
|
325
325
|
preview again. Apply validates a complete replacement before writing and
|
|
326
326
|
backs up each original immediately before replacement.
|
|
327
327
|
|
|
328
|
+
Common v2 → v3 blocked reasons and what to do about each — these need a
|
|
329
|
+
hand-authored replacement, not a re-run; see [the 0.9.1 to 0.9.2 migration
|
|
330
|
+
guide](../migration/v0.9.1-to-v0.9.2.md#v2--v3-blocked-cases) for the full v2
|
|
331
|
+
to v4 field mapping (`command:` array → `run:` + `shell:`, `timeoutMs:` →
|
|
332
|
+
`timeout:`, document-level `enabled:` → per-`schedule:`-entry `enabled`):
|
|
333
|
+
|
|
334
|
+
| Reason | Meaning | Fix |
|
|
335
|
+
|---|---|---|
|
|
336
|
+
| `argv-array-has-no-portable-shell-string` | The task's `command:` is an argv array; no single shell string is provably equivalent. | Rewrite the file by hand — a `run:` string plus `shell:` — using the field mapping above. |
|
|
337
|
+
| `shell-quoting-changes-v2-whitespace-split-semantics`, `shell-operators-change-v2-literal-argv-semantics`, `shell-command-resolution-changes-v2-literal-argv-semantics` | The `command:` string contains quoting, shell operators, or a bare executable name whose v2 argv-exec behavior a v3 `run:` (host-shell) invocation cannot reproduce unambiguously. | Review the command's intended shell semantics and author the v3/v4 `run:`/`shell:` fields by hand. |
|
|
338
|
+
|
|
328
339
|
Common v3 → v4 blocked reasons and what to do about each:
|
|
329
340
|
|
|
330
341
|
| Reason | Meaning | Fix |
|
|
@@ -572,6 +572,30 @@ that declares an `output` schema is unaffected — an empty response is not
|
|
|
572
572
|
valid JSON, so it fails as a parse error and can never satisfy a schema as a
|
|
573
573
|
silent `null`.
|
|
574
574
|
|
|
575
|
+
### When a step declares no `output:` schema
|
|
576
|
+
|
|
577
|
+
A step-level `output:` schema is **optional**, and omitting it is a supported
|
|
578
|
+
mode, not a deficiency. The step artifact is then exactly the value described
|
|
579
|
+
above — the unit's result for a solo step, the collected array for a `map`
|
|
580
|
+
step — carried untyped and never validated.
|
|
581
|
+
|
|
582
|
+
The shape is stable either way: it does not depend on whether a schema is
|
|
583
|
+
present, and it does not vary between a live run and a resume. `steps.<id>.output`
|
|
584
|
+
resolves the same in both cases, so downstream references work with or without
|
|
585
|
+
a schema.
|
|
586
|
+
|
|
587
|
+
What a schema adds is **enforcement**, not shape. With one, the promoted
|
|
588
|
+
artifact is checked against it and the step fails on mismatch; without one, a
|
|
589
|
+
downstream reference to a field the artifact happens not to carry surfaces at
|
|
590
|
+
resolution time instead (`steps.X.output is not an object — cannot resolve
|
|
591
|
+
property "y"`). Declare `output:` when you want that mismatch caught at the
|
|
592
|
+
producing step rather than at the consuming one.
|
|
593
|
+
|
|
594
|
+
akm does not warn about a missing step `output:` schema. It briefly did, and
|
|
595
|
+
the advisory was removed: it fired on essentially every step, could not be
|
|
596
|
+
enforced (advisories never reach `--fail-on-flagged`), and flagged a state
|
|
597
|
+
that is legitimate.
|
|
598
|
+
|
|
575
599
|
## Workflow outputs
|
|
576
600
|
|
|
577
601
|
A workflow can declare a run-level export: the values a **completed run**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "akm-cli",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.8-beta.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "akm (Agent Knowledge Manager) — a portable, local-first capability library for AI agents. Discover, load, share, and improve reusable skills, scripts, workflows, and knowledge across any shell-capable coding agent, including Claude Code, OpenCode, and Cursor.",
|
|
6
6
|
"keywords": [
|
|
@@ -77,17 +77,10 @@
|
|
|
77
77
|
"test:integration": "bash scripts/test-integration.sh",
|
|
78
78
|
"test:node-smoke": "bun scripts/node-smoke.ts",
|
|
79
79
|
"test:node-compat": "AKM_NODE_COMPAT_TESTS=1 bun test --timeout=120000 tests/integration/node-compat.test.ts",
|
|
80
|
-
"test:time": "bun scripts/test-timing-report.ts",
|
|
81
|
-
"lint:isolation": "bun scripts/lint-tests-isolation.ts",
|
|
82
80
|
"lint:doc-examples": "bun scripts/lint-doc-examples.ts",
|
|
83
|
-
"lint:active-docs-terminology": "bun scripts/lint-active-docs-terminology.ts",
|
|
84
|
-
"lint:devto-posts": "bun scripts/lint-devto-posts.ts",
|
|
85
|
-
"lint:devto-posts:fix": "bun scripts/lint-devto-posts.ts --fix",
|
|
86
81
|
"publish:devto": "npx -y @sinedied/devto-cli push \"docs/posts/**/*.md\" --token \"$DEVTO_TOKEN\" --repo \"$GITHUB_REPOSITORY\" --branch \"${GITHUB_REF_NAME:-main}\" --reconcile",
|
|
87
82
|
"release:check": "./tests/release-check.sh",
|
|
88
|
-
"lint": "bunx biome check src/ tests/ scripts/ && bun scripts/lint-
|
|
89
|
-
"lint:runtime-boundary": "bun scripts/lint-runtime-boundary.ts",
|
|
90
|
-
"lint:tests-isolation": "bun scripts/lint-tests-isolation.ts",
|
|
83
|
+
"lint": "bunx biome check src/ tests/ scripts/ && bun scripts/lint-secret-resolver-boundary.ts && bun scripts/lint-shipped-assets.ts && bun scripts/lint-doc-examples.ts",
|
|
91
84
|
"lint:fix": "bunx biome check --write src/ tests/ scripts/",
|
|
92
85
|
"format": "bunx biome format --write src/ tests/ scripts/",
|
|
93
86
|
"prepublishOnly": "cp .github/README.npm.md README.md && bun run build",
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
-
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
-
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
-
/**
|
|
5
|
-
* Locale-independent code-point ordering, shared by the workflow source-IR
|
|
6
|
-
* lane (job `needs` canonicalization) and `akm lint` (name sorting).
|
|
7
|
-
*
|
|
8
|
-
* Split out of the deleted `source-ir/ordering.ts` (P4 §3.3, docs/plans/specs/
|
|
9
|
-
* p4-deletions-closeout.md): that file's other export,
|
|
10
|
-
* `canonicalTopologicalJobs`, existed only to order MULTIPLE ready jobs —
|
|
11
|
-
* moot once the adapter confines a workflow source to exactly one job. This
|
|
12
|
-
* comparator has an unrelated consumer (`src/commands/lint/index.ts`) and
|
|
13
|
-
* survives on its own.
|
|
14
|
-
*/
|
|
15
|
-
export function compareWorkflowSourceCodePoints(left, right) {
|
|
16
|
-
return left < right ? -1 : left > right ? 1 : 0;
|
|
17
|
-
}
|