akm-cli 0.9.0-beta.53 → 0.9.0-beta.55

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.
Files changed (123) hide show
  1. package/dist/cli/clack.js +56 -0
  2. package/dist/cli/confirm.js +1 -1
  3. package/dist/cli.js +5 -3
  4. package/dist/commands/agent/contribute-cli.js +2 -3
  5. package/dist/commands/env/env-cli.js +187 -202
  6. package/dist/commands/env/secret-cli.js +109 -121
  7. package/dist/commands/feedback-cli.js +152 -155
  8. package/dist/commands/health/advisories.js +151 -0
  9. package/dist/commands/health/html-report.js +33 -10
  10. package/dist/commands/health/improve-metrics.js +754 -0
  11. package/dist/commands/health/llm-usage.js +65 -0
  12. package/dist/commands/health/md-report.js +103 -0
  13. package/dist/commands/health/metrics.js +278 -0
  14. package/dist/commands/health/task-runs.js +135 -0
  15. package/dist/commands/health/types.js +18 -0
  16. package/dist/commands/health/windows.js +196 -0
  17. package/dist/commands/health.js +15 -1492
  18. package/dist/commands/improve/anti-collapse.js +170 -0
  19. package/dist/commands/improve/collapse-detector.js +3 -2
  20. package/dist/commands/improve/consolidate.js +636 -633
  21. package/dist/commands/improve/dedup.js +1 -1
  22. package/dist/commands/improve/distill/content-repair.js +202 -0
  23. package/dist/commands/improve/distill/promote-memory.js +228 -0
  24. package/dist/commands/improve/distill/quality-gate.js +233 -0
  25. package/dist/commands/improve/distill-guards.js +127 -0
  26. package/dist/commands/improve/distill.js +49 -575
  27. package/dist/commands/improve/extract-cli.js +74 -76
  28. package/dist/commands/improve/extract.js +6 -4
  29. package/dist/commands/improve/hot-probation.js +45 -0
  30. package/dist/commands/improve/improve-auto-accept.js +3 -2
  31. package/dist/commands/improve/improve-cli.js +14 -13
  32. package/dist/commands/improve/improve-result-file.js +2 -1
  33. package/dist/commands/improve/improve.js +6 -5
  34. package/dist/commands/improve/loop-stages.js +19 -21
  35. package/dist/commands/improve/outcome-loop.js +18 -16
  36. package/dist/commands/improve/preparation.js +23 -5
  37. package/dist/commands/improve/procedural.js +10 -31
  38. package/dist/commands/improve/recombine.js +19 -43
  39. package/dist/commands/improve/reflect.js +1 -1
  40. package/dist/commands/improve/schema-similarity-gate.js +168 -0
  41. package/dist/commands/improve/shared.js +48 -0
  42. package/dist/commands/observability-cli.js +4 -4
  43. package/dist/commands/proposal/drain-policies.js +2 -2
  44. package/dist/commands/proposal/drain.js +1 -1
  45. package/dist/commands/proposal/legacy-import.js +115 -0
  46. package/dist/commands/proposal/proposal-cli.js +3 -3
  47. package/dist/commands/proposal/proposal.js +2 -1
  48. package/dist/commands/proposal/propose.js +1 -1
  49. package/dist/commands/proposal/repository.js +829 -0
  50. package/dist/commands/proposal/validators/proposals.js +5 -920
  51. package/dist/commands/read/curate.js +4 -4
  52. package/dist/commands/read/remember-cli.js +132 -137
  53. package/dist/commands/read/search-cli.js +7 -5
  54. package/dist/commands/read/search.js +7 -3
  55. package/dist/commands/read/show.js +3 -5
  56. package/dist/commands/registry-cli.js +76 -87
  57. package/dist/commands/sources/add-cli.js +91 -95
  58. package/dist/commands/sources/history.js +1 -1
  59. package/dist/commands/sources/init.js +12 -0
  60. package/dist/commands/sources/schema-repair.js +1 -1
  61. package/dist/commands/sources/sources-cli.js +3 -3
  62. package/dist/commands/sources/stash-cli.js +2 -2
  63. package/dist/commands/tasks/default-tasks.js +12 -0
  64. package/dist/commands/tasks/tasks-cli.js +1 -2
  65. package/dist/commands/wiki-cli.js +2 -3
  66. package/dist/core/common.js +3 -3
  67. package/dist/core/config/config-schema.js +6 -0
  68. package/dist/core/config/config.js +12 -0
  69. package/dist/core/deep-merge.js +38 -0
  70. package/dist/core/events.js +2 -1
  71. package/dist/core/logs-db.js +8 -13
  72. package/dist/core/paths.js +14 -14
  73. package/dist/core/state-db.js +13 -1140
  74. package/dist/core/warn.js +21 -0
  75. package/dist/indexer/db/db.js +72 -709
  76. package/dist/indexer/db/entry-mapper.js +41 -0
  77. package/dist/indexer/db/schema.js +516 -0
  78. package/dist/indexer/ensure-index.js +3 -2
  79. package/dist/indexer/feedback/utility-policy.js +85 -0
  80. package/dist/indexer/graph/graph-extraction.js +2 -1
  81. package/dist/indexer/index-writer-lock.js +18 -0
  82. package/dist/indexer/indexer.js +94 -27
  83. package/dist/indexer/read-preflight.js +23 -0
  84. package/dist/indexer/search/fts-query.js +51 -0
  85. package/dist/indexer/walk/walker.js +21 -13
  86. package/dist/integrations/agent/detect.js +9 -0
  87. package/dist/integrations/agent/index.js +1 -1
  88. package/dist/integrations/agent/spawn.js +15 -66
  89. package/dist/llm/client.js +12 -0
  90. package/dist/llm/embedder.js +26 -2
  91. package/dist/llm/embedders/local.js +7 -1
  92. package/dist/output/text/helpers.js +13 -0
  93. package/dist/scripts/migrate-storage.js +6903 -7424
  94. package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +49 -44
  95. package/dist/setup/detect.js +9 -0
  96. package/dist/setup/legacy-config.js +106 -0
  97. package/dist/setup/prompt.js +57 -0
  98. package/dist/setup/providers.js +14 -0
  99. package/dist/setup/registry-stash-loader.js +12 -0
  100. package/dist/setup/semantic-assets.js +124 -0
  101. package/dist/setup/setup.js +25 -1608
  102. package/dist/setup/steps/connection.js +734 -0
  103. package/dist/setup/steps/output.js +31 -0
  104. package/dist/setup/steps/platforms.js +124 -0
  105. package/dist/setup/steps/semantic.js +27 -0
  106. package/dist/setup/steps/sources.js +222 -0
  107. package/dist/setup/steps/stashdir.js +42 -0
  108. package/dist/setup/steps/tasks.js +152 -0
  109. package/dist/storage/repositories/canaries-repository.js +107 -0
  110. package/dist/storage/repositories/consolidation-repository.js +38 -0
  111. package/dist/storage/repositories/embeddings-repository.js +72 -0
  112. package/dist/storage/repositories/events-repository.js +187 -0
  113. package/dist/storage/repositories/extract-sessions-repository.js +96 -0
  114. package/dist/storage/repositories/improve-runs-repository.js +130 -0
  115. package/dist/storage/repositories/index-db.js +4 -7
  116. package/dist/storage/repositories/proposals-repository.js +220 -0
  117. package/dist/storage/repositories/recombine-repository.js +213 -0
  118. package/dist/storage/repositories/task-history-repository.js +93 -0
  119. package/dist/storage/sqlite-pragmas.js +3 -3
  120. package/dist/tasks/backends/index.js +9 -0
  121. package/dist/tasks/runner.js +11 -1
  122. package/package.json +2 -2
  123. package/dist/commands/improve/homeostatic.js +0 -497
@@ -58,7 +58,7 @@ const CURATE_REFERENCE_QUERY_RE = /\b(?:reference|docs?|guide|how|explain|learn|
58
58
  * Fire-and-forget: log a curate event to the usage_events table and events.jsonl.
59
59
  * Never blocks the caller; errors are silently ignored.
60
60
  */
61
- function logCurateEvent(query, result) {
61
+ function logCurateEvent(query, result, eventSource = "user") {
62
62
  const itemRefs = result.items.map((item) => ("ref" in item ? item.ref : `registry:${item.id}`));
63
63
  appendEvent({
64
64
  eventType: "curate",
@@ -73,7 +73,7 @@ function logCurateEvent(query, result) {
73
73
  itemCount: result.items.length,
74
74
  itemRefs,
75
75
  }),
76
- source: "user",
76
+ source: eventSource,
77
77
  });
78
78
  for (const item of result.items) {
79
79
  if (!("ref" in item) || typeof item.ref !== "string")
@@ -82,7 +82,7 @@ function logCurateEvent(query, result) {
82
82
  event_type: "curate",
83
83
  query,
84
84
  entry_ref: item.ref,
85
- source: "user",
85
+ source: eventSource,
86
86
  });
87
87
  }
88
88
  }, { busyTimeoutMs: TELEMETRY_BUSY_TIMEOUT_MS });
@@ -106,7 +106,7 @@ export async function akmCurate(options) {
106
106
  source,
107
107
  }));
108
108
  const result = await curateSearchResults(options.query, searchResponse, limit, options.type);
109
- logCurateEvent(options.query, result);
109
+ logCurateEvent(options.query, result, options.eventSource);
110
110
  return result;
111
111
  }
112
112
  export async function curateSearchResults(query, result, limit, selectedType) {
@@ -1,8 +1,8 @@
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 { defineCommand } from "citty";
5
- import { output, parseAllFlagValues, runWithJsonErrors } from "../../cli/shared.js";
4
+ import { getStringArg } from "../../cli/parse-args.js";
5
+ import { defineJsonCommand, output, parseAllFlagValues } from "../../cli/shared.js";
6
6
  import { UsageError } from "../../core/errors.js";
7
7
  import { appendEvent } from "../../core/events.js";
8
8
  import { buildMemoryFrontmatter, parseDuration, readMemoryContent, resolveRememberContentArg, runAutoHeuristics, runLlmEnrich, } from "../remember.js";
@@ -26,7 +26,7 @@ async function fetchSimilarMemories(query, excludeRef) {
26
26
  }
27
27
  }
28
28
  // ── Command definition ────────────────────────────────────────────────────────
29
- export const rememberCommand = defineCommand({
29
+ export const rememberCommand = defineJsonCommand({
30
30
  meta: {
31
31
  name: "remember",
32
32
  description: "Record a memory in the default stash",
@@ -102,141 +102,42 @@ export const rememberCommand = defineCommand({
102
102
  },
103
103
  },
104
104
  async run({ args }) {
105
- return runWithJsonErrors(async () => {
106
- const body = readMemoryContent(resolveRememberContentArg(args.content));
107
- // `--name` is a flat name; subdirectory placement is `--path`'s job.
108
- assertFlatAssetName(args.name);
109
- // Determine if the user has requested any structured metadata mode.
110
- // Collect all --tag occurrences directly from process.argv because citty
111
- // only exposes the last value for repeated string flags.
112
- const rawTags = parseAllFlagValues("--tag");
113
- // Collect scope flags. Scope alone counts as structured metadata so we
114
- // emit frontmatter, but it does NOT trigger the "tags required" check
115
- // memory + scope (no tags) is a valid combination for multi-tenant use.
116
- const scopeFields = {};
117
- if (typeof args.user === "string" && args.user.trim())
118
- scopeFields.user = args.user.trim();
119
- if (typeof args.agent === "string" && args.agent.trim())
120
- scopeFields.agent = args.agent.trim();
121
- if (typeof args.run === "string" && args.run.trim())
122
- scopeFields.run = args.run.trim();
123
- if (typeof args.channel === "string" && args.channel.trim())
124
- scopeFields.channel = args.channel.trim();
125
- const hasScope = Object.keys(scopeFields).length > 0;
126
- const hasTagRequiringArgs = rawTags.length > 0 || !!args.expires || !!args.source || !!args.description;
127
- const hasStructuredArgs = hasTagRequiringArgs || hasScope || args.auto;
128
- if (!hasStructuredArgs) {
129
- // Phase 1B / Rec 7: even the zero-flag hot-path emits
130
- // `captureMode: hot` + `beliefState: asserted` so user-supplied
131
- // memories outrank background-derived ones during ranking.
132
- const frontmatterBlock = buildMemoryFrontmatter({
133
- captureMode: "hot",
134
- beliefState: "asserted",
135
- });
136
- const contentWithFrontmatter = `${frontmatterBlock}\n${body}`;
137
- // Derive the asset slug from the body (not the frontmatter block);
138
- // otherwise inferAssetName would key off the leading `---` delimiter.
139
- const result = await writeMarkdownAsset({
140
- type: "memory",
141
- content: contentWithFrontmatter,
142
- name: args.name,
143
- fallbackPrefix: "memory",
144
- preferredName: inferAssetName(body, "memory"),
145
- force: args.force,
146
- target: args.target,
147
- path: args.path,
148
- });
149
- appendEvent({
150
- eventType: "remember",
151
- ref: result.ref,
152
- metadata: { path: result.path, force: args.force === true },
153
- });
154
- if (args.showSimilar) {
155
- const similar = await fetchSimilarMemories(body.slice(0, 500), result.ref);
156
- output("remember", { ok: true, ...result, similar });
157
- }
158
- else {
159
- output("remember", { ok: true, ...result });
160
- }
161
- return;
162
- }
163
- // ── Accumulate metadata from all three modes ──────────────────────────
164
- // Start with CLI args (Mode 1: always)
165
- const tags = [...rawTags];
166
- // --description is persisted as-is; LLM enrichment may fill it if absent.
167
- let description = args.description || undefined;
168
- let source = args.source;
169
- let observed_at;
170
- let expires;
171
- let subjective;
172
- // Resolve --expires to an ISO date string
173
- if (args.expires) {
174
- const durationMs = parseDuration(args.expires);
175
- const expiresDate = new Date(Date.now() + durationMs);
176
- expires = expiresDate.toISOString().slice(0, 10);
177
- }
178
- // Mode 2: --auto heuristics
179
- if (args.auto) {
180
- const auto = runAutoHeuristics(body);
181
- for (const t of auto.tags) {
182
- if (!tags.includes(t))
183
- tags.push(t);
184
- }
185
- if (!source && auto.source)
186
- source = auto.source;
187
- if (!observed_at && auto.observed_at)
188
- observed_at = auto.observed_at;
189
- if (!subjective && auto.subjective)
190
- subjective = auto.subjective;
191
- }
192
- // Mode 3: --enrich LLM (fail-soft)
193
- if (args.enrich) {
194
- const enriched = await runLlmEnrich(body);
195
- for (const t of enriched.tags) {
196
- if (!tags.includes(t))
197
- tags.push(t);
198
- }
199
- if (!description && enriched.description)
200
- description = enriched.description;
201
- if (!observed_at && enriched.observed_at)
202
- observed_at = enriched.observed_at;
203
- }
204
- // ── Required-field check (before any write) ───────────────────────────
205
- // Tags remain required when the user explicitly asked for tag-bearing
206
- // metadata (--tag / --enrich / --description / --source / --expires).
207
- // `--auto` alone is allowed even when its heuristics derive zero tags.
208
- // Scope-only writes (`akm remember "..." --user u1`) also skip this
209
- // check — scope is independent metadata and a memory with only scope is
210
- // valid.
211
- const missing = [];
212
- if (hasTagRequiringArgs && tags.length === 0)
213
- missing.push("tags");
214
- if (missing.length > 0) {
215
- throw new UsageError(`Memory is missing required frontmatter field(s): ${missing.join(", ")}. ` +
216
- "Provide them via --tag <value>, --auto (heuristics), or --enrich (LLM).");
217
- }
218
- // ── Build frontmatter and write ───────────────────────────────────────
219
- // Phase 1B / Rec 7: the hot-path CLI write always marks the memory as
220
- // `captureMode: hot` and `beliefState: asserted`. Ranking applies a
221
- // hot-capture boost so user-supplied memories outrank otherwise-equal
222
- // background-derived ones.
105
+ const body = readMemoryContent(resolveRememberContentArg(args.content));
106
+ // `--name` is a flat name; subdirectory placement is `--path`'s job.
107
+ assertFlatAssetName(args.name);
108
+ // Determine if the user has requested any structured metadata mode.
109
+ // Collect all --tag occurrences directly from process.argv because citty
110
+ // only exposes the last value for repeated string flags.
111
+ const rawTags = parseAllFlagValues("--tag");
112
+ // Collect scope flags. Scope alone counts as structured metadata so we
113
+ // emit frontmatter, but it does NOT trigger the "tags required" check —
114
+ // memory + scope (no tags) is a valid combination for multi-tenant use.
115
+ const scopeFields = {};
116
+ for (const k of ["user", "agent", "run", "channel"]) {
117
+ const v = getStringArg(args, k);
118
+ if (v)
119
+ scopeFields[k] = v;
120
+ }
121
+ const hasScope = Object.keys(scopeFields).length > 0;
122
+ const hasTagRequiringArgs = rawTags.length > 0 || !!args.expires || !!args.source || !!args.description;
123
+ const hasStructuredArgs = hasTagRequiringArgs || hasScope || args.auto;
124
+ if (!hasStructuredArgs) {
125
+ // Phase 1B / Rec 7: even the zero-flag hot-path emits
126
+ // `captureMode: hot` + `beliefState: asserted` so user-supplied
127
+ // memories outrank background-derived ones during ranking.
223
128
  const frontmatterBlock = buildMemoryFrontmatter({
224
- description,
225
- tags,
226
- source,
227
- observed_at,
228
- expires,
229
- subjective,
230
129
  captureMode: "hot",
231
130
  beliefState: "asserted",
232
- ...(hasScope ? { scope: scopeFields } : {}),
233
131
  });
234
132
  const contentWithFrontmatter = `${frontmatterBlock}\n${body}`;
133
+ // Derive the asset slug from the body (not the frontmatter block);
134
+ // otherwise inferAssetName would key off the leading `---` delimiter.
235
135
  const result = await writeMarkdownAsset({
236
136
  type: "memory",
237
137
  content: contentWithFrontmatter,
238
138
  name: args.name,
239
139
  fallbackPrefix: "memory",
140
+ preferredName: inferAssetName(body, "memory"),
240
141
  force: args.force,
241
142
  target: args.target,
242
143
  path: args.path,
@@ -244,22 +145,116 @@ export const rememberCommand = defineCommand({
244
145
  appendEvent({
245
146
  eventType: "remember",
246
147
  ref: result.ref,
247
- metadata: {
248
- path: result.path,
249
- force: args.force === true,
250
- tagCount: tags.length,
251
- enriched: args.enrich === true,
252
- auto: args.auto === true,
253
- ...(hasScope ? { scope: scopeFields } : {}),
254
- },
148
+ metadata: { path: result.path, force: args.force === true },
255
149
  });
256
150
  if (args.showSimilar) {
257
- const similar = await fetchSimilarMemories((body ?? args.content ?? "").slice(0, 500), result.ref);
151
+ const similar = await fetchSimilarMemories(body.slice(0, 500), result.ref);
258
152
  output("remember", { ok: true, ...result, similar });
259
153
  }
260
154
  else {
261
155
  output("remember", { ok: true, ...result });
262
156
  }
157
+ return;
158
+ }
159
+ // ── Accumulate metadata from all three modes ──────────────────────────
160
+ // Start with CLI args (Mode 1: always)
161
+ const tags = [...rawTags];
162
+ // --description is persisted as-is; LLM enrichment may fill it if absent.
163
+ let description = args.description || undefined;
164
+ let source = args.source;
165
+ let observed_at;
166
+ let expires;
167
+ let subjective;
168
+ // Resolve --expires to an ISO date string
169
+ if (args.expires) {
170
+ const durationMs = parseDuration(args.expires);
171
+ const expiresDate = new Date(Date.now() + durationMs);
172
+ expires = expiresDate.toISOString().slice(0, 10);
173
+ }
174
+ // Mode 2: --auto heuristics
175
+ if (args.auto) {
176
+ const auto = runAutoHeuristics(body);
177
+ for (const t of auto.tags) {
178
+ if (!tags.includes(t))
179
+ tags.push(t);
180
+ }
181
+ if (!source && auto.source)
182
+ source = auto.source;
183
+ if (!observed_at && auto.observed_at)
184
+ observed_at = auto.observed_at;
185
+ if (!subjective && auto.subjective)
186
+ subjective = auto.subjective;
187
+ }
188
+ // Mode 3: --enrich LLM (fail-soft)
189
+ if (args.enrich) {
190
+ const enriched = await runLlmEnrich(body);
191
+ for (const t of enriched.tags) {
192
+ if (!tags.includes(t))
193
+ tags.push(t);
194
+ }
195
+ if (!description && enriched.description)
196
+ description = enriched.description;
197
+ if (!observed_at && enriched.observed_at)
198
+ observed_at = enriched.observed_at;
199
+ }
200
+ // ── Required-field check (before any write) ───────────────────────────
201
+ // Tags remain required when the user explicitly asked for tag-bearing
202
+ // metadata (--tag / --enrich / --description / --source / --expires).
203
+ // `--auto` alone is allowed even when its heuristics derive zero tags.
204
+ // Scope-only writes (`akm remember "..." --user u1`) also skip this
205
+ // check — scope is independent metadata and a memory with only scope is
206
+ // valid.
207
+ const missing = [];
208
+ if (hasTagRequiringArgs && tags.length === 0)
209
+ missing.push("tags");
210
+ if (missing.length > 0) {
211
+ throw new UsageError(`Memory is missing required frontmatter field(s): ${missing.join(", ")}. ` +
212
+ "Provide them via --tag <value>, --auto (heuristics), or --enrich (LLM).");
213
+ }
214
+ // ── Build frontmatter and write ───────────────────────────────────────
215
+ // Phase 1B / Rec 7: the hot-path CLI write always marks the memory as
216
+ // `captureMode: hot` and `beliefState: asserted`. Ranking applies a
217
+ // hot-capture boost so user-supplied memories outrank otherwise-equal
218
+ // background-derived ones.
219
+ const frontmatterBlock = buildMemoryFrontmatter({
220
+ description,
221
+ tags,
222
+ source,
223
+ observed_at,
224
+ expires,
225
+ subjective,
226
+ captureMode: "hot",
227
+ beliefState: "asserted",
228
+ ...(hasScope ? { scope: scopeFields } : {}),
229
+ });
230
+ const contentWithFrontmatter = `${frontmatterBlock}\n${body}`;
231
+ const result = await writeMarkdownAsset({
232
+ type: "memory",
233
+ content: contentWithFrontmatter,
234
+ name: args.name,
235
+ fallbackPrefix: "memory",
236
+ force: args.force,
237
+ target: args.target,
238
+ path: args.path,
239
+ });
240
+ appendEvent({
241
+ eventType: "remember",
242
+ ref: result.ref,
243
+ metadata: {
244
+ path: result.path,
245
+ force: args.force === true,
246
+ tagCount: tags.length,
247
+ enriched: args.enrich === true,
248
+ auto: args.auto === true,
249
+ ...(hasScope ? { scope: scopeFields } : {}),
250
+ },
263
251
  });
252
+ if (args.showSimilar) {
253
+ const similar = await fetchSimilarMemories((body ?? args.content ?? "").slice(0, 500), result.ref);
254
+ output("remember", { ok: true, ...result, similar });
255
+ }
256
+ else {
257
+ output("remember", { ok: true, ...result });
258
+ }
264
259
  },
265
260
  });
@@ -21,13 +21,15 @@ import { getHyphenatedBoolean, getOutputMode, parseFlagValue } from "../../outpu
21
21
  import { akmCurate } from "./curate.js";
22
22
  import { akmSearch, parseBeliefFilterMode, parseScopeFilterFlags, parseSearchSource } from "./search.js";
23
23
  import { akmShowUnified } from "./show.js";
24
- // AKM_EVENT_SOURCE attributes a query to a `user` invocation or the internal
25
- // `improve` loop so the event log can distinguish them; any other value is
26
- // treated as unset.
24
+ // AKM_EVENT_SOURCE attributes a query to a `user` invocation, the internal
25
+ // `improve` loop, or the `task` runner so the event log can distinguish
26
+ // genuine demand from machine traffic; any other value is treated as unset.
27
27
  function resolveEventSource() {
28
28
  const raw = process.env.AKM_EVENT_SOURCE;
29
29
  if (raw === "improve")
30
30
  return "improve";
31
+ if (raw === "task")
32
+ return "task";
31
33
  if (raw === "user")
32
34
  return "user";
33
35
  return undefined;
@@ -84,7 +86,7 @@ export const searchCommand = defineJsonCommand({
84
86
  const includeProposed = args["include-proposed"] === true;
85
87
  const belief = parseBeliefFilterMode(typeof args.belief === "string" ? args.belief : undefined);
86
88
  const noProjectContext = getHyphenatedBoolean(args, "no-project-context");
87
- const includeSessions = getHyphenatedBoolean(args, "include-sessions");
89
+ const includeSessions = args["include-sessions"];
88
90
  const result = await akmSearch({
89
91
  query,
90
92
  type,
@@ -129,7 +131,7 @@ export const curateCommand = defineJsonCommand({
129
131
  const limitParsed = parsePositiveIntFlag(args.limit ?? undefined);
130
132
  const limit = limitParsed && limitParsed > 0 ? limitParsed : 4;
131
133
  const source = parseSearchSource(args.source ?? "stash");
132
- const curated = await akmCurate({ query: args.query, type, limit, source });
134
+ const curated = await akmCurate({ query: args.query, type, limit, source, eventSource: resolveEventSource() });
133
135
  output("curate", curated);
134
136
  },
135
137
  });
@@ -16,8 +16,8 @@ import { rethrowIfTestIsolationError, UsageError } from "../../core/errors.js";
16
16
  import { appendEvent } from "../../core/events.js";
17
17
  import { isTransientStashPath } from "../../core/paths.js";
18
18
  import { bumpUtilityScoresBatch, getEntryIdByFilePath } from "../../indexer/db/db.js";
19
+ import { resolveReadSources } from "../../indexer/read-preflight.js";
19
20
  import { searchLocal } from "../../indexer/search/db-search.js";
20
- import { resolveSourceEntries } from "../../indexer/search/search-source.js";
21
21
  import { getCurrentWorkflowScopeKey } from "../../workflows/authoring/scope-key.js";
22
22
  // Eagerly import source providers to trigger self-registration before the
23
23
  // indexer or path-resolution code runs.
@@ -57,7 +57,7 @@ export async function akmSearch(input) {
57
57
  else {
58
58
  source = parsedSource;
59
59
  }
60
- let allSources = resolveSourceEntries(undefined, config);
60
+ let allSources = resolveReadSources(undefined, config).sources;
61
61
  // When a named source was requested, narrow the sources list to just that entry.
62
62
  // `resolveSourceEntries` sets `registryId` to `entry.name` for each config source.
63
63
  if (namedSourceName !== undefined) {
@@ -243,7 +243,11 @@ function logSearchEvent(query, response, mode = "keyword", eventSource = "user",
243
243
  }
244
244
  // Bump utility scores for all resolved entries (MemRL retrieval signal).
245
245
  // The indexer overwrites these at next reindex; bumps are temporary hints.
246
- const resolvedIds = resolved.map((r) => r.entryId).filter((id) => id !== undefined);
246
+ // Gated to user-sourced events: pipeline searches (improve probes, task
247
+ // runner) must not feed the utility signal (meta-review 05 DRIFT-6 —
248
+ // the bump previously fired unconditionally, so even correctly-tagged
249
+ // machine traffic inflated utility).
250
+ const resolvedIds = eventSource === "user" ? resolved.map((r) => r.entryId).filter((id) => id !== undefined) : [];
247
251
  if (resolvedIds.length > 0) {
248
252
  let scopeKey;
249
253
  try {
@@ -28,10 +28,10 @@ import { NotFoundError, rethrowIfTestIsolationError, UsageError } from "../../co
28
28
  import { appendEvent, readEvents } from "../../core/events.js";
29
29
  import { closeDatabase, computeBodyHash, findEntryIdByRef, openExistingDatabase } from "../../indexer/db/db.js";
30
30
  import { hasGraphData } from "../../indexer/db/graph-db.js";
31
- import { ensureIndex } from "../../indexer/ensure-index.js";
32
31
  import { listRelatedPathsForFile } from "../../indexer/graph/graph-boost.js";
33
32
  import { extractGraphForSingleFile } from "../../indexer/graph/graph-extraction.js";
34
33
  import { lookup } from "../../indexer/indexer.js";
34
+ import { ensurePrimaryIndexForRead, resolveReadSources } from "../../indexer/read-preflight.js";
35
35
  import { buildEditHint, findSourceForPath, isEditable, resolveSourceEntries } from "../../indexer/search/search-source.js";
36
36
  import { insertUsageEvent } from "../../indexer/usage/usage-events.js";
37
37
  import { buildFileContext, buildRenderContext, getRenderer, runMatchers } from "../../indexer/walk/file-context.js";
@@ -147,10 +147,8 @@ export async function akmShowUnified(input) {
147
147
  }
148
148
  }
149
149
  // Auto-index when stale so the index is current before lookup.
150
- const allSources = resolveSourceEntries();
151
- if (allSources.length > 0) {
152
- await ensureIndex(allSources[0].path);
153
- }
150
+ const { primarySource } = resolveReadSources();
151
+ await ensurePrimaryIndexForRead(primarySource);
154
152
  // Try local filesystem (FTS5 index lookup)
155
153
  const result = await showLocal(input);
156
154
  // Scope filter narrows resolution: if --scope was supplied, the asset's