akm-cli 0.9.17-alpha.8 → 0.9.17
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 +277 -1694
- package/STABILITY.md +9 -8
- package/dist/assets/hints/cli-hints-full.md +6 -7
- package/dist/assets/improve-strategies/catchup.json +0 -3
- package/dist/assets/improve-strategies/consolidate.json +0 -1
- package/dist/assets/improve-strategies/default.json +1 -2
- package/dist/assets/improve-strategies/proactive-maintenance.json +1 -2
- package/dist/assets/improve-strategies/quick.json +1 -2
- package/dist/assets/improve-strategies/reflect-distill.json +1 -2
- package/dist/assets/improve-strategies/thorough.json +0 -3
- package/dist/assets/prompts/consolidate-pair.md +20 -0
- package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +17 -19
- package/dist/assets/stash-skeleton/facts/conventions/domains.md +2 -2
- package/dist/assets/templates/html/health.html +3 -5
- package/dist/cli/retired-commands.js +1 -1
- package/dist/cli/unknown-flags.js +24 -1
- package/dist/cli.js +46 -1
- package/dist/commands/health/archive-usage.js +92 -0
- package/dist/commands/health/data-dir-usage.js +25 -13
- package/dist/commands/health/html-report.js +1 -4
- package/dist/commands/health/improve-metrics.js +25 -37
- package/dist/commands/health/md-report.js +1 -6
- package/dist/commands/health/report-view-model.js +4 -14
- package/dist/commands/health/windows.js +0 -1
- package/dist/commands/health.js +13 -0
- package/dist/commands/improve/consolidate/continuity-check.js +137 -0
- package/dist/commands/improve/consolidate/pair-pass.js +791 -0
- package/dist/commands/improve/consolidate.js +38 -63
- package/dist/commands/improve/extract-prompt.js +1 -2
- package/dist/commands/improve/improve-cli.js +1 -1
- package/dist/commands/improve/improve-strategies.js +23 -5
- package/dist/commands/improve/improve.js +19 -30
- package/dist/commands/improve/ledger.js +3 -2
- package/dist/commands/improve/loop-stages.js +5 -85
- package/dist/commands/improve/memory/memory-belief.js +3 -1
- package/dist/commands/improve/memory/memory-improve.js +262 -11
- package/dist/commands/improve/planner.js +0 -5
- package/dist/commands/improve/preparation.js +20 -135
- package/dist/commands/improve/retrieval-scope.js +19 -4
- package/dist/commands/improve/salience.js +1 -14
- package/dist/commands/improve/stage.js +0 -1
- package/dist/commands/lint/base-linter.js +19 -11
- package/dist/commands/proposal/drain.js +8 -1
- package/dist/commands/proposal/proposal-cli.js +16 -2
- package/dist/commands/proposal/proposal-types.js +7 -0
- package/dist/commands/proposal/proposal.js +37 -6
- package/dist/commands/proposal/repository.js +613 -4
- package/dist/commands/proposal/validators/proposals.js +9 -0
- package/dist/commands/read/knowledge.js +3 -2
- package/dist/commands/read/show.js +0 -14
- package/dist/commands/sources/info.js +122 -18
- package/dist/commands/sources/stash-cli.js +23 -3
- package/dist/core/bundle-rename.js +1 -7
- package/dist/core/config/config-schema.js +8 -1
- package/dist/core/config/config.js +23 -48
- package/dist/core/config/engine-semantics.js +0 -2
- package/dist/core/config/schema/improve-processes.js +17 -42
- package/dist/core/config/schema/index-config.js +5 -25
- package/dist/core/file-change.js +13 -5
- package/dist/core/improve-result.js +22 -6
- package/dist/core/improve-types.js +0 -1
- package/dist/core/loopback.js +7 -12
- package/dist/core/parse.js +13 -16
- package/dist/core/state/migrations.js +15 -0
- package/dist/core/time.js +0 -20
- package/dist/indexer/db/llm-cache.js +2 -2
- package/dist/indexer/ensure-index.js +2 -2
- package/dist/indexer/index-written-assets.js +2 -3
- package/dist/indexer/indexer.js +18 -418
- package/dist/indexer/passes/metadata.js +0 -19
- package/dist/indexer/walk/walker.js +3 -4
- package/dist/llm/client.js +8 -10
- package/dist/llm/embedders/remote.js +1 -2
- package/dist/llm/feature-gate.js +0 -5
- package/dist/output/shapes/helpers.js +20 -4
- package/dist/output/text/command-format.js +9 -8
- package/dist/output/text/proposal-format.js +47 -1
- package/dist/output/text/show-format.js +0 -20
- package/dist/scripts/akm-migrate-node.js +923 -950
- package/dist/scripts/akm-migrate.js +923 -950
- package/dist/setup/steps/connection.js +5 -6
- package/dist/setup/steps/platforms.js +2 -2
- package/dist/sources/providers/git-stash.js +83 -4
- package/dist/storage/repositories/improve-ledger-repository.js +48 -7
- package/dist/storage/repositories/index-connection.js +5 -2
- package/dist/storage/repositories/index-entries-repository.js +4 -7
- package/dist/storage/repositories/index-entry-schema.js +4 -2
- package/dist/storage/repositories/index-llm-cache-repository.js +7 -26
- package/dist/storage/repositories/index-schema.js +55 -104
- package/dist/storage/repositories/proposals-repository.js +61 -0
- package/dist/storage/repositories/salience-repository.js +1 -19
- package/docs/migration/README.md +1 -1
- package/docs/migration/release-notes/0.9.17.md +130 -41
- package/docs/migration/release-notes/README.md +7 -0
- package/docs/reference/cli.md +27 -21
- package/docs/reference/configuration.md +21 -12
- package/docs/reference/data-and-telemetry.md +0 -1
- package/package.json +1 -1
- package/schemas/akm-config.json +0 -342
- package/dist/assets/improve-strategies/graph-refresh.json +0 -15
- package/dist/assets/prompts/contradiction-judge.md +0 -33
- package/dist/assets/prompts/graph-extract-system.md +0 -1
- package/dist/assets/prompts/graph-extract-user-prompt.md +0 -35
- package/dist/assets/prompts/metadata-enhance-system.md +0 -1
- package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +0 -4
- package/dist/indexer/db/graph-db.js +0 -399
- package/dist/indexer/graph/graph-extraction.js +0 -809
- package/dist/indexer/graph/graph-related.js +0 -131
- package/dist/indexer/graph/graph-types.js +0 -4
- package/dist/llm/graph-extract.js +0 -892
- package/dist/llm/metadata-enhance.js +0 -95
|
@@ -10,19 +10,6 @@ import { z } from "zod";
|
|
|
10
10
|
import { warnOnce } from "../../warn.js";
|
|
11
11
|
import { engineName, LlmInvocationOverridesSchema, nonEmptyString, positiveInt } from "./primitives.js";
|
|
12
12
|
// ── Index / per-pass ────────────────────────────────────────────────────────
|
|
13
|
-
//
|
|
14
|
-
// WI-9.6c: `graphExtractionIncludeTypes` is no longer validated against a
|
|
15
|
-
// hardcoded allowlist (the prior GRAPH_EXTRACTION_INCLUDE_TYPES_ALLOWED,
|
|
16
|
-
// which included a stale `wiki` entry and was already missing `fact` from the
|
|
17
|
-
// runtime consumer's own list — the schema-level allowlist had drifted from
|
|
18
|
-
// reality). Accept-any until Chunk 2 sources a real type list from adapter
|
|
19
|
-
// metadata: the field is now an array of arbitrary non-empty strings.
|
|
20
|
-
// Runtime consumers already handle unknown/unsupported type strings
|
|
21
|
-
// gracefully — src/indexer/graph/graph-extraction.ts's
|
|
22
|
-
// `SUPPORTED_GRAPH_EXTRACTION_INCLUDE_TYPES` set (and `collectEligibleFiles`)
|
|
23
|
-
// silently skips any type it doesn't recognize (no placement entry ⇒ zero
|
|
24
|
-
// eligible files for that type; no crash). This is a permissive-direction
|
|
25
|
-
// behavior change: configs with a previously-rejected type string now parse.
|
|
26
13
|
const INDEX_PASS_RETIRED_KEYS = new Set([
|
|
27
14
|
"endpoint",
|
|
28
15
|
"provider",
|
|
@@ -60,12 +47,8 @@ export const IndexPassConfigSchema = z.preprocess((raw, ctx) => {
|
|
|
60
47
|
timeoutMs: z.union([positiveInt, z.null()]).optional(),
|
|
61
48
|
enabled: z.boolean().optional(),
|
|
62
49
|
llm: LlmInvocationOverridesSchema.optional(),
|
|
63
|
-
graphExtractionBatchSize: positiveInt.optional(),
|
|
64
|
-
// Accept-any until Chunk 2 (WI-9.6c) — no longer enum-restricted.
|
|
65
|
-
graphExtractionIncludeTypes: z.array(z.string().min(1)).nonempty().optional(),
|
|
66
50
|
})
|
|
67
51
|
.passthrough());
|
|
68
|
-
const MetadataEnhanceSchema = z.object({ enabled: z.boolean().optional() }).passthrough();
|
|
69
52
|
const IndexDefaultsSchema = z
|
|
70
53
|
.object({
|
|
71
54
|
engine: engineName.optional(),
|
|
@@ -76,16 +59,14 @@ const IndexDefaultsSchema = z
|
|
|
76
59
|
.passthrough();
|
|
77
60
|
/**
|
|
78
61
|
* Index config is a union of reserved feature sections and per-pass entries.
|
|
79
|
-
* Passthrough so per-pass entries (keyed by arbitrary pass names like
|
|
80
|
-
* `
|
|
62
|
+
* Passthrough so per-pass entries (keyed by arbitrary pass names like
|
|
63
|
+
* `memory`, or a retired one like `graph`) can live next to the reserved keys.
|
|
81
64
|
* The outer preprocess emits the legacy parser's actionable error messages
|
|
82
65
|
* for the two most common type-shape mistakes:
|
|
83
66
|
* - An array at the `index` block.
|
|
84
67
|
* - A non-object at `index.<passName>`.
|
|
85
|
-
* Inner field validation (
|
|
86
|
-
*
|
|
87
|
-
* `graphExtractionIncludeTypes` accepts arbitrary non-empty strings
|
|
88
|
-
* (WI-9.6c — no hardcoded type allowlist; accept-any until Chunk 2).
|
|
68
|
+
* Inner field validation (invocation overrides, provider-key rejection) is
|
|
69
|
+
* delegated to {@link IndexPassConfigSchema}.
|
|
89
70
|
*/
|
|
90
71
|
const IndexConfigRuntimeSchema = z.preprocess((raw, ctx) => {
|
|
91
72
|
if (raw === undefined || raw === null)
|
|
@@ -93,7 +74,7 @@ const IndexConfigRuntimeSchema = z.preprocess((raw, ctx) => {
|
|
|
93
74
|
if (Array.isArray(raw)) {
|
|
94
75
|
ctx.addIssue({
|
|
95
76
|
code: z.ZodIssueCode.custom,
|
|
96
|
-
message: 'Invalid `index` config: expected an object keyed by pass name (e.g. `{ "
|
|
77
|
+
message: 'Invalid `index` config: expected an object keyed by pass name (e.g. `{ "memory": { "enabled": false } }`).',
|
|
97
78
|
});
|
|
98
79
|
return raw;
|
|
99
80
|
}
|
|
@@ -119,7 +100,6 @@ const IndexConfigRuntimeSchema = z.preprocess((raw, ctx) => {
|
|
|
119
100
|
}, z
|
|
120
101
|
.object({
|
|
121
102
|
defaults: IndexDefaultsSchema.optional(),
|
|
122
|
-
metadataEnhance: MetadataEnhanceSchema.optional(),
|
|
123
103
|
})
|
|
124
104
|
.catchall(IndexPassConfigSchema));
|
|
125
105
|
// The runtime catchall correctly validates arbitrary pass objects, but its
|
package/dist/core/file-change.js
CHANGED
|
@@ -4,13 +4,21 @@
|
|
|
4
4
|
/**
|
|
5
5
|
* The content of a proposal's PRIMARY change (WI-6.2 envelope read path).
|
|
6
6
|
*
|
|
7
|
-
* By construction `changes[0].after === payload.content
|
|
8
|
-
* through this accessor so the
|
|
9
|
-
*
|
|
10
|
-
*
|
|
7
|
+
* By construction `changes[0].after === payload.content` for a `create` or
|
|
8
|
+
* `update` change; consumers read through this accessor so the
|
|
9
|
+
* single-content assumption lives in ONE place. A `delete` primary change (a
|
|
10
|
+
* consolidate retire proposal, alpha.9) writes no content by definition, so
|
|
11
|
+
* it reads as `""` here rather than throwing — every generic proposal scan
|
|
12
|
+
* (dedup-by-body-hash, the triage drain's diff-size filter, the quality
|
|
13
|
+
* validators) can call this on ANY pending proposal, retire included,
|
|
14
|
+
* without special-casing the op first. Typed structurally (not against
|
|
15
|
+
* `Proposal`) so this module stays dependency-free.
|
|
11
16
|
*/
|
|
12
17
|
export function proposalContent(p) {
|
|
13
|
-
const
|
|
18
|
+
const primary = p.changes[0];
|
|
19
|
+
if (primary?.op === "delete")
|
|
20
|
+
return "";
|
|
21
|
+
const content = primary?.after;
|
|
14
22
|
if (content === undefined)
|
|
15
23
|
throw new Error("Proposal primary change has no content.");
|
|
16
24
|
return content;
|
|
@@ -37,6 +37,9 @@ const COMMON_FIELDS = [
|
|
|
37
37
|
"deadUrlCoverage",
|
|
38
38
|
"reflectsWithErrorContext",
|
|
39
39
|
"memoryInference",
|
|
40
|
+
// 0.9.17-alpha.9: no longer written (the LLM entity-graph extraction that
|
|
41
|
+
// wrote these was retired), kept readable for the same reason as
|
|
42
|
+
// `evalCasesWritten` above.
|
|
40
43
|
"graphExtraction",
|
|
41
44
|
"memoryInferenceDurationMs",
|
|
42
45
|
"graphExtractionDurationMs",
|
|
@@ -119,7 +122,7 @@ function validateConsolidationPlan(value) {
|
|
|
119
122
|
]));
|
|
120
123
|
if (!isRecord(value.configured))
|
|
121
124
|
fail("plan.consolidation.configured must be an object");
|
|
122
|
-
requireExactFields(value.configured, new Set(["enabled", "minPoolSize", "limit", "maxChunkSize"
|
|
125
|
+
requireExactFields(value.configured, new Set(["enabled", "minPoolSize", "limit", "maxChunkSize"]));
|
|
123
126
|
if (value.configured.enabled !== undefined && typeof value.configured.enabled !== "boolean") {
|
|
124
127
|
fail("plan.consolidation.configured.enabled must be a boolean");
|
|
125
128
|
}
|
|
@@ -128,9 +131,6 @@ function validateConsolidationPlan(value) {
|
|
|
128
131
|
fail(`plan.consolidation.configured.${field} must be a number`);
|
|
129
132
|
}
|
|
130
133
|
}
|
|
131
|
-
if (value.configured.incrementalSince !== undefined && typeof value.configured.incrementalSince !== "string") {
|
|
132
|
-
fail("plan.consolidation.configured.incrementalSince must be a string");
|
|
133
|
-
}
|
|
134
134
|
if (!isRecord(value.effective))
|
|
135
135
|
fail("plan.consolidation.effective must be an object");
|
|
136
136
|
requireExactFields(value.effective, new Set(["enabled", "minPoolSize", "limit", "chunkSize"]));
|
|
@@ -200,7 +200,15 @@ function validateProcessRoutingRows(value) {
|
|
|
200
200
|
fail("plan.processes entries must be objects");
|
|
201
201
|
requireExactFields(row, new Set(["process", "enabled", "engine", "model", "engineKind", "notices", "unavailable", "eligibleRefs"]));
|
|
202
202
|
if (typeof row.process !== "string" ||
|
|
203
|
-
!(canonicalNames.includes(row.process) ||
|
|
203
|
+
!(canonicalNames.includes(row.process) ||
|
|
204
|
+
row.process === "triage.judgment" ||
|
|
205
|
+
// 0.9.17-alpha.9: "graphExtraction" is no longer a process a fresh
|
|
206
|
+
// plan can route (the LLM entity-graph extraction it named was
|
|
207
|
+
// retired), but every pre-alpha.9 run recorded one here — kept
|
|
208
|
+
// readable, same as `stageNames`' "graph-extraction" above and
|
|
209
|
+
// `graphExtraction` in COMMON_FIELDS, so a historical run still
|
|
210
|
+
// decodes for `akm health` instead of failing this row outright.
|
|
211
|
+
row.process === "graphExtraction")) {
|
|
204
212
|
fail("plan.processes.process is invalid");
|
|
205
213
|
}
|
|
206
214
|
if (seen.has(row.process))
|
|
@@ -385,10 +393,18 @@ function validateImprovePlan(value, dryRun, plannedRefNames) {
|
|
|
385
393
|
if (value.limits.totalCeiling !== undefined && value.effectiveRefs.length > value.limits.totalCeiling) {
|
|
386
394
|
fail("plan.effectiveRefs cannot exceed plan.limits.totalCeiling");
|
|
387
395
|
}
|
|
388
|
-
|
|
396
|
+
// #947 added plan.processes (2026-09-09T09:03:19Z); every run recorded
|
|
397
|
+
// before it legitimately stored `plan` with no `processes` key at all —
|
|
398
|
+
// validate the rows only when present, same as `proactive` below, so a
|
|
399
|
+
// pre-#947 envelope still decodes (AGENTS.md "Reading persisted data").
|
|
400
|
+
if (value.processes !== undefined)
|
|
401
|
+
validateProcessRoutingRows(value.processes);
|
|
389
402
|
if (value.proactive !== undefined)
|
|
390
403
|
validateProactivePlan(value.proactive);
|
|
391
404
|
validateConsolidationPlan(value.consolidation);
|
|
405
|
+
// "graph-extraction" is no longer a stage a fresh plan can name — retired
|
|
406
|
+
// 0.9.17-alpha.9 — but stays here so a historical envelope that captured a
|
|
407
|
+
// dry-run plan before the retirement still decodes.
|
|
392
408
|
const stageNames = new Set(["consolidation", "extract", "graph-extraction", "memory-inference"]);
|
|
393
409
|
if (!Array.isArray(value.stages))
|
|
394
410
|
fail("plan.stages must be an array");
|
package/dist/core/loopback.js
CHANGED
|
@@ -20,10 +20,11 @@
|
|
|
20
20
|
*
|
|
21
21
|
* The ONE local-vs-remote answer for concurrency defaults — the workflow
|
|
22
22
|
* engine's frozen engine concurrency (`workflows/concurrency-policy.ts`) and
|
|
23
|
-
* the
|
|
24
|
-
* both classify through here. NOT shared with
|
|
25
|
-
*
|
|
26
|
-
*
|
|
23
|
+
* the embedding pool (`resolveEmbeddingConcurrency`,
|
|
24
|
+
* `src/llm/embedders/remote.ts`) both classify through here. NOT shared with
|
|
25
|
+
* the website snapshot fetcher's SSRF policy
|
|
26
|
+
* (`sources/snapshot-fetchers/host-guard.ts`), which deliberately draws
|
|
27
|
+
* different lines for a different threat model.
|
|
27
28
|
*
|
|
28
29
|
* @module core/loopback
|
|
29
30
|
*/
|
|
@@ -92,14 +93,8 @@ export function isLoopbackEndpoint(endpoint) {
|
|
|
92
93
|
* endpoint: 1 for a loopback endpoint (a local model server serves one
|
|
93
94
|
* inference at a time; concurrent requests thrash it — reload thrash, HTTP
|
|
94
95
|
* 500 "Model reloaded"), 2 for a remote one (enough to overlap request
|
|
95
|
-
* latency without hammering a rate-limited API).
|
|
96
|
-
*
|
|
97
|
-
* `src/indexer/indexer.ts`) and the embedding pool
|
|
98
|
-
* (`resolveEmbeddingConcurrency`, `src/llm/embedders/remote.ts`) — share one
|
|
99
|
-
* definition instead of mirroring it; `src/llm/embedders/remote.ts` cannot
|
|
100
|
-
* import `getDefaultLlmConcurrency` directly (`src/indexer/indexer.ts`
|
|
101
|
-
* already depends on this module transitively through
|
|
102
|
-
* materialize-embeddings.ts).
|
|
96
|
+
* latency without hammering a rate-limited API). Used by the embedding pool
|
|
97
|
+
* (`resolveEmbeddingConcurrency`, `src/llm/embedders/remote.ts`).
|
|
103
98
|
*/
|
|
104
99
|
export function defaultConcurrencyForEndpoint(endpoint) {
|
|
105
100
|
return isLoopbackEndpoint(endpoint) ? 1 : 2;
|
package/dist/core/parse.js
CHANGED
|
@@ -101,24 +101,24 @@ export function parseJsonResponse(raw) {
|
|
|
101
101
|
* balanced `{ }` or `[ ]` structure in the text and attempts to parse that
|
|
102
102
|
* substring. Returns `undefined` if no valid JSON structure is found.
|
|
103
103
|
*
|
|
104
|
-
*
|
|
105
|
-
* - `
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
*
|
|
104
|
+
* Object-preferring: a `{…}` object found first is returned immediately;
|
|
105
|
+
* a top-level array (`[…]`) is only a fallback, returned if no object is
|
|
106
|
+
* found. Preserves the historical behaviour every current caller (all in
|
|
107
|
+
* `src/`) relies on.
|
|
108
|
+
*
|
|
109
|
+
* An array-preferring mode (`expect: "array"`, for callers that specifically
|
|
110
|
+
* expect a JSON array) existed here for batched LLM entity-graph extraction
|
|
111
|
+
* (#635) and was removed with it in 0.9.17-alpha.9 — every remaining caller
|
|
112
|
+
* calls this with no options, so it was dead weight, not a live choice.
|
|
110
113
|
*/
|
|
111
|
-
export function parseEmbeddedJsonResponse(raw
|
|
112
|
-
const expectArray = options?.expect === "array";
|
|
114
|
+
export function parseEmbeddedJsonResponse(raw) {
|
|
113
115
|
const direct = parseJsonResponse(raw);
|
|
114
|
-
if (direct !== undefined
|
|
116
|
+
if (direct !== undefined)
|
|
115
117
|
return direct;
|
|
116
118
|
const text = escapeJsonStringControls(stripCodeFences(stripThinkBlocks(raw)));
|
|
117
119
|
let arrayFallback;
|
|
118
120
|
// Scan only *top-level* balanced structures: once a `{…}`/`[…]` is matched we
|
|
119
121
|
// jump `start` past its closing bracket rather than re-scanning its interior.
|
|
120
|
-
// This keeps array mode from salvaging an array *nested inside* a leading
|
|
121
|
-
// object (e.g. the `entities` array of a bare `{entities,relations}` object).
|
|
122
122
|
for (let start = 0; start < text.length; start++) {
|
|
123
123
|
const opener = text[start];
|
|
124
124
|
if (opener !== "{" && opener !== "[")
|
|
@@ -161,13 +161,10 @@ export function parseEmbeddedJsonResponse(raw, options) {
|
|
|
161
161
|
try {
|
|
162
162
|
const parsed = JSON.parse(text.slice(start, end + 1));
|
|
163
163
|
if (Array.isArray(parsed)) {
|
|
164
|
-
//
|
|
165
|
-
// fallback returned only if no object is found.
|
|
166
|
-
if (expectArray)
|
|
167
|
-
return parsed;
|
|
164
|
+
// Fallback, returned only if no object is found.
|
|
168
165
|
arrayFallback ??= parsed;
|
|
169
166
|
}
|
|
170
|
-
else
|
|
167
|
+
else {
|
|
171
168
|
return parsed;
|
|
172
169
|
}
|
|
173
170
|
// Skip past this balanced structure so we don't descend into it.
|
|
@@ -47,6 +47,7 @@ export const STATE_MIGRATION_SAFETY_BY_ID = Object.freeze({
|
|
|
47
47
|
"026-proposals-strip-legacy-fragment-refs": "data-preserving-rebuild",
|
|
48
48
|
"027-extract-sessions-seen-harness-rename": "data-preserving-rebuild",
|
|
49
49
|
"028-improve-ledger": "historical-destructive",
|
|
50
|
+
"029-improve-ledger-content-hash": "additive",
|
|
50
51
|
});
|
|
51
52
|
export const STATE_MIGRATIONS = [
|
|
52
53
|
// ── Migration 001 — initial schema ──────────────────────────────────────────
|
|
@@ -1284,6 +1285,20 @@ export const STATE_MIGRATIONS = [
|
|
|
1284
1285
|
DROP TABLE IF EXISTS canary_queries;
|
|
1285
1286
|
`,
|
|
1286
1287
|
},
|
|
1288
|
+
// ── Migration 029 — improve_ledger.content_hash ───────────────────────────
|
|
1289
|
+
//
|
|
1290
|
+
// The consolidate pair pass (alpha.9) selects initiators by content change,
|
|
1291
|
+
// not a time window: no row means eligible, a row whose content_hash
|
|
1292
|
+
// differs from the asset's current body means eligible, otherwise not
|
|
1293
|
+
// (src/commands/improve/consolidate/pair-pass.ts, selectInitiators). Every
|
|
1294
|
+
// other source still uses next_eligible_at; this column is additional, not
|
|
1295
|
+
// a replacement.
|
|
1296
|
+
{
|
|
1297
|
+
id: "029-improve-ledger-content-hash",
|
|
1298
|
+
up: `
|
|
1299
|
+
ALTER TABLE improve_ledger ADD COLUMN content_hash TEXT DEFAULT NULL;
|
|
1300
|
+
`,
|
|
1301
|
+
},
|
|
1287
1302
|
];
|
|
1288
1303
|
assertMigrationRegistry(STATE_MIGRATIONS);
|
|
1289
1304
|
function assertStateMigrationSafetyRegistry() {
|
package/dist/core/time.js
CHANGED
|
@@ -95,23 +95,3 @@ export function parseSinceToIso(since) {
|
|
|
95
95
|
}
|
|
96
96
|
return parsed.toISOString();
|
|
97
97
|
}
|
|
98
|
-
/**
|
|
99
|
-
* LENIENT variant for config-driven since-values that may be a duration
|
|
100
|
-
* shorthand (e.g. `"30m"`, `"24h"`, `"7d"` per {@link DURATION_UNITS}):
|
|
101
|
-
* a matching duration resolves to the ISO timestamp of `now - duration`;
|
|
102
|
-
* anything else is returned UNCHANGED (assumed to already be an ISO
|
|
103
|
-
* timestamp) — this function never throws.
|
|
104
|
-
*
|
|
105
|
-
* Deliberately distinct from {@link parseSinceToIso}, which rejects
|
|
106
|
-
* unparseable input with a UsageError: the identity fallback here is
|
|
107
|
-
* behavior consumers rely on (consolidate's incremental narrowing compares
|
|
108
|
-
* the returned string lexicographically against file-mtime ISO strings, so
|
|
109
|
-
* garbage input silently selects nothing rather than failing the run —
|
|
110
|
-
* pinned by tests/commands/goldens-duration-flags.test.ts).
|
|
111
|
-
*/
|
|
112
|
-
export function parseSinceToIsoLenient(since) {
|
|
113
|
-
const ms = parseDuration(since, DURATION_UNITS);
|
|
114
|
-
if (ms === null)
|
|
115
|
-
return since;
|
|
116
|
-
return new Date(Date.now() - ms).toISOString();
|
|
117
|
-
}
|
|
@@ -27,10 +27,10 @@ import { computeBodyHash, getLlmCacheEntry, upsertLlmCacheEntry, } from "../../s
|
|
|
27
27
|
* already hashed the body (e.g. to reuse it elsewhere)
|
|
28
28
|
* should pass this to avoid the redundant work.
|
|
29
29
|
* @param cacheVariant - Namespace token for the cache row so different passes'
|
|
30
|
-
* rows do not collide (e.g. memory-inference vs
|
|
30
|
+
* rows do not collide (e.g. memory-inference vs metadata-enhance).
|
|
31
31
|
* @param hooks - Optional event sink for telemetry (see {@link WithLlmCacheHooks}).
|
|
32
32
|
*/
|
|
33
|
-
export async function withLlmCache(db, cacheKey, body, reEnrich, llmFn, validate, precomputedHash, cacheVariant
|
|
33
|
+
export async function withLlmCache(db, cacheKey, body, reEnrich, llmFn, validate, precomputedHash, cacheVariant, hooks) {
|
|
34
34
|
const bodyHash = precomputedHash ?? computeBodyHash(body);
|
|
35
35
|
if (!reEnrich) {
|
|
36
36
|
const cacheHit = bestEffort(() => {
|
|
@@ -115,8 +115,8 @@ function hasNewerIndexableFiles(stashDir, builtAt, indexedPaths, indexedHashes)
|
|
|
115
115
|
continue;
|
|
116
116
|
// Newer than the last full build — only stale if its current content
|
|
117
117
|
// actually differs from what is indexed. No stored hash means the row
|
|
118
|
-
// predates content-hash tracking
|
|
119
|
-
//
|
|
118
|
+
// predates content-hash tracking, so fall back to the conservative
|
|
119
|
+
// mtime-stale answer.
|
|
120
120
|
const indexedHash = indexedHashes.get(file);
|
|
121
121
|
if (indexedHash === undefined)
|
|
122
122
|
return true;
|
|
@@ -15,9 +15,8 @@
|
|
|
15
15
|
* This is NOT a general reindex. It upserts exactly the files the caller just
|
|
16
16
|
* wrote: frontmatter/metadata via the shared matcher pipeline, the canonical
|
|
17
17
|
* row with its transactionally owned FTS projection, and vectors for changed
|
|
18
|
-
* entry IDs when semantic search is enabled.
|
|
19
|
-
*
|
|
20
|
-
* responsibilities.
|
|
18
|
+
* entry IDs when semantic search is enabled. `builtAt` and the per-dir walk
|
|
19
|
+
* cache remain full-index responsibilities.
|
|
21
20
|
*/
|
|
22
21
|
import fs from "node:fs";
|
|
23
22
|
import path from "node:path";
|