akm-cli 0.9.0-beta.52 → 0.9.0-beta.53
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/dist/assets/hints/cli-hints-full.md +6 -5
- package/dist/cli.js +0 -7
- package/dist/commands/env/env-cli.js +3 -2
- package/dist/commands/env/env.js +14 -67
- package/dist/commands/health/checks.js +28 -15
- package/dist/commands/health.js +68 -1
- package/dist/commands/improve/collapse-detector.js +419 -0
- package/dist/commands/improve/consolidate.js +72 -54
- package/dist/commands/improve/distill.js +79 -13
- package/dist/commands/improve/extract.js +13 -6
- package/dist/commands/improve/homeostatic.js +109 -79
- package/dist/commands/improve/improve-cli.js +67 -1
- package/dist/commands/improve/improve.js +10 -0
- package/dist/commands/improve/loop-stages.js +39 -1
- package/dist/commands/improve/outcome-loop.js +15 -3
- package/dist/commands/improve/preparation.js +17 -8
- package/dist/commands/improve/salience.js +49 -32
- package/dist/commands/read/curate.js +5 -9
- package/dist/commands/read/knowledge.js +4 -0
- package/dist/commands/read/search.js +5 -2
- package/dist/commands/read/show.js +3 -3
- package/dist/core/asset/asset-spec.js +3 -2
- package/dist/core/config/config-schema.js +39 -17
- package/dist/core/eval/rank-metrics.js +113 -0
- package/dist/core/state/migrations.js +56 -0
- package/dist/core/state-db.js +146 -19
- package/dist/indexer/ensure-index.js +33 -90
- package/dist/indexer/index-writer-lock.js +0 -11
- package/dist/indexer/index-written-assets.js +105 -0
- package/dist/indexer/passes/metadata.js +20 -0
- package/dist/indexer/search/db-search.js +29 -1
- package/dist/indexer/search/ranking-contributors.js +33 -1
- package/dist/indexer/search/ranking.js +66 -0
- package/dist/indexer/search/search-fields.js +6 -0
- package/dist/llm/feature-gate.js +6 -2
- package/dist/output/renderers.js +8 -13
- package/dist/output/shapes/helpers.js +0 -3
- package/dist/output/shapes/passthrough.js +1 -0
- package/dist/scripts/migrate-storage.js +152 -33
- package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +41 -18
- package/dist/storage/repositories/index-db.js +10 -1
- package/package.json +2 -4
|
@@ -1,8 +1,68 @@
|
|
|
1
1
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
2
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
3
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
+
import fs from "node:fs";
|
|
5
|
+
import { makeAssetRef } from "../../core/asset/asset-ref.js";
|
|
6
|
+
import { getStateDbPath } from "../../core/state-db.js";
|
|
7
|
+
import { openDatabase } from "../../storage/database.js";
|
|
4
8
|
import { getUtilityScoresByIds } from "../db/db.js";
|
|
5
9
|
import { applyScoreContributors, applyUtilityContributors } from "./ranking-contributors.js";
|
|
10
|
+
/**
|
|
11
|
+
* R2 — best-effort load of `asset_salience.rank_score` from state.db for the
|
|
12
|
+
* ranked items. Fail-open: any error (state.db locked by a concurrent improve
|
|
13
|
+
* run, missing table, unreadable path) returns an empty map, which makes the
|
|
14
|
+
* salience contributor a no-op — byte-identical to pre-R2 ranking.
|
|
15
|
+
*
|
|
16
|
+
* Deliberately NOT `openStateDatabase()`: that helper runs migrations and sets
|
|
17
|
+
* a 30 s busy timeout — too heavy for a search hot path. This opens read-only,
|
|
18
|
+
* never creates or migrates state.db (missing file / missing table = empty
|
|
19
|
+
* map), and caps lock waits at 250 ms so a concurrent improve run can only
|
|
20
|
+
* ever cost the search a quarter second, not a stall.
|
|
21
|
+
*/
|
|
22
|
+
export function loadSalienceRankScores(items) {
|
|
23
|
+
const result = new Map();
|
|
24
|
+
if (items.length === 0)
|
|
25
|
+
return result;
|
|
26
|
+
try {
|
|
27
|
+
const dbPath = getStateDbPath();
|
|
28
|
+
if (!fs.existsSync(dbPath))
|
|
29
|
+
return result; // improve loop has never run here
|
|
30
|
+
const idByRef = new Map();
|
|
31
|
+
for (const item of items) {
|
|
32
|
+
idByRef.set(makeAssetRef(item.entry.type, item.entry.name), item.id);
|
|
33
|
+
}
|
|
34
|
+
const stateDb = openDatabase(dbPath, { readonly: true });
|
|
35
|
+
try {
|
|
36
|
+
try {
|
|
37
|
+
stateDb.exec("PRAGMA busy_timeout = 250");
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
// pragma failure on a readonly handle is fine — default timeout applies
|
|
41
|
+
}
|
|
42
|
+
const refs = [...idByRef.keys()];
|
|
43
|
+
const CHUNK = 500;
|
|
44
|
+
for (let i = 0; i < refs.length; i += CHUNK) {
|
|
45
|
+
const chunk = refs.slice(i, i + CHUNK);
|
|
46
|
+
const placeholders = chunk.map(() => "?").join(",");
|
|
47
|
+
const rows = stateDb
|
|
48
|
+
.prepare(`SELECT asset_ref, rank_score FROM asset_salience WHERE asset_ref IN (${placeholders})`)
|
|
49
|
+
.all(...chunk);
|
|
50
|
+
for (const row of rows) {
|
|
51
|
+
const id = idByRef.get(row.asset_ref);
|
|
52
|
+
if (id !== undefined)
|
|
53
|
+
result.set(id, row.rank_score);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
finally {
|
|
58
|
+
stateDb.close();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// Fail open — search must never break because state.db is unavailable.
|
|
63
|
+
}
|
|
64
|
+
return result;
|
|
65
|
+
}
|
|
6
66
|
export function normalizeFtsScores(results) {
|
|
7
67
|
const ftsScoreMap = new Map();
|
|
8
68
|
if (results.length === 0)
|
|
@@ -71,12 +131,18 @@ export function applyRankingRules(options) {
|
|
|
71
131
|
applyScoreContributors(item, rankingContext);
|
|
72
132
|
}
|
|
73
133
|
const { global: utilScoresMap, scoped: scopedUtilScoresMap } = getUtilityScoresByIds(options.db, options.items.map((item) => item.id), options.scopeKey);
|
|
134
|
+
// R2 — compose the improve loop's salience into user-facing ranking.
|
|
135
|
+
// undefined = load from state.db (default); null = explicitly disabled.
|
|
136
|
+
const salienceRankScores = options.salienceRankScores === null
|
|
137
|
+
? new Map()
|
|
138
|
+
: (options.salienceRankScores ?? loadSalienceRankScores(options.items));
|
|
74
139
|
const utilityContext = {
|
|
75
140
|
...rankingContext,
|
|
76
141
|
utilityScores: utilScoresMap,
|
|
77
142
|
scopedUtilityScores: scopedUtilScoresMap,
|
|
78
143
|
utilityDecayConfig: options.utilityDecayConfig,
|
|
79
144
|
positiveFeedbackCounts: options.positiveFeedbackCounts,
|
|
145
|
+
salienceRankScores,
|
|
80
146
|
};
|
|
81
147
|
for (const item of options.items) {
|
|
82
148
|
applyUtilityContributors(item, utilityContext);
|
|
@@ -11,6 +11,12 @@
|
|
|
11
11
|
* - hints: searchHints + examples + usage + intent fields
|
|
12
12
|
* - content: TOC headings (lowest-weight catch-all)
|
|
13
13
|
*/
|
|
14
|
+
// NOTE (R5): the collapse detector's frozen canary queries are built from the
|
|
15
|
+
// same surface this function indexes (name tokens / tags / description) and
|
|
16
|
+
// scored via FTS against it. Changing what buildSearchFields includes shifts
|
|
17
|
+
// the detector's recall baseline for ALL existing canary sets — coordinate
|
|
18
|
+
// with src/commands/improve/collapse-detector.ts (buildCanaryQuery) and expect
|
|
19
|
+
// operators to re-mint via `akm improve canary --refresh` after such a change.
|
|
14
20
|
export function buildSearchFields(entry) {
|
|
15
21
|
const name = entry.name.replace(/[-_]/g, " ").toLowerCase();
|
|
16
22
|
const description = (entry.description ?? "").toLowerCase();
|
package/dist/llm/feature-gate.js
CHANGED
|
@@ -24,8 +24,12 @@ const FEATURE_LOCATION = {
|
|
|
24
24
|
metadata_enhance: (cfg) => cfg.index?.metadataEnhance?.enabled ?? false,
|
|
25
25
|
// Legacy default: false
|
|
26
26
|
curate_rerank: (cfg) => cfg.search?.curateRerank?.enabled ?? false,
|
|
27
|
-
//
|
|
28
|
-
|
|
27
|
+
// Default ON since R3 (docs/design/improve-self-learning-analysis.md G5):
|
|
28
|
+
// distill is a primary acquisition path and the judge fails open (no LLM /
|
|
29
|
+
// timeout / parse failure all pass through), so the gate only ever filters
|
|
30
|
+
// when a judge verdict actually exists. Opt out via
|
|
31
|
+
// profiles.improve.default.processes.distill.qualityGate.enabled: false.
|
|
32
|
+
lesson_quality_gate: (cfg) => cfg.profiles?.improve?.default?.processes?.distill?.qualityGate?.enabled ?? true,
|
|
29
33
|
// Legacy default: false
|
|
30
34
|
proposal_quality_gate: (cfg) => cfg.profiles?.improve?.default?.processes?.reflect?.qualityGate?.enabled ?? false,
|
|
31
35
|
// Legacy default: false
|
package/dist/output/renderers.js
CHANGED
|
@@ -364,23 +364,21 @@ const scriptSourceRenderer = {
|
|
|
364
364
|
};
|
|
365
365
|
// ── 8. env-file ───────────────────────────────────────────────────────────────
|
|
366
366
|
/**
|
|
367
|
-
* Env renderer. Returns ONLY key names
|
|
368
|
-
*
|
|
369
|
-
* through `akm show`.
|
|
367
|
+
* Env renderer. Returns ONLY key names — never values, and never comment
|
|
368
|
+
* text (comments routinely contain commented-out credentials). Deliberately
|
|
369
|
+
* omits content/template/prompt so env values cannot leak through `akm show`.
|
|
370
370
|
*/
|
|
371
371
|
const envFileRenderer = {
|
|
372
372
|
name: "env-file",
|
|
373
373
|
buildShowResponse(ctx) {
|
|
374
374
|
const name = deriveName(ctx);
|
|
375
|
-
const { keys
|
|
375
|
+
const { keys } = listVaultKeys(ctx.absPath);
|
|
376
376
|
return {
|
|
377
377
|
type: "env",
|
|
378
378
|
name,
|
|
379
379
|
path: ctx.absPath,
|
|
380
|
-
action: "Environment —
|
|
381
|
-
description: comments.length > 0 ? comments.join("\n") : undefined,
|
|
380
|
+
action: "Environment — key names only. Use `akm env run <ref> -- <command>` to run with the whole .env injected; prefer `--clean` to minimize inherited parent env. AKM itself does not print values, but child stdout/stderr is not redacted. `akm env export <ref> --out <file>` writes a sourceable script to a file. Never `source` the raw file. Values stay on disk and are never written to akm's stdout.",
|
|
382
381
|
keys,
|
|
383
|
-
comments,
|
|
384
382
|
};
|
|
385
383
|
},
|
|
386
384
|
enrichSearchHit(hit, _stashDir) {
|
|
@@ -647,12 +645,9 @@ function applyScriptMetadata(entry, ctx) {
|
|
|
647
645
|
}
|
|
648
646
|
}
|
|
649
647
|
function applyEnvMetadata(entry, ctx) {
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
entry.source = "comments";
|
|
654
|
-
entry.confidence = 0.7;
|
|
655
|
-
}
|
|
648
|
+
// Key names only — comment text must never reach description/search_text
|
|
649
|
+
// (comments routinely contain commented-out credentials).
|
|
650
|
+
const { keys } = listVaultKeys(ctx.absPath);
|
|
656
651
|
if (keys.length > 0) {
|
|
657
652
|
entry.searchHints = keys;
|
|
658
653
|
}
|
|
@@ -369,7 +369,6 @@ export function shapeShowOutput(result, detail, shape = "human") {
|
|
|
369
369
|
"workflowParameters",
|
|
370
370
|
"steps",
|
|
371
371
|
"keys",
|
|
372
|
-
"comments",
|
|
373
372
|
"related",
|
|
374
373
|
]);
|
|
375
374
|
}
|
|
@@ -385,7 +384,6 @@ export function shapeShowOutput(result, detail, shape = "human") {
|
|
|
385
384
|
"run",
|
|
386
385
|
"origin",
|
|
387
386
|
"keys",
|
|
388
|
-
"comments",
|
|
389
387
|
"related",
|
|
390
388
|
]);
|
|
391
389
|
}
|
|
@@ -411,7 +409,6 @@ export function shapeShowOutput(result, detail, shape = "human") {
|
|
|
411
409
|
"cwd",
|
|
412
410
|
"activeRun",
|
|
413
411
|
"keys",
|
|
414
|
-
"comments",
|
|
415
412
|
"related",
|
|
416
413
|
// path and editable are always projected so JSON consumers can locate and
|
|
417
414
|
// edit the asset without needing --detail full (QA #7).
|
|
@@ -468,21 +468,11 @@ function scanKeys(text) {
|
|
|
468
468
|
}
|
|
469
469
|
return keys;
|
|
470
470
|
}
|
|
471
|
-
function scanComments(text) {
|
|
472
|
-
const comments = [];
|
|
473
|
-
for (const line of text.split(/\r?\n/)) {
|
|
474
|
-
const trimmed = line.trimStart();
|
|
475
|
-
if (trimmed.startsWith("#")) {
|
|
476
|
-
comments.push(trimmed.slice(1).trimStart());
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
return comments;
|
|
480
|
-
}
|
|
481
471
|
function listKeys(envPath) {
|
|
482
472
|
if (!fs.existsSync(envPath))
|
|
483
|
-
return { keys: []
|
|
473
|
+
return { keys: [] };
|
|
484
474
|
const text = fs.readFileSync(envPath, "utf8");
|
|
485
|
-
return { keys: scanKeys(text)
|
|
475
|
+
return { keys: scanKeys(text) };
|
|
486
476
|
}
|
|
487
477
|
var import_dotenv, ASSIGN_RE;
|
|
488
478
|
var init_env = __esm(() => {
|
|
@@ -8325,12 +8315,7 @@ function applyScriptMetadata(entry, ctx) {
|
|
|
8325
8315
|
}
|
|
8326
8316
|
}
|
|
8327
8317
|
function applyEnvMetadata(entry, ctx) {
|
|
8328
|
-
const { keys
|
|
8329
|
-
if (comments.length > 0 && !entry.description) {
|
|
8330
|
-
entry.description = comments.join(" ").slice(0, 500);
|
|
8331
|
-
entry.source = "comments";
|
|
8332
|
-
entry.confidence = 0.7;
|
|
8333
|
-
}
|
|
8318
|
+
const { keys } = listKeys(ctx.absPath);
|
|
8334
8319
|
if (keys.length > 0) {
|
|
8335
8320
|
entry.searchHints = keys;
|
|
8336
8321
|
}
|
|
@@ -9401,6 +9386,44 @@ var init_migrations = __esm(() => {
|
|
|
9401
9386
|
up: `
|
|
9402
9387
|
ALTER TABLE asset_salience ADD COLUMN encoding_source TEXT DEFAULT NULL;
|
|
9403
9388
|
`
|
|
9389
|
+
},
|
|
9390
|
+
{
|
|
9391
|
+
id: "016-collapse-churn-detector",
|
|
9392
|
+
up: `
|
|
9393
|
+
CREATE TABLE IF NOT EXISTS canary_queries (
|
|
9394
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
9395
|
+
canary_set_id TEXT NOT NULL,
|
|
9396
|
+
anchor_ref TEXT NOT NULL,
|
|
9397
|
+
query TEXT NOT NULL,
|
|
9398
|
+
source TEXT NOT NULL DEFAULT 'auto',
|
|
9399
|
+
active INTEGER NOT NULL DEFAULT 1,
|
|
9400
|
+
created_at TEXT NOT NULL
|
|
9401
|
+
);
|
|
9402
|
+
CREATE INDEX IF NOT EXISTS idx_canary_queries_active
|
|
9403
|
+
ON canary_queries(active, canary_set_id);
|
|
9404
|
+
|
|
9405
|
+
CREATE TABLE IF NOT EXISTS improve_cycle_metrics (
|
|
9406
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
9407
|
+
run_id TEXT NOT NULL,
|
|
9408
|
+
ts TEXT NOT NULL,
|
|
9409
|
+
pass TEXT NOT NULL,
|
|
9410
|
+
canary_set_id TEXT NOT NULL,
|
|
9411
|
+
mean_recall REAL NOT NULL,
|
|
9412
|
+
mean_ndcg REAL NOT NULL,
|
|
9413
|
+
mean_mrr REAL NOT NULL,
|
|
9414
|
+
canary_ranks_json TEXT NOT NULL,
|
|
9415
|
+
store_total INTEGER NOT NULL,
|
|
9416
|
+
store_by_type_json TEXT NOT NULL,
|
|
9417
|
+
distinct_content_ratio REAL NOT NULL,
|
|
9418
|
+
mean_bigram_diversity REAL NOT NULL,
|
|
9419
|
+
over_generation_count INTEGER NOT NULL,
|
|
9420
|
+
accepted_actions INTEGER NOT NULL,
|
|
9421
|
+
merge_floor_violations INTEGER NOT NULL DEFAULT 0,
|
|
9422
|
+
alerts_json TEXT NOT NULL DEFAULT '[]'
|
|
9423
|
+
);
|
|
9424
|
+
CREATE INDEX IF NOT EXISTS idx_improve_cycle_metrics_ts
|
|
9425
|
+
ON improve_cycle_metrics(ts);
|
|
9426
|
+
`
|
|
9404
9427
|
}
|
|
9405
9428
|
];
|
|
9406
9429
|
});
|
|
@@ -9423,10 +9446,12 @@ __export(exports_state_db, {
|
|
|
9423
9446
|
recordFsProposalsImport: () => recordFsProposalsImport,
|
|
9424
9447
|
readStateEvents: () => readStateEvents,
|
|
9425
9448
|
queryTaskHistory: () => queryTaskHistory,
|
|
9449
|
+
queryRecentCycleMetrics: () => queryRecentCycleMetrics,
|
|
9426
9450
|
queryImproveRuns: () => queryImproveRuns,
|
|
9427
9451
|
queryCompletedTaskIntervals: () => queryCompletedTaskIntervals,
|
|
9428
9452
|
purgeOldImproveRuns: () => purgeOldImproveRuns,
|
|
9429
9453
|
purgeOldEvents: () => purgeOldEvents,
|
|
9454
|
+
purgeOldCycleMetrics: () => purgeOldCycleMetrics,
|
|
9430
9455
|
proposalToRowValues: () => proposalToRowValues,
|
|
9431
9456
|
proposalRowToProposal: () => proposalRowToProposal,
|
|
9432
9457
|
persistPhaseThreshold: () => persistPhaseThreshold,
|
|
@@ -9436,8 +9461,11 @@ __export(exports_state_db, {
|
|
|
9436
9461
|
listStateProposalIdsByPrefix: () => listStateProposalIdsByPrefix,
|
|
9437
9462
|
listProposalGateDecisions: () => listProposalGateDecisions,
|
|
9438
9463
|
listExistingTableNames: () => listExistingTableNames,
|
|
9464
|
+
listActiveCanarySetIds: () => listActiveCanarySetIds,
|
|
9439
9465
|
insertProposalIfAbsent: () => insertProposalIfAbsent,
|
|
9440
9466
|
insertEvent: () => insertEvent,
|
|
9467
|
+
insertCycleMetrics: () => insertCycleMetrics,
|
|
9468
|
+
insertCanaries: () => insertCanaries,
|
|
9441
9469
|
importEventsJsonl: () => importEventsJsonl,
|
|
9442
9470
|
hasImportedFsProposals: () => hasImportedFsProposals,
|
|
9443
9471
|
getTaskHistoryRuns: () => getTaskHistoryRuns,
|
|
@@ -9446,15 +9474,19 @@ __export(exports_state_db, {
|
|
|
9446
9474
|
getStateDbPath: () => getStateDbPath,
|
|
9447
9475
|
getRecombineHypothesis: () => getRecombineHypothesis,
|
|
9448
9476
|
getPhaseThreshold: () => getPhaseThreshold,
|
|
9477
|
+
getLatestCycleMetrics: () => getLatestCycleMetrics,
|
|
9449
9478
|
getLastExtractRunAt: () => getLastExtractRunAt,
|
|
9450
9479
|
getExtractedSessionsMap: () => getExtractedSessionsMap,
|
|
9451
9480
|
getExtractedSession: () => getExtractedSession,
|
|
9452
9481
|
getConsolidationJudgedMap: () => getConsolidationJudgedMap,
|
|
9482
|
+
getCanariesBySetId: () => getCanariesBySetId,
|
|
9453
9483
|
getBodyEmbeddings: () => getBodyEmbeddings,
|
|
9484
|
+
getActiveCanaries: () => getActiveCanaries,
|
|
9454
9485
|
findMatchingRecombineHypothesis: () => findMatchingRecombineHypothesis,
|
|
9455
9486
|
eventRowToEnvelope: () => eventRowToEnvelope,
|
|
9456
9487
|
embeddingToBlob: () => embeddingToBlob,
|
|
9457
9488
|
decayUnseenRecombineHypotheses: () => decayUnseenRecombineHypotheses,
|
|
9489
|
+
deactivateCanarySet: () => deactivateCanarySet,
|
|
9458
9490
|
computeImproveRunMetrics: () => computeImproveRunMetrics,
|
|
9459
9491
|
blobToEmbedding: () => blobToEmbedding
|
|
9460
9492
|
});
|
|
@@ -9686,7 +9718,7 @@ function insertProposalIfAbsent(db, proposal, stashDir) {
|
|
|
9686
9718
|
}
|
|
9687
9719
|
function isRetryableBeginError(err) {
|
|
9688
9720
|
const msg = (err instanceof Error ? err.message : String(err)).toLowerCase();
|
|
9689
|
-
return msg.includes("
|
|
9721
|
+
return msg.includes("database is locked") || msg.includes("database table is locked") || msg.includes("did not open a transaction");
|
|
9690
9722
|
}
|
|
9691
9723
|
function sleepSyncMs(ms) {
|
|
9692
9724
|
if (ms <= 0)
|
|
@@ -9694,6 +9726,9 @@ function sleepSyncMs(ms) {
|
|
|
9694
9726
|
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
9695
9727
|
}
|
|
9696
9728
|
function withImmediateTransaction(db, fn) {
|
|
9729
|
+
if (db.inTransaction) {
|
|
9730
|
+
return fn();
|
|
9731
|
+
}
|
|
9697
9732
|
let lastBeginErr;
|
|
9698
9733
|
for (let attempt = 1;attempt <= WITH_IMMEDIATE_TX_MAX_ATTEMPTS; attempt++) {
|
|
9699
9734
|
try {
|
|
@@ -9704,9 +9739,11 @@ function withImmediateTransaction(db, fn) {
|
|
|
9704
9739
|
} catch (err) {
|
|
9705
9740
|
lastBeginErr = err;
|
|
9706
9741
|
if (isRetryableBeginError(err) && attempt < WITH_IMMEDIATE_TX_MAX_ATTEMPTS) {
|
|
9707
|
-
|
|
9708
|
-
|
|
9709
|
-
|
|
9742
|
+
if (db.inTransaction) {
|
|
9743
|
+
try {
|
|
9744
|
+
db.exec("ROLLBACK");
|
|
9745
|
+
} catch {}
|
|
9746
|
+
}
|
|
9710
9747
|
sleepSyncMs(2 ** (attempt - 1));
|
|
9711
9748
|
continue;
|
|
9712
9749
|
}
|
|
@@ -9714,12 +9751,17 @@ function withImmediateTransaction(db, fn) {
|
|
|
9714
9751
|
}
|
|
9715
9752
|
try {
|
|
9716
9753
|
const result = fn();
|
|
9754
|
+
if (!db.inTransaction) {
|
|
9755
|
+
throw new Error("withImmediateTransaction invariant violated: transaction opened by BEGIN IMMEDIATE was no longer active after the transaction body ran; refusing to COMMIT (writes may have escaped serialization)");
|
|
9756
|
+
}
|
|
9717
9757
|
db.exec("COMMIT");
|
|
9718
9758
|
return result;
|
|
9719
9759
|
} catch (err) {
|
|
9720
|
-
|
|
9721
|
-
|
|
9722
|
-
|
|
9760
|
+
if (db.inTransaction) {
|
|
9761
|
+
try {
|
|
9762
|
+
db.exec("ROLLBACK");
|
|
9763
|
+
} catch {}
|
|
9764
|
+
}
|
|
9723
9765
|
throw err;
|
|
9724
9766
|
}
|
|
9725
9767
|
}
|
|
@@ -10091,6 +10133,75 @@ function upsertBodyEmbeddings(db, entries) {
|
|
|
10091
10133
|
}
|
|
10092
10134
|
})();
|
|
10093
10135
|
}
|
|
10136
|
+
function insertCanaries(db, canarySetId, canaries, now) {
|
|
10137
|
+
if (canaries.length === 0)
|
|
10138
|
+
return;
|
|
10139
|
+
const ts = now ?? new Date().toISOString();
|
|
10140
|
+
const stmt = db.prepare(`
|
|
10141
|
+
INSERT INTO canary_queries (canary_set_id, anchor_ref, query, source, active, created_at)
|
|
10142
|
+
VALUES (?, ?, ?, ?, 1, ?)
|
|
10143
|
+
`);
|
|
10144
|
+
db.transaction(() => {
|
|
10145
|
+
for (const c of canaries) {
|
|
10146
|
+
stmt.run(canarySetId, c.anchorRef, c.query, c.source ?? "auto", ts);
|
|
10147
|
+
}
|
|
10148
|
+
})();
|
|
10149
|
+
}
|
|
10150
|
+
function getActiveCanaries(db) {
|
|
10151
|
+
return db.prepare(`SELECT * FROM canary_queries
|
|
10152
|
+
WHERE active = 1 AND canary_set_id = (
|
|
10153
|
+
SELECT canary_set_id FROM canary_queries WHERE active = 1
|
|
10154
|
+
ORDER BY created_at DESC, id DESC LIMIT 1
|
|
10155
|
+
)
|
|
10156
|
+
ORDER BY id`).all();
|
|
10157
|
+
}
|
|
10158
|
+
function getCanariesBySetId(db, canarySetId) {
|
|
10159
|
+
return db.prepare(`SELECT * FROM canary_queries WHERE canary_set_id = ? ORDER BY id`).all(canarySetId);
|
|
10160
|
+
}
|
|
10161
|
+
function listActiveCanarySetIds(db) {
|
|
10162
|
+
const rows = db.prepare(`SELECT DISTINCT canary_set_id FROM canary_queries WHERE active = 1`).all();
|
|
10163
|
+
return rows.map((r) => r.canary_set_id);
|
|
10164
|
+
}
|
|
10165
|
+
function deactivateCanarySet(db, canarySetId) {
|
|
10166
|
+
const result = db.prepare(`UPDATE canary_queries SET active = 0 WHERE canary_set_id = ? AND active = 1`).run(canarySetId);
|
|
10167
|
+
const changes = result.changes ?? 0;
|
|
10168
|
+
return typeof changes === "bigint" ? Number(changes) : changes;
|
|
10169
|
+
}
|
|
10170
|
+
function insertCycleMetrics(db, row) {
|
|
10171
|
+
db.prepare(`
|
|
10172
|
+
INSERT INTO improve_cycle_metrics
|
|
10173
|
+
(run_id, ts, pass, canary_set_id, mean_recall, mean_ndcg, mean_mrr,
|
|
10174
|
+
canary_ranks_json, store_total, store_by_type_json, distinct_content_ratio,
|
|
10175
|
+
mean_bigram_diversity, over_generation_count, accepted_actions,
|
|
10176
|
+
merge_floor_violations, alerts_json)
|
|
10177
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
10178
|
+
`).run(row.run_id, row.ts, row.pass, row.canary_set_id, row.mean_recall, row.mean_ndcg, row.mean_mrr, row.canary_ranks_json, row.store_total, row.store_by_type_json, row.distinct_content_ratio, row.mean_bigram_diversity, row.over_generation_count, row.accepted_actions, row.merge_floor_violations, row.alerts_json);
|
|
10179
|
+
}
|
|
10180
|
+
function queryRecentCycleMetrics(db, canarySetId, limit) {
|
|
10181
|
+
const rows = db.prepare(`SELECT run_id, ts, pass, canary_set_id, mean_recall, mean_ndcg, mean_mrr,
|
|
10182
|
+
canary_ranks_json, store_total, store_by_type_json, distinct_content_ratio,
|
|
10183
|
+
mean_bigram_diversity, over_generation_count, accepted_actions,
|
|
10184
|
+
merge_floor_violations, alerts_json
|
|
10185
|
+
FROM improve_cycle_metrics WHERE canary_set_id = ?
|
|
10186
|
+
ORDER BY ts DESC, id DESC LIMIT ?`).all(canarySetId, Math.max(0, limit));
|
|
10187
|
+
return rows.reverse();
|
|
10188
|
+
}
|
|
10189
|
+
function getLatestCycleMetrics(db) {
|
|
10190
|
+
const row = db.prepare(`SELECT run_id, ts, pass, canary_set_id, mean_recall, mean_ndcg, mean_mrr,
|
|
10191
|
+
canary_ranks_json, store_total, store_by_type_json, distinct_content_ratio,
|
|
10192
|
+
mean_bigram_diversity, over_generation_count, accepted_actions,
|
|
10193
|
+
merge_floor_violations, alerts_json
|
|
10194
|
+
FROM improve_cycle_metrics ORDER BY ts DESC, id DESC LIMIT 1`).get();
|
|
10195
|
+
return row == null ? undefined : row;
|
|
10196
|
+
}
|
|
10197
|
+
function purgeOldCycleMetrics(db, retentionDays = 365) {
|
|
10198
|
+
if (!Number.isFinite(retentionDays) || retentionDays <= 0)
|
|
10199
|
+
return 0;
|
|
10200
|
+
const cutoff = new Date(Date.now() - retentionDays * 86400000).toISOString();
|
|
10201
|
+
const result = db.prepare("DELETE FROM improve_cycle_metrics WHERE ts < ?").run(cutoff);
|
|
10202
|
+
const changes = result.changes ?? 0;
|
|
10203
|
+
return typeof changes === "bigint" ? Number(changes) : changes;
|
|
10204
|
+
}
|
|
10094
10205
|
var WITH_IMMEDIATE_TX_MAX_ATTEMPTS = 5;
|
|
10095
10206
|
var init_state_db = __esm(() => {
|
|
10096
10207
|
init_managed_db();
|
|
@@ -15790,7 +15901,7 @@ var init_config_types = __esm(() => {
|
|
|
15790
15901
|
});
|
|
15791
15902
|
|
|
15792
15903
|
// src/core/config/config-schema.ts
|
|
15793
|
-
var positiveInt, nonNegativeNumber, nonEmptyString, httpUrl, LlmCapabilitiesSchema, LlmConnectionConfigSchema, LlmProfileConfigSchema, EmbeddingOllamaOptionsSchema, EmbeddingConnectionConfigSchema, AgentPlatformSchema, AgentProfileConfigSchema, ImproveProcessConfigSchema, ImproveProfileProcessesSchema, ImproveProfileConfigSchema, ProfilesSchema, DefaultsSchema, SourceConfigEntryOptionsSchema, SourceConfigEntrySchema, RegistryConfigEntrySchema, KitSourceSchema, InstalledStashEntrySchema, OutputConfigSchema, SearchGraphBoostSchema, SearchConfigSchema, FeedbackConfigSchema, ImproveUtilityDecaySchema, ImproveCalibrationSchema, ImproveExplorationSchema, ImproveSalienceSchema, ImproveConfigSchema, GRAPH_EXTRACTION_INCLUDE_TYPES_ALLOWED, INDEX_PASS_PROVIDER_KEYS, INDEX_PASS_KNOWN_KEYS, IndexPassConfigSchema, MetadataEnhanceSchema, StalenessDetectionSchema, IndexConfigSchema, SetupTaskSchedulesSchema, SetupConfigSchema, AkmConfigShape, AkmConfigBaseSchema, AkmConfigSchema;
|
|
15904
|
+
var positiveInt, nonNegativeNumber, nonEmptyString, httpUrl, LlmCapabilitiesSchema, LlmConnectionConfigSchema, LlmProfileConfigSchema, EmbeddingOllamaOptionsSchema, EmbeddingConnectionConfigSchema, AgentPlatformSchema, AgentProfileConfigSchema, ImproveProcessConfigSchema, ImproveProfileProcessesSchema, ImproveProfileConfigSchema, ProfilesSchema, DefaultsSchema, SourceConfigEntryOptionsSchema, SourceConfigEntrySchema, RegistryConfigEntrySchema, KitSourceSchema, InstalledStashEntrySchema, OutputConfigSchema, SearchGraphBoostSchema, SearchConfigSchema, FeedbackConfigSchema, ImproveUtilityDecaySchema, ImproveCalibrationSchema, ImproveExplorationSchema, ImproveSalienceSchema, ImproveCollapseDetectorSchema, ImproveConfigSchema, GRAPH_EXTRACTION_INCLUDE_TYPES_ALLOWED, INDEX_PASS_PROVIDER_KEYS, INDEX_PASS_KNOWN_KEYS, IndexPassConfigSchema, MetadataEnhanceSchema, StalenessDetectionSchema, IndexConfigSchema, SetupTaskSchedulesSchema, SetupConfigSchema, AkmConfigShape, AkmConfigBaseSchema, AkmConfigSchema;
|
|
15794
15905
|
var init_config_schema = __esm(() => {
|
|
15795
15906
|
init_zod();
|
|
15796
15907
|
init_config_types();
|
|
@@ -15883,11 +15994,6 @@ var init_config_schema = __esm(() => {
|
|
|
15883
15994
|
indexSessions: exports_external.boolean().optional(),
|
|
15884
15995
|
minSessionDuration: exports_external.number().min(0).optional(),
|
|
15885
15996
|
p90ChunkSecondsDefault: exports_external.number().finite().positive().optional(),
|
|
15886
|
-
homeostaticDemotion: exports_external.object({
|
|
15887
|
-
enabled: exports_external.boolean().optional(),
|
|
15888
|
-
staleDays: exports_external.number().int().min(0).optional(),
|
|
15889
|
-
demotionFactor: exports_external.number().min(0).max(1).optional()
|
|
15890
|
-
}).passthrough().optional(),
|
|
15891
15997
|
schemaSimilarity: exports_external.object({
|
|
15892
15998
|
enabled: exports_external.boolean().optional(),
|
|
15893
15999
|
epsilon: exports_external.number().min(0).max(1).optional(),
|
|
@@ -15900,7 +16006,9 @@ var init_config_schema = __esm(() => {
|
|
|
15900
16006
|
enabled: exports_external.boolean().optional(),
|
|
15901
16007
|
maxGeneration: exports_external.number().int().min(1).optional(),
|
|
15902
16008
|
lexicalDiversityCheck: exports_external.boolean().optional(),
|
|
15903
|
-
randomClusterFraction: exports_external.number().min(0).max(1).optional()
|
|
16009
|
+
randomClusterFraction: exports_external.number().min(0).max(1).optional(),
|
|
16010
|
+
mergeInformationFloor: exports_external.boolean().optional(),
|
|
16011
|
+
minSpecificityRetention: exports_external.number().min(0).max(1).optional()
|
|
15904
16012
|
}).passthrough().optional(),
|
|
15905
16013
|
cls: exports_external.object({
|
|
15906
16014
|
enabled: exports_external.boolean().optional(),
|
|
@@ -16068,12 +16176,23 @@ var init_config_schema = __esm(() => {
|
|
|
16068
16176
|
salienceThreshold: exports_external.number().min(0).max(1).optional(),
|
|
16069
16177
|
replayBudget: exports_external.number().int().min(0).optional()
|
|
16070
16178
|
}).passthrough();
|
|
16179
|
+
ImproveCollapseDetectorSchema = exports_external.object({
|
|
16180
|
+
enabled: exports_external.boolean().optional(),
|
|
16181
|
+
canaryCount: exports_external.number().int().min(3).max(200).optional(),
|
|
16182
|
+
k: exports_external.number().int().min(1).max(100).optional(),
|
|
16183
|
+
windowCycles: exports_external.number().int().min(2).max(50).optional(),
|
|
16184
|
+
recallDropThreshold: exports_external.number().min(0).max(1).optional(),
|
|
16185
|
+
entropyDropThreshold: exports_external.number().min(0).max(1).optional(),
|
|
16186
|
+
churnMinAcceptedActions: exports_external.number().int().min(1).optional(),
|
|
16187
|
+
retentionDays: exports_external.number().int().min(1).optional()
|
|
16188
|
+
}).passthrough();
|
|
16071
16189
|
ImproveConfigSchema = exports_external.object({
|
|
16072
16190
|
utilityDecay: ImproveUtilityDecaySchema.optional(),
|
|
16073
16191
|
eventRetentionDays: nonNegativeNumber.optional(),
|
|
16074
16192
|
calibration: ImproveCalibrationSchema.optional(),
|
|
16075
16193
|
exploration: ImproveExplorationSchema.optional(),
|
|
16076
|
-
salience: ImproveSalienceSchema.optional()
|
|
16194
|
+
salience: ImproveSalienceSchema.optional(),
|
|
16195
|
+
collapseDetector: ImproveCollapseDetectorSchema.optional()
|
|
16077
16196
|
}).passthrough();
|
|
16078
16197
|
GRAPH_EXTRACTION_INCLUDE_TYPES_ALLOWED = [
|
|
16079
16198
|
"memory",
|
|
@@ -455,21 +455,11 @@ function scanKeys(text) {
|
|
|
455
455
|
}
|
|
456
456
|
return keys;
|
|
457
457
|
}
|
|
458
|
-
function scanComments(text) {
|
|
459
|
-
const comments = [];
|
|
460
|
-
for (const line of text.split(/\r?\n/)) {
|
|
461
|
-
const trimmed = line.trimStart();
|
|
462
|
-
if (trimmed.startsWith("#")) {
|
|
463
|
-
comments.push(trimmed.slice(1).trimStart());
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
return comments;
|
|
467
|
-
}
|
|
468
458
|
function listKeys(envPath) {
|
|
469
459
|
if (!fs3.existsSync(envPath))
|
|
470
|
-
return { keys: []
|
|
460
|
+
return { keys: [] };
|
|
471
461
|
const text = fs3.readFileSync(envPath, "utf8");
|
|
472
|
-
return { keys: scanKeys(text)
|
|
462
|
+
return { keys: scanKeys(text) };
|
|
473
463
|
}
|
|
474
464
|
var import_dotenv, ASSIGN_RE;
|
|
475
465
|
var init_env = __esm(() => {
|
|
@@ -8211,12 +8201,7 @@ function applyScriptMetadata(entry, ctx) {
|
|
|
8211
8201
|
}
|
|
8212
8202
|
}
|
|
8213
8203
|
function applyEnvMetadata(entry, ctx) {
|
|
8214
|
-
const { keys
|
|
8215
|
-
if (comments.length > 0 && !entry.description) {
|
|
8216
|
-
entry.description = comments.join(" ").slice(0, 500);
|
|
8217
|
-
entry.source = "comments";
|
|
8218
|
-
entry.confidence = 0.7;
|
|
8219
|
-
}
|
|
8204
|
+
const { keys } = listKeys(ctx.absPath);
|
|
8220
8205
|
if (keys.length > 0) {
|
|
8221
8206
|
entry.searchHints = keys;
|
|
8222
8207
|
}
|
|
@@ -9142,6 +9127,44 @@ var MIGRATIONS = [
|
|
|
9142
9127
|
up: `
|
|
9143
9128
|
ALTER TABLE asset_salience ADD COLUMN encoding_source TEXT DEFAULT NULL;
|
|
9144
9129
|
`
|
|
9130
|
+
},
|
|
9131
|
+
{
|
|
9132
|
+
id: "016-collapse-churn-detector",
|
|
9133
|
+
up: `
|
|
9134
|
+
CREATE TABLE IF NOT EXISTS canary_queries (
|
|
9135
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
9136
|
+
canary_set_id TEXT NOT NULL,
|
|
9137
|
+
anchor_ref TEXT NOT NULL,
|
|
9138
|
+
query TEXT NOT NULL,
|
|
9139
|
+
source TEXT NOT NULL DEFAULT 'auto',
|
|
9140
|
+
active INTEGER NOT NULL DEFAULT 1,
|
|
9141
|
+
created_at TEXT NOT NULL
|
|
9142
|
+
);
|
|
9143
|
+
CREATE INDEX IF NOT EXISTS idx_canary_queries_active
|
|
9144
|
+
ON canary_queries(active, canary_set_id);
|
|
9145
|
+
|
|
9146
|
+
CREATE TABLE IF NOT EXISTS improve_cycle_metrics (
|
|
9147
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
9148
|
+
run_id TEXT NOT NULL,
|
|
9149
|
+
ts TEXT NOT NULL,
|
|
9150
|
+
pass TEXT NOT NULL,
|
|
9151
|
+
canary_set_id TEXT NOT NULL,
|
|
9152
|
+
mean_recall REAL NOT NULL,
|
|
9153
|
+
mean_ndcg REAL NOT NULL,
|
|
9154
|
+
mean_mrr REAL NOT NULL,
|
|
9155
|
+
canary_ranks_json TEXT NOT NULL,
|
|
9156
|
+
store_total INTEGER NOT NULL,
|
|
9157
|
+
store_by_type_json TEXT NOT NULL,
|
|
9158
|
+
distinct_content_ratio REAL NOT NULL,
|
|
9159
|
+
mean_bigram_diversity REAL NOT NULL,
|
|
9160
|
+
over_generation_count INTEGER NOT NULL,
|
|
9161
|
+
accepted_actions INTEGER NOT NULL,
|
|
9162
|
+
merge_floor_violations INTEGER NOT NULL DEFAULT 0,
|
|
9163
|
+
alerts_json TEXT NOT NULL DEFAULT '[]'
|
|
9164
|
+
);
|
|
9165
|
+
CREATE INDEX IF NOT EXISTS idx_improve_cycle_metrics_ts
|
|
9166
|
+
ON improve_cycle_metrics(ts);
|
|
9167
|
+
`
|
|
9145
9168
|
}
|
|
9146
9169
|
];
|
|
9147
9170
|
function runMigrations2(db) {
|
|
@@ -3,6 +3,12 @@
|
|
|
3
3
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
4
|
import { closeDatabase, openExistingDatabase } from "../../indexer/db/db.js";
|
|
5
5
|
import { resolveStorageLocations } from "../locations.js";
|
|
6
|
+
/**
|
|
7
|
+
* Busy-timeout (ms) for read-path telemetry writers. Small on purpose: a
|
|
8
|
+
* usage-event insert contending with a background reindex should be dropped,
|
|
9
|
+
* not waited on for the default 30s.
|
|
10
|
+
*/
|
|
11
|
+
export const TELEMETRY_BUSY_TIMEOUT_MS = 250;
|
|
6
12
|
/**
|
|
7
13
|
* Scoped-resource (loan pattern) helper for the index database (`index.db`).
|
|
8
14
|
*
|
|
@@ -32,9 +38,12 @@ import { resolveStorageLocations } from "../locations.js";
|
|
|
32
38
|
* @param fn Receives the open index database; must finish all DB work before returning.
|
|
33
39
|
* @returns Whatever `fn` returns.
|
|
34
40
|
*/
|
|
35
|
-
export function withIndexDb(fn) {
|
|
41
|
+
export function withIndexDb(fn, opts) {
|
|
36
42
|
const db = openExistingDatabase(resolveStorageLocations().indexDb);
|
|
37
43
|
try {
|
|
44
|
+
if (opts?.busyTimeoutMs !== undefined) {
|
|
45
|
+
db.exec(`PRAGMA busy_timeout = ${Math.max(0, Math.floor(opts.busyTimeoutMs))}`);
|
|
46
|
+
}
|
|
38
47
|
return fn(db);
|
|
39
48
|
}
|
|
40
49
|
finally {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "akm-cli",
|
|
3
|
-
"version": "0.9.0-beta.
|
|
3
|
+
"version": "0.9.0-beta.53",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "akm (Agent Knowledge Management) — A package manager for AI agent skills, commands, tools, and knowledge. Works with Claude Code, OpenCode, Cursor, and any AI coding assistant.",
|
|
6
6
|
"keywords": [
|
|
@@ -58,9 +58,7 @@
|
|
|
58
58
|
"sweep:tmp": "bun scripts/sweep-test-tmp.ts",
|
|
59
59
|
"test": "bash scripts/test-unit.sh",
|
|
60
60
|
"test:unit": "bash scripts/test-unit.sh",
|
|
61
|
-
"test:integration": "bun run sweep:tmp && bun test --isolate --timeout=30000 ./tests/integration
|
|
62
|
-
"test:unit:shard": "bun run sweep:tmp && bun test --isolate --timeout=30000 ./tests --path-ignore-patterns=tests/integration --shard=${SHARD:?set SHARD=k/N}",
|
|
63
|
-
"test:integration:shard": "bun run sweep:tmp && bun test --isolate --timeout=30000 ./tests/integration ./tests/commands ./tests/workflows --shard=${SHARD:?set SHARD=k/N}",
|
|
61
|
+
"test:integration": "bun run sweep:tmp && bun test --isolate --timeout=30000 ./tests/integration",
|
|
64
62
|
"test:node-smoke": "bun scripts/node-smoke.ts",
|
|
65
63
|
"test:node-compat": "AKM_NODE_COMPAT_TESTS=1 bun test --timeout=120000 tests/integration/node-compat.test.ts",
|
|
66
64
|
"test:time": "bun scripts/test-timing-report.ts",
|