akm-cli 0.9.0-rc.2 → 0.9.0-rc.3

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 (88) hide show
  1. package/CHANGELOG.md +34 -8
  2. package/dist/assets/improve-strategies/catchup.json +3 -1
  3. package/dist/assets/improve-strategies/consolidate.json +3 -1
  4. package/dist/assets/improve-strategies/frequent.json +3 -1
  5. package/dist/assets/improve-strategies/graph-refresh.json +3 -1
  6. package/dist/assets/improve-strategies/memory-focus.json +4 -1
  7. package/dist/assets/improve-strategies/proactive-maintenance.json +1 -0
  8. package/dist/assets/improve-strategies/quick.json +3 -1
  9. package/dist/assets/improve-strategies/recombine-only.json +2 -0
  10. package/dist/assets/improve-strategies/reflect-distill.json +1 -0
  11. package/dist/assets/improve-strategies/synthesize.json +2 -0
  12. package/dist/assets/tasks/core/backup.yml +2 -2
  13. package/dist/cli/config-migrate.js +554 -26
  14. package/dist/cli.js +3 -1
  15. package/dist/commands/backup-cli.js +6 -4
  16. package/dist/commands/config-cli.js +10 -3
  17. package/dist/commands/feedback-cli.js +24 -7
  18. package/dist/commands/health/checks.js +94 -15
  19. package/dist/commands/health/surfaces.js +2 -1
  20. package/dist/commands/improve/anti-collapse.js +3 -3
  21. package/dist/commands/improve/collapse-detector.js +5 -5
  22. package/dist/commands/improve/consolidate.js +123 -66
  23. package/dist/commands/improve/distill/promote-memory.js +8 -6
  24. package/dist/commands/improve/distill/quality-gate.js +1 -1
  25. package/dist/commands/improve/distill-guards.js +1 -1
  26. package/dist/commands/improve/distill-promotion-policy.js +32 -26
  27. package/dist/commands/improve/distill.js +52 -36
  28. package/dist/commands/improve/eligibility.js +9 -8
  29. package/dist/commands/improve/extract-prompt.js +2 -2
  30. package/dist/commands/improve/extract.js +43 -11
  31. package/dist/commands/improve/improve-auto-accept.js +14 -28
  32. package/dist/commands/improve/improve-cli.js +4 -2
  33. package/dist/commands/improve/improve-strategies.js +2 -1
  34. package/dist/commands/improve/improve.js +49 -8
  35. package/dist/commands/improve/loop-stages.js +11 -6
  36. package/dist/commands/improve/preparation.js +51 -17
  37. package/dist/commands/improve/procedural.js +10 -6
  38. package/dist/commands/improve/recombine.js +31 -7
  39. package/dist/commands/improve/reflect.js +24 -14
  40. package/dist/commands/improve/salience.js +5 -1
  41. package/dist/commands/improve/source-identity.js +43 -0
  42. package/dist/commands/migrate-cli.js +36 -0
  43. package/dist/commands/mv-cli.js +647 -258
  44. package/dist/commands/proposal/drain.js +16 -4
  45. package/dist/commands/proposal/proposal-cli.js +3 -2
  46. package/dist/commands/proposal/proposal.js +16 -55
  47. package/dist/commands/proposal/repository.js +664 -58
  48. package/dist/commands/read/curate.js +4 -2
  49. package/dist/commands/read/knowledge.js +4 -1
  50. package/dist/commands/read/search.js +6 -2
  51. package/dist/commands/read/show.js +8 -7
  52. package/dist/commands/sources/self-update.js +156 -112
  53. package/dist/commands/sources/sources-cli.js +7 -2
  54. package/dist/core/common.js +36 -3
  55. package/dist/core/config/config-io.js +2 -2
  56. package/dist/core/config/config-schema.js +15 -3
  57. package/dist/core/config/config.js +14 -13
  58. package/dist/core/file-lock.js +2 -2
  59. package/dist/core/migration-backup.js +816 -171
  60. package/dist/core/migration-operation.js +44 -0
  61. package/dist/core/state/migrations.js +4 -7
  62. package/dist/core/state-db.js +22 -7
  63. package/dist/core/write-source.js +86 -18
  64. package/dist/indexer/db/db.js +109 -53
  65. package/dist/indexer/index-writer-lock.js +38 -37
  66. package/dist/indexer/index-written-assets.js +73 -56
  67. package/dist/indexer/indexer.js +5 -1
  68. package/dist/indexer/search/search-source.js +2 -2
  69. package/dist/indexer/usage/usage-events.js +8 -2
  70. package/dist/integrations/agent/engine-resolution.js +14 -7
  71. package/dist/scripts/migrate-storage.js +867 -990
  72. package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +405 -566
  73. package/dist/setup/detected-engines.js +7 -13
  74. package/dist/setup/engine-config.js +14 -4
  75. package/dist/setup/setup.js +1 -1
  76. package/dist/setup/steps/connection.js +1 -1
  77. package/dist/setup/steps/tasks.js +1 -1
  78. package/dist/sources/providers/git-stash.js +58 -21
  79. package/dist/sources/providers/git.js +1 -1
  80. package/dist/storage/engines/sqlite-migrations.js +138 -3
  81. package/dist/storage/repositories/events-repository.js +24 -0
  82. package/dist/storage/repositories/proposals-repository.js +14 -0
  83. package/dist/tasks/embedded.js +2 -0
  84. package/dist/workflows/db.js +24 -13
  85. package/dist/workflows/validator.js +24 -2
  86. package/docs/migration/release-notes/0.9.0.md +24 -7
  87. package/docs/migration/v0.8-to-v0.9.md +124 -19
  88. package/package.json +1 -1
@@ -564,9 +564,9 @@ const ImproveProfileProcessesSchema = z
564
564
  // 0.8.0 removed the duplicated `feedbackDistillation` process key — it was
565
565
  // a thin wrapper around `processes.distill.enabled`. Keep the migration
566
566
  // hint so a stale config gets an actionable message rather than silently
567
- // doing nothing. All OTHER unknown process keys are tolerated (passthrough)
568
- // see the unknown-key policy in this file's header. Hard-rejecting them
569
- // turned benign cross-version skew into INVALID_CONFIG_FILE failures.
567
+ // doing nothing. Other unknown process keys remain preserved for
568
+ // cross-version compatibility, but an enabled one is rejected below because
569
+ // this version cannot execute it.
570
570
  if ("feedbackDistillation" in val) {
571
571
  ctx.addIssue({
572
572
  code: z.ZodIssueCode.custom,
@@ -574,6 +574,18 @@ const ImproveProfileProcessesSchema = z
574
574
  "It now controls both the orchestration gate and the LLM-call gate.",
575
575
  });
576
576
  }
577
+ for (const [name, process] of Object.entries(val)) {
578
+ if (!(name in IMPROVE_PROCESS_ENGINE_CAPABILITIES) &&
579
+ process !== null &&
580
+ typeof process === "object" &&
581
+ process.enabled === true) {
582
+ ctx.addIssue({
583
+ code: z.ZodIssueCode.custom,
584
+ path: [name],
585
+ message: `Unknown enabled improve process "${name}"`,
586
+ });
587
+ }
588
+ }
577
589
  });
578
590
  export const ImproveProfileConfigSchema = z
579
591
  .object({
@@ -4,7 +4,7 @@
4
4
  import fs from "node:fs";
5
5
  import path from "node:path";
6
6
  import { ConfigError } from "../errors.js";
7
- import { ensureMigrationBackupWithConfigLockHeld } from "../migration-backup.js";
7
+ import { assertNoPendingMigrationOperation } from "../migration-operation.js";
8
8
  import { acquireConfigLock, backupExistingConfig, parseConfigText, readConfigText, withConfigLock, writeConfigAtomic, } from "./config-io.js";
9
9
  import { AkmConfigSchema, CURRENT_CONFIG_VERSION } from "./config-schema.js";
10
10
  import { deepMergeConfig } from "./deep-merge.js";
@@ -68,6 +68,7 @@ export function resetConfigCache() {
68
68
  cachedConfig = undefined;
69
69
  }
70
70
  export function loadUserConfig() {
71
+ assertNoPendingMigrationOperation();
71
72
  const configPath = getConfigPath();
72
73
  let stat;
73
74
  try {
@@ -91,12 +92,12 @@ export function loadUserConfig() {
91
92
  }
92
93
  let text;
93
94
  try {
94
- text = fs.readFileSync(configPath, "utf8");
95
+ text = readConfigText(configPath);
95
96
  }
96
97
  catch (err) {
97
- if (err.code !== "ENOENT") {
98
- throw new ConfigError(`Unable to read config at ${configPath}: ${err instanceof Error ? err.message : String(err)}`, "INVALID_CONFIG_FILE");
99
- }
98
+ throw new ConfigError(`Unable to read config at ${configPath}: ${err instanceof Error ? err.message : String(err)}`, "INVALID_CONFIG_FILE");
99
+ }
100
+ if (text === undefined) {
100
101
  cachedConfig = undefined;
101
102
  return { ...DEFAULT_CONFIG };
102
103
  }
@@ -191,13 +192,14 @@ export function saveConfig(config) {
191
192
  saveConfigReal(currentConfig);
192
193
  }
193
194
  function saveConfigReal(config) {
195
+ assertNoPendingMigrationOperation();
194
196
  cachedConfig = undefined;
195
197
  const configPath = getConfigPath();
196
198
  const dir = path.dirname(configPath);
197
199
  fs.mkdirSync(dir, { recursive: true });
198
200
  withConfigLock(() => {
201
+ assertNoPendingMigrationOperation();
199
202
  const validated = validateCompleteConfig(config);
200
- ensureMigrationBackupWithConfigLockHeld();
201
203
  backupExistingConfig(configPath);
202
204
  writeConfigAtomic(configPath, sanitizeConfigForWrite(validated));
203
205
  });
@@ -212,12 +214,14 @@ export function validateCompleteConfig(config) {
212
214
  }
213
215
  /**
214
216
  * Mutate config under one fail-closed lock spanning read, merge, validation,
215
- * migration backup, ordinary backup, and atomic write.
217
+ * ordinary backup, and atomic write.
216
218
  */
217
219
  export function mutateConfig(mutate, options) {
220
+ assertNoPendingMigrationOperation();
218
221
  cachedConfig = undefined;
219
222
  const configPath = getConfigPath();
220
223
  return withConfigLock(() => {
224
+ assertNoPendingMigrationOperation();
221
225
  const text = readConfigText(configPath);
222
226
  if (text === undefined && options?.absentNoop) {
223
227
  return { config: { ...DEFAULT_CONFIG }, written: false };
@@ -227,7 +231,6 @@ export function mutateConfig(mutate, options) {
227
231
  if (mutated === current)
228
232
  return { config: current, written: false };
229
233
  const next = validateCompleteConfig({ ...mutated, configVersion: CURRENT_CONFIG_VERSION });
230
- ensureMigrationBackupWithConfigLockHeld();
231
234
  if (text !== undefined)
232
235
  backupExistingConfig(configPath);
233
236
  fs.mkdirSync(path.dirname(configPath), { recursive: true });
@@ -241,18 +244,16 @@ export function mutateConfig(mutate, options) {
241
244
  * its stash, while preventing another config writer from racing the final save.
242
245
  */
243
246
  export async function mutateConfigWithPrecommit(mutate, precommit) {
247
+ assertNoPendingMigrationOperation();
244
248
  cachedConfig = undefined;
245
249
  const configPath = getConfigPath();
246
250
  const release = acquireConfigLock();
247
251
  try {
252
+ assertNoPendingMigrationOperation();
248
253
  const text = readConfigText(configPath);
249
254
  const current = text === undefined ? { ...DEFAULT_CONFIG } : parseAndValidateConfigText(text, configPath);
250
255
  const mutated = mutate(current);
251
256
  const next = validateCompleteConfig({ ...mutated, configVersion: CURRENT_CONFIG_VERSION });
252
- // Setup's pre-commit may initialize the stash. Prove the rebase and final
253
- // config are valid, then require the recovery snapshots before that first
254
- // external side effect.
255
- ensureMigrationBackupWithConfigLockHeld();
256
257
  if (text !== undefined)
257
258
  backupExistingConfig(configPath);
258
259
  const precommitResult = await precommit(next);
@@ -281,7 +282,7 @@ export async function mutateConfigWithPrecommit(mutate, precommit) {
281
282
  * --yes` with an `apiKey` field expected persistence and got a wiped config
282
283
  * with no feedback.
283
284
  */
284
- function sanitizeConfigForWrite(config) {
285
+ export function sanitizeConfigForWrite(config) {
285
286
  const sanitized = { ...config };
286
287
  const stripped = [];
287
288
  if (config.embedding?.apiKey !== undefined) {
@@ -5,7 +5,7 @@ import { randomUUID } from "node:crypto";
5
5
  import fs from "node:fs";
6
6
  import path from "node:path";
7
7
  import { openDatabase } from "../storage/database.js";
8
- import { isProcessAlive } from "./common.js";
8
+ import { isProcessAlive, MAX_LOCK_METADATA_BYTES, readTextFileDescriptorWithLimit } from "./common.js";
9
9
  function readLockSnapshot(lockPath) {
10
10
  let fd;
11
11
  try {
@@ -17,7 +17,7 @@ function readLockSnapshot(lockPath) {
17
17
  throw err;
18
18
  }
19
19
  try {
20
- const rawContent = fs.readFileSync(fd, "utf8");
20
+ const rawContent = readTextFileDescriptorWithLimit(fd, MAX_LOCK_METADATA_BYTES, "Lock metadata", lockPath);
21
21
  const stat = fs.fstatSync(fd);
22
22
  return {
23
23
  rawContent,