akm-cli 0.9.4 → 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 (123) hide show
  1. package/CHANGELOG.md +265 -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/anti-collapse.js +4 -91
  9. package/dist/commands/improve/autonomy-gate.js +1 -1
  10. package/dist/commands/improve/consolidate.js +25 -26
  11. package/dist/commands/improve/distill.js +2 -2
  12. package/dist/commands/improve/extract.js +8 -1
  13. package/dist/commands/improve/improve.js +3 -5
  14. package/dist/commands/improve/locks.js +13 -5
  15. package/dist/commands/improve/loop-stages.js +8 -9
  16. package/dist/commands/improve/memory/memory-contradiction-detect.js +1 -19
  17. package/dist/commands/improve/outcome-loop.js +0 -6
  18. package/dist/commands/improve/reflect.js +20 -9
  19. package/dist/commands/lint/index.js +4 -6
  20. package/dist/commands/proposal/validators/proposal-quality-validators.js +0 -7
  21. package/dist/commands/proposal/validators/proposal-validators.js +12 -0
  22. package/dist/commands/read/search.js +14 -24
  23. package/dist/commands/sources/bundle-config-ops.js +9 -4
  24. package/dist/commands/sources/info.js +14 -10
  25. package/dist/commands/tasks/tasks-cli.js +56 -3
  26. package/dist/commands/tasks/tasks.js +101 -18
  27. package/dist/commands/workflow-cli.js +2 -2
  28. package/dist/core/adapter/adapters/akm-adapter.js +2 -0
  29. package/dist/core/common.js +61 -24
  30. package/dist/core/config/config-io.js +2 -2
  31. package/dist/core/config/config-sources.js +32 -2
  32. package/dist/core/config/config-version-shim.js +101 -0
  33. package/dist/core/config/config.js +7 -7
  34. package/dist/core/config/schema/engines.js +9 -7
  35. package/dist/core/config/schema/primitives.js +0 -5
  36. package/dist/core/config/schema/search.js +2 -1
  37. package/dist/core/file-lock.js +2 -1
  38. package/dist/core/improve-result.js +35 -14
  39. package/dist/core/maintenance-barrier.js +2 -14
  40. package/dist/core/paths.js +0 -3
  41. package/dist/core/redaction.js +2 -2
  42. package/dist/core/spawn-env.js +8 -12
  43. package/dist/core/state/migrations.js +1 -12
  44. package/dist/core/state-db.js +9 -27
  45. package/dist/core/write-source.js +12 -19
  46. package/dist/execution/directory-identity.js +36 -10
  47. package/dist/execution/guarded-source.js +0 -10
  48. package/dist/indexer/graph/graph-boost.js +0 -4
  49. package/dist/indexer/index-writer-lock.js +43 -24
  50. package/dist/indexer/index-written-assets.js +5 -6
  51. package/dist/indexer/indexer.js +2 -39
  52. package/dist/indexer/materialize-embeddings.js +85 -41
  53. package/dist/indexer/passes/metadata.js +12 -4
  54. package/dist/indexer/scan/doc-to-entry.js +2 -0
  55. package/dist/indexer/search/db-search.js +15 -48
  56. package/dist/indexer/search/ranking-contributors.js +0 -25
  57. package/dist/indexer/search/ranking.js +3 -13
  58. package/dist/indexer/search/search-fields.js +16 -1
  59. package/dist/integrations/agent/builder-shared.js +0 -25
  60. package/dist/integrations/agent/model-map.js +2 -60
  61. package/dist/integrations/harnesses/aider/agent-builder.js +1 -3
  62. package/dist/integrations/harnesses/amazonq/agent-builder.js +2 -7
  63. package/dist/integrations/harnesses/claude/agent-builder.js +1 -4
  64. package/dist/integrations/harnesses/codex/agent-builder.js +1 -4
  65. package/dist/integrations/harnesses/copilot/agent-builder.js +2 -6
  66. package/dist/integrations/harnesses/gemini/agent-builder.js +2 -7
  67. package/dist/integrations/harnesses/opencode/agent-builder.js +1 -4
  68. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +2 -16
  69. package/dist/integrations/harnesses/openhands/agent-builder.js +1 -3
  70. package/dist/integrations/harnesses/pi/agent-builder.js +2 -4
  71. package/dist/integrations/session-logs/index.js +0 -9
  72. package/dist/llm/client.js +75 -42
  73. package/dist/llm/embedder.js +7 -3
  74. package/dist/llm/embedders/remote.js +141 -42
  75. package/dist/output/shapes/passthrough.js +17 -5
  76. package/dist/registry/network.js +5 -37
  77. package/dist/runtime.js +2 -10
  78. package/dist/scripts/akm-migrate-node.js +180 -238
  79. package/dist/scripts/akm-migrate.js +180 -238
  80. package/dist/setup/engine-config.js +2 -5
  81. package/dist/setup/registry-stash-loader.js +0 -8
  82. package/dist/setup/setup.js +9 -46
  83. package/dist/setup/steps/connection-shared.js +10 -13
  84. package/dist/sources/providers/git-install.js +1 -1
  85. package/dist/storage/engines/sqlite-migrations.js +20 -1
  86. package/dist/storage/repositories/index-entries-repository.js +0 -15
  87. package/dist/storage/repositories/proposals-repository.js +32 -6
  88. package/dist/tasks/backends/launchd.js +15 -20
  89. package/dist/tasks/backends/schtasks.js +18 -8
  90. package/dist/tasks/run/run-native-task.js +8 -6
  91. package/dist/tasks/scheduler-binding.js +15 -5
  92. package/dist/tasks/scheduler-sync-preview.js +4 -2
  93. package/dist/tasks/scheduler-sync.js +77 -42
  94. package/dist/tasks/source/bounded-document.js +2 -4
  95. package/dist/tasks/source/task-source-v3-frozen.js +5 -7
  96. package/dist/tasks/source/task-source-v4.js +5 -10
  97. package/dist/tasks/source/task-to-v3.js +29 -22
  98. package/dist/tasks/source/task-to-v4.js +1 -14
  99. package/dist/tasks/source-v3.js +6 -6
  100. package/dist/workflows/exec/native-executor.js +21 -31
  101. package/dist/workflows/exec/run-workflow.js +5 -6
  102. package/dist/workflows/exec/scheduler.js +3 -19
  103. package/dist/workflows/exec/step-work.js +1 -4
  104. package/dist/workflows/exec/unit-dispatch.js +2 -2
  105. package/dist/workflows/exec/worktree.js +1 -13
  106. package/dist/workflows/freeze/targets/child-workflow.js +2 -10
  107. package/dist/workflows/ir/plan-hash.js +4 -6
  108. package/dist/workflows/ir/schema-v4.js +0 -12
  109. package/dist/workflows/ir/schema.js +20 -31
  110. package/dist/workflows/parser.js +11 -52
  111. package/dist/workflows/renderer.js +2 -3
  112. package/dist/workflows/resource-limits.js +11 -41
  113. package/dist/workflows/runtime/runs.js +3 -4
  114. package/dist/workflows/source-ir/schema.js +14 -30
  115. package/dist/workflows/validator.js +1 -7
  116. package/docs/reference/cli.md +25 -3
  117. package/docs/reference/configuration.md +28 -7
  118. package/docs/reference/tasks.md +10 -0
  119. package/package.json +1 -1
  120. package/schemas/akm-config.json +4 -23
  121. package/schemas/akm-task.json +1 -2
  122. package/schemas/akm-workflow.json +1 -13
  123. package/dist/indexer/search/semantic-status.js +0 -142
@@ -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({
@@ -2423,9 +2423,10 @@ shell commands. It manages on-disk task definitions under
2423
2423
  (cron / launchd / schtasks). Task source v4 YAML (`version: 4`) is the only
2424
2424
  executable source contract this release accepts; `akm task add` writes v4 —
2425
2425
  see the canonical [Tasks reference](tasks.md). The
2426
- group is `add | run | explain | sync | doctor | history` — there is no `list`
2427
- or `remove`; use `akm search --type task` / `akm show tasks/<id>` to inspect,
2428
- and edit the file + `akm task sync` to change or remove a schedule.
2426
+ group is `add | run | explain | sync | doctor | history | prune` — there is
2427
+ no `list` or `remove`; use `akm search --type task` / `akm show tasks/<id>`
2428
+ to inspect, and edit the file + `akm task sync` to change or remove a
2429
+ schedule.
2429
2430
 
2430
2431
  ```sh
2431
2432
  akm search --type task # List tasks (cross-bundle)
@@ -2439,8 +2440,12 @@ akm task run <id> # Execute now (what the scheduler ca
2439
2440
  akm task explain <ref> # Read-only: declared inputs, target, schedule — spawns nothing
2440
2441
  akm task history [--id <id>] [--limit <n>] # Recent runs from state.db
2441
2442
  akm task sync # Reconcile on-disk YAML with scheduler
2443
+ akm task sync --dry-run # Preview the reconcile — zero scheduler writes
2442
2444
  akm task sync --rebind # Also capture the current installed runtime
2443
2445
  akm task doctor # Report scheduler backend + paths
2446
+ akm task prune # Preview orphaned scheduler entries — zero writes
2447
+ akm task prune --yes # Remove every currently-computed orphan
2448
+ akm task prune --id ghost,stale --yes # Remove only the named orphan ids
2444
2449
  ```
2445
2450
 
2446
2451
  `task add` also accepts `--disabled` (register but leave off in the OS
@@ -2466,6 +2471,23 @@ schedule-binding) and run `akm task sync`. To remove one, delete its file
2466
2471
  (`<bundle>/tasks/<id>.yml`) and run `akm task sync` — sync uninstalls the
2467
2472
  orphaned scheduler entry.
2468
2473
 
2474
+ `akm task sync --dry-run` prints the planned adds/updates/removes (removals
2475
+ carry their owning bundle) without touching the scheduler — zero writes.
2476
+ Exits non-zero when removals are pending, so it can gate a CI/health check
2477
+ on "sync would change something."
2478
+
2479
+ `akm task prune` reclaims installed scheduler entries that `sync` can never
2480
+ clean up on its own: entries whose own `--scheduler-context` descriptor no
2481
+ longer resolves to a live bundle (a corrupt/missing descriptor, or the
2482
+ bundle directory it pointed at is gone). It never touches an entry that
2483
+ still resolves to a live bundle — that's `sync`'s job. Like `sync
2484
+ --dry-run`, the default is a dry-run preview (zero scheduler writes) that
2485
+ exits non-zero when there are candidates to remove; `--yes` executes the
2486
+ printed plan, and `--id <id1,id2,...>` narrows a `--yes` run (or a preview)
2487
+ to specific binding ids — naming an id that isn't a current orphan
2488
+ candidate (not installed, or it still resolves to a live bundle) is
2489
+ refused with a usage error and removes nothing.
2490
+
2469
2491
  Scheduler activation captures the installed akm runtime. Ordinary `task sync`
2470
2492
  reconciles definitions, schedules, and enabled state while preserving that
2471
2493
  runtime binding. Use `task sync --rebind` only after intentionally moving or
@@ -7,12 +7,33 @@ directory. Project `.akm/config.json` files are not merged.
7
7
 
8
8
  ## Version 0.9
9
9
 
10
- A present configuration file must set `configVersion` to exactly `"0.9.0"`.
11
- Missing, older, newer, numeric, and malformed versions are rejected by ordinary
12
- commands without rewriting the file. Pre-0.9 config and database layouts are
13
- not runtime inputs and are not migrated by `akm upgrade`. Configure the current
14
- schema directly. The standalone migrator exists only for explicit task
15
- migration: task v2 to task v3, then task v3 to task source v4, in one pass.
10
+ A present configuration file must set `configVersion` to a version this
11
+ binary knows: the current `"0.9.0"`, or a known older version it can
12
+ auto-upgrade in memory (see "Version read shim" below). Missing, newer,
13
+ numeric, and any other unrecognized version are rejected by ordinary
14
+ commands without rewriting the file an older binary never guesses at a
15
+ newer, unknown shape. Pre-0.9 config and database layouts are not runtime
16
+ inputs and are not migrated by `akm upgrade`. Configure the current schema
17
+ directly. The standalone migrator exists only for explicit task migration:
18
+ task v2 to task v3, then task v3 to task source v4, in one pass.
19
+
20
+ ### Version read shim
21
+
22
+ Like the task-source v2/v3 auto-shim (`akm migrate apply`'s in-memory
23
+ counterpart, documented under Migration below), a known older `configVersion`
24
+ is converted to the current shape in memory on load — with a one-line stderr
25
+ deprecation warning — rather than hard-failing every command. Nothing is
26
+ written back to disk by the shim itself; the very next config-mutating
27
+ command (`akm config set`, etc.) persists the upgrade for free, since every
28
+ config write already forces `configVersion` to the current value, which
29
+ silences the warning. A `configVersion` this binary does not recognize at
30
+ all — including anything newer than current — still fails closed with
31
+ `UNSUPPORTED_CONFIG_VERSION`.
32
+
33
+ As of this writing `"0.9.0"` is the only `configVersion` akm has ever
34
+ shipped, so there is no real older shape for the shim to convert yet; the
35
+ mechanism (`src/core/config/config-version-shim.ts`) is established ahead of
36
+ the first bump that will need it, per #863.
16
37
 
17
38
  ```jsonc
18
39
  {
@@ -345,7 +366,7 @@ reference, same rules as engine `apiKey`), `dimension`, `localModel`,
345
366
 
346
367
  | Key | Purpose |
347
368
  | --- | --- |
348
- | `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`) |
349
370
 
350
371
  ## Feedback
351
372
 
@@ -389,6 +389,16 @@ for full before/after examples and recovery guidance.
389
389
  - Disable a binding by editing the source and syncing: set that schedule
390
390
  entry's own `enabled: false`.
391
391
  - Delete the `.yml` source and sync to remove its derived binding(s).
392
+ - `akm task sync --dry-run` previews the reconcile (adds/updates/removes,
393
+ removals annotated with their owning bundle) without writing to the
394
+ scheduler; exits non-zero when removals are pending.
395
+ - `akm task prune` removes installed scheduler entries `sync` cannot reach
396
+ because their own descriptor no longer resolves to a live bundle
397
+ (corrupt/missing `--scheduler-context`, or the owning bundle directory is
398
+ gone). It never touches an entry that still resolves to a live bundle.
399
+ Defaults to a dry-run preview (zero writes); `--yes` executes it; `--id
400
+ <id1,id2,...>` scopes to specific ids and refuses any id that isn't a
401
+ current orphan candidate.
392
402
  - Use `akm task sync --rebind` only when deliberately changing the captured
393
403
  AKM runtime, then verify with `akm task doctor`.
394
404
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "akm-cli",
3
- "version": "0.9.4",
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"
@@ -1,142 +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
- import fs from "node:fs";
5
- import { writeFileAtomic } from "../../core/common.js";
6
- import { getCacheDir, getSemanticStatusPath } from "../../core/paths.js";
7
- import { DETERMINISTIC_EMBED_MODEL_ID, isDeterministicEmbedEnabled } from "../../llm/embedders/deterministic.js";
8
- import { DEFAULT_LOCAL_MODEL } from "../../llm/embedders/local.js";
9
- let semanticStatusMutationForTests;
10
- /** TEST-ONLY. Inject a semantic-status filesystem publication fault. */
11
- export function _setSemanticStatusMutationForTests(mutation) {
12
- semanticStatusMutationForTests = mutation;
13
- }
14
- export function deriveSemanticProviderFingerprint(embedding) {
15
- if (isDeterministicEmbedEnabled()) {
16
- return `deterministic:${DETERMINISTIC_EMBED_MODEL_ID}`;
17
- }
18
- if (embedding?.endpoint) {
19
- // Fingerprint keys on vector identity only (model + dimension). The endpoint
20
- // is transport/routing and has no bearing on vector compatibility, so moving
21
- // the same model+dimension to a different host must not force a full re-embed.
22
- return `remote:${embedding.model}|${embedding.dimension ?? "default"}`;
23
- }
24
- return `local:${embedding?.localModel ?? DEFAULT_LOCAL_MODEL}`;
25
- }
26
- export function readSemanticStatus() {
27
- try {
28
- const raw = JSON.parse(fs.readFileSync(getSemanticStatusPath(), "utf8"));
29
- if ((raw.status === "pending" ||
30
- raw.status === "ready-js" ||
31
- raw.status === "ready-vec" ||
32
- raw.status === "blocked") &&
33
- typeof raw.providerFingerprint === "string" &&
34
- typeof raw.lastCheckedAt === "string") {
35
- const status = {
36
- status: raw.status,
37
- providerFingerprint: raw.providerFingerprint,
38
- lastCheckedAt: raw.lastCheckedAt,
39
- };
40
- if (typeof raw.reason === "string")
41
- status.reason = raw.reason;
42
- if (typeof raw.message === "string")
43
- status.message = raw.message;
44
- if (typeof raw.entryCount === "number")
45
- status.entryCount = raw.entryCount;
46
- if (typeof raw.embeddingCount === "number")
47
- status.embeddingCount = raw.embeddingCount;
48
- return status;
49
- }
50
- }
51
- catch {
52
- // ignore corrupt or missing semantic status
53
- }
54
- return undefined;
55
- }
56
- export function writeSemanticStatus(status) {
57
- semanticStatusMutationForTests?.("write");
58
- const dir = getCacheDir();
59
- fs.mkdirSync(dir, { recursive: true });
60
- writeFileAtomic(getSemanticStatusPath(), `${JSON.stringify(status, null, 2)}\n`);
61
- }
62
- export function clearSemanticStatus() {
63
- semanticStatusMutationForTests?.("clear");
64
- try {
65
- fs.unlinkSync(getSemanticStatusPath());
66
- }
67
- catch {
68
- // ignore missing file
69
- }
70
- }
71
- /** How long a "blocked" status is retained before the system retries. 24 hours. */
72
- export const BLOCKED_TTL_MS = 24 * 60 * 60 * 1000;
73
- export function getEffectiveSemanticStatus(config, status = readSemanticStatus()) {
74
- if (config.semanticSearchMode === "off")
75
- return "disabled";
76
- if (!status)
77
- return "pending";
78
- const fingerprint = deriveSemanticProviderFingerprint(config.embedding);
79
- if (status.providerFingerprint !== fingerprint)
80
- return "pending";
81
- // Auto-recovery: if blocked status is older than BLOCKED_TTL_MS, treat as pending
82
- // so the next index run will re-attempt semantic setup.
83
- if (status.status === "blocked") {
84
- const checkedAt = new Date(status.lastCheckedAt).getTime();
85
- if (Number.isNaN(checkedAt) || Date.now() - checkedAt > BLOCKED_TTL_MS) {
86
- return "pending";
87
- }
88
- }
89
- return status.status;
90
- }
91
- export function isSemanticRuntimeReady(status) {
92
- return status === "ready-js" || status === "ready-vec";
93
- }
94
- export function classifySemanticFailure(message) {
95
- const lower = message.toLowerCase();
96
- if (lower.includes("401") || lower.includes("403") || lower.includes("auth") || lower.includes("unauthorized")) {
97
- return "remote-auth";
98
- }
99
- if (lower.includes("429") || lower.includes("rate limit") || lower.includes("quota")) {
100
- return "remote-rate-limit";
101
- }
102
- if (lower.includes("eacces") || lower.includes("permission denied")) {
103
- return "permission-denied";
104
- }
105
- // Native library / linker errors must be checked before the generic ONNX
106
- // match because Alpine/musl linker errors often contain "onnxruntime" in
107
- // the library path (e.g. onnxruntime_binding.node).
108
- if (lower.includes("shared library") ||
109
- lower.includes("glibc") ||
110
- lower.includes("musl") ||
111
- lower.includes("libc.so")) {
112
- return "native-lib-missing";
113
- }
114
- if (lower.includes("onnx") || lower.includes("onnxruntime")) {
115
- return "onnx-runtime-failed";
116
- }
117
- if (lower.includes("404") || lower.includes("model not found") || lower.includes("bad request")) {
118
- return "remote-model";
119
- }
120
- if (lower.includes("transformers") ||
121
- lower.includes("missing-package") ||
122
- lower.includes("local embedding dependency is unavailable")) {
123
- return "missing-package";
124
- }
125
- if (lower.includes("download")) {
126
- return "local-model-download";
127
- }
128
- if (lower.includes("dimension mismatch")) {
129
- return "dimension-mismatch";
130
- }
131
- if (lower.includes("db") || lower.includes("sqlite") || lower.includes("cache dir")) {
132
- return "db-open";
133
- }
134
- if (lower.includes("timeout") ||
135
- lower.includes("unreachable") ||
136
- lower.includes("refused") ||
137
- lower.includes("network") ||
138
- lower.includes("fetch")) {
139
- return "remote-network";
140
- }
141
- return "unknown";
142
- }