akm-cli 0.9.14 → 0.9.15-beta.1

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 (110) hide show
  1. package/CHANGELOG.md +397 -0
  2. package/STABILITY.md +6 -3
  3. package/dist/assets/prompts/reflect-feedback-framing.md +1 -0
  4. package/dist/assets/prompts/reflect-llm-framed-contract.md +2 -0
  5. package/dist/assets/prompts/reflect-llm-schema-contract.md +2 -0
  6. package/dist/assets/tasks/core/improve.yml +1 -1
  7. package/dist/assets/tasks/core/index-refresh.yml +1 -1
  8. package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +1 -1
  9. package/dist/assets/tasks/improve/akm-improve-catchup.yml +1 -1
  10. package/dist/assets/tasks/improve/akm-improve-consolidate.yml +1 -1
  11. package/dist/assets/tasks/improve/akm-improve-frequent.yml +1 -1
  12. package/dist/assets/tasks/improve/akm-improve-nightly.yml +1 -1
  13. package/dist/cli/retired-commands.js +0 -1
  14. package/dist/cli/shared.js +9 -0
  15. package/dist/cli/unknown-flags.js +1 -0
  16. package/dist/cli.js +3 -2
  17. package/dist/commands/config-cli.js +85 -3
  18. package/dist/commands/env/env-cli.js +1 -42
  19. package/dist/commands/env/env.js +1 -1
  20. package/dist/commands/env/secret-cli.js +1 -2
  21. package/dist/commands/health/checks.js +357 -63
  22. package/dist/commands/health/engine-usage.js +45 -0
  23. package/dist/commands/health/improve-metrics.js +18 -0
  24. package/dist/commands/health/llm-usage.js +41 -1
  25. package/dist/commands/health/plugin-staleness.js +7 -3
  26. package/dist/commands/health/version-drift.js +93 -0
  27. package/dist/commands/health/windows.js +3 -1
  28. package/dist/commands/health.js +44 -9
  29. package/dist/commands/improve/consolidate/chunking.js +4 -2
  30. package/dist/commands/improve/improve-cli.js +99 -5
  31. package/dist/commands/improve/improve-report.js +154 -0
  32. package/dist/commands/improve/improve-result-file.js +45 -33
  33. package/dist/commands/improve/improve-strategies.js +133 -3
  34. package/dist/commands/improve/improve-usage-report.js +182 -0
  35. package/dist/commands/improve/improve.js +40 -3
  36. package/dist/commands/improve/locks.js +27 -78
  37. package/dist/commands/improve/planner.js +1 -0
  38. package/dist/commands/improve/preparation.js +9 -1
  39. package/dist/commands/improve/reflect.js +44 -4
  40. package/dist/commands/models-cli.js +50 -1
  41. package/dist/commands/proposal/repository.js +8 -3
  42. package/dist/commands/proposal/validators/proposal-quality-validators.js +41 -6
  43. package/dist/commands/proposal/validators/proposal-validators.js +24 -0
  44. package/dist/commands/read/search-cli.js +38 -2
  45. package/dist/commands/read/show.js +103 -4
  46. package/dist/commands/sources/info.js +5 -1
  47. package/dist/commands/sources/self-update.js +2 -2
  48. package/dist/commands/sources/stash-cli.js +31 -0
  49. package/dist/commands/tasks/tasks-cli.js +49 -2
  50. package/dist/commands/workflow-cli.js +86 -12
  51. package/dist/core/asset/markdown-fragments.js +35 -0
  52. package/dist/core/config/config-schema.js +14 -0
  53. package/dist/core/config/config.js +302 -24
  54. package/dist/core/env-secret-ref.js +58 -5
  55. package/dist/core/errors.js +30 -0
  56. package/dist/core/improve-result.js +51 -0
  57. package/dist/core/loopback.js +17 -0
  58. package/dist/core/paths.js +11 -0
  59. package/dist/core/run-lock.js +96 -0
  60. package/dist/core/sensitive-marker-path.js +19 -0
  61. package/dist/core/state-db.js +74 -14
  62. package/dist/indexer/index-rebuild-lock.js +73 -0
  63. package/dist/indexer/index-writer-lock.js +40 -1
  64. package/dist/indexer/index-written-assets.js +21 -1
  65. package/dist/indexer/indexer.js +18 -17
  66. package/dist/indexer/materialize-embeddings.js +282 -32
  67. package/dist/indexer/search/db-search.js +49 -2
  68. package/dist/integrations/agent/engine-resolution.js +96 -6
  69. package/dist/integrations/agent/execution-definitions.js +6 -15
  70. package/dist/integrations/agent/execution-lowering.js +6 -1
  71. package/dist/integrations/agent/execution-preparation.js +1 -1
  72. package/dist/integrations/agent/model-map.js +123 -20
  73. package/dist/integrations/agent/prompts.js +40 -8
  74. package/dist/integrations/agent/runner-dispatch.js +9 -3
  75. package/dist/integrations/agent/runner.js +2 -0
  76. package/dist/llm/client.js +8 -3
  77. package/dist/llm/embedder.js +20 -8
  78. package/dist/llm/embedders/local.js +10 -2
  79. package/dist/llm/embedders/remote.js +188 -21
  80. package/dist/output/shapes/helpers.js +38 -2
  81. package/dist/output/shapes/models-list.js +16 -0
  82. package/dist/output/shapes/passthrough.js +2 -0
  83. package/dist/output/shapes.js +4 -0
  84. package/dist/output/text/command-format.js +29 -0
  85. package/dist/output/text/helpers.js +1 -1
  86. package/dist/output/text/improve-report.js +27 -0
  87. package/dist/{commands/env/marker-path.js → output/text/models.js} +4 -3
  88. package/dist/output/text/show-format.js +4 -0
  89. package/dist/output/text.js +4 -0
  90. package/dist/scripts/akm-migrate-node.js +24798 -21732
  91. package/dist/scripts/akm-migrate.js +23408 -20343
  92. package/dist/storage/repositories/improve-runs-repository.js +34 -0
  93. package/dist/storage/repositories/index-fts-repository.js +49 -6
  94. package/dist/storage/repositories/index-vec-repository.js +30 -0
  95. package/dist/storage/repositories/workflow-runs-repository.js +55 -18
  96. package/dist/tasks/backends/cron.js +14 -7
  97. package/dist/tasks/run/run-workflow-task.js +16 -0
  98. package/dist/workflows/exec/child-workflow.js +2 -2
  99. package/dist/workflows/exec/dispatch-redaction.js +21 -9
  100. package/dist/workflows/exec/run-workflow.js +6 -5
  101. package/dist/workflows/runtime/runs.js +33 -5
  102. package/docs/migration/release-notes/0.9.15.md +52 -0
  103. package/docs/migration/release-notes/README.md +4 -0
  104. package/docs/reference/cli.md +245 -29
  105. package/docs/reference/configuration.md +180 -19
  106. package/docs/reference/data-and-telemetry.md +8 -0
  107. package/docs/reference/tasks.md +16 -1
  108. package/docs/reference/workflow-schema.md +5 -1
  109. package/package.json +1 -1
  110. package/schemas/akm-config.json +8 -0
@@ -3,7 +3,10 @@
3
3
  AKM reads one user configuration file: `$XDG_CONFIG_HOME/akm/config.json`
4
4
  (normally `~/.config/akm/config.json` on Linux and macOS, or
5
5
  `%APPDATA%\akm\config.json` on Windows). Set `AKM_CONFIG_DIR` to override the
6
- directory. Project `.akm/config.json` files are not merged.
6
+ directory. Project `.akm/config.json` files are not merged. A config file may
7
+ optionally extend one other config via `extends` (see "Sharing configuration
8
+ across installs" below) — this is a single, explicit, user-opted-in key, not
9
+ automatic project-config discovery.
7
10
 
8
11
  ## Version 0.9
9
12
 
@@ -89,14 +92,21 @@ LLM endpoints must be complete `http://` or `https://` chat-completions URLs
89
92
  ending in `/chat/completions`, without userinfo, query, or fragment. API keys
90
93
  are symbolic only: `$VAR` or `${VAR}`. AKM resolves them only at dispatch.
91
94
 
92
- An LLM engine may set `reasoningEffort` to a non-empty provider-supported
93
- value such as `"none"`, `"low"`, or `"high"`. AKM sends it as the top-level
94
- OpenAI-compatible `reasoning_effort` parameter alongside the existing
95
- `enableThinking` control, because providers do not all honor the same thinking
96
- switch. `reasoning_effort` is AKM-owned and cannot be set through
97
- `extraParams`. If a response reports reasoning tokens despite
98
- `enableThinking: false`, AKM emits a runtime warning so an ineffective provider
99
- control is visible.
95
+ An LLM engine may set `enableThinking: false` to turn thinking off and
96
+ `reasoningEffort` to a value such as `"none"`, `"low"`, or `"high"`. AKM sends
97
+ **both** wire forms — `chat_template_kwargs.enable_thinking` and top-level
98
+ `enable_thinking` — whenever `enableThinking` resolves, from engine config
99
+ or a calling process (improve's `consolidate`/`reflect` and the distill
100
+ quality gate always request `enableThinking: false` for a machine-readable
101
+ payload); `reasoningEffort` is always sent as top-level `reasoning_effort`
102
+ when set. Backend support: llama.cpp direct honors both forms
103
+ (`reasoning_effort` from build ≥ b10644); vLLM honors
104
+ `chat_template_kwargs`; Bifrost drops `chat_template_kwargs` and passes
105
+ `reasoning_effort` through, so also set `reasoningEffort: "none"` behind it; a
106
+ strict hosted API may 400 on unrecognized keys. Both fields are AKM-owned, not
107
+ settable via `extraParams`. A response with reasoning tokens despite
108
+ `enableThinking: false` triggers a runtime warning and the `akm health`
109
+ `thinking-control` advisory.
100
110
 
101
111
  An agent engine may set `bin`, `args`, `workspace`, `model`, and `timeoutMs`.
102
112
  Only `platform: "opencode-sdk"` may set `llmEngine`; it names
@@ -147,19 +157,54 @@ the same version-1 schema as the installed file:
147
157
  ```
148
158
 
149
159
  Each engine mapping is either a non-empty exact model string or a structured
150
- profile with the documented fields `model` and `inference`. A user profile may
151
- omit `model` when the installed layer already supplies it, as the partial
152
- Claude override above does. After overlay, every alias/engine entry must have a
153
- usable model. Unknown profile fields are rejected; JSON-safe fields inside
154
- `inference` are preserved for engine adapters to lower optimistically.
160
+ profile with the documented fields `model`, `inference`, and `engine`. A user
161
+ profile may omit `model` when the installed layer already supplies it, as the
162
+ partial Claude override above does. After overlay, every alias/engine entry
163
+ must have a usable model. Unknown profile fields are rejected; JSON-safe
164
+ fields inside `inference` are preserved for engine adapters to lower
165
+ optimistically.
166
+
167
+ A profile's `engine` field (0.9.15, #946) borrows a column's `model` (and, for
168
+ an `llm`-kind engine, its inference defaults) from a configured
169
+ `engines.<name>` connection instead of hand-typing a literal model a second
170
+ time:
171
+
172
+ ```json
173
+ {
174
+ "version": 1,
175
+ "aliases": {
176
+ "fast": {
177
+ "opencode": { "engine": "local-fast" }
178
+ }
179
+ }
180
+ }
181
+ ```
182
+
183
+ With `engines.local-fast` configured (agent-kind or llm-kind), this column
184
+ resolves to that engine's own `model` string. `model` and `engine` are
185
+ mutually exclusive on the same profile — `engine` is an indirection for the
186
+ model value, never an engine-selection override; which engine `akm agent`
187
+ dispatches to is still decided entirely by `--engine`/`defaults.engine` (see
188
+ [Engine selection](#engines)). The referenced engine's `model` must itself be
189
+ literal, not another alias, and akm copies it verbatim: it does not translate
190
+ between an engine's connection and an agent platform's own provider registry,
191
+ so the value must already be meaningful for the column's platform (e.g. a
192
+ `kind: "agent", platform: "opencode"` engine's `model` should already be a
193
+ string opencode itself understands, such as `krang/qwen3.5-9b`). Run
194
+ `akm models list` to see, for every alias/column, the resolved model and
195
+ whether it came from the installed defaults, the user overlay, and a literal
196
+ value or an `engine` reference.
155
197
 
156
198
  The user file overlays the installed file by alias, engine, and nested object
157
199
  field. Objects merge recursively. Arrays, scalars, and explicit `null` replace
158
- the lower value; omitted fields preserve it. Alias and engine keys are
159
- case-normalized, and case-colliding definitions are rejected. Unknown model
160
- inputs still pass through byte-for-byte as exact identifiers. Once a name is a
161
- known merged alias, selecting an engine with no mapping is an actionable
162
- configuration error rather than silently sending the alias as a model ID.
200
+ the lower value; omitted fields preserve it. A layer setting a literal `model`
201
+ clears any `engine` inherited from a farther layer, and vice versa — the
202
+ nearer layer's choice of literal-vs-engine always wins outright rather than
203
+ merging. Alias and engine keys are case-normalized, and case-colliding
204
+ definitions are rejected. Unknown model inputs still pass through
205
+ byte-for-byte as exact identifiers. Once a name is a known merged alias,
206
+ selecting an engine with no mapping is an actionable configuration error
207
+ rather than silently sending the alias as a model ID.
163
208
 
164
209
  The common execution cascade reads these files for current direct command and
165
210
  non-interactive agent calls, task source v4 runs, and improve/proposal/index
@@ -354,6 +399,19 @@ reference, same rules as engine `apiKey`), `dimension`, `localModel`,
354
399
  `maxTokens`, `batchSize`, `chunkSize`, `contextLength`, and
355
400
  `ollamaOptions.num_ctx`.
356
401
 
402
+ `akm index` keeps a small, fixed number of `/v1/embeddings` requests in
403
+ flight at once (a remote endpoint only; the local transformer path is
404
+ unaffected): `1` for a loopback endpoint (`localhost`, `127.0.0.0/8`, etc. —
405
+ a local model server serves one inference at a time, and parallel requests
406
+ thrash it) and `2` for a remote one. This width is not configurable. The
407
+ actual throughput knob is request SIZE, not request count: `embedding.batchSize`
408
+ (a document-count cap, default 100) together with `embedding.maxTokens` /
409
+ `embedding.contextLength` (an estimated token budget per request, default
410
+ 8000) control how many documents land in one request — a batch of 16-32
411
+ documents takes about the same wall time as a single one against a healthy
412
+ endpoint, so growing the batch is where most of the win is, not adding more
413
+ concurrent requests.
414
+
357
415
  ## Search tuning
358
416
 
359
417
  `search` tunes ranking, not behavior an ordinary user needs to touch:
@@ -484,6 +542,94 @@ Arrays replace, `null` is only valid for nullable fields, and `config unset` is
484
542
  the only deletion operation. `configVersion` cannot be set or unset with the
485
543
  generic walker.
486
544
 
545
+ `config get <key> --show-source` wraps the (redacted) value as
546
+ `{ value, source }`, where `source` is `"local"` when the local file's own
547
+ JSON sets the key, `"extends:<ref>"` for the nearest `extends` chain member
548
+ that sets it, or `"default"` when neither does. It is opt-in — plain
549
+ `config get` keeps its Stable, script-safe bare-value shape.
550
+
551
+ ### Sharing configuration across installs
552
+
553
+ Five hosts running the same fleet often carry an identical `engines` map and
554
+ `improve.strategies` block, differing only in credential delivery (`apiKey`
555
+ vs `apiKeyFile`), bundle paths, and cron offsets. Hand-syncing that block
556
+ across hosts drifts silently. `extends` fixes this: put the shared block in
557
+ one file, and have each host's local config extend it.
558
+
559
+ ```jsonc
560
+ // bundles/fleet/config/shared.json — versioned with the bundle, shared by every host
561
+ {
562
+ "configVersion": "0.9.0",
563
+ "engines": {
564
+ "fast": { "kind": "llm", "endpoint": "https://api.example.test/v1/chat/completions", "model": "qwen3" }
565
+ },
566
+ "improve": { "strategies": { "nightly": { "engine": "fast" } } }
567
+ }
568
+ ```
569
+
570
+ ```jsonc
571
+ // ~/.config/akm/config.json — this host's local file, under 20 lines
572
+ {
573
+ "configVersion": "0.9.0",
574
+ "extends": "fleet//config/shared.json",
575
+ "bundles": {
576
+ "fleet": { "git": "https://github.com/example/fleet-bundle.git" },
577
+ "stash": { "path": "~/akm-stash", "writable": true }
578
+ },
579
+ "defaultBundle": "stash",
580
+ "engines": { "fast": { "apiKeyFile": "/run/secrets/fast-api-key" } }
581
+ }
582
+ ```
583
+
584
+ `extends` accepts either form:
585
+
586
+ - A filesystem path — relative paths resolve against the directory of the
587
+ config file that declares them; a leading `~` expands.
588
+ - A `bundle//<path>` ref — a plain file path *relative to that bundle's
589
+ content root* (e.g. `config/shared.json`), resolved through the bundle's
590
+ configured `path`, not the search index — so it never needs `akm index` to
591
+ have run. This is not an asset ref: the path after `//` needs no asset type
592
+ (`scripts/`, `knowledge/`, …) and the shared file is never indexed; it can
593
+ live anywhere under the bundle. An empty, absolute, or content-root-escaping
594
+ path is rejected. Only a filesystem bundle (`bundles.<id>.path`) can host an
595
+ `extends` source; sync a `git`/`website` bundle with `akm bundle
596
+ add`/`akm sync` first so the file is materialized locally, then point
597
+ `extends` at it.
598
+
599
+ There is no `extends: <url>` form: config load is synchronous and runs on
600
+ every invocation, and akm deliberately does not fetch network resources at
601
+ load time (the same reason `registries` is never fetched until a
602
+ registry-touching command runs). A URL-backed shared config should be synced
603
+ as a `git`/`website` bundle and referenced as `extends: bundle//<path>`
604
+ once materialized, reusing the sync machinery akm already has instead of a
605
+ second one inside config load.
606
+
607
+ The base config runs through the exact same load pipeline as the local
608
+ file — its own version shim, its own legacy-shape shim — so it can carry an
609
+ older `configVersion` independently, and it may itself set `extends`
610
+ (chained). Cycle detection (`ConfigError`, "extends cycle detected") stops A
611
+ extends B extends A instead of recursing forever. Merge order is
612
+ `DEFAULT_CONFIG` (outermost) → the resolved `extends` chain → the local
613
+ file's own keys (local always wins) — the same `deepMergeConfig` "override
614
+ wins" semantics `config set` already uses. A referenced file/bundle that does
615
+ not already exist locally is a load-time `ConfigError` naming the ref — akm
616
+ never fetches or syncs one on your behalf.
617
+
618
+ `akm config diff <path|bundle//path>` compares this host's EFFECTIVE
619
+ config (its own `extends` already applied) against another config file or
620
+ bundle-relative file (loaded through the same loader, so ITS `extends` is
621
+ honoured too), printing sorted `{ path, local, other }` rows for every leaf
622
+ that differs. Both sides are redacted the same way `config get`/`list` are
623
+ before comparison, so a differing secret never round-trips into the diff
624
+ output. Cross-host comparison (`ssh host2 akm config diff ...` in a loop) is
625
+ left to the operator; akm has no concept of a networked fleet to compare
626
+ against directly.
627
+
628
+ ```sh
629
+ akm config diff ~/other-host/config.json
630
+ akm config diff fleet//config/shared.json
631
+ ```
632
+
487
633
  ## Environment
488
634
 
489
635
  | Variable | Purpose |
@@ -513,6 +659,21 @@ not itself a secret. Setting both `apiKey` and `apiKeyFile` on the same
513
659
  engine is rejected. A missing, unreadable, or empty file fails the call
514
660
  closed, naming the engine and path but never the file's content.
515
661
 
662
+ `engines.<name>.apiKey` also accepts `secret://<name>`, a reference into
663
+ AKM's own secret store (`akm secret set <name> --from-file <file>`), for a
664
+ launch context where the credential's environment variable is deliberately
665
+ not sourced into the process — a scheduled task's crontab preamble, or a
666
+ container entrypoint that keeps the user's env out on purpose — and a
667
+ file-backed credential is not an option. Like `apiKeyFile`, only the
668
+ reference is kept in `config.json`; the store lookup happens at dispatch
669
+ time, and an unresolved reference fails the call closed, naming the
670
+ reference but never the value. `akm improve`, workflow LLM steps, and `akm
671
+ health`'s engine probes all resolve `secret://` the same way direct LLM and
672
+ embedding calls have since 0.9.13 (#917); resolution order for a single
673
+ `apiKey` field is: an env reference (`$VAR`/`${VAR}`) first, then
674
+ `apiKeyFile`, then `secret://<name>` — though in practice a config sets only
675
+ one of the three per engine.
676
+
516
677
  Use `AKM_SQLITE_JOURNAL_MODE=DELETE` or `TRUNCATE` when WAL is unavailable,
517
678
  such as on some NFS/SMB mounts. With the default `WAL` setting, AKM detects a
518
679
  network filesystem for the data directory and falls back to `DELETE`.
@@ -230,6 +230,14 @@ the set of types the code actually emits at HEAD (verified against every
230
230
  | `llm_usage_summary` | The owning LLM telemetry sink's terminal-record count marker | `expectedTerminalRecords` |
231
231
  | `health_probe` | `akm health`'s state.db round-trip write/read probe. **Not durably retained**: the row is inserted then deleted within the same connection once the round trip is confirmed, so the net effect on the `events` table is always zero rows | n/a (ephemeral) |
232
232
 
233
+ `llm_usage` rows also carry `process`/`engine`/`stage` (each optional; a call
234
+ made outside any attributed scope carries none of them). `akm improve`
235
+ (#944) aggregates a run's own `llm_usage` events into a process x engine x
236
+ model cross-tab — `summarizeLlmUsageCrossTab` in `src/commands/health/llm-usage.ts`
237
+ — persisted on the run result as `usageReport.byProcessEngineModel` and
238
+ queryable per-run or aggregated with `akm improve report`; see
239
+ `docs/reference/cli.md`'s `#### improve report` section.
240
+
233
241
  ### 2. Usage Events Table
234
242
 
235
243
  `usage_events` is the local analytical record behind utility ranking,
@@ -121,6 +121,20 @@ key in task source v4: `agent`, `engine`, `model`, `inference`, `tools`,
121
121
  number, or boolean values. Keep credentials out of task source; `redact`
122
122
  contains environment variable names, never secret values.
123
123
 
124
+ `timeout:` (milliseconds) means a different mechanism depending on the
125
+ target. For `run:` (native shell/script) and `workflows/<name>` targets it is
126
+ an outer supervisory deadline: the runner kills the child process, or aborts
127
+ the workflow run at its next step boundary, when it fires. For `uses:
128
+ akm/command`, `commands/<name>`, and any other agent/LLM dispatch target
129
+ there is no outer process kill — `timeout:` instead resolves through the
130
+ execution cascade (config/persona/command/task layers) into the dispatch's
131
+ own deadline, and the SDK/CLI runner races each phase against it internally.
132
+ Either way, a dispatch that times out is recorded as `status: failed` with
133
+ `detail.reason: "timeout"` in `task_history` — not a silent `completed` — and
134
+ `akm task run` exits non-zero for it; see [health-advisories.md's
135
+ `task-fail-rate` row](https://github.com/itlackey/akm/blob/main/docs/architecture/internals/health-advisories.md)
136
+ for how `akm health` surfaces a timeout-dominant failure pattern.
137
+
124
138
  ## Scheduling
125
139
 
126
140
  Scheduling is **optional**. Omit `schedule:` entirely for a manual-only
@@ -381,7 +395,8 @@ for full before/after examples and recovery guidance.
381
395
 
382
396
  ## Operations
383
397
 
384
- - `akm search --type task` and `akm show tasks/<id>` inspect task assets.
398
+ - `akm search --type task` (or its alias `akm task list`) and
399
+ `akm show tasks/<id>` inspect task assets.
385
400
  - `akm task explain <ref>` prints a task's declared inputs, resolved target,
386
401
  effective execution settings, and schedule bindings without running
387
402
  anything — see [`akm task explain`](#akm-task-explain) above.
@@ -1352,7 +1352,11 @@ optional user `models.json` files:
1352
1352
 
1353
1353
  Resolution checks the selected engine name or canonical harness column. A
1354
1354
  known alias without that column fails rather than guessing a provider model;
1355
- an unknown string is treated as an exact model selector. See the [Author's Guide](https://github.com/itlackey/akm/blob/main/docs/guides/author-workflows.md#choosing-engines-and-models)
1355
+ an unknown string is treated as an exact model selector. A column may itself
1356
+ be an `engine`-backed indirection (0.9.15) rather than a literal model string
1357
+ — see [Model-map files](configuration.md#model-map-files) — so a step naming
1358
+ `model: fast` also benefits from whatever engine-backed mapping the operator
1359
+ configured for the selected engine, with no workflow-authored change. See the [Author's Guide](https://github.com/itlackey/akm/blob/main/docs/guides/author-workflows.md#choosing-engines-and-models)
1356
1360
  for guidance on which tier to pick per step.
1357
1361
 
1358
1362
  ## See also
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "akm-cli",
3
- "version": "0.9.14",
3
+ "version": "0.9.15-beta.1",
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": [
@@ -9,6 +9,10 @@
9
9
  "type": "string",
10
10
  "const": "0.9.0"
11
11
  },
12
+ "extends": {
13
+ "type": "string",
14
+ "minLength": 1
15
+ },
12
16
  "engines": {
13
17
  "type": "object",
14
18
  "additionalProperties": {
@@ -1696,6 +1700,10 @@
1696
1700
  "type": "string",
1697
1701
  "const": "0.9.0"
1698
1702
  },
1703
+ "extends": {
1704
+ "type": "string",
1705
+ "minLength": 1
1706
+ },
1699
1707
  "engines": {
1700
1708
  "type": "object",
1701
1709
  "additionalProperties": {