@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
@@ -2095,6 +2095,14 @@ async function ensureSchema() {
2095
2095
  );
2096
2096
  } catch {
2097
2097
  }
2098
+ try {
2099
+ await client.execute(
2100
+ `CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
2101
+ ON memories(content_hash, agent_id, project_name, memory_type)
2102
+ WHERE content_hash IS NOT NULL`
2103
+ );
2104
+ } catch {
2105
+ }
2098
2106
  await client.executeMultiple(`
2099
2107
  CREATE TABLE IF NOT EXISTS entities (
2100
2108
  id TEXT PRIMARY KEY,
@@ -2764,7 +2772,8 @@ async function ensureShardSchema(client) {
2764
2772
  }
2765
2773
  for (const idx of [
2766
2774
  "CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
2767
- "CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
2775
+ "CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
2776
+ "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"
2768
2777
  ]) {
2769
2778
  try {
2770
2779
  await client.execute(idx);
@@ -3973,7 +3982,6 @@ import { spawnSync } from "child_process";
3973
3982
  // src/lib/store.ts
3974
3983
  init_memory();
3975
3984
  init_database();
3976
- import { createHash } from "crypto";
3977
3985
 
3978
3986
  // src/lib/keychain.ts
3979
3987
  import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
@@ -4206,6 +4214,87 @@ var StateBus = class {
4206
4214
  };
4207
4215
  var orgBus = new StateBus();
4208
4216
 
4217
+ // src/lib/memory-write-governor.ts
4218
+ import { createHash } from "crypto";
4219
+ var HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
4220
+ "decision",
4221
+ "adr",
4222
+ "behavior",
4223
+ "procedure"
4224
+ ]);
4225
+ async function runPostWriteMemoryHygiene(memoryId) {
4226
+ try {
4227
+ const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
4228
+ const client = getClient2();
4229
+ const current = await client.execute({
4230
+ sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
4231
+ importance, timestamp
4232
+ FROM memories
4233
+ WHERE id = ?
4234
+ LIMIT 1`,
4235
+ args: [memoryId]
4236
+ });
4237
+ const row = current.rows[0];
4238
+ if (!row) return;
4239
+ const memoryType = String(row.memory_type ?? "raw");
4240
+ const contentHash = row.content_hash ? String(row.content_hash) : null;
4241
+ const agentId = String(row.agent_id);
4242
+ const projectName = String(row.project_name);
4243
+ if (contentHash) {
4244
+ await client.execute({
4245
+ sql: `UPDATE memories
4246
+ SET status = 'deleted',
4247
+ outcome = COALESCE(outcome, 'superseded')
4248
+ WHERE id != ?
4249
+ AND content_hash = ?
4250
+ AND agent_id = ?
4251
+ AND project_name = ?
4252
+ AND COALESCE(memory_type, 'raw') = ?
4253
+ AND COALESCE(status, 'active') = 'active'`,
4254
+ args: [memoryId, contentHash, agentId, projectName, memoryType]
4255
+ });
4256
+ }
4257
+ const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
4258
+ if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
4259
+ const old = await client.execute({
4260
+ sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
4261
+ args: [supersedesId]
4262
+ });
4263
+ const oldImportance = Number(old.rows[0]?.importance ?? 0);
4264
+ const newImportance = Number(row.importance ?? 0);
4265
+ await client.batch([
4266
+ {
4267
+ sql: `UPDATE memories
4268
+ SET status = 'archived',
4269
+ outcome = COALESCE(outcome, 'superseded')
4270
+ WHERE id = ?`,
4271
+ args: [supersedesId]
4272
+ },
4273
+ {
4274
+ sql: `UPDATE memories
4275
+ SET importance = MAX(COALESCE(importance, 5), ?),
4276
+ parent_memory_id = COALESCE(parent_memory_id, ?)
4277
+ WHERE id = ?`,
4278
+ args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
4279
+ }
4280
+ ], "write");
4281
+ }
4282
+ } catch (err) {
4283
+ process.stderr.write(
4284
+ `[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
4285
+ `
4286
+ );
4287
+ }
4288
+ }
4289
+ function schedulePostWriteMemoryHygiene(memoryIds) {
4290
+ if (memoryIds.length === 0) return;
4291
+ const run = () => {
4292
+ void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
4293
+ };
4294
+ if (typeof setImmediate === "function") setImmediate(run);
4295
+ else setTimeout(run, 0);
4296
+ }
4297
+
4209
4298
  // src/lib/store.ts
4210
4299
  var INIT_MAX_RETRIES = 3;
4211
4300
  var INIT_RETRY_DELAY_MS = 1e3;
@@ -4405,6 +4494,7 @@ async function flushBatch() {
4405
4494
  const globalClient = getClient();
4406
4495
  const globalStmts = batch.map(buildStmt);
4407
4496
  await globalClient.batch(globalStmts, "write");
4497
+ schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
4408
4498
  _pendingRecords.splice(0, batch.length);
4409
4499
  try {
4410
4500
  const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
@@ -2084,6 +2084,14 @@ async function ensureSchema() {
2084
2084
  );
2085
2085
  } catch {
2086
2086
  }
2087
+ try {
2088
+ await client.execute(
2089
+ `CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
2090
+ ON memories(content_hash, agent_id, project_name, memory_type)
2091
+ WHERE content_hash IS NOT NULL`
2092
+ );
2093
+ } catch {
2094
+ }
2087
2095
  await client.executeMultiple(`
2088
2096
  CREATE TABLE IF NOT EXISTS entities (
2089
2097
  id TEXT PRIMARY KEY,
@@ -2753,7 +2761,8 @@ async function ensureShardSchema(client) {
2753
2761
  }
2754
2762
  for (const idx of [
2755
2763
  "CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
2756
- "CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
2764
+ "CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
2765
+ "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"
2757
2766
  ]) {
2758
2767
  try {
2759
2768
  await client.execute(idx);
@@ -3827,7 +3836,6 @@ import { spawnSync } from "child_process";
3827
3836
  // src/lib/store.ts
3828
3837
  init_memory();
3829
3838
  init_database();
3830
- import { createHash } from "crypto";
3831
3839
 
3832
3840
  // src/lib/keychain.ts
3833
3841
  import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
@@ -4060,6 +4068,87 @@ var StateBus = class {
4060
4068
  };
4061
4069
  var orgBus = new StateBus();
4062
4070
 
4071
+ // src/lib/memory-write-governor.ts
4072
+ import { createHash } from "crypto";
4073
+ var HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
4074
+ "decision",
4075
+ "adr",
4076
+ "behavior",
4077
+ "procedure"
4078
+ ]);
4079
+ async function runPostWriteMemoryHygiene(memoryId) {
4080
+ try {
4081
+ const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
4082
+ const client = getClient2();
4083
+ const current = await client.execute({
4084
+ sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
4085
+ importance, timestamp
4086
+ FROM memories
4087
+ WHERE id = ?
4088
+ LIMIT 1`,
4089
+ args: [memoryId]
4090
+ });
4091
+ const row = current.rows[0];
4092
+ if (!row) return;
4093
+ const memoryType = String(row.memory_type ?? "raw");
4094
+ const contentHash = row.content_hash ? String(row.content_hash) : null;
4095
+ const agentId = String(row.agent_id);
4096
+ const projectName = String(row.project_name);
4097
+ if (contentHash) {
4098
+ await client.execute({
4099
+ sql: `UPDATE memories
4100
+ SET status = 'deleted',
4101
+ outcome = COALESCE(outcome, 'superseded')
4102
+ WHERE id != ?
4103
+ AND content_hash = ?
4104
+ AND agent_id = ?
4105
+ AND project_name = ?
4106
+ AND COALESCE(memory_type, 'raw') = ?
4107
+ AND COALESCE(status, 'active') = 'active'`,
4108
+ args: [memoryId, contentHash, agentId, projectName, memoryType]
4109
+ });
4110
+ }
4111
+ const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
4112
+ if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
4113
+ const old = await client.execute({
4114
+ sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
4115
+ args: [supersedesId]
4116
+ });
4117
+ const oldImportance = Number(old.rows[0]?.importance ?? 0);
4118
+ const newImportance = Number(row.importance ?? 0);
4119
+ await client.batch([
4120
+ {
4121
+ sql: `UPDATE memories
4122
+ SET status = 'archived',
4123
+ outcome = COALESCE(outcome, 'superseded')
4124
+ WHERE id = ?`,
4125
+ args: [supersedesId]
4126
+ },
4127
+ {
4128
+ sql: `UPDATE memories
4129
+ SET importance = MAX(COALESCE(importance, 5), ?),
4130
+ parent_memory_id = COALESCE(parent_memory_id, ?)
4131
+ WHERE id = ?`,
4132
+ args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
4133
+ }
4134
+ ], "write");
4135
+ }
4136
+ } catch (err) {
4137
+ process.stderr.write(
4138
+ `[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
4139
+ `
4140
+ );
4141
+ }
4142
+ }
4143
+ function schedulePostWriteMemoryHygiene(memoryIds) {
4144
+ if (memoryIds.length === 0) return;
4145
+ const run = () => {
4146
+ void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
4147
+ };
4148
+ if (typeof setImmediate === "function") setImmediate(run);
4149
+ else setTimeout(run, 0);
4150
+ }
4151
+
4063
4152
  // src/lib/store.ts
4064
4153
  var INIT_MAX_RETRIES = 3;
4065
4154
  var INIT_RETRY_DELAY_MS = 1e3;
@@ -4259,6 +4348,7 @@ async function flushBatch() {
4259
4348
  const globalClient = getClient();
4260
4349
  const globalStmts = batch.map(buildStmt);
4261
4350
  await globalClient.batch(globalStmts, "write");
4351
+ schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
4262
4352
  _pendingRecords.splice(0, batch.length);
4263
4353
  try {
4264
4354
  const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
@@ -2310,6 +2310,14 @@ async function ensureSchema() {
2310
2310
  );
2311
2311
  } catch {
2312
2312
  }
2313
+ try {
2314
+ await client.execute(
2315
+ `CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
2316
+ ON memories(content_hash, agent_id, project_name, memory_type)
2317
+ WHERE content_hash IS NOT NULL`
2318
+ );
2319
+ } catch {
2320
+ }
2313
2321
  await client.executeMultiple(`
2314
2322
  CREATE TABLE IF NOT EXISTS entities (
2315
2323
  id TEXT PRIMARY KEY,
@@ -3055,6 +3063,196 @@ var init_state_bus = __esm({
3055
3063
  }
3056
3064
  });
3057
3065
 
3066
+ // src/lib/memory-write-governor.ts
3067
+ import { createHash } from "crypto";
3068
+ function normalizeMemoryText(text) {
3069
+ return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
3070
+ }
3071
+ function classifyMemoryType(input) {
3072
+ if (input.memory_type && input.memory_type.trim()) return input.memory_type.trim();
3073
+ const tool = input.tool_name.toLowerCase();
3074
+ const text = input.raw_text.toLowerCase();
3075
+ if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
3076
+ if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
3077
+ if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
3078
+ if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
3079
+ if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
3080
+ if (tool === "store_memory" || tool === "manual") return "observation";
3081
+ return "raw";
3082
+ }
3083
+ function shouldDropMemory(text) {
3084
+ const normalized = normalizeMemoryText(text);
3085
+ if (normalized.length < 10) return { drop: true, reason: "too_short" };
3086
+ if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
3087
+ return { drop: true, reason: "known_boilerplate_noise" };
3088
+ }
3089
+ return { drop: false };
3090
+ }
3091
+ function shouldSkipEmbedding(input) {
3092
+ const type = classifyMemoryType(input);
3093
+ if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
3094
+ if (type === "raw" && input.raw_text.length > 2e4) return true;
3095
+ if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input.raw_text))) return true;
3096
+ return false;
3097
+ }
3098
+ function hashMemoryContent(text) {
3099
+ return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
3100
+ }
3101
+ function scopedDedupArgs(input) {
3102
+ return [input.contentHash, input.agentId, input.projectName, input.memoryType];
3103
+ }
3104
+ function governMemoryRecord(record) {
3105
+ const normalized = normalizeMemoryText(record.raw_text);
3106
+ const memoryType = classifyMemoryType({
3107
+ raw_text: normalized,
3108
+ agent_id: record.agent_id,
3109
+ project_name: record.project_name,
3110
+ tool_name: record.tool_name,
3111
+ memory_type: record.memory_type
3112
+ });
3113
+ const drop = shouldDropMemory(normalized);
3114
+ const skipEmbedding = shouldSkipEmbedding({
3115
+ raw_text: normalized,
3116
+ agent_id: record.agent_id,
3117
+ project_name: record.project_name,
3118
+ tool_name: record.tool_name,
3119
+ memory_type: memoryType
3120
+ });
3121
+ return {
3122
+ record: {
3123
+ ...record,
3124
+ raw_text: normalized,
3125
+ memory_type: memoryType,
3126
+ vector: skipEmbedding ? null : record.vector
3127
+ },
3128
+ contentHash: hashMemoryContent(normalized),
3129
+ shouldDrop: drop.drop,
3130
+ dropReason: drop.reason,
3131
+ skipEmbedding,
3132
+ hygiene: {
3133
+ dedup: true,
3134
+ supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
3135
+ }
3136
+ };
3137
+ }
3138
+ async function findScopedDuplicate(input) {
3139
+ const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
3140
+ const client = getClient2();
3141
+ const args = scopedDedupArgs(input);
3142
+ let sql = `SELECT id FROM memories
3143
+ WHERE content_hash = ?
3144
+ AND agent_id = ?
3145
+ AND project_name = ?
3146
+ AND COALESCE(memory_type, 'raw') = ?
3147
+ AND COALESCE(status, 'active') != 'deleted'`;
3148
+ if (input.excludeId) {
3149
+ sql += " AND id != ?";
3150
+ args.push(input.excludeId);
3151
+ }
3152
+ sql += " ORDER BY timestamp DESC LIMIT 1";
3153
+ const result = await client.execute({ sql, args });
3154
+ return result.rows[0]?.id ? String(result.rows[0].id) : null;
3155
+ }
3156
+ async function runPostWriteMemoryHygiene(memoryId) {
3157
+ try {
3158
+ const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
3159
+ const client = getClient2();
3160
+ const current = await client.execute({
3161
+ sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
3162
+ importance, timestamp
3163
+ FROM memories
3164
+ WHERE id = ?
3165
+ LIMIT 1`,
3166
+ args: [memoryId]
3167
+ });
3168
+ const row = current.rows[0];
3169
+ if (!row) return;
3170
+ const memoryType = String(row.memory_type ?? "raw");
3171
+ const contentHash = row.content_hash ? String(row.content_hash) : null;
3172
+ const agentId = String(row.agent_id);
3173
+ const projectName = String(row.project_name);
3174
+ if (contentHash) {
3175
+ await client.execute({
3176
+ sql: `UPDATE memories
3177
+ SET status = 'deleted',
3178
+ outcome = COALESCE(outcome, 'superseded')
3179
+ WHERE id != ?
3180
+ AND content_hash = ?
3181
+ AND agent_id = ?
3182
+ AND project_name = ?
3183
+ AND COALESCE(memory_type, 'raw') = ?
3184
+ AND COALESCE(status, 'active') = 'active'`,
3185
+ args: [memoryId, contentHash, agentId, projectName, memoryType]
3186
+ });
3187
+ }
3188
+ const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
3189
+ if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
3190
+ const old = await client.execute({
3191
+ sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
3192
+ args: [supersedesId]
3193
+ });
3194
+ const oldImportance = Number(old.rows[0]?.importance ?? 0);
3195
+ const newImportance = Number(row.importance ?? 0);
3196
+ await client.batch([
3197
+ {
3198
+ sql: `UPDATE memories
3199
+ SET status = 'archived',
3200
+ outcome = COALESCE(outcome, 'superseded')
3201
+ WHERE id = ?`,
3202
+ args: [supersedesId]
3203
+ },
3204
+ {
3205
+ sql: `UPDATE memories
3206
+ SET importance = MAX(COALESCE(importance, 5), ?),
3207
+ parent_memory_id = COALESCE(parent_memory_id, ?)
3208
+ WHERE id = ?`,
3209
+ args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
3210
+ }
3211
+ ], "write");
3212
+ }
3213
+ } catch (err) {
3214
+ process.stderr.write(
3215
+ `[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
3216
+ `
3217
+ );
3218
+ }
3219
+ }
3220
+ function schedulePostWriteMemoryHygiene(memoryIds) {
3221
+ if (memoryIds.length === 0) return;
3222
+ const run = () => {
3223
+ void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
3224
+ };
3225
+ if (typeof setImmediate === "function") setImmediate(run);
3226
+ else setTimeout(run, 0);
3227
+ }
3228
+ var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
3229
+ var init_memory_write_governor = __esm({
3230
+ "src/lib/memory-write-governor.ts"() {
3231
+ "use strict";
3232
+ HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
3233
+ "decision",
3234
+ "adr",
3235
+ "behavior",
3236
+ "procedure"
3237
+ ]);
3238
+ NOISE_DROP_PATTERNS = [
3239
+ /^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
3240
+ /^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
3241
+ /^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
3242
+ /^\s*Intercom is a speedup, not delivery/im,
3243
+ /^\s*Context bar reads as USAGE not remaining/im
3244
+ ];
3245
+ SKIP_EMBED_PATTERNS = [
3246
+ /tmux capture-pane\b/i,
3247
+ /docker ps\b/i,
3248
+ /docker images\b/i,
3249
+ /git status\b/i,
3250
+ /grep .*node_modules/i,
3251
+ /npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
3252
+ ];
3253
+ }
3254
+ });
3255
+
3058
3256
  // src/lib/shard-manager.ts
3059
3257
  var shard_manager_exports = {};
3060
3258
  __export(shard_manager_exports, {
@@ -3219,7 +3417,8 @@ async function ensureShardSchema(client) {
3219
3417
  }
3220
3418
  for (const idx of [
3221
3419
  "CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
3222
- "CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
3420
+ "CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
3421
+ "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"
3223
3422
  ]) {
3224
3423
  try {
3225
3424
  await client.execute(idx);
@@ -3644,7 +3843,6 @@ __export(store_exports, {
3644
3843
  vectorToBlob: () => vectorToBlob,
3645
3844
  writeMemory: () => writeMemory
3646
3845
  });
3647
- import { createHash } from "crypto";
3648
3846
  function isBusyError2(err) {
3649
3847
  if (err instanceof Error) {
3650
3848
  const msg = err.message.toLowerCase();
@@ -3758,17 +3956,24 @@ async function writeMemory(record) {
3758
3956
  `Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
3759
3957
  );
3760
3958
  }
3761
- const contentHash = createHash("md5").update(record.raw_text).digest("hex");
3762
- if (_pendingRecords.some((r) => r.content_hash === contentHash && r.agent_id === record.agent_id)) {
3959
+ const governed = governMemoryRecord(record);
3960
+ if (governed.shouldDrop) return;
3961
+ record = governed.record;
3962
+ const contentHash = governed.contentHash;
3963
+ const memoryType = record.memory_type ?? "raw";
3964
+ if (_pendingRecords.some(
3965
+ (r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
3966
+ )) {
3763
3967
  return;
3764
3968
  }
3765
3969
  try {
3766
- const client = getClient();
3767
- const existing = await client.execute({
3768
- sql: "SELECT id FROM memories WHERE content_hash = ? AND agent_id = ? LIMIT 1",
3769
- args: [contentHash, record.agent_id]
3970
+ const existing = await findScopedDuplicate({
3971
+ contentHash,
3972
+ agentId: record.agent_id,
3973
+ projectName: record.project_name,
3974
+ memoryType
3770
3975
  });
3771
- if (existing.rows.length > 0) return;
3976
+ if (existing) return;
3772
3977
  } catch {
3773
3978
  }
3774
3979
  const dbRow = {
@@ -3799,7 +4004,7 @@ async function writeMemory(record) {
3799
4004
  tier: record.tier ?? classifyTier(record),
3800
4005
  supersedes_id: record.supersedes_id ?? null,
3801
4006
  draft: record.draft ? 1 : 0,
3802
- memory_type: record.memory_type ?? "raw",
4007
+ memory_type: memoryType,
3803
4008
  trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
3804
4009
  content_hash: contentHash,
3805
4010
  intent: record.intent ?? null,
@@ -3958,6 +4163,7 @@ async function flushBatch() {
3958
4163
  const globalClient = getClient();
3959
4164
  const globalStmts = batch.map(buildStmt);
3960
4165
  await globalClient.batch(globalStmts, "write");
4166
+ schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
3961
4167
  _pendingRecords.splice(0, batch.length);
3962
4168
  try {
3963
4169
  const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
@@ -4216,6 +4422,7 @@ var init_store = __esm({
4216
4422
  init_keychain();
4217
4423
  init_config();
4218
4424
  init_state_bus();
4425
+ init_memory_write_governor();
4219
4426
  INIT_MAX_RETRIES = 3;
4220
4427
  INIT_RETRY_DELAY_MS = 1e3;
4221
4428
  _pendingRecords = [];