akm-cli 0.9.0-beta.0 → 0.9.0-beta.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +511 -0
- package/dist/assets/profiles/quick.json +2 -1
- package/dist/assets/templates/html/default.html +78 -0
- package/dist/assets/templates/html/health.html +732 -0
- package/dist/assets/templates/html/vendor/echarts.min.js +45 -0
- package/dist/cli/shared.js +21 -5
- package/dist/cli.js +47 -5
- package/dist/commands/config-cli.js +0 -10
- package/dist/commands/feedback-cli.js +42 -37
- package/dist/commands/graph/graph.js +75 -71
- package/dist/commands/health/checks.js +48 -0
- package/dist/commands/health/html-report.js +666 -0
- package/dist/commands/health.js +186 -13
- package/dist/commands/improve/consolidate.js +39 -6
- package/dist/commands/improve/distill.js +26 -5
- package/dist/commands/improve/extract-prompt.js +1 -1
- package/dist/commands/improve/extract.js +52 -8
- package/dist/commands/improve/improve-auto-accept.js +33 -1
- package/dist/commands/improve/improve-cli.js +7 -0
- package/dist/commands/improve/improve-profiles.js +4 -0
- package/dist/commands/improve/improve.js +877 -433
- package/dist/commands/improve/proactive-maintenance.js +113 -0
- package/dist/commands/improve/reflect-noise.js +0 -0
- package/dist/commands/improve/reflect.js +31 -0
- package/dist/commands/proposal/drain.js +73 -6
- package/dist/commands/proposal/proposal-cli.js +22 -10
- package/dist/commands/proposal/proposal.js +17 -1
- package/dist/commands/proposal/validators/proposals.js +365 -329
- package/dist/commands/read/curate.js +17 -0
- package/dist/commands/remember.js +6 -2
- package/dist/commands/sources/stash-cli.js +10 -2
- package/dist/commands/tasks/tasks.js +32 -8
- package/dist/core/config/config-schema.js +30 -0
- package/dist/core/file-lock.js +22 -0
- package/dist/core/logs-db.js +304 -0
- package/dist/core/paths.js +3 -0
- package/dist/core/state-db.js +152 -14
- package/dist/indexer/db/db.js +99 -13
- package/dist/indexer/ensure-index.js +152 -17
- package/dist/indexer/index-writer-lock.js +99 -0
- package/dist/indexer/indexer.js +114 -111
- package/dist/indexer/passes/memory-inference.js +61 -22
- package/dist/integrations/harnesses/claude/session-log.js +17 -5
- package/dist/llm/client.js +38 -4
- package/dist/llm/usage-persist.js +77 -0
- package/dist/llm/usage-telemetry.js +103 -0
- package/dist/output/context.js +3 -2
- package/dist/output/html-render.js +73 -0
- package/dist/output/shapes/helpers.js +17 -1
- package/dist/output/text/helpers.js +69 -1
- package/dist/scripts/migrate-storage.js +153 -25
- package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +21 -2
- package/dist/sources/providers/tar-utils.js +16 -8
- package/dist/tasks/backends/cron.js +46 -9
- package/dist/tasks/runner.js +99 -16
- package/dist/workflows/db.js +4 -0
- package/package.json +3 -2
- package/dist/commands/config-edit.js +0 -344
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,336 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.9.0-beta.10] - 2026-06-15
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- **#603** — `akm health` pool-saturation advisory. Instead of alerting on the
|
|
14
|
+
raw `sessionsScanned` count (which false-alarmed on normal cadence changes),
|
|
15
|
+
a new `pool-saturation` advisory reports the ratio of new (unseen) sessions
|
|
16
|
+
to the total session pool: informational below 10% (expected steady state),
|
|
17
|
+
warning below 2% (possible discovery/dedup bug). Heuristic, never gates
|
|
18
|
+
overall status.
|
|
19
|
+
- **#576** — the `akm health` HTML report now renders the real per-stage LLM
|
|
20
|
+
token/time aggregate (a "🧠 LLM Work" KPI card + LLM token/call/wall-time
|
|
21
|
+
summary rows) from the captured `llm_usage` events, replacing the GPU-time
|
|
22
|
+
proxy.
|
|
23
|
+
- **Built-in `akm health --format html` report overhaul** — the report is now a
|
|
24
|
+
strict superset of (and supersedes) the external `akm-health-report` stash
|
|
25
|
+
skill. Restored the interactive filter bar (time-slice 1d–21d, task, status)
|
|
26
|
+
with client-side chart/table re-render and the Last-10 "Task" column;
|
|
27
|
+
reordered sections to a decision-first flow (verdict → action items → KPIs →
|
|
28
|
+
table → charts); added a synthesized one-sentence **Verdict** (status + 2–3
|
|
29
|
+
drivers) and a freshness line; merged the duplicate Advisories / What-to-Watch
|
|
30
|
+
into one prioritized, de-duplicated **Action Items** list (P1/P2/P3 +
|
|
31
|
+
remediation command); added a per-stage **LLM token** stacked-bar chart and
|
|
32
|
+
`dataZoom` sliders on dense charts; fixed the failed-run scatter x-alignment
|
|
33
|
+
(now shape-encoded); KPI-card colors are now health signals (not decoration);
|
|
34
|
+
added metric-glossary tooltips, chart `aria-label`s, contrast fixes, and
|
|
35
|
+
empty-state overlays. Deterministic output preserved.
|
|
36
|
+
|
|
37
|
+
### Fixed
|
|
38
|
+
|
|
39
|
+
- **Health report accuracy** (follow-ups to the overhaul): the per-run **Task**
|
|
40
|
+
column/filter now show the real scheduled task (`akm-improve-frequent`, …) via
|
|
41
|
+
a ±5min `task_history` join instead of the run's scope (which is `all` for
|
|
42
|
+
every scheduled run); the time-**slice** filter options are now derived from
|
|
43
|
+
the report's `--since` window (e.g. All/3d/1d/12h/6h for a 7d report) and
|
|
44
|
+
default to "All" — replacing the hard-coded 1d–21d list that didn't match the
|
|
45
|
+
window; and the trend **deltas** now default their compare window to `--since`
|
|
46
|
+
(like-for-like, e.g. last 7d vs prior 7d) instead of a fixed 24h, which had
|
|
47
|
+
produced nonsensical period-over-period percentages on multi-day reports.
|
|
48
|
+
- **Inflated stash-snapshot metrics in `akm health`.** `memorySummary`
|
|
49
|
+
(derived/eligible) and `profileFilteredRefs` are whole-stash snapshots recorded
|
|
50
|
+
on every run, but the window aggregator was **summing** them across all runs —
|
|
51
|
+
e.g. "915,258 of 1,226,025 eligible" and a 2.4M filtered-ref count. They now
|
|
52
|
+
take the most recent run's snapshot (the current state). Per-run *work* metrics
|
|
53
|
+
(promoted, MI written, graph entities, …) remain genuine window sums.
|
|
54
|
+
- **Health report polish:** the akm version is stamped in the header (under the
|
|
55
|
+
AKM logo) and footer; the steady-state `no new signal since last proposal`
|
|
56
|
+
distill reason is excluded from the skip-reason chart (it drowned out the
|
|
57
|
+
actionable reasons); and the Consolidation Output chart now draws Promoted as a
|
|
58
|
+
line on a secondary right-hand axis (it dwarfs merged/deleted) with merged and
|
|
59
|
+
deleted as bars on the left axis.
|
|
60
|
+
|
|
61
|
+
- **#598** — process-level tuning fields (`consolidate.incrementalSince`,
|
|
62
|
+
`minPoolSize`, `neighborsPerChanged`, `extract.minContentChars`, per-process
|
|
63
|
+
`enabled` flags) now survive an `akm config` rewrite. They are first-class
|
|
64
|
+
typed `ImproveProcessConfigSchema` fields, so the load→save round trip no
|
|
65
|
+
longer silently drops them. Unknown process sub-keys hard-error at load
|
|
66
|
+
(`ConfigError`) rather than being silently discarded — the deliberate,
|
|
67
|
+
documented resolution. Regression-guarded by
|
|
68
|
+
`tests/config-process-roundtrip.test.ts`.
|
|
69
|
+
|
|
70
|
+
## [0.9.0-beta.9] - 2026-06-14
|
|
71
|
+
|
|
72
|
+
Restore and instrument `akm improve` steady-state output. The reflect/distill
|
|
73
|
+
self-improvement lanes had been near-zero in steady state because the
|
|
74
|
+
signal-delta eligibility gate was the only lane (cache "no-access = no-work"
|
|
75
|
+
pathology) and the high-retrieval fallback was structurally dead. This release
|
|
76
|
+
revives proactive improvement, adds attribution + a measurement/kill-criterion
|
|
77
|
+
system so the lane must prove its value, and right-sizes reflect budgets to
|
|
78
|
+
their task timeouts.
|
|
79
|
+
|
|
80
|
+
### Added
|
|
81
|
+
|
|
82
|
+
- **Proactive maintenance selector** (`proactiveMaintenance` improve process):
|
|
83
|
+
due-gated, composite-priority (`importance × log(1+retrievalFreq) ×
|
|
84
|
+
recencyDecay / log(size)`), bounded rotating top-N reflect/distill over
|
|
85
|
+
stale/never-reflected assets. **Disabled by default**; enable per profile.
|
|
86
|
+
- **Eligibility attribution**: every reflect/distill proposal is stamped
|
|
87
|
+
`eligibilitySource ∈ {signal-delta, high-retrieval, proactive, scope,
|
|
88
|
+
unknown}` on `reflect_invoked`/`distill_invoked`/`promoted` events and the
|
|
89
|
+
proposal record, so outcomes are sliceable by lane.
|
|
90
|
+
- **Measurement system** under `scripts/akm-eval/`: a real-query retrieval suite
|
|
91
|
+
generated from `usage_events`, and `akm-eval-proactive-verdict` — a read-only
|
|
92
|
+
kill-criterion runner comparing the proactive lane (treatment) vs due-but-
|
|
93
|
+
untouched assets (control). Emits PASS/FAIL/INCONCLUSIVE and recommends
|
|
94
|
+
disabling the lane on FAIL. New `proactive_selected` event +
|
|
95
|
+
`proactiveSelected`/`proactiveDueTotal`/`proactiveNeverReflected` fields on
|
|
96
|
+
`improve_completed`.
|
|
97
|
+
|
|
98
|
+
### Fixed
|
|
99
|
+
|
|
100
|
+
- Revived the P0-A high-retrieval fallback: genuinely zero-feedback assets were
|
|
101
|
+
routed to the fully-skipped branch one phase before the fallback could see
|
|
102
|
+
them, so frequently-retrieved-but-never-rated assets were never improved.
|
|
103
|
+
- `getRetrievalCounts` now normalizes bare vs `origin//`-prefixed refs (it was
|
|
104
|
+
dropping ~half the retrieval signal) and counts `curate` events
|
|
105
|
+
(`akm curate` now records per-item `entry_ref`).
|
|
106
|
+
- The fully-skipped `no_new_signal` branch emitted one `improve_skipped` event
|
|
107
|
+
per ref (~11K writes/run, ~400K rows/day) — a contributor to 900s improve
|
|
108
|
+
timeouts and state.db bloat. Collapsed into one aggregated counted event.
|
|
109
|
+
|
|
110
|
+
## [0.9.0-beta.8] - 2026-06-13
|
|
111
|
+
|
|
112
|
+
Fix multi-process SQLite contention in `index.db` and harden concurrent proposal
|
|
113
|
+
queue mutations.
|
|
114
|
+
|
|
115
|
+
### Changed
|
|
116
|
+
|
|
117
|
+
- Added a global `index.db` writer lease used by foreground indexing,
|
|
118
|
+
background auto-index, improve maintenance index writers, graph updates, and
|
|
119
|
+
feedback writes.
|
|
120
|
+
- Replaced the racy background index PID-file dedup flow with lease-based
|
|
121
|
+
coordination and explicit handoff to the spawned worker.
|
|
122
|
+
- `akm feedback` now uses blocking index preparation and writes under the same
|
|
123
|
+
`index.db` lease, avoiding self-inflicted `database is locked` failures.
|
|
124
|
+
- Proposal queue create/archive/gate-decision mutations now run under
|
|
125
|
+
`BEGIN IMMEDIATE` state.db transactions so concurrent processes serialize on
|
|
126
|
+
live queue state.
|
|
127
|
+
|
|
128
|
+
## [0.9.0-beta.7] - 2026-06-13
|
|
129
|
+
|
|
130
|
+
Fix the `akm improve` regression introduced by background `ensureIndex`.
|
|
131
|
+
|
|
132
|
+
### Changed
|
|
133
|
+
|
|
134
|
+
- Added an explicit `ensureIndex` mode so callers choose `background` or
|
|
135
|
+
`blocking` behavior directly instead of relying on hidden environment state.
|
|
136
|
+
- `akm improve` now uses blocking index preparation before collecting eligible
|
|
137
|
+
refs, restoring the post-upgrade empty-index recovery path.
|
|
138
|
+
- Removed the `AKM_INDEX_INLINE` test-only override so tests exercise the same
|
|
139
|
+
index behavior model as production.
|
|
140
|
+
|
|
141
|
+
## [0.9.0-beta.6] - 2026-06-12
|
|
142
|
+
|
|
143
|
+
Pipeline optimization: new per-process config fields wire up the consolidation
|
|
144
|
+
and improve pipeline knobs exposed by the optimization report — incremental
|
|
145
|
+
consolidation, pool caps, distill gating, and memory inference throttling.
|
|
146
|
+
|
|
147
|
+
### Added
|
|
148
|
+
|
|
149
|
+
- **`consolidate.incrementalSince`** — profile config field that narrows the
|
|
150
|
+
consolidation candidate pool to memories modified within the given window
|
|
151
|
+
(e.g. `"1h"`, `"4h"`) plus their graph neighbours. Enables frequent
|
|
152
|
+
consolidation passes (e.g. `quick-shredder` every 15 min) without full-pool
|
|
153
|
+
sweeps. Absent = full-pool sweep (correct for nightly runs).
|
|
154
|
+
- **`consolidate.limit`** — hard cap on memories processed per consolidation
|
|
155
|
+
pass, applied after incremental narrowing. Prevents runaway full-pool sweeps
|
|
156
|
+
in the nightly default profile.
|
|
157
|
+
- **`consolidate.neighborsPerChanged`** — configurable graph-neighbour count
|
|
158
|
+
per changed memory during incremental consolidation (was hardcoded to 5).
|
|
159
|
+
`quick-shredder` sets this to 3 for a 40% candidate reduction per burst.
|
|
160
|
+
- **`distill.requirePlannedRefs`** — when `true`, the distill process is
|
|
161
|
+
skipped entirely for distill-only refs when the reflect phase produced zero
|
|
162
|
+
planned refs. Eliminates hundreds of `distill-skipped` events on quiet passes
|
|
163
|
+
where all refs are on reflect cooldown.
|
|
164
|
+
- **`memoryInference.minPendingCount`** — minimum pending split-parent memory
|
|
165
|
+
count below which the inference pass is skipped entirely (zero LLM calls).
|
|
166
|
+
Prevents lock acquisition on passes where there is nothing to infer.
|
|
167
|
+
- **`reflect.limit`** — per-process ref limit for the reflect/distill loop,
|
|
168
|
+
applied as the improve run limit when no CLI `--limit` is given.
|
|
169
|
+
- **New `reflect-distill` improve profile** — dedicated reflect + distill +
|
|
170
|
+
memoryInference + triage profile for the every-4h `akm-improve-frequent`
|
|
171
|
+
task. `reflect.limit: 25` bounds LLM cost per pass.
|
|
172
|
+
|
|
173
|
+
### Changed
|
|
174
|
+
|
|
175
|
+
- **`quick-shredder` profile tuned**: `incrementalSince` `4h` → `1h`,
|
|
176
|
+
`maxChunkSize` 25 → 35, added `minPoolSize: 10`, `neighborsPerChanged: 3`,
|
|
177
|
+
`memoryInference.minPendingCount: 5`. All `profile: "qwen-9b-shredder"`
|
|
178
|
+
process references removed — falls back to default LLM.
|
|
179
|
+
- **`default` improve profile** (nightly): extract disabled (dedicated
|
|
180
|
+
`akm-extract` task runs at 01:48), consolidate gets `limit: 500`,
|
|
181
|
+
reflect gets `limit: 100` and `allowedTypes`, distill gets
|
|
182
|
+
`requirePlannedRefs: true`, triage enabled at 50 accepts/run,
|
|
183
|
+
graphExtraction explicitly enabled.
|
|
184
|
+
- **Cron schedule optimised**: extract reverted to `8,28,48 * * * *` (3×/hr),
|
|
185
|
+
quick-shredder shifted to `4,19,34,49` (4-min extract gap), health-report
|
|
186
|
+
shifted to `:03` (avoids `:00` collision), `akm-improve-frequent` re-enabled
|
|
187
|
+
at `45 */4` with `reflect-distill` profile.
|
|
188
|
+
|
|
189
|
+
## [0.9.0-beta.3] - 2026-06-12
|
|
190
|
+
|
|
191
|
+
Stabilization batch closing the remaining 0.9.0 milestone: DB-locking and
|
|
192
|
+
improve-pipeline perf backports, extract/reflect gate fixes, SQLite-first
|
|
193
|
+
proposal and log storage, `--format html` output, and per-stage LLM telemetry.
|
|
194
|
+
|
|
195
|
+
### Added
|
|
196
|
+
|
|
197
|
+
- **`--format html` output with per-command templates** (#582). `akm health
|
|
198
|
+
--format html` renders the full interactive health report (ECharts inlined by
|
|
199
|
+
default, or via CDN with `AKM_ECHARTS=cdn`); every other command falls back to
|
|
200
|
+
a dark-mode default template that pretty-prints its JSON. A global `--output
|
|
201
|
+
<path>` flag writes the rendered HTML to a file instead of stdout. Token
|
|
202
|
+
replacement only — no template engine. The standalone health-report skill is
|
|
203
|
+
now folded into core.
|
|
204
|
+
- **Per-stage LLM telemetry** (#576). Every `chatCompletion` call now records
|
|
205
|
+
tokens (prompt/completion/total/reasoning), wall-time, model, and
|
|
206
|
+
finish_reason as an `llm_usage` event, attributed to the pipeline stage via an
|
|
207
|
+
ambient `AsyncLocalStorage` context (`withLlmStage`) set once per phase — no
|
|
208
|
+
`stage` parameter threaded through call sites. `akm health` exposes per-stage
|
|
209
|
+
token and time aggregates. Telemetry is best-effort and can never fail a run;
|
|
210
|
+
capture is forward-only.
|
|
211
|
+
- **Per-proposal gate decision + confidence** (#577). When a proposal passes
|
|
212
|
+
through the auto-accept/triage gate, its outcome (`auto-accepted` /
|
|
213
|
+
`deferred` / `auto-rejected`), reason, confidence, measured value, and the
|
|
214
|
+
thresholds in effect are persisted on the proposal (in the SQLite metadata).
|
|
215
|
+
`akm proposal show`/`list` surface them with reconstructable comparisons
|
|
216
|
+
(e.g. `0.72 < 0.90`), so tooling can explain *why* each proposal is pending
|
|
217
|
+
instead of relying on a run-level aggregate. Forward-only; legacy proposals
|
|
218
|
+
render `unknown`.
|
|
219
|
+
|
|
220
|
+
### Fixed
|
|
221
|
+
|
|
222
|
+
- **`SQLITE_BUSY` / "database is locked" under concurrent runs** (#584, #585,
|
|
223
|
+
#589). `busy_timeout` raised from 5 s to 30 s on every SQLite open path
|
|
224
|
+
(index.db and state.db); the improve maintenance pass now closes its index.db
|
|
225
|
+
handle before each reindex (which opens its own writer to the same WAL file);
|
|
226
|
+
and the post-loop purge reuses the long-lived events connection instead of
|
|
227
|
+
opening a second state.db writer. Together these eliminate all observed
|
|
228
|
+
lock failures from overlapping cron improve runs. (Backports of 0.8.8.)
|
|
229
|
+
- **Extract gate ignored the active profile's `extract.enabled: false`** (#593,
|
|
230
|
+
#594). The session-extraction gate hardcoded the `default` profile, so a
|
|
231
|
+
non-default profile (e.g. a quick pass) ran extract anyway — 300–600 s of
|
|
232
|
+
redundant work per run when a dedicated extract task also exists. The gate
|
|
233
|
+
now resolves `extract` against the active improve profile. (Backport of
|
|
234
|
+
0.8.11.)
|
|
235
|
+
- **Memory inference burned LLM calls on already-derived parents** (#588). The
|
|
236
|
+
primary pass now checks for the `<parent>.derived.md` child on disk *before*
|
|
237
|
+
the LLM/cache call, and opportunistically marks the parent processed so it
|
|
238
|
+
never re-pends. Previously ~55 % of the inference budget was spent
|
|
239
|
+
rediscovering children that already existed.
|
|
240
|
+
- **Reflect no longer queues empty-diff or cosmetic-only proposals** (#580).
|
|
241
|
+
A deterministic, LLM-free noise gate diffs each candidate against the current
|
|
242
|
+
asset; byte-identical edits are dropped and changes that are pure formatting
|
|
243
|
+
(whitespace reflow, hard-wrap changes, code-fence language hints, YAML scalar
|
|
244
|
+
re-folding) are suppressed, each recorded via summary events so suppression
|
|
245
|
+
rates are visible in `akm health`.
|
|
246
|
+
|
|
247
|
+
### Added
|
|
248
|
+
|
|
249
|
+
- **`minContentChars` pre-LLM extract gate** (#595, #596). Sessions whose raw
|
|
250
|
+
size is below `profiles.improve.<name>.processes.extract.minContentChars`
|
|
251
|
+
(default 10 — only truly empty sessions/journal files) skip the extract LLM
|
|
252
|
+
call entirely. Gates on raw input size, not post-noise-filter size.
|
|
253
|
+
(Backports of 0.8.12–0.8.14.)
|
|
254
|
+
- **Structured logs database** (#579). Task and run log lines now land in a
|
|
255
|
+
dedicated `logs.db` (WAL, 30 s busy_timeout) keyed by task, run, stream, and
|
|
256
|
+
time, with retention/purge wired into the existing purge pass and `ATTACH`
|
|
257
|
+
support for joining log lines to `state.db` rows (e.g. a failed
|
|
258
|
+
`task_history` row to its log output). The scattered-log audit and per-source
|
|
259
|
+
keep/move/drop decisions are documented in `docs/technical/logs-audit.md`.
|
|
260
|
+
|
|
261
|
+
### Changed
|
|
262
|
+
|
|
263
|
+
- **Proposals are now stored canonically in SQLite** (#578). The previously
|
|
264
|
+
bypassed `proposals` table in state.db is the single source of truth; all
|
|
265
|
+
proposal commands (`list`/`show`/`diff`/`accept`/`reject`/`revert`/`drain`),
|
|
266
|
+
the improve auto-accept gate, and health metrics read and write it through
|
|
267
|
+
one storage layer. Pending file-based proposals are imported on first read;
|
|
268
|
+
`akm proposal *` UX is unchanged. Design and migration notes live in
|
|
269
|
+
`docs/technical/proposal-storage.md`.
|
|
270
|
+
- **Improve planning no longer does per-ref DB lookups or per-ref skip events**
|
|
271
|
+
(#591, #592). Eligible refs carry a pre-resolved `filePath`, removing a
|
|
272
|
+
serial async lookup per ref (~500 s on 9 k-ref stashes), and the
|
|
273
|
+
profile-filtered skip loop emits one summary event with a count instead of
|
|
274
|
+
thousands of rows. (Backports of 0.8.9–0.8.10.)
|
|
275
|
+
|
|
276
|
+
## [0.9.0-beta.2] - 2026-06-09
|
|
277
|
+
|
|
278
|
+
### Fixed
|
|
279
|
+
|
|
280
|
+
- **Consolidation starved merge recall; the memory pool grew unbounded.** Commit
|
|
281
|
+
`633ece41` made the `incrementalSince` narrowing unconditional, so every
|
|
282
|
+
consolidation run only judged memories changed since the last run plus their
|
|
283
|
+
immediate vector-neighbors. Stale-but-unmerged duplicate clusters were never
|
|
284
|
+
re-examined, so the eligible pool grew monotonically and never shrank, and
|
|
285
|
+
contradiction detection (which rides on the consolidation pass) went dark.
|
|
286
|
+
Consolidation only runs on the nightly default-profile pass (`quick`/`frequent`
|
|
287
|
+
disable it), so a full-pool sweep is correct and affordable; the override is
|
|
288
|
+
removed. `lastConsolidateTs` still gates whether the pass runs. (Forward-port
|
|
289
|
+
of the 0.8.5 fix.)
|
|
290
|
+
- **`akm tasks sync` ignored schedule changes** — forward-ported from 0.8.4.
|
|
291
|
+
Sync classified any task already present in the OS scheduler as "unchanged"
|
|
292
|
+
without comparing its installed entry, so editing a task's `schedule:` in the
|
|
293
|
+
`.yml` never reached the crontab; the same gap affected `tasks enable`/`disable`
|
|
294
|
+
(toggled the comment, re-enabling a stale schedule). Sync now compares the
|
|
295
|
+
backend's installed signature against the signature the current definition
|
|
296
|
+
renders to and reinstalls on drift (new `updated[]` field); `enable`/`disable`
|
|
297
|
+
reinstall from the current `.yml`. The cron backend gains `expectedSignature()`
|
|
298
|
+
and a per-entry signature on `list()`; other backends fall back to an
|
|
299
|
+
idempotent reinstall.
|
|
300
|
+
|
|
301
|
+
### Added
|
|
302
|
+
|
|
303
|
+
- **`akm improve --skip-if-locked`** — forward-ported from 0.8.4. When another
|
|
304
|
+
improve run already holds the lock, the run logs and exits 0 with a no-op
|
|
305
|
+
result (`skipped.reason: "lock-held"`) instead of failing with the "already
|
|
306
|
+
running" config error (exit 78). Intended for high-frequency scheduled runs
|
|
307
|
+
(e.g. an every-30-min `quick` pass) that overlap a longer run. Default off.
|
|
308
|
+
|
|
309
|
+
### Removed
|
|
310
|
+
|
|
311
|
+
- **`akm config edit`** — the interactive menu-based editor was removed. A
|
|
312
|
+
prompt-driven drill-down was clunkier than just editing the file. Edit the
|
|
313
|
+
config directly (the path is shown by `akm config path`), use
|
|
314
|
+
`akm config set/get/unset` for scripted changes, and `akm config validate` to
|
|
315
|
+
check it.
|
|
316
|
+
|
|
317
|
+
## [0.9.0-beta.1] - 2026-06-08
|
|
318
|
+
|
|
319
|
+
### Fixed
|
|
320
|
+
|
|
321
|
+
- **`improve.lock` leaked on signal death (cron timeout)** — forward-ported from
|
|
322
|
+
0.8.3. The improve SIGTERM/SIGINT/SIGHUP handler calls `process.exit()`, which
|
|
323
|
+
skips `finally` blocks, so the `finally` releasing `improve.lock` never ran and
|
|
324
|
+
every timed-out cron run leaked the lock. It is now released from a
|
|
325
|
+
`process.on("exit", …)` handler registered at acquire time, via a new
|
|
326
|
+
ownership-checked `releaseLockIfOwned(path, pid)`.
|
|
327
|
+
- **`quick` profile was not quick** — forward-ported from 0.8.3. It did not
|
|
328
|
+
disable the default-ON session-`extract` process, so a `quick` run processed
|
|
329
|
+
the entire session backlog (~40 min). `quick` now sets
|
|
330
|
+
`processes.extract.enabled: false`.
|
|
331
|
+
- **`akm-eval` smoke suite adapted to the 0.9.0 CLI** (CI/tooling only). The
|
|
332
|
+
eval harness called `akm search --detail agent`, but 0.9.0 moved the
|
|
333
|
+
agent/summary projections to `--shape`; it now uses `--shape agent`.
|
|
334
|
+
Additionally, the improve-run history readers (`listRecentImproveRunIds` /
|
|
335
|
+
`resolveImproveRunId`) treated a missing `state.db` as an error rather than
|
|
336
|
+
"no runs", which broke the read-only smoke + replay-determinism gates on a
|
|
337
|
+
fresh checkout; a missing `state.db` is now handled as an empty history.
|
|
338
|
+
|
|
9
339
|
## [0.9.0-beta.0] - 2026-06-08
|
|
10
340
|
|
|
11
341
|
### Added
|
|
@@ -191,6 +521,187 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
191
521
|
`migrate-storage` change is pinned by a sha256 + file-mode fixture-stash
|
|
192
522
|
differential test.
|
|
193
523
|
|
|
524
|
+
## [0.8.14] - 2026-06-11
|
|
525
|
+
|
|
526
|
+
### Fixed
|
|
527
|
+
|
|
528
|
+
- **`akm extract` minContentChars default lowered from 500 to 10.** The 500-char
|
|
529
|
+
threshold used inputCount (raw session size) but analysis showed 209 of 218
|
|
530
|
+
candidate-producing sessions had inputCount < 500 — tiny agent sessions (22–368
|
|
531
|
+
chars) regularly yield 1–5 candidates. The only reliably skippable sessions are
|
|
532
|
+
empty ones (0 chars, journal files). Default lowered to 10 to catch only
|
|
533
|
+
truly empty sessions while preserving all signal-bearing content. Closes #597.
|
|
534
|
+
|
|
535
|
+
## [0.8.13] - 2026-06-11
|
|
536
|
+
|
|
537
|
+
### Fixed
|
|
538
|
+
|
|
539
|
+
- **`akm extract` minContentChars gate filtered all sessions.** The threshold was
|
|
540
|
+
checked against `filtered.stats.outputCount` (post-noise-filter chars), but the
|
|
541
|
+
pre-filter strips so much boilerplate that even signal-bearing sessions end up
|
|
542
|
+
below 500 chars of output. All 75 sessions in the first post-deploy run were
|
|
543
|
+
filtered, dropping candidates from 4–13 to 0. Fix: gate on `inputCount` (raw
|
|
544
|
+
session size) instead — a session with < 500 raw chars has nothing worth
|
|
545
|
+
extracting regardless of what the pre-filter produces. Closes #596.
|
|
546
|
+
|
|
547
|
+
## [0.8.12] - 2026-06-11
|
|
548
|
+
|
|
549
|
+
### Fixed
|
|
550
|
+
|
|
551
|
+
- **`akm extract` calling the LLM for noise sessions that never yield candidates.**
|
|
552
|
+
96% of processed sessions (72/75 measured) produced zero candidates, consuming
|
|
553
|
+
~330 s of LLM time per run. The pre-filter had no minimum content threshold —
|
|
554
|
+
sessions as short as 50 chars were sent to the LLM regardless. A new
|
|
555
|
+
`minContentChars` gate (default 500) skips the LLM call when post-filter
|
|
556
|
+
content falls below threshold, cutting extract LLM calls by ~95% on typical
|
|
557
|
+
stashes. Configurable via `profiles.improve.<name>.processes.extract.minContentChars`.
|
|
558
|
+
Closes #595.
|
|
559
|
+
|
|
560
|
+
## [0.8.11] - 2026-06-11
|
|
561
|
+
|
|
562
|
+
### Fixed
|
|
563
|
+
|
|
564
|
+
- **`akm improve --profile <name>` ignored profile's `extract.enabled: false` setting.**
|
|
565
|
+
The session-extraction gate in the preparation stage called
|
|
566
|
+
`isLlmFeatureEnabled(config, "session_extraction")`, which hardcodes a lookup
|
|
567
|
+
against `profiles.improve.default.processes.extract.enabled`. Any non-default
|
|
568
|
+
profile that set `extract.enabled: false` (e.g. `quick-shredder`) was silently
|
|
569
|
+
ignored, causing the extract pass to run regardless. The fix adds a
|
|
570
|
+
`resolveProcessEnabled("extract", improveProfile)` check so the active
|
|
571
|
+
resolved profile gates the pass correctly. Closes #593.
|
|
572
|
+
|
|
573
|
+
## [0.8.10] - 2026-06-11
|
|
574
|
+
|
|
575
|
+
### Fixed
|
|
576
|
+
|
|
577
|
+
- **`akm improve` taking 8–10 minutes per run due to O(n) DB writes for
|
|
578
|
+
profile-filtered refs.** When a profile disables reflect and distill for
|
|
579
|
+
certain asset types, `collectEligibleRefs` marks those refs as
|
|
580
|
+
`profile_filtered_all_passes`. The caller then emitted one `improve_skipped`
|
|
581
|
+
event per ref — a sequential DB write for each. On a ~9 000-ref stash this
|
|
582
|
+
was ~500 s of SQLite writes before any consolidation or memory inference
|
|
583
|
+
began. The fix collapses the per-ref loop into a single summary event
|
|
584
|
+
carrying a `count` field, eliminating ~9 000 sequential writes per run.
|
|
585
|
+
Closes #590.
|
|
586
|
+
|
|
587
|
+
## [0.8.9] - 2026-06-11
|
|
588
|
+
|
|
589
|
+
### Fixed
|
|
590
|
+
|
|
591
|
+
- **`akm improve` validation pass was O(n) in stash size, causing ~510 s overhead
|
|
592
|
+
on large stashes.** For every indexed ref, the preparation phase called
|
|
593
|
+
`findAssetFilePath()` — an async round-trip to the index DB followed by a
|
|
594
|
+
filesystem probe — serially inside a `for…await` loop. With ~9 000 indexed
|
|
595
|
+
refs at ~55 ms each, this loop consumed the entire 600–900 s run budget before
|
|
596
|
+
any reflect, triage, or memory-inference work began. The fix threads
|
|
597
|
+
`filePath` from the planning stage (`collectEligibleRefs`) through
|
|
598
|
+
`ImproveEligibleRef` so the validation pass and the disk-existence guard can
|
|
599
|
+
use the pre-resolved path directly. The async lookup is retained only as a
|
|
600
|
+
fallback for refs that enter via a narrow scope (e.g. `--scope ref:foo`).
|
|
601
|
+
Closes #587.
|
|
602
|
+
|
|
603
|
+
## [0.8.8] - 2026-06-11
|
|
604
|
+
|
|
605
|
+
### Fixed
|
|
606
|
+
|
|
607
|
+
- **SQLite `SQLITE_BUSY` errors under concurrent improve runs.** `busy_timeout`
|
|
608
|
+
was set to 5 000 ms in all three database open paths (`openDatabase`,
|
|
609
|
+
`openExistingDatabase`, `openStateDatabase`). Under a busy cron schedule — or
|
|
610
|
+
when a reindex triggered by memory inference ran concurrently with an event
|
|
611
|
+
write — the 5 s window was routinely exhausted, producing "database is locked"
|
|
612
|
+
failures. Raised to 30 000 ms across all three paths so transient lock
|
|
613
|
+
contention is retried for up to 30 s before surfacing as an error.
|
|
614
|
+
|
|
615
|
+
## [0.8.7] - 2026-06-09
|
|
616
|
+
|
|
617
|
+
### Fixed
|
|
618
|
+
|
|
619
|
+
- **`incrementalSince` duration strings were silently ignored.** Values like
|
|
620
|
+
`"30m"`, `"24h"`, `"7d"` were passed raw to `narrowToIncrementalCandidates`,
|
|
621
|
+
which compared them against ISO timestamps via string sort. All `2026-...`
|
|
622
|
+
timestamps are lexicographically less than `"30m"` (`'2' < '3'`) and `"24h"`
|
|
623
|
+
(`"20" < "24"`), so `isChanged()` always returned `false` and the candidate
|
|
624
|
+
pool was silently emptied rather than filtered to the window. The fix adds
|
|
625
|
+
`parseSinceToIso()`, which resolves human duration strings to absolute ISO
|
|
626
|
+
timestamps before comparison. Values that already look like ISO timestamps
|
|
627
|
+
are passed through unchanged.
|
|
628
|
+
|
|
629
|
+
## [0.8.6] - 2026-06-09
|
|
630
|
+
|
|
631
|
+
### Added
|
|
632
|
+
|
|
633
|
+
- **`consolidate.incrementalSince` profile config field.** Setting
|
|
634
|
+
`incrementalSince: "7d"` (or any duration string) in the `consolidate` block
|
|
635
|
+
of an improve profile narrows the candidate pool to memories modified within
|
|
636
|
+
that window plus their top-5 graph neighbours, keeping each pass focused on
|
|
637
|
+
recent changes. This makes it practical to run consolidation more often than
|
|
638
|
+
once per day (e.g. via `akm-improve-consolidate` every 4 h) without
|
|
639
|
+
re-scanning the full pool every time. The nightly default profile leaves this
|
|
640
|
+
unset (full-pool sweep, same as before). The `incrementalSince` option already
|
|
641
|
+
existed in `akmConsolidate()` but was hardcoded off at the call site; the
|
|
642
|
+
field is now surfaced in the config schema and read from the profile.
|
|
643
|
+
|
|
644
|
+
## [0.8.5] - 2026-06-09
|
|
645
|
+
|
|
646
|
+
### Fixed
|
|
647
|
+
|
|
648
|
+
- **Consolidation starved merge recall; the memory pool grew unbounded.** Commit
|
|
649
|
+
`633ece41` made the `incrementalSince` narrowing unconditional, so every
|
|
650
|
+
consolidation run only judged memories changed since the last run plus their
|
|
651
|
+
immediate vector-neighbors. Stale-but-unmerged duplicate clusters were never
|
|
652
|
+
re-examined, so the eligible pool grew monotonically and never shrank, and
|
|
653
|
+
contradiction detection (which rides on the consolidation pass) went dark.
|
|
654
|
+
Consolidation only runs on the nightly default-profile pass (`quick`/`frequent`
|
|
655
|
+
disable it), so a full-pool sweep is correct and affordable; the override is
|
|
656
|
+
removed. `lastConsolidateTs` still gates whether the pass runs.
|
|
657
|
+
|
|
658
|
+
## [0.8.4] - 2026-06-08
|
|
659
|
+
|
|
660
|
+
### Fixed
|
|
661
|
+
|
|
662
|
+
- **`akm tasks sync` ignored schedule changes.** Sync classified any task already
|
|
663
|
+
present in the OS scheduler as "unchanged" without comparing its installed
|
|
664
|
+
entry, so editing a task's `schedule:` in the `.yml` never reached the crontab —
|
|
665
|
+
the only way to apply a new schedule was to `remove` and re-`add` the task. The
|
|
666
|
+
same gap affected `tasks enable`/`disable`, which merely toggled the existing
|
|
667
|
+
cron line's comment and so re-enabled a stale schedule. Sync now compares the
|
|
668
|
+
backend's installed signature against the signature the current definition would
|
|
669
|
+
produce and reinstalls on drift (reported in a new `updated[]` field);
|
|
670
|
+
`enable`/`disable` reinstall from the current `.yml` instead of toggling in
|
|
671
|
+
place. Backends that can't cheaply read their installed form fall back to an
|
|
672
|
+
idempotent reinstall, so the fix is correct on launchd/schtasks too. The cron
|
|
673
|
+
backend gains `expectedSignature()` and a signature on each `list()` entry.
|
|
674
|
+
|
|
675
|
+
### Added
|
|
676
|
+
|
|
677
|
+
- **`akm improve --skip-if-locked`.** When another improve run already holds the
|
|
678
|
+
lock, the run logs and exits 0 with a no-op result (`skipped.reason:
|
|
679
|
+
"lock-held"`) instead of failing with the "already running" config error
|
|
680
|
+
(exit 78). Intended for high-frequency scheduled runs (e.g. an every-30-min
|
|
681
|
+
`quick` pass) that would otherwise pile up exit-78 failures whenever a longer
|
|
682
|
+
run overlaps them. Default off — the hard error is preserved for interactive
|
|
683
|
+
use. The result is still recorded so the skip is auditable.
|
|
684
|
+
|
|
685
|
+
## [0.8.3] - 2026-06-08
|
|
686
|
+
|
|
687
|
+
### Fixed
|
|
688
|
+
|
|
689
|
+
- **`improve.lock` leaked on signal death (cron timeout).** The improve
|
|
690
|
+
SIGTERM/SIGINT/SIGHUP handler calls `process.exit()`, which skips `finally`
|
|
691
|
+
blocks — so the `finally` that releases `improve.lock` never ran, and every
|
|
692
|
+
timed-out cron run leaked the lock sentinel. (It wasn't a permanent deadlock
|
|
693
|
+
only because the next run reclaims a dead-PID lock, a path that PID reuse can
|
|
694
|
+
defeat.) The lock is now released from a `process.on("exit", …)` handler
|
|
695
|
+
registered at acquire time (exit handlers DO run on `process.exit()`), via a
|
|
696
|
+
new ownership-checked `releaseLockIfOwned(path, pid)` so a backstop release can
|
|
697
|
+
never delete a different run's lock. This generalizes to the budget watchdog
|
|
698
|
+
and any future exit path.
|
|
699
|
+
- **`quick` profile was not quick.** It was documented "Reflect-only" but did
|
|
700
|
+
not disable the session-`extract` process (which is default-ON), so a `quick`
|
|
701
|
+
run processed the entire unindexed-session backlog (~40 min) — guaranteeing a
|
|
702
|
+
5-minute cron timeout → SIGTERM → the lock leak above, every run. `quick` now
|
|
703
|
+
explicitly sets `processes.extract.enabled: false`.
|
|
704
|
+
|
|
194
705
|
## [0.8.2] - 2026-06-05
|
|
195
706
|
|
|
196
707
|
### Added
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
|
-
"description": "Reflect-only pass — no distill, consolidate, memoryInference, or graphExtraction.",
|
|
2
|
+
"description": "Reflect-only pass — no extract, distill, consolidate, memoryInference, or graphExtraction.",
|
|
3
3
|
"processes": {
|
|
4
4
|
"reflect": {
|
|
5
5
|
"enabled": true,
|
|
6
6
|
"allowedTypes": ["agent", "command", "knowledge", "lesson", "memory", "skill", "wiki", "workflow"]
|
|
7
7
|
},
|
|
8
|
+
"extract": { "enabled": false },
|
|
8
9
|
"distill": { "enabled": false },
|
|
9
10
|
"consolidate": { "enabled": false },
|
|
10
11
|
"memoryInference": { "enabled": false },
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>akm %%COMMAND%%</title>
|
|
7
|
+
<style>
|
|
8
|
+
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
9
|
+
|
|
10
|
+
:root {
|
|
11
|
+
--bg: #0d1117;
|
|
12
|
+
--surface: #161b22;
|
|
13
|
+
--border: #30363d;
|
|
14
|
+
--text: #e6edf3;
|
|
15
|
+
--muted: #8b949e;
|
|
16
|
+
--accent: #58a6ff;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
body {
|
|
20
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans', sans-serif;
|
|
21
|
+
background: var(--bg);
|
|
22
|
+
color: var(--text);
|
|
23
|
+
font-size: 14px;
|
|
24
|
+
line-height: 1.6;
|
|
25
|
+
padding: 24px;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
header {
|
|
29
|
+
max-width: 980px;
|
|
30
|
+
margin: 0 auto 16px;
|
|
31
|
+
display: flex;
|
|
32
|
+
align-items: baseline;
|
|
33
|
+
gap: 12px;
|
|
34
|
+
flex-wrap: wrap;
|
|
35
|
+
}
|
|
36
|
+
header .logo { font-size: 20px; font-weight: 700; color: var(--accent); letter-spacing: -0.5px; }
|
|
37
|
+
header .command { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; color: var(--muted); }
|
|
38
|
+
|
|
39
|
+
main { max-width: 980px; margin: 0 auto; }
|
|
40
|
+
|
|
41
|
+
pre {
|
|
42
|
+
background: var(--surface);
|
|
43
|
+
border: 1px solid var(--border);
|
|
44
|
+
border-radius: 8px;
|
|
45
|
+
padding: 16px 20px;
|
|
46
|
+
overflow-x: auto;
|
|
47
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
48
|
+
font-size: 13px;
|
|
49
|
+
white-space: pre-wrap;
|
|
50
|
+
word-break: break-word;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
footer {
|
|
54
|
+
max-width: 980px;
|
|
55
|
+
margin: 16px auto 0;
|
|
56
|
+
color: var(--muted);
|
|
57
|
+
font-size: 12px;
|
|
58
|
+
display: flex;
|
|
59
|
+
justify-content: space-between;
|
|
60
|
+
gap: 12px;
|
|
61
|
+
flex-wrap: wrap;
|
|
62
|
+
}
|
|
63
|
+
</style>
|
|
64
|
+
</head>
|
|
65
|
+
<body>
|
|
66
|
+
<header>
|
|
67
|
+
<span class="logo">akm</span>
|
|
68
|
+
<span class="command">%%COMMAND%%</span>
|
|
69
|
+
</header>
|
|
70
|
+
<main>
|
|
71
|
+
<pre>%%CONTENT_JSON%%</pre>
|
|
72
|
+
</main>
|
|
73
|
+
<footer>
|
|
74
|
+
<span>akm — Agent Knowledge Management</span>
|
|
75
|
+
<span>Generated %%GENERATED_AT%%</span>
|
|
76
|
+
</footer>
|
|
77
|
+
</body>
|
|
78
|
+
</html>
|