@askexenow/exe-os 0.9.34 → 0.9.35

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 (70) hide show
  1. package/dist/bin/backfill-conversations.js +210 -10
  2. package/dist/bin/backfill-responses.js +210 -10
  3. package/dist/bin/backfill-vectors.js +13 -2
  4. package/dist/bin/cleanup-stale-review-tasks.js +217 -10
  5. package/dist/bin/cli.js +222 -11
  6. package/dist/bin/exe-assign.js +210 -10
  7. package/dist/bin/exe-boot.js +24 -3
  8. package/dist/bin/exe-dispatch.js +222 -11
  9. package/dist/bin/exe-doctor.js +13 -2
  10. package/dist/bin/exe-export-behaviors.js +217 -10
  11. package/dist/bin/exe-forget.js +217 -10
  12. package/dist/bin/exe-gateway.js +222 -11
  13. package/dist/bin/exe-heartbeat.js +217 -10
  14. package/dist/bin/exe-kill.js +217 -10
  15. package/dist/bin/exe-launch-agent.js +92 -2
  16. package/dist/bin/exe-link.js +8 -0
  17. package/dist/bin/exe-pending-messages.js +217 -10
  18. package/dist/bin/exe-pending-notifications.js +217 -10
  19. package/dist/bin/exe-pending-reviews.js +217 -10
  20. package/dist/bin/exe-rename.js +8 -0
  21. package/dist/bin/exe-review.js +217 -10
  22. package/dist/bin/exe-search.js +217 -10
  23. package/dist/bin/exe-session-cleanup.js +222 -11
  24. package/dist/bin/exe-start-codex.js +92 -2
  25. package/dist/bin/exe-start-opencode.js +92 -2
  26. package/dist/bin/exe-status.js +217 -10
  27. package/dist/bin/exe-team.js +217 -10
  28. package/dist/bin/git-sweep.js +222 -11
  29. package/dist/bin/graph-backfill.js +92 -2
  30. package/dist/bin/graph-export.js +217 -10
  31. package/dist/bin/intercom-check.js +222 -11
  32. package/dist/bin/scan-tasks.js +222 -11
  33. package/dist/bin/setup.js +8 -0
  34. package/dist/bin/shard-migrate.js +92 -2
  35. package/dist/gateway/index.js +222 -11
  36. package/dist/hooks/bug-report-worker.js +222 -11
  37. package/dist/hooks/codex-stop-task-finalizer.js +217 -10
  38. package/dist/hooks/commit-complete.js +222 -11
  39. package/dist/hooks/error-recall.js +217 -10
  40. package/dist/hooks/ingest.js +217 -10
  41. package/dist/hooks/instructions-loaded.js +217 -10
  42. package/dist/hooks/notification.js +217 -10
  43. package/dist/hooks/post-compact.js +217 -10
  44. package/dist/hooks/post-tool-combined.js +217 -10
  45. package/dist/hooks/pre-compact.js +222 -11
  46. package/dist/hooks/pre-tool-use.js +217 -10
  47. package/dist/hooks/prompt-submit.js +222 -11
  48. package/dist/hooks/session-end.js +222 -11
  49. package/dist/hooks/session-start.js +217 -10
  50. package/dist/hooks/stop.js +217 -10
  51. package/dist/hooks/subagent-stop.js +217 -10
  52. package/dist/hooks/summary-worker.js +14 -1
  53. package/dist/index.js +222 -11
  54. package/dist/lib/cloud-sync.js +8 -0
  55. package/dist/lib/consolidation.js +3 -1
  56. package/dist/lib/database.js +8 -0
  57. package/dist/lib/db.js +8 -0
  58. package/dist/lib/device-registry.js +8 -0
  59. package/dist/lib/exe-daemon.js +1667 -1413
  60. package/dist/lib/hybrid-search.js +217 -10
  61. package/dist/lib/schedules.js +13 -2
  62. package/dist/lib/store.js +210 -10
  63. package/dist/lib/tasks.js +5 -1
  64. package/dist/lib/tmux-routing.js +5 -1
  65. package/dist/mcp/server.js +222 -11
  66. package/dist/mcp/tools/create-task.js +5 -1
  67. package/dist/mcp/tools/update-task.js +5 -1
  68. package/dist/runtime/index.js +222 -11
  69. package/dist/tui/App.js +222 -11
  70. package/package.json +1 -1
@@ -2223,6 +2223,14 @@ async function ensureSchema() {
2223
2223
  );
2224
2224
  } catch {
2225
2225
  }
2226
+ try {
2227
+ await client.execute(
2228
+ `CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
2229
+ ON memories(content_hash, agent_id, project_name, memory_type)
2230
+ WHERE content_hash IS NOT NULL`
2231
+ );
2232
+ } catch {
2233
+ }
2226
2234
  await client.executeMultiple(`
2227
2235
  CREATE TABLE IF NOT EXISTS entities (
2228
2236
  id TEXT PRIMARY KEY,
@@ -2892,7 +2900,8 @@ async function ensureShardSchema(client) {
2892
2900
  }
2893
2901
  for (const idx of [
2894
2902
  "CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
2895
- "CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
2903
+ "CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
2904
+ "CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash ON memories(content_hash, agent_id, project_name, memory_type) WHERE content_hash IS NOT NULL"
2896
2905
  ]) {
2897
2906
  try {
2898
2907
  await client.execute(idx);
@@ -3448,7 +3457,6 @@ async function embed(text) {
3448
3457
  // src/lib/store.ts
3449
3458
  init_memory();
3450
3459
  init_database();
3451
- import { createHash } from "crypto";
3452
3460
 
3453
3461
  // src/lib/keychain.ts
3454
3462
  import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
@@ -3681,6 +3689,190 @@ var StateBus = class {
3681
3689
  };
3682
3690
  var orgBus = new StateBus();
3683
3691
 
3692
+ // src/lib/memory-write-governor.ts
3693
+ import { createHash } from "crypto";
3694
+ var HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
3695
+ "decision",
3696
+ "adr",
3697
+ "behavior",
3698
+ "procedure"
3699
+ ]);
3700
+ var NOISE_DROP_PATTERNS = [
3701
+ /^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
3702
+ /^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
3703
+ /^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
3704
+ /^\s*Intercom is a speedup, not delivery/im,
3705
+ /^\s*Context bar reads as USAGE not remaining/im
3706
+ ];
3707
+ var SKIP_EMBED_PATTERNS = [
3708
+ /tmux capture-pane\b/i,
3709
+ /docker ps\b/i,
3710
+ /docker images\b/i,
3711
+ /git status\b/i,
3712
+ /grep .*node_modules/i,
3713
+ /npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
3714
+ ];
3715
+ function normalizeMemoryText(text) {
3716
+ return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
3717
+ }
3718
+ function classifyMemoryType(input) {
3719
+ if (input.memory_type && input.memory_type.trim()) return input.memory_type.trim();
3720
+ const tool = input.tool_name.toLowerCase();
3721
+ const text = input.raw_text.toLowerCase();
3722
+ if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
3723
+ if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
3724
+ if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
3725
+ if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
3726
+ if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
3727
+ if (tool === "store_memory" || tool === "manual") return "observation";
3728
+ return "raw";
3729
+ }
3730
+ function shouldDropMemory(text) {
3731
+ const normalized = normalizeMemoryText(text);
3732
+ if (normalized.length < 10) return { drop: true, reason: "too_short" };
3733
+ if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
3734
+ return { drop: true, reason: "known_boilerplate_noise" };
3735
+ }
3736
+ return { drop: false };
3737
+ }
3738
+ function shouldSkipEmbedding(input) {
3739
+ const type = classifyMemoryType(input);
3740
+ if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
3741
+ if (type === "raw" && input.raw_text.length > 2e4) return true;
3742
+ if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input.raw_text))) return true;
3743
+ return false;
3744
+ }
3745
+ function hashMemoryContent(text) {
3746
+ return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
3747
+ }
3748
+ function scopedDedupArgs(input) {
3749
+ return [input.contentHash, input.agentId, input.projectName, input.memoryType];
3750
+ }
3751
+ function governMemoryRecord(record) {
3752
+ const normalized = normalizeMemoryText(record.raw_text);
3753
+ const memoryType = classifyMemoryType({
3754
+ raw_text: normalized,
3755
+ agent_id: record.agent_id,
3756
+ project_name: record.project_name,
3757
+ tool_name: record.tool_name,
3758
+ memory_type: record.memory_type
3759
+ });
3760
+ const drop = shouldDropMemory(normalized);
3761
+ const skipEmbedding = shouldSkipEmbedding({
3762
+ raw_text: normalized,
3763
+ agent_id: record.agent_id,
3764
+ project_name: record.project_name,
3765
+ tool_name: record.tool_name,
3766
+ memory_type: memoryType
3767
+ });
3768
+ return {
3769
+ record: {
3770
+ ...record,
3771
+ raw_text: normalized,
3772
+ memory_type: memoryType,
3773
+ vector: skipEmbedding ? null : record.vector
3774
+ },
3775
+ contentHash: hashMemoryContent(normalized),
3776
+ shouldDrop: drop.drop,
3777
+ dropReason: drop.reason,
3778
+ skipEmbedding,
3779
+ hygiene: {
3780
+ dedup: true,
3781
+ supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
3782
+ }
3783
+ };
3784
+ }
3785
+ async function findScopedDuplicate(input) {
3786
+ const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
3787
+ const client = getClient2();
3788
+ const args = scopedDedupArgs(input);
3789
+ let sql = `SELECT id FROM memories
3790
+ WHERE content_hash = ?
3791
+ AND agent_id = ?
3792
+ AND project_name = ?
3793
+ AND COALESCE(memory_type, 'raw') = ?
3794
+ AND COALESCE(status, 'active') != 'deleted'`;
3795
+ if (input.excludeId) {
3796
+ sql += " AND id != ?";
3797
+ args.push(input.excludeId);
3798
+ }
3799
+ sql += " ORDER BY timestamp DESC LIMIT 1";
3800
+ const result = await client.execute({ sql, args });
3801
+ return result.rows[0]?.id ? String(result.rows[0].id) : null;
3802
+ }
3803
+ async function runPostWriteMemoryHygiene(memoryId) {
3804
+ try {
3805
+ const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
3806
+ const client = getClient2();
3807
+ const current = await client.execute({
3808
+ sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
3809
+ importance, timestamp
3810
+ FROM memories
3811
+ WHERE id = ?
3812
+ LIMIT 1`,
3813
+ args: [memoryId]
3814
+ });
3815
+ const row = current.rows[0];
3816
+ if (!row) return;
3817
+ const memoryType = String(row.memory_type ?? "raw");
3818
+ const contentHash = row.content_hash ? String(row.content_hash) : null;
3819
+ const agentId = String(row.agent_id);
3820
+ const projectName = String(row.project_name);
3821
+ if (contentHash) {
3822
+ await client.execute({
3823
+ sql: `UPDATE memories
3824
+ SET status = 'deleted',
3825
+ outcome = COALESCE(outcome, 'superseded')
3826
+ WHERE id != ?
3827
+ AND content_hash = ?
3828
+ AND agent_id = ?
3829
+ AND project_name = ?
3830
+ AND COALESCE(memory_type, 'raw') = ?
3831
+ AND COALESCE(status, 'active') = 'active'`,
3832
+ args: [memoryId, contentHash, agentId, projectName, memoryType]
3833
+ });
3834
+ }
3835
+ const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
3836
+ if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
3837
+ const old = await client.execute({
3838
+ sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
3839
+ args: [supersedesId]
3840
+ });
3841
+ const oldImportance = Number(old.rows[0]?.importance ?? 0);
3842
+ const newImportance = Number(row.importance ?? 0);
3843
+ await client.batch([
3844
+ {
3845
+ sql: `UPDATE memories
3846
+ SET status = 'archived',
3847
+ outcome = COALESCE(outcome, 'superseded')
3848
+ WHERE id = ?`,
3849
+ args: [supersedesId]
3850
+ },
3851
+ {
3852
+ sql: `UPDATE memories
3853
+ SET importance = MAX(COALESCE(importance, 5), ?),
3854
+ parent_memory_id = COALESCE(parent_memory_id, ?)
3855
+ WHERE id = ?`,
3856
+ args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
3857
+ }
3858
+ ], "write");
3859
+ }
3860
+ } catch (err) {
3861
+ process.stderr.write(
3862
+ `[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
3863
+ `
3864
+ );
3865
+ }
3866
+ }
3867
+ function schedulePostWriteMemoryHygiene(memoryIds) {
3868
+ if (memoryIds.length === 0) return;
3869
+ const run = () => {
3870
+ void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
3871
+ };
3872
+ if (typeof setImmediate === "function") setImmediate(run);
3873
+ else setTimeout(run, 0);
3874
+ }
3875
+
3684
3876
  // src/lib/store.ts
3685
3877
  var INIT_MAX_RETRIES = 3;
3686
3878
  var INIT_RETRY_DELAY_MS = 1e3;
@@ -3803,17 +3995,24 @@ async function writeMemory(record) {
3803
3995
  `Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
3804
3996
  );
3805
3997
  }
3806
- const contentHash = createHash("md5").update(record.raw_text).digest("hex");
3807
- if (_pendingRecords.some((r) => r.content_hash === contentHash && r.agent_id === record.agent_id)) {
3998
+ const governed = governMemoryRecord(record);
3999
+ if (governed.shouldDrop) return;
4000
+ record = governed.record;
4001
+ const contentHash = governed.contentHash;
4002
+ const memoryType = record.memory_type ?? "raw";
4003
+ if (_pendingRecords.some(
4004
+ (r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
4005
+ )) {
3808
4006
  return;
3809
4007
  }
3810
4008
  try {
3811
- const client = getClient();
3812
- const existing = await client.execute({
3813
- sql: "SELECT id FROM memories WHERE content_hash = ? AND agent_id = ? LIMIT 1",
3814
- args: [contentHash, record.agent_id]
4009
+ const existing = await findScopedDuplicate({
4010
+ contentHash,
4011
+ agentId: record.agent_id,
4012
+ projectName: record.project_name,
4013
+ memoryType
3815
4014
  });
3816
- if (existing.rows.length > 0) return;
4015
+ if (existing) return;
3817
4016
  } catch {
3818
4017
  }
3819
4018
  const dbRow = {
@@ -3844,7 +4043,7 @@ async function writeMemory(record) {
3844
4043
  tier: record.tier ?? classifyTier(record),
3845
4044
  supersedes_id: record.supersedes_id ?? null,
3846
4045
  draft: record.draft ? 1 : 0,
3847
- memory_type: record.memory_type ?? "raw",
4046
+ memory_type: memoryType,
3848
4047
  trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
3849
4048
  content_hash: contentHash,
3850
4049
  intent: record.intent ?? null,
@@ -4003,6 +4202,7 @@ async function flushBatch() {
4003
4202
  const globalClient = getClient();
4004
4203
  const globalStmts = batch.map(buildStmt);
4005
4204
  await globalClient.batch(globalStmts, "write");
4205
+ schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
4006
4206
  _pendingRecords.splice(0, batch.length);
4007
4207
  try {
4008
4208
  const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
@@ -2365,6 +2365,14 @@ async function ensureSchema() {
2365
2365
  );
2366
2366
  } catch {
2367
2367
  }
2368
+ try {
2369
+ await client.execute(
2370
+ `CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
2371
+ ON memories(content_hash, agent_id, project_name, memory_type)
2372
+ WHERE content_hash IS NOT NULL`
2373
+ );
2374
+ } catch {
2375
+ }
2368
2376
  await client.executeMultiple(`
2369
2377
  CREATE TABLE IF NOT EXISTS entities (
2370
2378
  id TEXT PRIMARY KEY,
@@ -3474,6 +3482,14 @@ var init_state_bus = __esm({
3474
3482
  }
3475
3483
  });
3476
3484
 
3485
+ // src/lib/memory-write-governor.ts
3486
+ import { createHash } from "crypto";
3487
+ var init_memory_write_governor = __esm({
3488
+ "src/lib/memory-write-governor.ts"() {
3489
+ "use strict";
3490
+ }
3491
+ });
3492
+
3477
3493
  // src/lib/shard-manager.ts
3478
3494
  var shard_manager_exports = {};
3479
3495
  __export(shard_manager_exports, {
@@ -3638,7 +3654,8 @@ async function ensureShardSchema(client) {
3638
3654
  }
3639
3655
  for (const idx of [
3640
3656
  "CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
3641
- "CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
3657
+ "CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
3658
+ "CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash ON memories(content_hash, agent_id, project_name, memory_type) WHERE content_hash IS NOT NULL"
3642
3659
  ]) {
3643
3660
  try {
3644
3661
  await client.execute(idx);
@@ -3797,7 +3814,6 @@ var init_shard_manager = __esm({
3797
3814
  });
3798
3815
 
3799
3816
  // src/lib/store.ts
3800
- import { createHash } from "crypto";
3801
3817
  function isBusyError2(err) {
3802
3818
  if (err instanceof Error) {
3803
3819
  const msg = err.message.toLowerCase();
@@ -3882,6 +3898,7 @@ var init_store = __esm({
3882
3898
  init_keychain();
3883
3899
  init_config();
3884
3900
  init_state_bus();
3901
+ init_memory_write_governor();
3885
3902
  INIT_MAX_RETRIES = 3;
3886
3903
  INIT_RETRY_DELAY_MS = 1e3;
3887
3904
  _pendingRecords = [];
@@ -7996,7 +8013,11 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
7996
8013
  }
7997
8014
  if (!useExeAgent && !useCodex && !useOpencode && !useBinSymlink) {
7998
8015
  if (agentRtConfig.runtime === "claude" && agentRtConfig.model) {
7999
- envPrefix = `${envPrefix} ANTHROPIC_MODEL=${agentRtConfig.model}`;
8016
+ let ccModel = agentRtConfig.model.replace(/(\d+)\.(\d+)/g, "$1-$2");
8017
+ if (/claude-(opus|sonnet)-4-[6-9]/.test(ccModel) && !ccModel.includes("[1m]")) {
8018
+ ccModel += "[1m]";
8019
+ }
8020
+ envPrefix = `${envPrefix} ANTHROPIC_MODEL=${ccModel}`;
8000
8021
  }
8001
8022
  }
8002
8023
  let spawnCommand;
@@ -2867,6 +2867,14 @@ async function ensureSchema() {
2867
2867
  );
2868
2868
  } catch {
2869
2869
  }
2870
+ try {
2871
+ await client.execute(
2872
+ `CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
2873
+ ON memories(content_hash, agent_id, project_name, memory_type)
2874
+ WHERE content_hash IS NOT NULL`
2875
+ );
2876
+ } catch {
2877
+ }
2870
2878
  await client.executeMultiple(`
2871
2879
  CREATE TABLE IF NOT EXISTS entities (
2872
2880
  id TEXT PRIMARY KEY,
@@ -6399,7 +6407,11 @@ function spawnEmployee(employeeName2, exeSession2, projectDir2, opts) {
6399
6407
  }
6400
6408
  if (!useExeAgent && !useCodex && !useOpencode && !useBinSymlink) {
6401
6409
  if (agentRtConfig.runtime === "claude" && agentRtConfig.model) {
6402
- envPrefix = `${envPrefix} ANTHROPIC_MODEL=${agentRtConfig.model}`;
6410
+ let ccModel = agentRtConfig.model.replace(/(\d+)\.(\d+)/g, "$1-$2");
6411
+ if (/claude-(opus|sonnet)-4-[6-9]/.test(ccModel) && !ccModel.includes("[1m]")) {
6412
+ ccModel += "[1m]";
6413
+ }
6414
+ envPrefix = `${envPrefix} ANTHROPIC_MODEL=${ccModel}`;
6403
6415
  }
6404
6416
  }
6405
6417
  let spawnCommand;
@@ -6713,6 +6725,196 @@ var init_keychain = __esm({
6713
6725
  }
6714
6726
  });
6715
6727
 
6728
+ // src/lib/memory-write-governor.ts
6729
+ import { createHash } from "crypto";
6730
+ function normalizeMemoryText(text) {
6731
+ return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
6732
+ }
6733
+ function classifyMemoryType(input) {
6734
+ if (input.memory_type && input.memory_type.trim()) return input.memory_type.trim();
6735
+ const tool = input.tool_name.toLowerCase();
6736
+ const text = input.raw_text.toLowerCase();
6737
+ if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
6738
+ if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
6739
+ if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
6740
+ if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
6741
+ if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
6742
+ if (tool === "store_memory" || tool === "manual") return "observation";
6743
+ return "raw";
6744
+ }
6745
+ function shouldDropMemory(text) {
6746
+ const normalized = normalizeMemoryText(text);
6747
+ if (normalized.length < 10) return { drop: true, reason: "too_short" };
6748
+ if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
6749
+ return { drop: true, reason: "known_boilerplate_noise" };
6750
+ }
6751
+ return { drop: false };
6752
+ }
6753
+ function shouldSkipEmbedding(input) {
6754
+ const type = classifyMemoryType(input);
6755
+ if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
6756
+ if (type === "raw" && input.raw_text.length > 2e4) return true;
6757
+ if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input.raw_text))) return true;
6758
+ return false;
6759
+ }
6760
+ function hashMemoryContent(text) {
6761
+ return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
6762
+ }
6763
+ function scopedDedupArgs(input) {
6764
+ return [input.contentHash, input.agentId, input.projectName, input.memoryType];
6765
+ }
6766
+ function governMemoryRecord(record) {
6767
+ const normalized = normalizeMemoryText(record.raw_text);
6768
+ const memoryType = classifyMemoryType({
6769
+ raw_text: normalized,
6770
+ agent_id: record.agent_id,
6771
+ project_name: record.project_name,
6772
+ tool_name: record.tool_name,
6773
+ memory_type: record.memory_type
6774
+ });
6775
+ const drop = shouldDropMemory(normalized);
6776
+ const skipEmbedding = shouldSkipEmbedding({
6777
+ raw_text: normalized,
6778
+ agent_id: record.agent_id,
6779
+ project_name: record.project_name,
6780
+ tool_name: record.tool_name,
6781
+ memory_type: memoryType
6782
+ });
6783
+ return {
6784
+ record: {
6785
+ ...record,
6786
+ raw_text: normalized,
6787
+ memory_type: memoryType,
6788
+ vector: skipEmbedding ? null : record.vector
6789
+ },
6790
+ contentHash: hashMemoryContent(normalized),
6791
+ shouldDrop: drop.drop,
6792
+ dropReason: drop.reason,
6793
+ skipEmbedding,
6794
+ hygiene: {
6795
+ dedup: true,
6796
+ supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
6797
+ }
6798
+ };
6799
+ }
6800
+ async function findScopedDuplicate(input) {
6801
+ const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
6802
+ const client = getClient2();
6803
+ const args = scopedDedupArgs(input);
6804
+ let sql = `SELECT id FROM memories
6805
+ WHERE content_hash = ?
6806
+ AND agent_id = ?
6807
+ AND project_name = ?
6808
+ AND COALESCE(memory_type, 'raw') = ?
6809
+ AND COALESCE(status, 'active') != 'deleted'`;
6810
+ if (input.excludeId) {
6811
+ sql += " AND id != ?";
6812
+ args.push(input.excludeId);
6813
+ }
6814
+ sql += " ORDER BY timestamp DESC LIMIT 1";
6815
+ const result2 = await client.execute({ sql, args });
6816
+ return result2.rows[0]?.id ? String(result2.rows[0].id) : null;
6817
+ }
6818
+ async function runPostWriteMemoryHygiene(memoryId) {
6819
+ try {
6820
+ const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
6821
+ const client = getClient2();
6822
+ const current = await client.execute({
6823
+ sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
6824
+ importance, timestamp
6825
+ FROM memories
6826
+ WHERE id = ?
6827
+ LIMIT 1`,
6828
+ args: [memoryId]
6829
+ });
6830
+ const row = current.rows[0];
6831
+ if (!row) return;
6832
+ const memoryType = String(row.memory_type ?? "raw");
6833
+ const contentHash = row.content_hash ? String(row.content_hash) : null;
6834
+ const agentId = String(row.agent_id);
6835
+ const projectName = String(row.project_name);
6836
+ if (contentHash) {
6837
+ await client.execute({
6838
+ sql: `UPDATE memories
6839
+ SET status = 'deleted',
6840
+ outcome = COALESCE(outcome, 'superseded')
6841
+ WHERE id != ?
6842
+ AND content_hash = ?
6843
+ AND agent_id = ?
6844
+ AND project_name = ?
6845
+ AND COALESCE(memory_type, 'raw') = ?
6846
+ AND COALESCE(status, 'active') = 'active'`,
6847
+ args: [memoryId, contentHash, agentId, projectName, memoryType]
6848
+ });
6849
+ }
6850
+ const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
6851
+ if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
6852
+ const old = await client.execute({
6853
+ sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
6854
+ args: [supersedesId]
6855
+ });
6856
+ const oldImportance = Number(old.rows[0]?.importance ?? 0);
6857
+ const newImportance = Number(row.importance ?? 0);
6858
+ await client.batch([
6859
+ {
6860
+ sql: `UPDATE memories
6861
+ SET status = 'archived',
6862
+ outcome = COALESCE(outcome, 'superseded')
6863
+ WHERE id = ?`,
6864
+ args: [supersedesId]
6865
+ },
6866
+ {
6867
+ sql: `UPDATE memories
6868
+ SET importance = MAX(COALESCE(importance, 5), ?),
6869
+ parent_memory_id = COALESCE(parent_memory_id, ?)
6870
+ WHERE id = ?`,
6871
+ args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
6872
+ }
6873
+ ], "write");
6874
+ }
6875
+ } catch (err) {
6876
+ process.stderr.write(
6877
+ `[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
6878
+ `
6879
+ );
6880
+ }
6881
+ }
6882
+ function schedulePostWriteMemoryHygiene(memoryIds) {
6883
+ if (memoryIds.length === 0) return;
6884
+ const run = () => {
6885
+ void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
6886
+ };
6887
+ if (typeof setImmediate === "function") setImmediate(run);
6888
+ else setTimeout(run, 0);
6889
+ }
6890
+ var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
6891
+ var init_memory_write_governor = __esm({
6892
+ "src/lib/memory-write-governor.ts"() {
6893
+ "use strict";
6894
+ HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
6895
+ "decision",
6896
+ "adr",
6897
+ "behavior",
6898
+ "procedure"
6899
+ ]);
6900
+ NOISE_DROP_PATTERNS = [
6901
+ /^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
6902
+ /^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
6903
+ /^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
6904
+ /^\s*Intercom is a speedup, not delivery/im,
6905
+ /^\s*Context bar reads as USAGE not remaining/im
6906
+ ];
6907
+ SKIP_EMBED_PATTERNS = [
6908
+ /tmux capture-pane\b/i,
6909
+ /docker ps\b/i,
6910
+ /docker images\b/i,
6911
+ /git status\b/i,
6912
+ /grep .*node_modules/i,
6913
+ /npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
6914
+ ];
6915
+ }
6916
+ });
6917
+
6716
6918
  // src/lib/shard-manager.ts
6717
6919
  var shard_manager_exports = {};
6718
6920
  __export(shard_manager_exports, {
@@ -6877,7 +7079,8 @@ async function ensureShardSchema(client) {
6877
7079
  }
6878
7080
  for (const idx of [
6879
7081
  "CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
6880
- "CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
7082
+ "CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
7083
+ "CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash ON memories(content_hash, agent_id, project_name, memory_type) WHERE content_hash IS NOT NULL"
6881
7084
  ]) {
6882
7085
  try {
6883
7086
  await client.execute(idx);
@@ -7302,7 +7505,6 @@ __export(store_exports, {
7302
7505
  vectorToBlob: () => vectorToBlob,
7303
7506
  writeMemory: () => writeMemory
7304
7507
  });
7305
- import { createHash } from "crypto";
7306
7508
  function isBusyError2(err) {
7307
7509
  if (err instanceof Error) {
7308
7510
  const msg = err.message.toLowerCase();
@@ -7416,17 +7618,24 @@ async function writeMemory(record) {
7416
7618
  `Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
7417
7619
  );
7418
7620
  }
7419
- const contentHash = createHash("md5").update(record.raw_text).digest("hex");
7420
- if (_pendingRecords.some((r) => r.content_hash === contentHash && r.agent_id === record.agent_id)) {
7621
+ const governed = governMemoryRecord(record);
7622
+ if (governed.shouldDrop) return;
7623
+ record = governed.record;
7624
+ const contentHash = governed.contentHash;
7625
+ const memoryType = record.memory_type ?? "raw";
7626
+ if (_pendingRecords.some(
7627
+ (r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
7628
+ )) {
7421
7629
  return;
7422
7630
  }
7423
7631
  try {
7424
- const client = getClient();
7425
- const existing = await client.execute({
7426
- sql: "SELECT id FROM memories WHERE content_hash = ? AND agent_id = ? LIMIT 1",
7427
- args: [contentHash, record.agent_id]
7632
+ const existing = await findScopedDuplicate({
7633
+ contentHash,
7634
+ agentId: record.agent_id,
7635
+ projectName: record.project_name,
7636
+ memoryType
7428
7637
  });
7429
- if (existing.rows.length > 0) return;
7638
+ if (existing) return;
7430
7639
  } catch {
7431
7640
  }
7432
7641
  const dbRow = {
@@ -7457,7 +7666,7 @@ async function writeMemory(record) {
7457
7666
  tier: record.tier ?? classifyTier(record),
7458
7667
  supersedes_id: record.supersedes_id ?? null,
7459
7668
  draft: record.draft ? 1 : 0,
7460
- memory_type: record.memory_type ?? "raw",
7669
+ memory_type: memoryType,
7461
7670
  trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
7462
7671
  content_hash: contentHash,
7463
7672
  intent: record.intent ?? null,
@@ -7616,6 +7825,7 @@ async function flushBatch() {
7616
7825
  const globalClient = getClient();
7617
7826
  const globalStmts = batch.map(buildStmt);
7618
7827
  await globalClient.batch(globalStmts, "write");
7828
+ schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
7619
7829
  _pendingRecords.splice(0, batch.length);
7620
7830
  try {
7621
7831
  const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
@@ -7874,6 +8084,7 @@ var init_store = __esm({
7874
8084
  init_keychain();
7875
8085
  init_config();
7876
8086
  init_state_bus();
8087
+ init_memory_write_governor();
7877
8088
  INIT_MAX_RETRIES = 3;
7878
8089
  INIT_RETRY_DELAY_MS = 1e3;
7879
8090
  _pendingRecords = [];
@@ -2073,6 +2073,14 @@ async function ensureSchema() {
2073
2073
  );
2074
2074
  } catch {
2075
2075
  }
2076
+ try {
2077
+ await client.execute(
2078
+ `CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
2079
+ ON memories(content_hash, agent_id, project_name, memory_type)
2080
+ WHERE content_hash IS NOT NULL`
2081
+ );
2082
+ } catch {
2083
+ }
2076
2084
  await client.executeMultiple(`
2077
2085
  CREATE TABLE IF NOT EXISTS entities (
2078
2086
  id TEXT PRIMARY KEY,
@@ -2742,7 +2750,8 @@ async function ensureShardSchema(client) {
2742
2750
  }
2743
2751
  for (const idx of [
2744
2752
  "CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
2745
- "CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
2753
+ "CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
2754
+ "CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash ON memories(content_hash, agent_id, project_name, memory_type) WHERE content_hash IS NOT NULL"
2746
2755
  ]) {
2747
2756
  try {
2748
2757
  await client.execute(idx);
@@ -3368,7 +3377,6 @@ import os6 from "os";
3368
3377
  // src/lib/store.ts
3369
3378
  init_memory();
3370
3379
  init_database();
3371
- import { createHash } from "crypto";
3372
3380
 
3373
3381
  // src/lib/keychain.ts
3374
3382
  import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
@@ -3601,6 +3609,9 @@ var StateBus = class {
3601
3609
  };
3602
3610
  var orgBus = new StateBus();
3603
3611
 
3612
+ // src/lib/memory-write-governor.ts
3613
+ import { createHash } from "crypto";
3614
+
3604
3615
  // src/lib/store.ts
3605
3616
  var INIT_MAX_RETRIES = 3;
3606
3617
  var INIT_RETRY_DELAY_MS = 1e3;