akm-cli 0.9.31 → 0.9.32
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 +11 -0
- package/dist/commands/feedback-cli.js +21 -13
- package/dist/core/errors.js +1 -0
- package/dist/indexer/usage/usage-events.js +6 -2
- package/dist/scripts/akm-migrate-node.js +1 -0
- package/dist/scripts/akm-migrate.js +1 -0
- package/docs/reference/data-and-telemetry.md +6 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,17 @@ 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.32] - 2026-10-13
|
|
10
|
+
|
|
11
|
+
A patch release: `akm feedback` no longer reports success for feedback it could not store.
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- **`akm feedback` fails when it cannot record the feedback.** When `state.db` was read-only to the process, as it is
|
|
16
|
+
inside an agent sandbox that only allows writes in the workspace, the feedback row was silently dropped and the
|
|
17
|
+
command still printed `"ok": true`. It now fails with a `DATA_DIR_NOT_WRITABLE` config error (exit 78) and records
|
|
18
|
+
nothing, so an agent knows its feedback was lost. Add akm's data directory to the sandbox's writable paths.
|
|
19
|
+
|
|
9
20
|
## [0.9.31] - 2026-10-09
|
|
10
21
|
|
|
11
22
|
A patch release: concurrent config writes no longer lose a change, OpenAI's reasoning models work as `llm`
|
|
@@ -13,16 +13,16 @@ import { parseFrontmatter, parseFrontmatterBlock, spliceFrontmatterLine } from "
|
|
|
13
13
|
import { conceptIdFromTypeName, parseRefInput, typeNameFromConceptId } from "../core/asset/resolve-ref.js";
|
|
14
14
|
import { isWithin, resolveStashDir, safeRealpath, writeFileAtomic } from "../core/common.js";
|
|
15
15
|
import { loadConfig } from "../core/config/config.js";
|
|
16
|
-
import { NotFoundError, UsageError } from "../core/errors.js";
|
|
16
|
+
import { ConfigError, NotFoundError, UsageError } from "../core/errors.js";
|
|
17
17
|
import { appendEvent } from "../core/events.js";
|
|
18
18
|
import { resolveMutationTarget } from "../core/mutation-target.js";
|
|
19
19
|
import { isPathAbsent } from "../core/path-access.js";
|
|
20
20
|
import { getDbPath } from "../core/paths.js";
|
|
21
|
-
import { withStateDb } from "../core/state-db.js";
|
|
21
|
+
import { getStateDbPath, withStateDb } from "../core/state-db.js";
|
|
22
22
|
import { warn } from "../core/warn.js";
|
|
23
23
|
import { withWriteTargetMutation } from "../core/write-source.js";
|
|
24
24
|
import { resolveSourceEntries } from "../indexer/search/search-source.js";
|
|
25
|
-
import { countFeedbackSignals,
|
|
25
|
+
import { countFeedbackSignals, insertUsageEventStrict, resolveUsageEventSource } from "../indexer/usage/usage-events.js";
|
|
26
26
|
import { resolveSourcesForOrigin } from "../registry/origin-resolve.js";
|
|
27
27
|
import { closeDatabase, openExistingDatabase } from "../storage/repositories/index-connection.js";
|
|
28
28
|
import { findEntryIdByRef, getEntryFilePathById, getItemRefById, } from "../storage/repositories/index-entries-repository.js";
|
|
@@ -157,8 +157,9 @@ function buildLessonStrengthUpdate(rawBytes, feedbackRef) {
|
|
|
157
157
|
* `task`, `audit`, `unknown`) — an anti-self-reinforcement guard that stops an
|
|
158
158
|
* agent or automated pipeline from boosting its own picks. This is by design
|
|
159
159
|
* and must not be removed; R-034 only asks that the skip be surfaced to the
|
|
160
|
-
* caller rather than passing silently.
|
|
161
|
-
* never fails the
|
|
160
|
+
* caller rather than passing silently. A failed usage-event write fails the
|
|
161
|
+
* command (DATA_DIR_NOT_WRITABLE); a utility-update failure never fails the
|
|
162
|
+
* feedback record.
|
|
162
163
|
*/
|
|
163
164
|
function recordFeedbackUsage(indexDb, entryId, durableEntryRef, signal, metadataStr) {
|
|
164
165
|
let utilityResult;
|
|
@@ -166,14 +167,21 @@ function recordFeedbackUsage(indexDb, entryId, durableEntryRef, signal, metadata
|
|
|
166
167
|
let rankingUpdateSkippedReason;
|
|
167
168
|
const eventSource = resolveUsageEventSource();
|
|
168
169
|
withStateDb((stateDb) => {
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
170
|
+
// The usage event IS the feedback record, so a failed write fails the command
|
|
171
|
+
// instead of reporting ok while nothing was stored.
|
|
172
|
+
try {
|
|
173
|
+
insertUsageEventStrict(stateDb, {
|
|
174
|
+
event_type: "feedback",
|
|
175
|
+
entry_ref: durableEntryRef,
|
|
176
|
+
entry_id: entryId,
|
|
177
|
+
signal,
|
|
178
|
+
metadata: metadataStr,
|
|
179
|
+
source: eventSource,
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
catch (err) {
|
|
183
|
+
throw new ConfigError(`Feedback was not recorded: could not write ${getStateDbPath()} (${err instanceof Error ? err.message : String(err)}).`, "DATA_DIR_NOT_WRITABLE");
|
|
184
|
+
}
|
|
177
185
|
if (eventSource !== "user") {
|
|
178
186
|
rankingUpdateSkippedReason =
|
|
179
187
|
`feedback source is "${eventSource}", not "user" — ranking updates only apply to user-attributed ` +
|
package/dist/core/errors.js
CHANGED
|
@@ -10,6 +10,7 @@ const CONFIG_HINTS = {
|
|
|
10
10
|
STASH_DIR_NOT_A_DIRECTORY: "The configured default bundle path exists but isn't a directory. Update it to point at a folder.",
|
|
11
11
|
STASH_DIR_UNREADABLE: "Check the path exists and your user has read permission, or update the default bundle path.",
|
|
12
12
|
DATA_DIR_UNREADABLE: "The data directory is not readable by the user running akm. Check its owner and mode, or point AKM_DATA_DIR / XDG_DATA_HOME somewhere this user owns.",
|
|
13
|
+
DATA_DIR_NOT_WRITABLE: "Nothing was recorded. Make the akm data directory writable for this process (for an agent sandbox, add it to the writable paths), or point AKM_DATA_DIR / XDG_DATA_HOME somewhere this user can write, then retry.",
|
|
13
14
|
INDEX_SCHEMA_INCOMPATIBLE: "Run `akm index --full` to rebuild the derived index from the currently materialized sources.",
|
|
14
15
|
LLM_NOT_CONFIGURED: 'Run `akm setup` or configure an `engines` entry with `kind: "llm"`, then select it with `defaults.llmEngine`.',
|
|
15
16
|
TEST_ISOLATION_MISSING: "Under bun test, when AKM_BUNDLE_DIR is set you MUST also set XDG_DATA_HOME (or AKM_DATA_DIR) and XDG_STATE_HOME (or AKM_STATE_DIR) to temp directories so the test does not touch the developer's real ~/.local/share/akm or ~/.local/state/akm.",
|
|
@@ -54,14 +54,18 @@ export function ensureUsageEventsSchema(db) {
|
|
|
54
54
|
*/
|
|
55
55
|
export function insertUsageEvent(db, event) {
|
|
56
56
|
try {
|
|
57
|
-
db
|
|
58
|
-
VALUES (?, ?, ?, ?, ?, ?, ?)`).run(event.event_type, event.query ?? null, event.entry_id ?? null, event.entry_ref ?? null, event.signal ?? null, event.metadata ?? null, event.source ?? "unknown");
|
|
57
|
+
insertUsageEventStrict(db, event);
|
|
59
58
|
}
|
|
60
59
|
catch (error) {
|
|
61
60
|
rethrowIfTestIsolationError(error);
|
|
62
61
|
/* fire-and-forget: silently ignore errors */
|
|
63
62
|
}
|
|
64
63
|
}
|
|
64
|
+
/** Insert a usage event and let a failed write throw, for callers whose record is the event itself. */
|
|
65
|
+
export function insertUsageEventStrict(db, event) {
|
|
66
|
+
db.prepare(`INSERT INTO usage_events (event_type, query, entry_id, entry_ref, signal, metadata, source)
|
|
67
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`).run(event.event_type, event.query ?? null, event.entry_id ?? null, event.entry_ref ?? null, event.signal ?? null, event.metadata ?? null, event.source ?? "unknown");
|
|
68
|
+
}
|
|
65
69
|
// ── Query ────────────────────────────────────────────────────────────────────
|
|
66
70
|
/**
|
|
67
71
|
* Aggregate positive/negative feedback counts for a single entry.
|
|
@@ -25664,6 +25664,7 @@ var CONFIG_HINTS = {
|
|
|
25664
25664
|
STASH_DIR_NOT_A_DIRECTORY: "The configured default bundle path exists but isn't a directory. Update it to point at a folder.",
|
|
25665
25665
|
STASH_DIR_UNREADABLE: "Check the path exists and your user has read permission, or update the default bundle path.",
|
|
25666
25666
|
DATA_DIR_UNREADABLE: "The data directory is not readable by the user running akm. Check its owner and mode, or point AKM_DATA_DIR / XDG_DATA_HOME somewhere this user owns.",
|
|
25667
|
+
DATA_DIR_NOT_WRITABLE: "Nothing was recorded. Make the akm data directory writable for this process (for an agent sandbox, add it to the writable paths), or point AKM_DATA_DIR / XDG_DATA_HOME somewhere this user can write, then retry.",
|
|
25667
25668
|
INDEX_SCHEMA_INCOMPATIBLE: "Run `akm index --full` to rebuild the derived index from the currently materialized sources.",
|
|
25668
25669
|
LLM_NOT_CONFIGURED: 'Run `akm setup` or configure an `engines` entry with `kind: "llm"`, then select it with `defaults.llmEngine`.',
|
|
25669
25670
|
TEST_ISOLATION_MISSING: "Under bun test, when AKM_BUNDLE_DIR is set you MUST also set XDG_DATA_HOME (or AKM_DATA_DIR) and XDG_STATE_HOME (or AKM_STATE_DIR) to temp directories so the test does not touch the developer's real ~/.local/share/akm or ~/.local/state/akm.",
|
|
@@ -24992,6 +24992,7 @@ var CONFIG_HINTS = {
|
|
|
24992
24992
|
STASH_DIR_NOT_A_DIRECTORY: "The configured default bundle path exists but isn't a directory. Update it to point at a folder.",
|
|
24993
24993
|
STASH_DIR_UNREADABLE: "Check the path exists and your user has read permission, or update the default bundle path.",
|
|
24994
24994
|
DATA_DIR_UNREADABLE: "The data directory is not readable by the user running akm. Check its owner and mode, or point AKM_DATA_DIR / XDG_DATA_HOME somewhere this user owns.",
|
|
24995
|
+
DATA_DIR_NOT_WRITABLE: "Nothing was recorded. Make the akm data directory writable for this process (for an agent sandbox, add it to the writable paths), or point AKM_DATA_DIR / XDG_DATA_HOME somewhere this user can write, then retry.",
|
|
24995
24996
|
INDEX_SCHEMA_INCOMPATIBLE: "Run `akm index --full` to rebuild the derived index from the currently materialized sources.",
|
|
24996
24997
|
LLM_NOT_CONFIGURED: 'Run `akm setup` or configure an `engines` entry with `kind: "llm"`, then select it with `defaults.llmEngine`.',
|
|
24997
24998
|
TEST_ISOLATION_MISSING: "Under bun test, when AKM_BUNDLE_DIR is set you MUST also set XDG_DATA_HOME (or AKM_DATA_DIR) and XDG_STATE_HOME (or AKM_STATE_DIR) to temp directories so the test does not touch the developer's real ~/.local/share/akm or ~/.local/state/akm.",
|
|
@@ -79,6 +79,12 @@ mode and owner, and the uid you are running as. They do **not** report an empty
|
|
|
79
79
|
index. `akm health` stays runnable in that state and reports it as a failing
|
|
80
80
|
`state-db-readable` check, so it remains the command to reach for.
|
|
81
81
|
|
|
82
|
+
If akm can read this directory but **cannot write** it — an agent sandbox that
|
|
83
|
+
only allows writes inside the workspace, for instance — `akm feedback` fails with
|
|
84
|
+
a `DATA_DIR_NOT_WRITABLE` config error (exit 78) and records nothing, instead of
|
|
85
|
+
reporting success for feedback it could not store. Add the data directory to the
|
|
86
|
+
sandbox's writable paths.
|
|
87
|
+
|
|
82
88
|
> **0.9.1 note.** A pre-release build briefly chmodded this directory to `0700`
|
|
83
89
|
> and the databases to `0600` on every open. That was reverted: it silently
|
|
84
90
|
> changed the permissions of directories akm did not create, which broke installs
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "akm-cli",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.32",
|
|
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": [
|