akm-cli 0.9.0-beta.54 → 0.9.0-beta.56
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/cli.js +5 -3
- package/dist/commands/agent/contribute-cli.js +2 -3
- package/dist/commands/env/env-cli.js +187 -202
- package/dist/commands/env/secret-cli.js +109 -121
- package/dist/commands/feedback-cli.js +152 -155
- package/dist/commands/health/advisories.js +151 -0
- package/dist/commands/health/improve-metrics.js +754 -0
- package/dist/commands/health/llm-usage.js +65 -0
- package/dist/commands/health/md-report.js +103 -0
- package/dist/commands/health/metrics.js +278 -0
- package/dist/commands/health/task-runs.js +135 -0
- package/dist/commands/health/types.js +18 -0
- package/dist/commands/health/windows.js +196 -0
- package/dist/commands/health.js +14 -1624
- package/dist/commands/improve/anti-collapse.js +170 -0
- package/dist/commands/improve/collapse-detector.js +3 -2
- package/dist/commands/improve/consolidate.js +636 -633
- package/dist/commands/improve/dedup.js +1 -1
- package/dist/commands/improve/distill/content-repair.js +202 -0
- package/dist/commands/improve/distill/promote-memory.js +228 -0
- package/dist/commands/improve/distill/quality-gate.js +233 -0
- package/dist/commands/improve/distill-guards.js +127 -0
- package/dist/commands/improve/distill.js +49 -575
- package/dist/commands/improve/extract-cli.js +74 -76
- package/dist/commands/improve/extract.js +6 -4
- package/dist/commands/improve/hot-probation.js +45 -0
- package/dist/commands/improve/improve-auto-accept.js +3 -2
- package/dist/commands/improve/improve-cli.js +14 -13
- package/dist/commands/improve/improve-result-file.js +2 -1
- package/dist/commands/improve/improve.js +6 -5
- package/dist/commands/improve/loop-stages.js +19 -21
- package/dist/commands/improve/preparation.js +4 -2
- package/dist/commands/improve/procedural.js +10 -31
- package/dist/commands/improve/recombine.js +19 -43
- package/dist/commands/improve/reflect.js +1 -1
- package/dist/commands/improve/schema-similarity-gate.js +168 -0
- package/dist/commands/improve/shared.js +48 -0
- package/dist/commands/observability-cli.js +4 -4
- package/dist/commands/proposal/drain-policies.js +2 -2
- package/dist/commands/proposal/drain.js +1 -1
- package/dist/commands/proposal/legacy-import.js +115 -0
- package/dist/commands/proposal/proposal-cli.js +3 -3
- package/dist/commands/proposal/proposal.js +2 -1
- package/dist/commands/proposal/propose.js +1 -1
- package/dist/commands/proposal/repository.js +829 -0
- package/dist/commands/proposal/validators/proposals.js +5 -920
- package/dist/commands/read/remember-cli.js +132 -137
- package/dist/commands/read/search-cli.js +1 -1
- package/dist/commands/registry-cli.js +76 -87
- package/dist/commands/sources/add-cli.js +90 -94
- package/dist/commands/sources/history.js +1 -1
- package/dist/commands/sources/schema-repair.js +1 -1
- package/dist/commands/sources/sources-cli.js +3 -3
- package/dist/commands/sources/stash-cli.js +1 -1
- package/dist/commands/tasks/tasks-cli.js +1 -2
- package/dist/commands/wiki-cli.js +2 -3
- package/dist/core/common.js +3 -3
- package/dist/core/config/config-schema.js +6 -0
- package/dist/core/deep-merge.js +38 -0
- package/dist/core/events.js +2 -1
- package/dist/core/logs-db.js +8 -13
- package/dist/core/paths.js +14 -14
- package/dist/core/state-db.js +13 -1140
- package/dist/indexer/db/db.js +96 -723
- package/dist/indexer/db/entry-mapper.js +41 -0
- package/dist/indexer/db/schema.js +516 -0
- package/dist/indexer/feedback/utility-policy.js +75 -0
- package/dist/indexer/graph/graph-extraction.js +2 -1
- package/dist/indexer/index-writer-lock.js +9 -0
- package/dist/indexer/indexer.js +78 -23
- package/dist/indexer/search/fts-query.js +51 -0
- package/dist/integrations/agent/spawn.js +15 -66
- package/dist/llm/embedders/cache.js +3 -1
- package/dist/output/text/helpers.js +13 -0
- package/dist/registry/resolve.js +5 -0
- package/dist/scripts/migrate-storage.js +6908 -7447
- package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +44 -43
- package/dist/setup/legacy-config.js +106 -0
- package/dist/setup/prompt.js +57 -0
- package/dist/setup/providers.js +14 -0
- package/dist/setup/semantic-assets.js +124 -0
- package/dist/setup/setup.js +24 -1607
- package/dist/setup/steps/connection.js +734 -0
- package/dist/setup/steps/output.js +31 -0
- package/dist/setup/steps/platforms.js +124 -0
- package/dist/setup/steps/semantic.js +27 -0
- package/dist/setup/steps/sources.js +222 -0
- package/dist/setup/steps/stashdir.js +42 -0
- package/dist/setup/steps/tasks.js +152 -0
- package/dist/storage/repositories/canaries-repository.js +107 -0
- package/dist/storage/repositories/consolidation-repository.js +38 -0
- package/dist/storage/repositories/embeddings-repository.js +72 -0
- package/dist/storage/repositories/events-repository.js +187 -0
- package/dist/storage/repositories/extract-sessions-repository.js +96 -0
- package/dist/storage/repositories/improve-runs-repository.js +130 -0
- package/dist/storage/repositories/index-db.js +4 -7
- package/dist/storage/repositories/proposals-repository.js +220 -0
- package/dist/storage/repositories/recombine-repository.js +213 -0
- package/dist/storage/repositories/task-history-repository.js +93 -0
- package/dist/storage/sqlite-pragmas.js +3 -3
- package/dist/tasks/runner.js +2 -1
- package/package.json +1 -1
- package/dist/commands/improve/homeostatic.js +0 -497
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
+
/**
|
|
5
|
+
* Window handling for `akm health`: `--window-compare` / `--windows` parsing,
|
|
6
|
+
* per-window metric assembly, log-backing partition, and delta computation.
|
|
7
|
+
*/
|
|
8
|
+
import fs from "node:fs";
|
|
9
|
+
import { UsageError } from "../../core/errors.js";
|
|
10
|
+
import { readEvents } from "../../core/events.js";
|
|
11
|
+
import { buildTaskRunId, getLoggedRunIds } from "../../core/logs-db.js";
|
|
12
|
+
import { queryTaskHistory } from "../../storage/repositories/task-history-repository.js";
|
|
13
|
+
import { buildImproveSkipSummary, computeWallTimeStats, parseTaskMetadata, roundRate, summarizeImproveCompleted, summarizeImproveRuns, } from "./improve-metrics.js";
|
|
14
|
+
import { readLlmUsageAggregate } from "./llm-usage.js";
|
|
15
|
+
import { computeDegradationMetrics, computeDenominatorFixedCoverage, readCalibration } from "./metrics.js";
|
|
16
|
+
import { buildPerRunSummaries } from "./task-runs.js";
|
|
17
|
+
import { ACTIVE_RUN_WARN_MS, IMPROVE_COMPLETED_EVENT, } from "./types.js";
|
|
18
|
+
/**
|
|
19
|
+
* Parse a `--window-compare <duration>` shorthand into two adjacent windows
|
|
20
|
+
* (current, prior). Duration syntax matches {@link parseHealthSince}.
|
|
21
|
+
*/
|
|
22
|
+
export function resolveWindowCompare(duration, now = () => Date.now()) {
|
|
23
|
+
const trimmed = duration.trim();
|
|
24
|
+
const durationMatch = trimmed.match(/^(\d+)([dhm])$/i);
|
|
25
|
+
if (!durationMatch) {
|
|
26
|
+
throw new UsageError("--window-compare must be a duration like '24h', '7d', or '30m'.", "INVALID_FLAG_VALUE");
|
|
27
|
+
}
|
|
28
|
+
const amount = Number.parseInt(durationMatch[1] ?? "0", 10);
|
|
29
|
+
const unit = (durationMatch[2] ?? "h").toLowerCase();
|
|
30
|
+
if (!Number.isFinite(amount) || amount <= 0) {
|
|
31
|
+
throw new UsageError("--window-compare must be a positive duration.", "INVALID_FLAG_VALUE");
|
|
32
|
+
}
|
|
33
|
+
const multiplier = unit === "h" ? 60 * 60 * 1000 : unit === "m" ? 60 * 1000 : 24 * 60 * 60 * 1000;
|
|
34
|
+
const ms = amount * multiplier;
|
|
35
|
+
const nowMs = now();
|
|
36
|
+
const currentSince = new Date(nowMs - ms).toISOString();
|
|
37
|
+
const currentUntil = new Date(nowMs).toISOString();
|
|
38
|
+
const priorSince = new Date(nowMs - 2 * ms).toISOString();
|
|
39
|
+
const priorUntil = currentSince;
|
|
40
|
+
return [
|
|
41
|
+
{ name: "current", since: currentSince, until: currentUntil },
|
|
42
|
+
{ name: "prior", since: priorSince, until: priorUntil },
|
|
43
|
+
];
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Parse a single repeatable `--windows` value of the form
|
|
47
|
+
* `name=...,since=...,until=...`. All keys are optional EXCEPT name and since.
|
|
48
|
+
*/
|
|
49
|
+
export function parseWindowSpec(raw) {
|
|
50
|
+
const fields = {};
|
|
51
|
+
for (const part of raw.split(",")) {
|
|
52
|
+
const trimmed = part.trim();
|
|
53
|
+
if (!trimmed)
|
|
54
|
+
continue;
|
|
55
|
+
const eq = trimmed.indexOf("=");
|
|
56
|
+
if (eq < 0) {
|
|
57
|
+
throw new UsageError(`--windows entry must be a comma-separated list of key=value pairs: ${raw}`, "INVALID_FLAG_VALUE");
|
|
58
|
+
}
|
|
59
|
+
const key = trimmed.slice(0, eq).trim();
|
|
60
|
+
const value = trimmed.slice(eq + 1).trim();
|
|
61
|
+
fields[key] = value;
|
|
62
|
+
}
|
|
63
|
+
if (!fields.name) {
|
|
64
|
+
throw new UsageError(`--windows entry is missing required 'name': ${raw}`, "INVALID_FLAG_VALUE");
|
|
65
|
+
}
|
|
66
|
+
if (!fields.since) {
|
|
67
|
+
throw new UsageError(`--windows entry is missing required 'since': ${raw}`, "INVALID_FLAG_VALUE");
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
name: fields.name,
|
|
71
|
+
since: fields.since,
|
|
72
|
+
...(fields.until ? { until: fields.until } : {}),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
/** Hard-coded list of "interesting" metric paths for window-compare deltas. */
|
|
76
|
+
export const INTERESTING_DELTA_PATHS = [
|
|
77
|
+
"improve.actions.reflect.failed",
|
|
78
|
+
"improve.actions.reflect.guardRejected",
|
|
79
|
+
"improve.actions.distill.llmFailed",
|
|
80
|
+
"improve.actions.distill.queued",
|
|
81
|
+
"improve.actions.distill.deferred",
|
|
82
|
+
"improve.consolidation.promoted",
|
|
83
|
+
"improve.memoryInference.written",
|
|
84
|
+
"improve.memoryInference.yieldRate",
|
|
85
|
+
"improve.memoryInference.skippedNoFacts",
|
|
86
|
+
"improve.memoryInference.htmlErrorCount",
|
|
87
|
+
"improve.graphExtraction.cacheHitRate",
|
|
88
|
+
"improve.graphExtraction.failures",
|
|
89
|
+
"improve.graphExtraction.htmlErrors",
|
|
90
|
+
"improve.graphExtraction.nonArrayBatchFailures",
|
|
91
|
+
"improve.sessionExtraction.sessionsScanned",
|
|
92
|
+
"improve.sessionExtraction.proposalsCreated",
|
|
93
|
+
"improve.autoAccept.promoted",
|
|
94
|
+
"improve.autoAccept.validationFailed",
|
|
95
|
+
"improve.wallTime.medianMs",
|
|
96
|
+
"improve.wallTime.p95Ms",
|
|
97
|
+
];
|
|
98
|
+
export function readNumericPath(obj, path) {
|
|
99
|
+
const parts = path.split(".");
|
|
100
|
+
let cursor = obj;
|
|
101
|
+
for (const part of parts) {
|
|
102
|
+
if (typeof cursor !== "object" || cursor === null)
|
|
103
|
+
return 0;
|
|
104
|
+
cursor = cursor[part];
|
|
105
|
+
}
|
|
106
|
+
return typeof cursor === "number" && Number.isFinite(cursor) ? cursor : 0;
|
|
107
|
+
}
|
|
108
|
+
export function computeDeltas(first, last) {
|
|
109
|
+
const out = {};
|
|
110
|
+
for (const path of INTERESTING_DELTA_PATHS) {
|
|
111
|
+
const from = readNumericPath(first, path);
|
|
112
|
+
const to = readNumericPath(last, path);
|
|
113
|
+
if (from === 0 && to === 0)
|
|
114
|
+
continue;
|
|
115
|
+
let pctChange;
|
|
116
|
+
if (from === 0) {
|
|
117
|
+
pctChange = to === 0 ? 0 : "+inf";
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
pctChange = Number((((to - from) / from) * 100).toFixed(2));
|
|
121
|
+
}
|
|
122
|
+
out[path] = { from, to, pctChange };
|
|
123
|
+
}
|
|
124
|
+
return out;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Partition task_history rows into "should have a log" (non-null log_path) and
|
|
128
|
+
* "log is actually backed". A run counts as backed when logs.db holds rows for
|
|
129
|
+
* its run_id (#579 — the DB is the primary record); rows written before logs.db
|
|
130
|
+
* existed fall back to the transitional on-disk file check. `logsDb` may be
|
|
131
|
+
* undefined when logs.db could not be opened — then only the file check runs.
|
|
132
|
+
*/
|
|
133
|
+
export function partitionLogBackedRows(taskRows, logsDb) {
|
|
134
|
+
const withLogs = taskRows.filter((row) => row.log_path !== null);
|
|
135
|
+
const loggedRunIds = logsDb
|
|
136
|
+
? getLoggedRunIds(logsDb, withLogs.map((row) => buildTaskRunId(row.task_id, row.started_at)))
|
|
137
|
+
: new Set();
|
|
138
|
+
const backed = withLogs.filter((row) => loggedRunIds.has(buildTaskRunId(row.task_id, row.started_at)) ||
|
|
139
|
+
(row.log_path !== null && fs.existsSync(row.log_path)));
|
|
140
|
+
return { withLogs, backed };
|
|
141
|
+
}
|
|
142
|
+
export function buildWindowMetrics(db, stateDbPath, since, until, now = () => Date.now(), logsDb) {
|
|
143
|
+
const taskRows = queryTaskHistory(db, { since }).filter((row) => {
|
|
144
|
+
const startMs = new Date(row.started_at).getTime();
|
|
145
|
+
const untilMs = new Date(until).getTime();
|
|
146
|
+
return !Number.isFinite(untilMs) || startMs < untilMs;
|
|
147
|
+
});
|
|
148
|
+
const { withLogs: taskRowsWithLogs, backed: existingLogRows } = partitionLogBackedRows(taskRows, logsDb);
|
|
149
|
+
const failedTaskRows = taskRows.filter((row) => row.status === "failed");
|
|
150
|
+
const activeRows = taskRows.filter((row) => row.status === "active");
|
|
151
|
+
const stuckActiveRuns = activeRows.filter((row) => now() - new Date(row.started_at).getTime() > ACTIVE_RUN_WARN_MS).length;
|
|
152
|
+
const promptRows = taskRows.filter((row) => row.target_kind === "prompt");
|
|
153
|
+
const promptFailures = promptRows.filter((row) => {
|
|
154
|
+
const detail = parseTaskMetadata(row).detail;
|
|
155
|
+
return typeof detail?.reason === "string" && detail.reason.length > 0;
|
|
156
|
+
});
|
|
157
|
+
const logBackingRate = taskRowsWithLogs.length === 0 ? 1 : existingLogRows.length / taskRowsWithLogs.length;
|
|
158
|
+
const taskFailRate = taskRows.length === 0 ? 0 : failedTaskRows.length / taskRows.length;
|
|
159
|
+
const agentFailureRate = promptRows.length === 0 ? 0 : promptFailures.length / promptRows.length;
|
|
160
|
+
const improveInvoked = readEvents({ since, type: "improve_invoked" }, { dbPath: stateDbPath }).events.filter((event) => new Date(event.ts ?? since).getTime() < new Date(until).getTime()).length;
|
|
161
|
+
const improveCompletedEvents = readEvents({ since, type: IMPROVE_COMPLETED_EVENT }, { dbPath: stateDbPath }).events.filter((event) => new Date(event.ts ?? since).getTime() < new Date(until).getTime());
|
|
162
|
+
const improveSkippedEvents = readEvents({ since, type: "improve_skipped" }, { dbPath: stateDbPath }).events.filter((event) => new Date(event.ts ?? since).getTime() < new Date(until).getTime());
|
|
163
|
+
const eventsMetrics = summarizeImproveCompleted(improveCompletedEvents);
|
|
164
|
+
const { metrics: improveSummary, runCount } = summarizeImproveRuns(db, since, until);
|
|
165
|
+
improveSummary.invoked = improveInvoked;
|
|
166
|
+
improveSummary.completed = eventsMetrics.completed;
|
|
167
|
+
const skipSummary = buildImproveSkipSummary(improveSkippedEvents);
|
|
168
|
+
improveSummary.skipped = skipSummary.skipped;
|
|
169
|
+
improveSummary.skipReasons = skipSummary.skipReasons;
|
|
170
|
+
// Preserve the per-phase aggregation computed by summarizeImproveRuns and
|
|
171
|
+
// derive top-level wall times from the same improve-runs window so counts
|
|
172
|
+
// and percentiles stay aligned with per-run reporting.
|
|
173
|
+
const perRunSummaries = buildPerRunSummaries(db, since, until);
|
|
174
|
+
const wallTimes = perRunSummaries.map((run) => run.wallTimeMs).filter((ms) => Number.isFinite(ms) && ms > 0);
|
|
175
|
+
improveSummary.wallTime = computeWallTimeStats(wallTimes, improveSummary.wallTime.byPhase);
|
|
176
|
+
improveSummary.calibration = readCalibration(db, since, until);
|
|
177
|
+
// WS-5: Compute denominator-fixed coverage from the most recent run's
|
|
178
|
+
// memorySummary (totalAssets = eligible + derived — the fixed denominator).
|
|
179
|
+
const totalAssets = improveSummary.memorySummary.eligible + improveSummary.memorySummary.derived;
|
|
180
|
+
improveSummary.coverage = computeDenominatorFixedCoverage(db, totalAssets, improveSummary.memorySummary.eligible, since, until);
|
|
181
|
+
// WS-5: Compute per-run degradation metrics (corpus diversity, merge fidelity,
|
|
182
|
+
// generation distribution, oracle spot-check). Health VIEWS only.
|
|
183
|
+
const degradation = computeDegradationMetrics(db, since, until);
|
|
184
|
+
if (degradation) {
|
|
185
|
+
improveSummary.degradation = degradation;
|
|
186
|
+
}
|
|
187
|
+
const metrics = {
|
|
188
|
+
taskFailRate: roundRate(taskFailRate),
|
|
189
|
+
agentFailureRate: roundRate(agentFailureRate),
|
|
190
|
+
stuckActiveRuns,
|
|
191
|
+
logBackingRate: roundRate(logBackingRate),
|
|
192
|
+
probeRoundTripMs: null,
|
|
193
|
+
llmUsage: readLlmUsageAggregate(stateDbPath, since, until),
|
|
194
|
+
};
|
|
195
|
+
return { improve: improveSummary, metrics, runs: runCount };
|
|
196
|
+
}
|