@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
@@ -2299,6 +2299,14 @@ async function ensureSchema() {
2299
2299
  );
2300
2300
  } catch {
2301
2301
  }
2302
+ try {
2303
+ await client.execute(
2304
+ `CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
2305
+ ON memories(content_hash, agent_id, project_name, memory_type)
2306
+ WHERE content_hash IS NOT NULL`
2307
+ );
2308
+ } catch {
2309
+ }
2302
2310
  await client.executeMultiple(`
2303
2311
  CREATE TABLE IF NOT EXISTS entities (
2304
2312
  id TEXT PRIMARY KEY,
@@ -3044,6 +3052,196 @@ var init_state_bus = __esm({
3044
3052
  }
3045
3053
  });
3046
3054
 
3055
+ // src/lib/memory-write-governor.ts
3056
+ import { createHash } from "crypto";
3057
+ function normalizeMemoryText(text) {
3058
+ return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
3059
+ }
3060
+ function classifyMemoryType(input) {
3061
+ if (input.memory_type && input.memory_type.trim()) return input.memory_type.trim();
3062
+ const tool = input.tool_name.toLowerCase();
3063
+ const text = input.raw_text.toLowerCase();
3064
+ if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
3065
+ if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
3066
+ if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
3067
+ if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
3068
+ if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
3069
+ if (tool === "store_memory" || tool === "manual") return "observation";
3070
+ return "raw";
3071
+ }
3072
+ function shouldDropMemory(text) {
3073
+ const normalized = normalizeMemoryText(text);
3074
+ if (normalized.length < 10) return { drop: true, reason: "too_short" };
3075
+ if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
3076
+ return { drop: true, reason: "known_boilerplate_noise" };
3077
+ }
3078
+ return { drop: false };
3079
+ }
3080
+ function shouldSkipEmbedding(input) {
3081
+ const type = classifyMemoryType(input);
3082
+ if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
3083
+ if (type === "raw" && input.raw_text.length > 2e4) return true;
3084
+ if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input.raw_text))) return true;
3085
+ return false;
3086
+ }
3087
+ function hashMemoryContent(text) {
3088
+ return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
3089
+ }
3090
+ function scopedDedupArgs(input) {
3091
+ return [input.contentHash, input.agentId, input.projectName, input.memoryType];
3092
+ }
3093
+ function governMemoryRecord(record) {
3094
+ const normalized = normalizeMemoryText(record.raw_text);
3095
+ const memoryType = classifyMemoryType({
3096
+ raw_text: normalized,
3097
+ agent_id: record.agent_id,
3098
+ project_name: record.project_name,
3099
+ tool_name: record.tool_name,
3100
+ memory_type: record.memory_type
3101
+ });
3102
+ const drop = shouldDropMemory(normalized);
3103
+ const skipEmbedding = shouldSkipEmbedding({
3104
+ raw_text: normalized,
3105
+ agent_id: record.agent_id,
3106
+ project_name: record.project_name,
3107
+ tool_name: record.tool_name,
3108
+ memory_type: memoryType
3109
+ });
3110
+ return {
3111
+ record: {
3112
+ ...record,
3113
+ raw_text: normalized,
3114
+ memory_type: memoryType,
3115
+ vector: skipEmbedding ? null : record.vector
3116
+ },
3117
+ contentHash: hashMemoryContent(normalized),
3118
+ shouldDrop: drop.drop,
3119
+ dropReason: drop.reason,
3120
+ skipEmbedding,
3121
+ hygiene: {
3122
+ dedup: true,
3123
+ supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
3124
+ }
3125
+ };
3126
+ }
3127
+ async function findScopedDuplicate(input) {
3128
+ const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
3129
+ const client = getClient2();
3130
+ const args = scopedDedupArgs(input);
3131
+ let sql = `SELECT id FROM memories
3132
+ WHERE content_hash = ?
3133
+ AND agent_id = ?
3134
+ AND project_name = ?
3135
+ AND COALESCE(memory_type, 'raw') = ?
3136
+ AND COALESCE(status, 'active') != 'deleted'`;
3137
+ if (input.excludeId) {
3138
+ sql += " AND id != ?";
3139
+ args.push(input.excludeId);
3140
+ }
3141
+ sql += " ORDER BY timestamp DESC LIMIT 1";
3142
+ const result = await client.execute({ sql, args });
3143
+ return result.rows[0]?.id ? String(result.rows[0].id) : null;
3144
+ }
3145
+ async function runPostWriteMemoryHygiene(memoryId) {
3146
+ try {
3147
+ const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
3148
+ const client = getClient2();
3149
+ const current = await client.execute({
3150
+ sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
3151
+ importance, timestamp
3152
+ FROM memories
3153
+ WHERE id = ?
3154
+ LIMIT 1`,
3155
+ args: [memoryId]
3156
+ });
3157
+ const row = current.rows[0];
3158
+ if (!row) return;
3159
+ const memoryType = String(row.memory_type ?? "raw");
3160
+ const contentHash = row.content_hash ? String(row.content_hash) : null;
3161
+ const agentId = String(row.agent_id);
3162
+ const projectName = String(row.project_name);
3163
+ if (contentHash) {
3164
+ await client.execute({
3165
+ sql: `UPDATE memories
3166
+ SET status = 'deleted',
3167
+ outcome = COALESCE(outcome, 'superseded')
3168
+ WHERE id != ?
3169
+ AND content_hash = ?
3170
+ AND agent_id = ?
3171
+ AND project_name = ?
3172
+ AND COALESCE(memory_type, 'raw') = ?
3173
+ AND COALESCE(status, 'active') = 'active'`,
3174
+ args: [memoryId, contentHash, agentId, projectName, memoryType]
3175
+ });
3176
+ }
3177
+ const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
3178
+ if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
3179
+ const old = await client.execute({
3180
+ sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
3181
+ args: [supersedesId]
3182
+ });
3183
+ const oldImportance = Number(old.rows[0]?.importance ?? 0);
3184
+ const newImportance = Number(row.importance ?? 0);
3185
+ await client.batch([
3186
+ {
3187
+ sql: `UPDATE memories
3188
+ SET status = 'archived',
3189
+ outcome = COALESCE(outcome, 'superseded')
3190
+ WHERE id = ?`,
3191
+ args: [supersedesId]
3192
+ },
3193
+ {
3194
+ sql: `UPDATE memories
3195
+ SET importance = MAX(COALESCE(importance, 5), ?),
3196
+ parent_memory_id = COALESCE(parent_memory_id, ?)
3197
+ WHERE id = ?`,
3198
+ args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
3199
+ }
3200
+ ], "write");
3201
+ }
3202
+ } catch (err) {
3203
+ process.stderr.write(
3204
+ `[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
3205
+ `
3206
+ );
3207
+ }
3208
+ }
3209
+ function schedulePostWriteMemoryHygiene(memoryIds) {
3210
+ if (memoryIds.length === 0) return;
3211
+ const run = () => {
3212
+ void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
3213
+ };
3214
+ if (typeof setImmediate === "function") setImmediate(run);
3215
+ else setTimeout(run, 0);
3216
+ }
3217
+ var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
3218
+ var init_memory_write_governor = __esm({
3219
+ "src/lib/memory-write-governor.ts"() {
3220
+ "use strict";
3221
+ HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
3222
+ "decision",
3223
+ "adr",
3224
+ "behavior",
3225
+ "procedure"
3226
+ ]);
3227
+ NOISE_DROP_PATTERNS = [
3228
+ /^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
3229
+ /^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
3230
+ /^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
3231
+ /^\s*Intercom is a speedup, not delivery/im,
3232
+ /^\s*Context bar reads as USAGE not remaining/im
3233
+ ];
3234
+ SKIP_EMBED_PATTERNS = [
3235
+ /tmux capture-pane\b/i,
3236
+ /docker ps\b/i,
3237
+ /docker images\b/i,
3238
+ /git status\b/i,
3239
+ /grep .*node_modules/i,
3240
+ /npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
3241
+ ];
3242
+ }
3243
+ });
3244
+
3047
3245
  // src/lib/shard-manager.ts
3048
3246
  var shard_manager_exports = {};
3049
3247
  __export(shard_manager_exports, {
@@ -3208,7 +3406,8 @@ async function ensureShardSchema(client) {
3208
3406
  }
3209
3407
  for (const idx of [
3210
3408
  "CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
3211
- "CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
3409
+ "CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
3410
+ "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"
3212
3411
  ]) {
3213
3412
  try {
3214
3413
  await client.execute(idx);
@@ -3633,7 +3832,6 @@ __export(store_exports, {
3633
3832
  vectorToBlob: () => vectorToBlob,
3634
3833
  writeMemory: () => writeMemory
3635
3834
  });
3636
- import { createHash } from "crypto";
3637
3835
  function isBusyError2(err) {
3638
3836
  if (err instanceof Error) {
3639
3837
  const msg = err.message.toLowerCase();
@@ -3747,17 +3945,24 @@ async function writeMemory(record) {
3747
3945
  `Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
3748
3946
  );
3749
3947
  }
3750
- const contentHash = createHash("md5").update(record.raw_text).digest("hex");
3751
- if (_pendingRecords.some((r) => r.content_hash === contentHash && r.agent_id === record.agent_id)) {
3948
+ const governed = governMemoryRecord(record);
3949
+ if (governed.shouldDrop) return;
3950
+ record = governed.record;
3951
+ const contentHash = governed.contentHash;
3952
+ const memoryType = record.memory_type ?? "raw";
3953
+ if (_pendingRecords.some(
3954
+ (r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
3955
+ )) {
3752
3956
  return;
3753
3957
  }
3754
3958
  try {
3755
- const client = getClient();
3756
- const existing = await client.execute({
3757
- sql: "SELECT id FROM memories WHERE content_hash = ? AND agent_id = ? LIMIT 1",
3758
- args: [contentHash, record.agent_id]
3959
+ const existing = await findScopedDuplicate({
3960
+ contentHash,
3961
+ agentId: record.agent_id,
3962
+ projectName: record.project_name,
3963
+ memoryType
3759
3964
  });
3760
- if (existing.rows.length > 0) return;
3965
+ if (existing) return;
3761
3966
  } catch {
3762
3967
  }
3763
3968
  const dbRow = {
@@ -3788,7 +3993,7 @@ async function writeMemory(record) {
3788
3993
  tier: record.tier ?? classifyTier(record),
3789
3994
  supersedes_id: record.supersedes_id ?? null,
3790
3995
  draft: record.draft ? 1 : 0,
3791
- memory_type: record.memory_type ?? "raw",
3996
+ memory_type: memoryType,
3792
3997
  trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
3793
3998
  content_hash: contentHash,
3794
3999
  intent: record.intent ?? null,
@@ -3947,6 +4152,7 @@ async function flushBatch() {
3947
4152
  const globalClient = getClient();
3948
4153
  const globalStmts = batch.map(buildStmt);
3949
4154
  await globalClient.batch(globalStmts, "write");
4155
+ schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
3950
4156
  _pendingRecords.splice(0, batch.length);
3951
4157
  try {
3952
4158
  const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
@@ -4205,6 +4411,7 @@ var init_store = __esm({
4205
4411
  init_keychain();
4206
4412
  init_config();
4207
4413
  init_state_bus();
4414
+ init_memory_write_governor();
4208
4415
  INIT_MAX_RETRIES = 3;
4209
4416
  INIT_RETRY_DELAY_MS = 1e3;
4210
4417
  _pendingRecords = [];
package/dist/bin/cli.js CHANGED
@@ -3920,6 +3920,14 @@ async function ensureSchema() {
3920
3920
  );
3921
3921
  } catch {
3922
3922
  }
3923
+ try {
3924
+ await client.execute(
3925
+ `CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
3926
+ ON memories(content_hash, agent_id, project_name, memory_type)
3927
+ WHERE content_hash IS NOT NULL`
3928
+ );
3929
+ } catch {
3930
+ }
3923
3931
  await client.executeMultiple(`
3924
3932
  CREATE TABLE IF NOT EXISTS entities (
3925
3933
  id TEXT PRIMARY KEY,
@@ -6794,6 +6802,196 @@ var init_state_bus = __esm({
6794
6802
  }
6795
6803
  });
6796
6804
 
6805
+ // src/lib/memory-write-governor.ts
6806
+ import { createHash } from "crypto";
6807
+ function normalizeMemoryText(text) {
6808
+ return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
6809
+ }
6810
+ function classifyMemoryType(input) {
6811
+ if (input.memory_type && input.memory_type.trim()) return input.memory_type.trim();
6812
+ const tool = input.tool_name.toLowerCase();
6813
+ const text = input.raw_text.toLowerCase();
6814
+ if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
6815
+ if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
6816
+ if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
6817
+ if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
6818
+ if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
6819
+ if (tool === "store_memory" || tool === "manual") return "observation";
6820
+ return "raw";
6821
+ }
6822
+ function shouldDropMemory(text) {
6823
+ const normalized = normalizeMemoryText(text);
6824
+ if (normalized.length < 10) return { drop: true, reason: "too_short" };
6825
+ if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
6826
+ return { drop: true, reason: "known_boilerplate_noise" };
6827
+ }
6828
+ return { drop: false };
6829
+ }
6830
+ function shouldSkipEmbedding(input) {
6831
+ const type = classifyMemoryType(input);
6832
+ if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
6833
+ if (type === "raw" && input.raw_text.length > 2e4) return true;
6834
+ if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input.raw_text))) return true;
6835
+ return false;
6836
+ }
6837
+ function hashMemoryContent(text) {
6838
+ return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
6839
+ }
6840
+ function scopedDedupArgs(input) {
6841
+ return [input.contentHash, input.agentId, input.projectName, input.memoryType];
6842
+ }
6843
+ function governMemoryRecord(record) {
6844
+ const normalized = normalizeMemoryText(record.raw_text);
6845
+ const memoryType = classifyMemoryType({
6846
+ raw_text: normalized,
6847
+ agent_id: record.agent_id,
6848
+ project_name: record.project_name,
6849
+ tool_name: record.tool_name,
6850
+ memory_type: record.memory_type
6851
+ });
6852
+ const drop = shouldDropMemory(normalized);
6853
+ const skipEmbedding = shouldSkipEmbedding({
6854
+ raw_text: normalized,
6855
+ agent_id: record.agent_id,
6856
+ project_name: record.project_name,
6857
+ tool_name: record.tool_name,
6858
+ memory_type: memoryType
6859
+ });
6860
+ return {
6861
+ record: {
6862
+ ...record,
6863
+ raw_text: normalized,
6864
+ memory_type: memoryType,
6865
+ vector: skipEmbedding ? null : record.vector
6866
+ },
6867
+ contentHash: hashMemoryContent(normalized),
6868
+ shouldDrop: drop.drop,
6869
+ dropReason: drop.reason,
6870
+ skipEmbedding,
6871
+ hygiene: {
6872
+ dedup: true,
6873
+ supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
6874
+ }
6875
+ };
6876
+ }
6877
+ async function findScopedDuplicate(input) {
6878
+ const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
6879
+ const client = getClient2();
6880
+ const args2 = scopedDedupArgs(input);
6881
+ let sql = `SELECT id FROM memories
6882
+ WHERE content_hash = ?
6883
+ AND agent_id = ?
6884
+ AND project_name = ?
6885
+ AND COALESCE(memory_type, 'raw') = ?
6886
+ AND COALESCE(status, 'active') != 'deleted'`;
6887
+ if (input.excludeId) {
6888
+ sql += " AND id != ?";
6889
+ args2.push(input.excludeId);
6890
+ }
6891
+ sql += " ORDER BY timestamp DESC LIMIT 1";
6892
+ const result = await client.execute({ sql, args: args2 });
6893
+ return result.rows[0]?.id ? String(result.rows[0].id) : null;
6894
+ }
6895
+ async function runPostWriteMemoryHygiene(memoryId) {
6896
+ try {
6897
+ const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
6898
+ const client = getClient2();
6899
+ const current = await client.execute({
6900
+ sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
6901
+ importance, timestamp
6902
+ FROM memories
6903
+ WHERE id = ?
6904
+ LIMIT 1`,
6905
+ args: [memoryId]
6906
+ });
6907
+ const row = current.rows[0];
6908
+ if (!row) return;
6909
+ const memoryType = String(row.memory_type ?? "raw");
6910
+ const contentHash2 = row.content_hash ? String(row.content_hash) : null;
6911
+ const agentId = String(row.agent_id);
6912
+ const projectName = String(row.project_name);
6913
+ if (contentHash2) {
6914
+ await client.execute({
6915
+ sql: `UPDATE memories
6916
+ SET status = 'deleted',
6917
+ outcome = COALESCE(outcome, 'superseded')
6918
+ WHERE id != ?
6919
+ AND content_hash = ?
6920
+ AND agent_id = ?
6921
+ AND project_name = ?
6922
+ AND COALESCE(memory_type, 'raw') = ?
6923
+ AND COALESCE(status, 'active') = 'active'`,
6924
+ args: [memoryId, contentHash2, agentId, projectName, memoryType]
6925
+ });
6926
+ }
6927
+ const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
6928
+ if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
6929
+ const old = await client.execute({
6930
+ sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
6931
+ args: [supersedesId]
6932
+ });
6933
+ const oldImportance = Number(old.rows[0]?.importance ?? 0);
6934
+ const newImportance = Number(row.importance ?? 0);
6935
+ await client.batch([
6936
+ {
6937
+ sql: `UPDATE memories
6938
+ SET status = 'archived',
6939
+ outcome = COALESCE(outcome, 'superseded')
6940
+ WHERE id = ?`,
6941
+ args: [supersedesId]
6942
+ },
6943
+ {
6944
+ sql: `UPDATE memories
6945
+ SET importance = MAX(COALESCE(importance, 5), ?),
6946
+ parent_memory_id = COALESCE(parent_memory_id, ?)
6947
+ WHERE id = ?`,
6948
+ args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
6949
+ }
6950
+ ], "write");
6951
+ }
6952
+ } catch (err) {
6953
+ process.stderr.write(
6954
+ `[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
6955
+ `
6956
+ );
6957
+ }
6958
+ }
6959
+ function schedulePostWriteMemoryHygiene(memoryIds) {
6960
+ if (memoryIds.length === 0) return;
6961
+ const run = () => {
6962
+ void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
6963
+ };
6964
+ if (typeof setImmediate === "function") setImmediate(run);
6965
+ else setTimeout(run, 0);
6966
+ }
6967
+ var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
6968
+ var init_memory_write_governor = __esm({
6969
+ "src/lib/memory-write-governor.ts"() {
6970
+ "use strict";
6971
+ HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
6972
+ "decision",
6973
+ "adr",
6974
+ "behavior",
6975
+ "procedure"
6976
+ ]);
6977
+ NOISE_DROP_PATTERNS = [
6978
+ /^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
6979
+ /^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
6980
+ /^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
6981
+ /^\s*Intercom is a speedup, not delivery/im,
6982
+ /^\s*Context bar reads as USAGE not remaining/im
6983
+ ];
6984
+ SKIP_EMBED_PATTERNS = [
6985
+ /tmux capture-pane\b/i,
6986
+ /docker ps\b/i,
6987
+ /docker images\b/i,
6988
+ /git status\b/i,
6989
+ /grep .*node_modules/i,
6990
+ /npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
6991
+ ];
6992
+ }
6993
+ });
6994
+
6797
6995
  // src/lib/shard-manager.ts
6798
6996
  var shard_manager_exports = {};
6799
6997
  __export(shard_manager_exports, {
@@ -6958,7 +7156,8 @@ async function ensureShardSchema(client) {
6958
7156
  }
6959
7157
  for (const idx of [
6960
7158
  "CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
6961
- "CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
7159
+ "CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
7160
+ "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"
6962
7161
  ]) {
6963
7162
  try {
6964
7163
  await client.execute(idx);
@@ -7383,7 +7582,6 @@ __export(store_exports, {
7383
7582
  vectorToBlob: () => vectorToBlob,
7384
7583
  writeMemory: () => writeMemory
7385
7584
  });
7386
- import { createHash } from "crypto";
7387
7585
  function isBusyError2(err) {
7388
7586
  if (err instanceof Error) {
7389
7587
  const msg = err.message.toLowerCase();
@@ -7497,17 +7695,24 @@ async function writeMemory(record) {
7497
7695
  `Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
7498
7696
  );
7499
7697
  }
7500
- const contentHash2 = createHash("md5").update(record.raw_text).digest("hex");
7501
- if (_pendingRecords.some((r) => r.content_hash === contentHash2 && r.agent_id === record.agent_id)) {
7698
+ const governed = governMemoryRecord(record);
7699
+ if (governed.shouldDrop) return;
7700
+ record = governed.record;
7701
+ const contentHash2 = governed.contentHash;
7702
+ const memoryType = record.memory_type ?? "raw";
7703
+ if (_pendingRecords.some(
7704
+ (r) => r.content_hash === contentHash2 && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
7705
+ )) {
7502
7706
  return;
7503
7707
  }
7504
7708
  try {
7505
- const client = getClient();
7506
- const existing = await client.execute({
7507
- sql: "SELECT id FROM memories WHERE content_hash = ? AND agent_id = ? LIMIT 1",
7508
- args: [contentHash2, record.agent_id]
7709
+ const existing = await findScopedDuplicate({
7710
+ contentHash: contentHash2,
7711
+ agentId: record.agent_id,
7712
+ projectName: record.project_name,
7713
+ memoryType
7509
7714
  });
7510
- if (existing.rows.length > 0) return;
7715
+ if (existing) return;
7511
7716
  } catch {
7512
7717
  }
7513
7718
  const dbRow = {
@@ -7538,7 +7743,7 @@ async function writeMemory(record) {
7538
7743
  tier: record.tier ?? classifyTier(record),
7539
7744
  supersedes_id: record.supersedes_id ?? null,
7540
7745
  draft: record.draft ? 1 : 0,
7541
- memory_type: record.memory_type ?? "raw",
7746
+ memory_type: memoryType,
7542
7747
  trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
7543
7748
  content_hash: contentHash2,
7544
7749
  intent: record.intent ?? null,
@@ -7697,6 +7902,7 @@ async function flushBatch() {
7697
7902
  const globalClient = getClient();
7698
7903
  const globalStmts = batch.map(buildStmt);
7699
7904
  await globalClient.batch(globalStmts, "write");
7905
+ schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
7700
7906
  _pendingRecords.splice(0, batch.length);
7701
7907
  try {
7702
7908
  const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
@@ -7955,6 +8161,7 @@ var init_store = __esm({
7955
8161
  init_keychain();
7956
8162
  init_config();
7957
8163
  init_state_bus();
8164
+ init_memory_write_governor();
7958
8165
  INIT_MAX_RETRIES = 3;
7959
8166
  INIT_RETRY_DELAY_MS = 1e3;
7960
8167
  _pendingRecords = [];
@@ -12955,7 +13162,11 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
12955
13162
  }
12956
13163
  if (!useExeAgent && !useCodex && !useOpencode && !useBinSymlink) {
12957
13164
  if (agentRtConfig.runtime === "claude" && agentRtConfig.model) {
12958
- envPrefix = `${envPrefix} ANTHROPIC_MODEL=${agentRtConfig.model}`;
13165
+ let ccModel = agentRtConfig.model.replace(/(\d+)\.(\d+)/g, "$1-$2");
13166
+ if (/claude-(opus|sonnet)-4-[6-9]/.test(ccModel) && !ccModel.includes("[1m]")) {
13167
+ ccModel += "[1m]";
13168
+ }
13169
+ envPrefix = `${envPrefix} ANTHROPIC_MODEL=${ccModel}`;
12959
13170
  }
12960
13171
  }
12961
13172
  let spawnCommand;