akm-cli 0.9.5 → 0.9.6

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.
Files changed (105) hide show
  1. package/CHANGELOG.md +105 -0
  2. package/dist/commands/env/env-cli.js +1 -2
  3. package/dist/commands/env/secret-cli.js +1 -5
  4. package/dist/commands/feedback-cli.js +0 -4
  5. package/dist/commands/health/checks.js +0 -32
  6. package/dist/commands/health/surfaces.js +2 -2
  7. package/dist/commands/health.js +6 -15
  8. package/dist/commands/improve/autonomy-gate.js +1 -1
  9. package/dist/commands/improve/consolidate.js +25 -26
  10. package/dist/commands/improve/distill.js +2 -2
  11. package/dist/commands/improve/extract.js +8 -1
  12. package/dist/commands/improve/improve.js +3 -5
  13. package/dist/commands/improve/locks.js +13 -5
  14. package/dist/commands/improve/loop-stages.js +8 -9
  15. package/dist/commands/improve/memory/memory-contradiction-detect.js +1 -19
  16. package/dist/commands/improve/outcome-loop.js +0 -6
  17. package/dist/commands/improve/reflect.js +20 -9
  18. package/dist/commands/lint/index.js +4 -6
  19. package/dist/commands/proposal/validators/proposal-quality-validators.js +0 -7
  20. package/dist/commands/sources/bundle-config-ops.js +9 -4
  21. package/dist/commands/sources/info.js +14 -10
  22. package/dist/commands/tasks/tasks.js +12 -16
  23. package/dist/commands/workflow-cli.js +2 -2
  24. package/dist/core/common.js +61 -24
  25. package/dist/core/config/config-io.js +2 -2
  26. package/dist/core/config/config-sources.js +32 -2
  27. package/dist/core/config/config.js +1 -1
  28. package/dist/core/config/schema/engines.js +9 -7
  29. package/dist/core/config/schema/primitives.js +0 -5
  30. package/dist/core/config/schema/search.js +2 -1
  31. package/dist/core/file-lock.js +2 -1
  32. package/dist/core/maintenance-barrier.js +2 -14
  33. package/dist/core/paths.js +0 -3
  34. package/dist/core/redaction.js +2 -2
  35. package/dist/core/spawn-env.js +8 -12
  36. package/dist/core/state/migrations.js +1 -12
  37. package/dist/core/state-db.js +9 -27
  38. package/dist/core/write-source.js +12 -19
  39. package/dist/execution/directory-identity.js +36 -10
  40. package/dist/indexer/graph/graph-boost.js +0 -4
  41. package/dist/indexer/index-writer-lock.js +43 -24
  42. package/dist/indexer/index-written-assets.js +5 -6
  43. package/dist/indexer/indexer.js +2 -39
  44. package/dist/indexer/materialize-embeddings.js +85 -41
  45. package/dist/indexer/search/db-search.js +15 -48
  46. package/dist/indexer/search/ranking-contributors.js +0 -25
  47. package/dist/indexer/search/ranking.js +3 -13
  48. package/dist/integrations/agent/builder-shared.js +0 -25
  49. package/dist/integrations/agent/model-map.js +2 -60
  50. package/dist/integrations/harnesses/aider/agent-builder.js +1 -3
  51. package/dist/integrations/harnesses/amazonq/agent-builder.js +2 -7
  52. package/dist/integrations/harnesses/claude/agent-builder.js +1 -4
  53. package/dist/integrations/harnesses/codex/agent-builder.js +1 -4
  54. package/dist/integrations/harnesses/copilot/agent-builder.js +2 -6
  55. package/dist/integrations/harnesses/gemini/agent-builder.js +2 -7
  56. package/dist/integrations/harnesses/opencode/agent-builder.js +1 -4
  57. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +2 -16
  58. package/dist/integrations/harnesses/openhands/agent-builder.js +1 -3
  59. package/dist/integrations/harnesses/pi/agent-builder.js +2 -4
  60. package/dist/integrations/session-logs/index.js +0 -9
  61. package/dist/llm/client.js +75 -42
  62. package/dist/llm/embedder.js +7 -3
  63. package/dist/llm/embedders/remote.js +141 -42
  64. package/dist/registry/network.js +5 -37
  65. package/dist/runtime.js +2 -10
  66. package/dist/scripts/akm-migrate-node.js +60 -126
  67. package/dist/scripts/akm-migrate.js +60 -126
  68. package/dist/setup/engine-config.js +2 -5
  69. package/dist/setup/registry-stash-loader.js +0 -8
  70. package/dist/setup/setup.js +9 -46
  71. package/dist/setup/steps/connection-shared.js +10 -13
  72. package/dist/sources/providers/git-install.js +1 -1
  73. package/dist/storage/engines/sqlite-migrations.js +20 -1
  74. package/dist/storage/repositories/index-entries-repository.js +0 -15
  75. package/dist/tasks/backends/launchd.js +15 -20
  76. package/dist/tasks/backends/schtasks.js +18 -8
  77. package/dist/tasks/run/run-native-task.js +8 -6
  78. package/dist/tasks/source/bounded-document.js +2 -4
  79. package/dist/tasks/source/task-source-v3-frozen.js +5 -7
  80. package/dist/tasks/source/task-source-v4.js +5 -10
  81. package/dist/tasks/source/task-to-v3.js +5 -10
  82. package/dist/tasks/source/task-to-v4.js +1 -4
  83. package/dist/tasks/source-v3.js +6 -6
  84. package/dist/workflows/exec/native-executor.js +21 -31
  85. package/dist/workflows/exec/run-workflow.js +5 -6
  86. package/dist/workflows/exec/scheduler.js +3 -19
  87. package/dist/workflows/exec/step-work.js +1 -4
  88. package/dist/workflows/exec/unit-dispatch.js +2 -2
  89. package/dist/workflows/exec/worktree.js +1 -13
  90. package/dist/workflows/freeze/targets/child-workflow.js +2 -10
  91. package/dist/workflows/ir/plan-hash.js +4 -6
  92. package/dist/workflows/ir/schema-v4.js +0 -12
  93. package/dist/workflows/ir/schema.js +20 -31
  94. package/dist/workflows/parser.js +11 -52
  95. package/dist/workflows/renderer.js +2 -3
  96. package/dist/workflows/resource-limits.js +11 -41
  97. package/dist/workflows/runtime/runs.js +3 -4
  98. package/dist/workflows/source-ir/schema.js +14 -30
  99. package/dist/workflows/validator.js +1 -7
  100. package/docs/reference/configuration.md +1 -1
  101. package/package.json +1 -1
  102. package/schemas/akm-config.json +4 -23
  103. package/schemas/akm-task.json +1 -2
  104. package/schemas/akm-workflow.json +1 -13
  105. package/dist/indexer/search/semantic-status.js +0 -142
@@ -38,7 +38,7 @@ import { formatExtraParamsIssue, validateExtraParams } from "../core/extra-param
38
38
  import { checkJsonSchemaDefinition, JSON_SCHEMA_SUBSET_SUPPORTED_KEYWORDS } from "../core/json-schema.js";
39
39
  import { parseReference } from "./program/expressions.js";
40
40
  import { PROGRAM_ISOLATION_KINDS, PROGRAM_ON_ERROR, PROGRAM_PARAM_NAME_PATTERN, PROGRAM_REDUCERS, PROGRAM_RETRY_REASONS, PROGRAM_STEP_ID_PATTERN, } from "./program/schema.js";
41
- import { jsonBytes, utf8Bytes, WORKFLOW_ENGINE_NAME_PATTERN, WORKFLOW_ENV_VAR_NAME_PATTERN, WORKFLOW_MAX_CONCURRENCY, WORKFLOW_MAX_ENGINE_NAME_LENGTH, WORKFLOW_MAX_EXEC_ARG_BYTES, WORKFLOW_MAX_EXEC_ARGV, WORKFLOW_MAX_EXEC_CWD_LENGTH, WORKFLOW_MAX_EXEC_PASS_ENV, WORKFLOW_MAX_EXTRA_PARAMS_BYTES, WORKFLOW_MAX_GATE_LOOPS, WORKFLOW_MAX_INPUTS, WORKFLOW_MAX_MAP_EXPANSION, WORKFLOW_MAX_OUTPUTS, WORKFLOW_MAX_PARAMS, WORKFLOW_MAX_RETRIES, WORKFLOW_MAX_ROUTE_BRANCHES, WORKFLOW_MAX_SCHEMA_BYTES, WORKFLOW_MAX_SOURCE_BYTES, WORKFLOW_MAX_STEPS, WORKFLOW_MAX_TIMEOUT_MS, } from "./resource-limits.js";
41
+ import { jsonBytes, utf8Bytes, 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_SOURCE_BYTES, WORKFLOW_MAX_TIMEOUT_MS, } from "./resource-limits.js";
42
42
  import { WORKFLOW_SCHEMA_VERSION, } from "./schema.js";
43
43
  import { runSemanticChecks } from "./validator.js";
44
44
  /** Envelope keys every AKM markdown asset carries ($ref'd from schemas/akm-asset-envelope.json). */
@@ -406,9 +406,6 @@ function parseParams(ctx, 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
  }
409
- if (Object.keys(raw).length > WORKFLOW_MAX_PARAMS) {
410
- ctx.err(["params"], `"params" must contain at most ${WORKFLOW_MAX_PARAMS} entries.`);
411
- }
412
409
  const params = {};
413
410
  for (const [paramName, value] of Object.entries(raw)) {
414
411
  if (!PROGRAM_PARAM_NAME_PATTERN.test(paramName)) {
@@ -448,9 +445,6 @@ function parseOutputs(ctx, raw) {
448
445
  ctx.err(["outputs"], `"outputs" must be a mapping of output name to { from, schema? } (e.g. report: { from: steps.summarize.output }).`);
449
446
  return undefined;
450
447
  }
451
- if (Object.keys(raw).length > WORKFLOW_MAX_OUTPUTS) {
452
- ctx.err(["outputs"], `"outputs" must contain at most ${WORKFLOW_MAX_OUTPUTS} entries.`);
453
- }
454
448
  const outputs = {};
455
449
  for (const [outputName, value] of Object.entries(raw)) {
456
450
  const path = ["outputs", outputName];
@@ -546,14 +540,11 @@ function parseBudget(ctx, raw) {
546
540
  }
547
541
  }
548
542
  if (raw.max_units !== undefined) {
549
- if (typeof raw.max_units === "number" &&
550
- Number.isInteger(raw.max_units) &&
551
- raw.max_units >= 1 &&
552
- raw.max_units <= WORKFLOW_MAX_MAP_EXPANSION) {
543
+ if (typeof raw.max_units === "number" && Number.isInteger(raw.max_units) && raw.max_units >= 1) {
553
544
  budget.maxUnits = raw.max_units;
554
545
  }
555
546
  else {
556
- ctx.err([...path, "max_units"], `"budget.max_units" must be an integer from 1 through ${WORKFLOW_MAX_MAP_EXPANSION}.`);
547
+ ctx.err([...path, "max_units"], `"budget.max_units" must be an integer >= 1.`);
557
548
  }
558
549
  }
559
550
  return Object.keys(budget).length > 0 ? budget : undefined;
@@ -563,9 +554,6 @@ function parseSteps(ctx, raw) {
563
554
  ctx.err(["steps"], `"steps" is required and must be a list with at least one step.`);
564
555
  return [];
565
556
  }
566
- if (raw.length > WORKFLOW_MAX_STEPS) {
567
- ctx.err(["steps"], `"steps" must contain at most ${WORKFLOW_MAX_STEPS} entries.`);
568
- }
569
557
  // First pass: collect ids so route targets can be checked against ALL steps
570
558
  // (including ones that fail their own validation).
571
559
  const idIndex = new Map();
@@ -775,11 +763,6 @@ function parseExecPassEnv(ctx, raw, path, stepLabel) {
775
763
  `akm's own environment, e.g. pass_env: [CARGO_HOME]. Values never appear here — use "env:" bindings for those.`);
776
764
  return undefined;
777
765
  }
778
- if (raw.length > WORKFLOW_MAX_EXEC_PASS_ENV) {
779
- ctx.err(path, `${stepLabel} "exec.pass_env" must have at most ${WORKFLOW_MAX_EXEC_PASS_ENV} entries. A command needing ` +
780
- `more than that should use explicit named "env:" bindings instead.`);
781
- return undefined;
782
- }
783
766
  const names = [];
784
767
  for (const [index, entry] of raw.entries()) {
785
768
  if (typeof entry !== "string" || !WORKFLOW_ENV_VAR_NAME_PATTERN.test(entry)) {
@@ -795,17 +778,13 @@ function parseExecPassEnv(ctx, raw, path, stepLabel) {
795
778
  }
796
779
  return names;
797
780
  }
798
- /** The argv array itself: 1..WORKFLOW_MAX_EXEC_ARGV bounded non-empty strings. */
781
+ /** The argv array itself: a non-empty list of non-empty strings. */
799
782
  function parseExecCommand(ctx, raw, path, stepLabel) {
800
783
  if (!Array.isArray(raw) || raw.length === 0) {
801
784
  ctx.err(path, `${stepLabel} "exec" requires "command": a non-empty argv list, e.g. command: ["bun", "run", "test:unit"]. ` +
802
785
  `A single shell string is not accepted — the command is spawned directly, never through a shell.`);
803
786
  return undefined;
804
787
  }
805
- if (raw.length > WORKFLOW_MAX_EXEC_ARGV) {
806
- ctx.err(path, `${stepLabel} "exec.command" must have at most ${WORKFLOW_MAX_EXEC_ARGV} entries.`);
807
- return undefined;
808
- }
809
788
  const argv = [];
810
789
  for (const [index, entry] of raw.entries()) {
811
790
  if (typeof entry !== "string" || entry === "") {
@@ -816,10 +795,6 @@ function parseExecCommand(ctx, raw, path, stepLabel) {
816
795
  ctx.err([...path, index], `${stepLabel} "exec.command[${index}]" may not contain NUL bytes.`);
817
796
  return undefined;
818
797
  }
819
- if (utf8Bytes(entry) > WORKFLOW_MAX_EXEC_ARG_BYTES) {
820
- ctx.err(path, `${stepLabel} "exec.command[${index}]" exceeds ${WORKFLOW_MAX_EXEC_ARG_BYTES} bytes.`);
821
- return undefined;
822
- }
823
798
  argv.push(entry);
824
799
  }
825
800
  return argv;
@@ -837,10 +812,6 @@ function parseExecCwd(ctx, raw, path, stepLabel) {
837
812
  return undefined;
838
813
  }
839
814
  const value = raw.trim();
840
- if (value.length > WORKFLOW_MAX_EXEC_CWD_LENGTH) {
841
- ctx.err(path, `${stepLabel} "exec.cwd" exceeds ${WORKFLOW_MAX_EXEC_CWD_LENGTH} characters.`);
842
- return undefined;
843
- }
844
815
  if (!isContainedRelativePath(value)) {
845
816
  ctx.err(path, `${stepLabel} "exec.cwd" (${JSON.stringify(value)}) must be a RELATIVE path inside the unit's working ` +
846
817
  `directory — absolute paths, Windows drive letters, "~", and ".." segments are rejected.`);
@@ -876,14 +847,11 @@ function parseMap(ctx, raw, path, stepLabel) {
876
847
  }
877
848
  let concurrency;
878
849
  if (raw.concurrency !== undefined) {
879
- if (typeof raw.concurrency === "number" &&
880
- Number.isInteger(raw.concurrency) &&
881
- raw.concurrency > 0 &&
882
- raw.concurrency <= WORKFLOW_MAX_CONCURRENCY) {
883
- concurrency = raw.concurrency;
850
+ if (typeof raw.concurrency === "number" && Number.isInteger(raw.concurrency) && raw.concurrency > 0) {
851
+ concurrency = Math.min(raw.concurrency, WORKFLOW_MAX_CONCURRENCY);
884
852
  }
885
853
  else {
886
- ctx.err([...path, "concurrency"], `${stepLabel} "concurrency" must be an integer from 1 through ${WORKFLOW_MAX_CONCURRENCY}.`);
854
+ ctx.err([...path, "concurrency"], `${stepLabel} "concurrency" must be a positive integer.`);
887
855
  }
888
856
  }
889
857
  const reducer = parseEnumField(ctx, raw.reducer, [...path, "reducer"], `${stepLabel} "reducer"`, PROGRAM_REDUCERS);
@@ -917,9 +885,6 @@ function parseRoute(ctx, raw, path, stepLabel, stepIndex, routeChecks) {
917
885
  ctx.err(whenPath, `${stepLabel} "route" requires "when": a non-empty list of { match, step } branches (e.g. when: [{ match: pass, step: ship }]).`);
918
886
  }
919
887
  else {
920
- if (raw.when.length > WORKFLOW_MAX_ROUTE_BRANCHES) {
921
- ctx.err(whenPath, `${stepLabel} "when" must contain at most ${WORKFLOW_MAX_ROUTE_BRANCHES} branches.`);
922
- }
923
888
  const seenMatches = new Map();
924
889
  raw.when.forEach((branch, i) => {
925
890
  const branchPath = [...whenPath, i];
@@ -980,9 +945,6 @@ function parseInputs(ctx, raw, path, stepLabel) {
980
945
  ctx.err(path, `${stepLabel} "inputs" must be a non-empty list of reference strings.`);
981
946
  return undefined;
982
947
  }
983
- if (raw.length > WORKFLOW_MAX_INPUTS) {
984
- ctx.err(path, `${stepLabel} "inputs" must contain at most ${WORKFLOW_MAX_INPUTS} entries.`);
985
- }
986
948
  const out = [];
987
949
  // The frozen-plan decoder requires uniqueness (validateStringArray(..., true)),
988
950
  // so duplicates parsed and linted clean and then failed the run with an
@@ -1012,14 +974,11 @@ function parseGate(ctx, raw, path, stepLabel) {
1012
974
  checkUnknownKeys(ctx, raw, path, GATE_KEYS, `${stepLabel} "gate"`);
1013
975
  const gate = {};
1014
976
  if (raw.max_loops !== undefined) {
1015
- if (typeof raw.max_loops === "number" &&
1016
- Number.isInteger(raw.max_loops) &&
1017
- raw.max_loops >= 1 &&
1018
- raw.max_loops <= WORKFLOW_MAX_GATE_LOOPS) {
977
+ if (typeof raw.max_loops === "number" && Number.isInteger(raw.max_loops) && raw.max_loops >= 1) {
1019
978
  gate.maxLoops = raw.max_loops;
1020
979
  }
1021
980
  else {
1022
- ctx.err([...path, "max_loops"], `${stepLabel} "gate.max_loops" must be an integer from 1 through ${WORKFLOW_MAX_GATE_LOOPS}.`);
981
+ ctx.err([...path, "max_loops"], `${stepLabel} "gate.max_loops" must be an integer of at least 1.`);
1023
982
  }
1024
983
  }
1025
984
  return gate;
@@ -1056,8 +1015,8 @@ function parseRetry(ctx, raw, path, stepLabel) {
1056
1015
  }
1057
1016
  checkUnknownKeys(ctx, raw, path, RETRY_KEYS, `${stepLabel} "retry"`);
1058
1017
  let ok = true;
1059
- if (!(typeof raw.max === "number" && Number.isInteger(raw.max) && raw.max >= 0 && raw.max <= WORKFLOW_MAX_RETRIES)) {
1060
- ctx.err([...path, "max"], `${stepLabel} "retry.max" is required and must be an integer from 0 through ${WORKFLOW_MAX_RETRIES}.`);
1018
+ if (!(typeof raw.max === "number" && Number.isInteger(raw.max) && raw.max >= 0)) {
1019
+ ctx.err([...path, "max"], `${stepLabel} "retry.max" is required and must be a non-negative integer.`);
1061
1020
  ok = false;
1062
1021
  }
1063
1022
  const on = [];
@@ -87,9 +87,8 @@ function projectParameters(sourceIr) {
87
87
  * visible in the workflow file `show` is rendering, and (b) pointless to clip:
88
88
  * the whole finding this projection answers is `show` describing something
89
89
  * other than what runs, and a truncated argv is that same bug in miniature.
90
- * The parser bounds it anyway (`WORKFLOW_MAX_EXEC_ARGV` entries of
91
- * `WORKFLOW_MAX_EXEC_ARG_BYTES`), and it is far smaller than the step
92
- * `instructions` this same projection already carries whole.
90
+ * It is far smaller than the step `instructions` this same projection
91
+ * already carries whole.
93
92
  */
94
93
  function summarizeStepOrchestration(step, defaults) {
95
94
  const unit = step.unit;
@@ -4,22 +4,12 @@
4
4
  import { EXECUTION_MAX_TIMEOUT_MS } from "../execution/limits.js";
5
5
  export const WORKFLOW_MAX_PLAN_BYTES = 2 * 1024 * 1024;
6
6
  export const WORKFLOW_MAX_SOURCE_BYTES = 1024 * 1024;
7
- export const WORKFLOW_MAX_STEPS = 256;
8
- export const WORKFLOW_MAX_ENGINES = 64;
9
- export const WORKFLOW_MAX_PARAMS = 128;
10
- export const WORKFLOW_MAX_ROUTE_BRANCHES = 256;
11
7
  export const WORKFLOW_MAX_INSTRUCTION_BYTES = 256 * 1024;
12
8
  export const WORKFLOW_MAX_SCHEMA_BYTES = 256 * 1024;
13
9
  export const WORKFLOW_MAX_EXTRA_PARAMS_BYTES = 64 * 1024;
14
- export const WORKFLOW_MAX_JSON_DEPTH = 64;
15
- export const WORKFLOW_MAX_MAP_EXPANSION = 10_000;
16
- /** Max declared `inputs:` reference strings on one unit/map step. */
17
- export const WORKFLOW_MAX_INPUTS = 64;
18
- /** Max declared `outputs:` entries on one workflow document (P3b, spec §4.1). */
19
- export const WORKFLOW_MAX_OUTPUTS = 64;
20
10
  // ── Dispatch-significant bounds shared across validation layers ──────────────
21
11
  //
22
- // Defined ONCE here so the three enforcement layers cannot drift:
12
+ // Defined ONCE here so the enforcement layers cannot drift:
23
13
  // 1. the parser (`../parser.ts`) — line-anchored authoring-time errors,
24
14
  // 2. the published JSON Schema (`schemas/akm-workflow.json`) — mirrored
25
15
  // `maximum`/`pattern`/`maxLength` values, pinned against these constants
@@ -31,33 +21,12 @@ export const WORKFLOW_MAX_OUTPUTS = 64;
31
21
  // `workflow create` already said the document was fine.
32
22
  /** Max per-step map fan-out concurrency (also the run-level concurrency ceiling). */
33
23
  export const WORKFLOW_MAX_CONCURRENCY = 64;
34
- /** Max evaluator-optimizer gate loops per step. */
35
- export const WORKFLOW_MAX_GATE_LOOPS = 100;
36
- /** Max retry attempts per unit. */
37
- export const WORKFLOW_MAX_RETRIES = 100;
38
24
  /** Max timeout in milliseconds (setTimeout's 32-bit signed ceiling: 2^31-1, ~24.8 days). */
39
25
  export const WORKFLOW_MAX_TIMEOUT_MS = EXECUTION_MAX_TIMEOUT_MS;
40
26
  /** Engine names: lowercase dash-separated runs of letters/digits, starting with a letter. */
41
27
  export const WORKFLOW_ENGINE_NAME_PATTERN = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/;
42
28
  export const WORKFLOW_MAX_ENGINE_NAME_LENGTH = 63;
43
29
  // ── exec (shell) unit bounds ─────────────────────────────────────────────────
44
- /**
45
- * Max entries in an exec unit's `command:` argv array. Generous for a real
46
- * command line, small enough that a corrupted plan cannot ask the OS to spawn
47
- * a megabyte of arguments.
48
- */
49
- export const WORKFLOW_MAX_EXEC_ARGV = 64;
50
- /** Max UTF-8 bytes of ONE argv entry (well under every platform's ARG_MAX per-arg limit). */
51
- export const WORKFLOW_MAX_EXEC_ARG_BYTES = 4096;
52
- /** Max characters of an exec unit's relative `cwd:`. */
53
- export const WORKFLOW_MAX_EXEC_CWD_LENGTH = 1024;
54
- /**
55
- * Max entries in an exec unit's `pass_env:` list.
56
- *
57
- * `pass_env` is bounded; use explicit named `env:` bindings for a larger set.
58
- * New v4 starts do not offer whole-process inheritance.
59
- */
60
- export const WORKFLOW_MAX_EXEC_PASS_ENV = 32;
61
30
  /**
62
31
  * Grammar for an env var NAME in `pass_env:`. Matches the frozen-plan
63
32
  * `envPassthrough` grammar in `ir/schema.ts` so both allowlist surfaces accept
@@ -86,8 +55,7 @@ export const DEFAULT_EXEC_TIMEOUT_MS = 600_000;
86
55
  * ten minutes to do so. What the cap buys is a BOUND where there was none: the
87
56
  * retained prefix is promoted into the unit's outcome text and every outcome is
88
57
  * held until the step reduces, so the worst case is (units in the STEP × this
89
- * cap) rather than unbounded — the step's width, not the in-flight width, is
90
- * what sizes it, up to {@link WORKFLOW_MAX_MAP_EXPANSION}.
58
+ * cap) — the step's width, not the in-flight width, is what sizes it.
91
59
  *
92
60
  * On reaching the cap the reader switches to DRAIN-AND-DISCARD: it keeps
93
61
  * pulling from the pipe (so the child never blocks on backpressure) and stops
@@ -180,11 +148,10 @@ export function clip(text, max) {
180
148
  *
181
149
  * The promoted step artifact (`evidence.output`) is deliberately NOT clipped
182
150
  * when it is built — gates judge the full artifact and downstream
183
- * `steps.<id>.output` references need it intact — but a `collect`
184
- * reducer over a fan-out bounded only by {@link WORKFLOW_MAX_MAP_EXPANSION}
185
- * (10 000 units, each contributing up to a full unit result) would otherwise
186
- * write an unbounded blob into a single SQLite row. Persistence is therefore
187
- * bounded here, at the write boundary, by
151
+ * `steps.<id>.output` references need it intact — but a `collect` reducer
152
+ * over an unbounded fan-out (each unit contributing up to a full unit
153
+ * result) would otherwise write an unbounded blob into a single SQLite row.
154
+ * Persistence is therefore bounded here, at the write boundary, by
188
155
  * `clipStepEvidenceForPersistence` (`runtime/runs.ts`), which replaces
189
156
  * oversized values with an explicitly-marked truncation envelope rather than
190
157
  * silently shortening them.
@@ -213,8 +180,11 @@ export function jsonBytes(value) {
213
180
  // (`src/workflows/ir/schema-v4.ts`'s recursive `decodeChildWorkflowTarget`).
214
181
  // Full design history, including the rejected alternative for the byte cap:
215
182
  // docs/architecture/decisions/0007-workflow-composition-bounds.md.
216
- /** Max workflow composition depth (root = depth 0; a 9th descendant level fails). */
217
- export const WORKFLOW_MAX_COMPOSITION_DEPTH = 8;
183
+ //
184
+ // Composition DEPTH is unbounded — `assertNoCompositionCycle` (freeze/targets/
185
+ // child-workflow.ts) already makes infinite composition mathematically
186
+ // impossible, so a depth ceiling on top of it only bounded how many
187
+ // legitimately distinct workflows an author could nest.
218
188
  /**
219
189
  * Max AGGREGATE canonical-JSON bytes of every embedded child plan in ONE root
220
190
  * freeze (the sum across the whole composition tree, not per child).
@@ -396,10 +396,9 @@ export function isTruncatedEvidence(value) {
396
396
  * `buildEvidence` (exec/step-work.ts) promotes `evidence.output` UNCLIPPED by
397
397
  * design: gates judge the full promoted artifact and the in-memory
398
398
  * {@link StepExecutionResult} carries it to the caller intact. Nothing bounded
399
- * the PERSISTED form, though — a `collect` reducer over a fan-out capped only by
400
- * `WORKFLOW_MAX_MAP_EXPANSION` (10 000 units) can serialize to hundreds of
401
- * megabytes. This is the write boundary, so the bound lives here rather than in
402
- * the shared step-semantics module.
399
+ * the PERSISTED form, though — a `collect` reducer over an unbounded fan-out
400
+ * can serialize to hundreds of megabytes. This is the write boundary, so the
401
+ * bound lives here rather than in the shared step-semantics module.
403
402
  *
404
403
  * Over-cap values are REPLACED (largest top-level entry BY UTF-8 BYTES first —
405
404
  * the unit the cap is measured in — until the row fits) with a
@@ -14,7 +14,7 @@ import { validateExtraParams } from "../../core/extra-params.js";
14
14
  import { checkJsonSchemaDefinition } from "../../core/json-schema.js";
15
15
  import { parseReference } from "../program/expressions.js";
16
16
  import { PROGRAM_RETRY_REASONS } from "../program/schema.js";
17
- import { jsonBytes, utf8Bytes, WORKFLOW_ENGINE_NAME_PATTERN, WORKFLOW_ENV_VAR_NAME_PATTERN, WORKFLOW_MAX_CONCURRENCY, WORKFLOW_MAX_ENGINE_NAME_LENGTH, WORKFLOW_MAX_EXEC_ARG_BYTES, WORKFLOW_MAX_EXEC_ARGV, WORKFLOW_MAX_EXEC_CWD_LENGTH, WORKFLOW_MAX_EXEC_PASS_ENV, WORKFLOW_MAX_EXTRA_PARAMS_BYTES, WORKFLOW_MAX_GATE_LOOPS, WORKFLOW_MAX_INPUTS, WORKFLOW_MAX_MAP_EXPANSION, WORKFLOW_MAX_OUTPUTS, WORKFLOW_MAX_PARAMS, WORKFLOW_MAX_RETRIES, WORKFLOW_MAX_ROUTE_BRANCHES, WORKFLOW_MAX_SCHEMA_BYTES, WORKFLOW_MAX_TIMEOUT_MS, } from "../resource-limits.js";
17
+ 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
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;
@@ -255,7 +255,7 @@ function validateStep(value, jobId, index, stepIds, options) {
255
255
  }
256
256
  validateMap(step.map, `step ${id} map`);
257
257
  validateRoute(step.route, `step ${id} route`);
258
- optionalStringList(step.inputs, `step ${id} inputs`, WORKFLOW_MAX_INPUTS);
258
+ optionalStringList(step.inputs, `step ${id} inputs`, Infinity);
259
259
  if (Array.isArray(step.inputs)) {
260
260
  for (const [inputIndex, input] of step.inputs.entries())
261
261
  validateReference(input, `step ${id} inputs[${inputIndex}]`);
@@ -277,12 +277,7 @@ function validateExec(value, location, options) {
277
277
  return;
278
278
  const exec = record(value, location);
279
279
  keys(exec, ["command", "cwd", "passEnv"], location);
280
- stringList(exec.command, `${location}.command`, WORKFLOW_MAX_EXEC_ARGV, false);
281
- for (const [index, argument] of exec.command.entries()) {
282
- if (utf8Bytes(argument) > WORKFLOW_MAX_EXEC_ARG_BYTES) {
283
- fail(`${location}.command[${index}] exceeds ${WORKFLOW_MAX_EXEC_ARG_BYTES} bytes`);
284
- }
285
- }
280
+ stringList(exec.command, `${location}.command`, Infinity, false);
286
281
  try {
287
282
  rejectNulInArgv(exec.command);
288
283
  }
@@ -291,9 +286,6 @@ function validateExec(value, location, options) {
291
286
  }
292
287
  optionalString(exec.cwd, `${location}.cwd`);
293
288
  if (exec.cwd !== undefined) {
294
- if (exec.cwd.length > WORKFLOW_MAX_EXEC_CWD_LENGTH) {
295
- fail(`${location}.cwd exceeds ${WORKFLOW_MAX_EXEC_CWD_LENGTH} characters`);
296
- }
297
289
  try {
298
290
  exec.cwd = canonicalizeWorkflowWorkingDirectory(exec.cwd, options.workspaceRoot);
299
291
  }
@@ -302,7 +294,7 @@ function validateExec(value, location, options) {
302
294
  }
303
295
  }
304
296
  if (exec.passEnv !== undefined) {
305
- stringList(exec.passEnv, `${location}.passEnv`, WORKFLOW_MAX_EXEC_PASS_ENV, false);
297
+ stringList(exec.passEnv, `${location}.passEnv`, Infinity, false);
306
298
  }
307
299
  if (Array.isArray(exec.passEnv)) {
308
300
  for (const name of exec.passEnv) {
@@ -359,8 +351,8 @@ function validateRetry(value, location) {
359
351
  return;
360
352
  const retry = record(value, location);
361
353
  keys(retry, ["max", "on"], location);
362
- if (!Number.isSafeInteger(retry.max) || retry.max < 0 || retry.max > WORKFLOW_MAX_RETRIES) {
363
- fail(`${location}.max must be an integer from 0 through ${WORKFLOW_MAX_RETRIES}`);
354
+ if (!Number.isSafeInteger(retry.max) || retry.max < 0) {
355
+ fail(`${location}.max must be a non-negative integer`);
364
356
  }
365
357
  stringList(retry.on, `${location}.on`, PROGRAM_RETRY_REASONS.length, false);
366
358
  for (const reason of retry.on) {
@@ -393,10 +385,8 @@ function validateRoute(value, location) {
393
385
  keys(route, ["input", "branches", "defaultStepId"], location);
394
386
  nonEmptyString(route.input, `${location}.input`);
395
387
  validateReference(route.input, `${location}.input`);
396
- if (!Array.isArray(route.branches) ||
397
- route.branches.length === 0 ||
398
- route.branches.length > WORKFLOW_MAX_ROUTE_BRANCHES) {
399
- fail(`${location}.branches must contain 1 through ${WORKFLOW_MAX_ROUTE_BRANCHES} entries`);
388
+ if (!Array.isArray(route.branches) || route.branches.length === 0) {
389
+ fail(`${location}.branches must be a non-empty array`);
400
390
  }
401
391
  const matches = new Set();
402
392
  for (const [index, value] of route.branches.entries()) {
@@ -416,11 +406,8 @@ function validateGate(value, location) {
416
406
  return;
417
407
  const gate = record(value, location);
418
408
  keys(gate, ["maxLoops", "rubric"], location);
419
- if (gate.maxLoops !== undefined &&
420
- (!Number.isSafeInteger(gate.maxLoops) ||
421
- gate.maxLoops < 1 ||
422
- gate.maxLoops > WORKFLOW_MAX_GATE_LOOPS)) {
423
- fail(`${location}.maxLoops must be an integer from 1 through ${WORKFLOW_MAX_GATE_LOOPS}`);
409
+ if (gate.maxLoops !== undefined && (!Number.isSafeInteger(gate.maxLoops) || gate.maxLoops < 1)) {
410
+ fail(`${location}.maxLoops must be an integer of at least 1`);
424
411
  }
425
412
  optionalString(gate.rubric, `${location}.rubric`);
426
413
  }
@@ -453,8 +440,8 @@ function validateParams(value) {
453
440
  if (value === undefined)
454
441
  return;
455
442
  const params = record(value, "params");
456
- if (Object.keys(params).length === 0 || Object.keys(params).length > WORKFLOW_MAX_PARAMS) {
457
- fail(`params must contain 1 through ${WORKFLOW_MAX_PARAMS} entries`);
443
+ if (Object.keys(params).length === 0) {
444
+ fail(`params must contain at least one entry`);
458
445
  }
459
446
  for (const [name, schema] of Object.entries(params)) {
460
447
  if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name))
@@ -472,8 +459,8 @@ function validateOutputs(value) {
472
459
  if (value === undefined)
473
460
  return;
474
461
  const outputs = record(value, "outputs");
475
- if (Object.keys(outputs).length === 0 || Object.keys(outputs).length > WORKFLOW_MAX_OUTPUTS) {
476
- fail(`outputs must contain 1 through ${WORKFLOW_MAX_OUTPUTS} entries`);
462
+ if (Object.keys(outputs).length === 0) {
463
+ fail(`outputs must contain at least one entry`);
477
464
  }
478
465
  for (const [name, declaration] of Object.entries(outputs)) {
479
466
  if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name))
@@ -496,9 +483,6 @@ function validateBudget(value) {
496
483
  fail(`budget.${key} must be a positive integer`);
497
484
  }
498
485
  }
499
- if (typeof budget.maxUnits === "number" && budget.maxUnits > WORKFLOW_MAX_MAP_EXPANSION) {
500
- fail(`budget.maxUnits must be at most ${WORKFLOW_MAX_MAP_EXPANSION}`);
501
- }
502
486
  }
503
487
  function validateLlm(value, location) {
504
488
  if (value === undefined)
@@ -16,7 +16,7 @@
16
16
  * resource limits).
17
17
  */
18
18
  import { bundleRefToString, parseBundleRef } from "../core/asset/asset-ref.js";
19
- import { utf8Bytes, WORKFLOW_MAX_INSTRUCTION_BYTES, WORKFLOW_MAX_PARAMS, WORKFLOW_MAX_STEPS } from "./resource-limits.js";
19
+ import { utf8Bytes, WORKFLOW_MAX_INSTRUCTION_BYTES } from "./resource-limits.js";
20
20
  export function runSemanticChecks(draft, frontmatterData, frontmatterEndLine, errors) {
21
21
  checkXrefs(frontmatterData.xrefs, frontmatterEndLine, errors);
22
22
  checkResourceLimits(draft, errors);
@@ -43,12 +43,6 @@ function checkXrefs(value, line, errors) {
43
43
  }
44
44
  }
45
45
  function checkResourceLimits(draft, errors) {
46
- if (draft.steps.length > WORKFLOW_MAX_STEPS) {
47
- errors.push({ line: 1, message: `Workflow must contain at most ${WORKFLOW_MAX_STEPS} steps.` });
48
- }
49
- if (Object.keys(draft.params ?? {}).length > WORKFLOW_MAX_PARAMS) {
50
- errors.push({ line: 1, message: `Workflow must contain at most ${WORKFLOW_MAX_PARAMS} parameters.` });
51
- }
52
46
  for (const step of draft.steps) {
53
47
  if (step.instructions && utf8Bytes(step.instructions.text) > WORKFLOW_MAX_INSTRUCTION_BYTES) {
54
48
  errors.push({
@@ -366,7 +366,7 @@ reference, same rules as engine `apiKey`), `dimension`, `localModel`,
366
366
 
367
367
  | Key | Purpose |
368
368
  | --- | --- |
369
- | `search.graphBoost.*` | Entity-graph relevance boost: `directBoostPerEntity`/`directBoostCap` (directly related entities), `hopBoostPerEntity`/`hopBoostCap` (multi-hop, capped at `maxHops` ≤ 3), `confidenceMode` (`off`\|`blend`\|`multiply`, default `blend`), `confidenceWeight` (0–1, default `0.2`) |
369
+ | `search.graphBoost.*` | Entity-graph relevance boost: `directBoostPerEntity`/`directBoostCap` (directly related entities), `hopBoostPerEntity`/`hopBoostCap` (multi-hop, capped at `maxHops` ≤ 3), `confidenceMode` (`blend`, the only supported value), `confidenceWeight` (0–1, default `0.2`) |
370
370
 
371
371
  ## Feedback
372
372
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "akm-cli",
3
- "version": "0.9.5",
3
+ "version": "0.9.6",
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": [
@@ -57,9 +57,6 @@
57
57
  "type": "integer",
58
58
  "exclusiveMinimum": 0
59
59
  },
60
- "supportsJsonSchema": {
61
- "type": "boolean"
62
- },
63
60
  "extraParams": {
64
61
  "type": "object",
65
62
  "additionalProperties": {}
@@ -572,9 +569,7 @@
572
569
  "confidenceMode": {
573
570
  "type": "string",
574
571
  "enum": [
575
- "off",
576
- "blend",
577
- "multiply"
572
+ "blend"
578
573
  ],
579
574
  "default": "blend"
580
575
  },
@@ -1747,9 +1742,6 @@
1747
1742
  "type": "integer",
1748
1743
  "exclusiveMinimum": 0
1749
1744
  },
1750
- "supportsJsonSchema": {
1751
- "type": "boolean"
1752
- },
1753
1745
  "extraParams": {
1754
1746
  "type": "object",
1755
1747
  "additionalProperties": {}
@@ -2262,9 +2254,7 @@
2262
2254
  "confidenceMode": {
2263
2255
  "type": "string",
2264
2256
  "enum": [
2265
- "off",
2266
- "blend",
2267
- "multiply"
2257
+ "blend"
2268
2258
  ],
2269
2259
  "default": "blend"
2270
2260
  },
@@ -3420,14 +3410,8 @@
3420
3410
  "type": "integer",
3421
3411
  "exclusiveMinimum": 0
3422
3412
  },
3423
- "capabilities": {
3424
- "type": "object",
3425
- "properties": {
3426
- "structuredOutput": {
3427
- "type": "boolean"
3428
- }
3429
- },
3430
- "additionalProperties": true
3413
+ "supportsJsonSchema": {
3414
+ "type": "boolean"
3431
3415
  },
3432
3416
  "extraParams": {
3433
3417
  "type": "object",
@@ -3443,9 +3427,6 @@
3443
3427
  "reasoningEffort": {
3444
3428
  "type": "string",
3445
3429
  "minLength": 1
3446
- },
3447
- "supportsJsonSchema": {
3448
- "type": "boolean"
3449
3430
  }
3450
3431
  },
3451
3432
  "required": [
@@ -27,7 +27,6 @@
27
27
  },
28
28
  "inputs": {
29
29
  "type": "object",
30
- "maxProperties": 128,
31
30
  "propertyNames": { "$ref": "#/definitions/taskInputName" },
32
31
  "additionalProperties": { "$ref": "#/definitions/inputDeclaration" }
33
32
  },
@@ -105,7 +104,7 @@
105
104
  "items": { "type": "string", "pattern": "^[A-Za-z_][A-Za-z0-9_]*$" }
106
105
  },
107
106
  "maxSteps": { "type": "integer", "minimum": 1, "maximum": 9007199254740991 },
108
- "maxRetries": { "type": "integer", "minimum": 0, "maximum": 100 }
107
+ "maxRetries": { "type": "integer", "minimum": 0 }
109
108
  },
110
109
  "oneOf": [
111
110
  {
@@ -22,7 +22,6 @@
22
22
  "params": {
23
23
  "type": "object",
24
24
  "minProperties": 1,
25
- "maxProperties": 128,
26
25
  "description": "Run parameters: param name -> JSON Schema declaration. Names must be params.<name>-addressable identifiers.",
27
26
  "propertyNames": {
28
27
  "pattern": "^[A-Za-z_][A-Za-z0-9_]*$"
@@ -34,7 +33,6 @@
34
33
  "outputs": {
35
34
  "type": "object",
36
35
  "minProperties": 1,
37
- "maxProperties": 64,
38
36
  "description": "Named, optionally schema-validated projections of step artifacts, exported when the run completes. Names must be steps.<child>.output.<name>-addressable identifiers. Each entry: { from: steps.<id>.output(.<seg>)*, schema?: <bounded JSON Schema> }. Markdown-frontmatter only.",
39
37
  "propertyNames": {
40
38
  "pattern": "^[A-Za-z_][A-Za-z0-9_]*$"
@@ -52,7 +50,6 @@
52
50
  "steps": {
53
51
  "type": "array",
54
52
  "minItems": 1,
55
- "maxItems": 256,
56
53
  "items": {
57
54
  "$ref": "#/definitions/step"
58
55
  }
@@ -188,7 +185,6 @@
188
185
  "max": {
189
186
  "type": "integer",
190
187
  "minimum": 0,
191
- "maximum": 100,
192
188
  "description": "Maximum retry attempts per unit."
193
189
  },
194
190
  "on": {
@@ -213,7 +209,6 @@
213
209
  "max_units": {
214
210
  "type": "integer",
215
211
  "minimum": 1,
216
- "maximum": 10000,
217
212
  "description": "Ceiling on total dispatched units across the run (seeded from journaled unit rows)."
218
213
  }
219
214
  }
@@ -252,25 +247,21 @@
252
247
  "type": "array",
253
248
  "description": "argv; command[0] is the program, resolved through PATH. Never shell-parsed.",
254
249
  "minItems": 1,
255
- "maxItems": 64,
256
250
  "items": {
257
251
  "type": "string",
258
- "minLength": 1,
259
- "maxLength": 4096
252
+ "minLength": 1
260
253
  }
261
254
  },
262
255
  "cwd": {
263
256
  "type": "string",
264
257
  "description": "Optional RELATIVE working directory inside the unit's working directory (its fresh worktree under `isolation: worktree`). Absolute paths, Windows drive letters, \"~\", and \"..\" segments are rejected here, and containment is re-checked against the resolved base — symlinks included — before the command is spawned.",
265
258
  "minLength": 1,
266
- "maxLength": 1024,
267
259
  "pattern": "^(?!/|\\\\|~|[A-Za-z]:)(?!.*(?:^|[/\\\\])\\.\\.(?:[/\\\\]|$)).+$"
268
260
  },
269
261
  "pass_env": {
270
262
  "type": "array",
271
263
  "description": "Extra environment variable NAMES copied through from akm's own environment on top of the default allowlist — for a per-machine toolchain variable (e.g. CARGO_HOME) that no committed `env:` asset could carry. NAMES ONLY: values never appear in a workflow, and unlike `env:` bindings these values are NOT redacted from the command's output, so never list a credential here.",
272
264
  "minItems": 1,
273
- "maxItems": 32,
274
265
  "uniqueItems": true,
275
266
  "items": {
276
267
  "type": "string",
@@ -398,7 +389,6 @@
398
389
  "when": {
399
390
  "type": "array",
400
391
  "minItems": 1,
401
- "maxItems": 256,
402
392
  "items": {
403
393
  "$ref": "#/definitions/routeBranch"
404
394
  }
@@ -416,7 +406,6 @@
416
406
  "max_loops": {
417
407
  "type": "integer",
418
408
  "minimum": 1,
419
- "maximum": 100,
420
409
  "description": "Evaluator-optimizer loop bound."
421
410
  }
422
411
  }
@@ -424,7 +413,6 @@
424
413
  "inputs": {
425
414
  "type": "array",
426
415
  "minItems": 1,
427
- "maxItems": 64,
428
416
  "uniqueItems": true,
429
417
  "items": {
430
418
  "$ref": "#/definitions/reference"