@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
@@ -2871,6 +2871,14 @@ async function ensureSchema() {
2871
2871
  );
2872
2872
  } catch {
2873
2873
  }
2874
+ try {
2875
+ await client.execute(
2876
+ `CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
2877
+ ON memories(content_hash, agent_id, project_name, memory_type)
2878
+ WHERE content_hash IS NOT NULL`
2879
+ );
2880
+ } catch {
2881
+ }
2874
2882
  await client.executeMultiple(`
2875
2883
  CREATE TABLE IF NOT EXISTS entities (
2876
2884
  id TEXT PRIMARY KEY,
@@ -3922,6 +3930,196 @@ var init_state_bus = __esm({
3922
3930
  }
3923
3931
  });
3924
3932
 
3933
+ // src/lib/memory-write-governor.ts
3934
+ import { createHash } from "crypto";
3935
+ function normalizeMemoryText(text) {
3936
+ return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
3937
+ }
3938
+ function classifyMemoryType(input2) {
3939
+ if (input2.memory_type && input2.memory_type.trim()) return input2.memory_type.trim();
3940
+ const tool = input2.tool_name.toLowerCase();
3941
+ const text = input2.raw_text.toLowerCase();
3942
+ if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
3943
+ if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
3944
+ if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
3945
+ if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
3946
+ if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
3947
+ if (tool === "store_memory" || tool === "manual") return "observation";
3948
+ return "raw";
3949
+ }
3950
+ function shouldDropMemory(text) {
3951
+ const normalized = normalizeMemoryText(text);
3952
+ if (normalized.length < 10) return { drop: true, reason: "too_short" };
3953
+ if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
3954
+ return { drop: true, reason: "known_boilerplate_noise" };
3955
+ }
3956
+ return { drop: false };
3957
+ }
3958
+ function shouldSkipEmbedding(input2) {
3959
+ const type = classifyMemoryType(input2);
3960
+ if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
3961
+ if (type === "raw" && input2.raw_text.length > 2e4) return true;
3962
+ if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input2.raw_text))) return true;
3963
+ return false;
3964
+ }
3965
+ function hashMemoryContent(text) {
3966
+ return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
3967
+ }
3968
+ function scopedDedupArgs(input2) {
3969
+ return [input2.contentHash, input2.agentId, input2.projectName, input2.memoryType];
3970
+ }
3971
+ function governMemoryRecord(record) {
3972
+ const normalized = normalizeMemoryText(record.raw_text);
3973
+ const memoryType = classifyMemoryType({
3974
+ raw_text: normalized,
3975
+ agent_id: record.agent_id,
3976
+ project_name: record.project_name,
3977
+ tool_name: record.tool_name,
3978
+ memory_type: record.memory_type
3979
+ });
3980
+ const drop = shouldDropMemory(normalized);
3981
+ const skipEmbedding = shouldSkipEmbedding({
3982
+ raw_text: normalized,
3983
+ agent_id: record.agent_id,
3984
+ project_name: record.project_name,
3985
+ tool_name: record.tool_name,
3986
+ memory_type: memoryType
3987
+ });
3988
+ return {
3989
+ record: {
3990
+ ...record,
3991
+ raw_text: normalized,
3992
+ memory_type: memoryType,
3993
+ vector: skipEmbedding ? null : record.vector
3994
+ },
3995
+ contentHash: hashMemoryContent(normalized),
3996
+ shouldDrop: drop.drop,
3997
+ dropReason: drop.reason,
3998
+ skipEmbedding,
3999
+ hygiene: {
4000
+ dedup: true,
4001
+ supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
4002
+ }
4003
+ };
4004
+ }
4005
+ async function findScopedDuplicate(input2) {
4006
+ const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
4007
+ const client = getClient2();
4008
+ const args = scopedDedupArgs(input2);
4009
+ let sql = `SELECT id FROM memories
4010
+ WHERE content_hash = ?
4011
+ AND agent_id = ?
4012
+ AND project_name = ?
4013
+ AND COALESCE(memory_type, 'raw') = ?
4014
+ AND COALESCE(status, 'active') != 'deleted'`;
4015
+ if (input2.excludeId) {
4016
+ sql += " AND id != ?";
4017
+ args.push(input2.excludeId);
4018
+ }
4019
+ sql += " ORDER BY timestamp DESC LIMIT 1";
4020
+ const result = await client.execute({ sql, args });
4021
+ return result.rows[0]?.id ? String(result.rows[0].id) : null;
4022
+ }
4023
+ async function runPostWriteMemoryHygiene(memoryId) {
4024
+ try {
4025
+ const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
4026
+ const client = getClient2();
4027
+ const current = await client.execute({
4028
+ sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
4029
+ importance, timestamp
4030
+ FROM memories
4031
+ WHERE id = ?
4032
+ LIMIT 1`,
4033
+ args: [memoryId]
4034
+ });
4035
+ const row = current.rows[0];
4036
+ if (!row) return;
4037
+ const memoryType = String(row.memory_type ?? "raw");
4038
+ const contentHash = row.content_hash ? String(row.content_hash) : null;
4039
+ const agentId = String(row.agent_id);
4040
+ const projectName = String(row.project_name);
4041
+ if (contentHash) {
4042
+ await client.execute({
4043
+ sql: `UPDATE memories
4044
+ SET status = 'deleted',
4045
+ outcome = COALESCE(outcome, 'superseded')
4046
+ WHERE id != ?
4047
+ AND content_hash = ?
4048
+ AND agent_id = ?
4049
+ AND project_name = ?
4050
+ AND COALESCE(memory_type, 'raw') = ?
4051
+ AND COALESCE(status, 'active') = 'active'`,
4052
+ args: [memoryId, contentHash, agentId, projectName, memoryType]
4053
+ });
4054
+ }
4055
+ const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
4056
+ if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
4057
+ const old = await client.execute({
4058
+ sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
4059
+ args: [supersedesId]
4060
+ });
4061
+ const oldImportance = Number(old.rows[0]?.importance ?? 0);
4062
+ const newImportance = Number(row.importance ?? 0);
4063
+ await client.batch([
4064
+ {
4065
+ sql: `UPDATE memories
4066
+ SET status = 'archived',
4067
+ outcome = COALESCE(outcome, 'superseded')
4068
+ WHERE id = ?`,
4069
+ args: [supersedesId]
4070
+ },
4071
+ {
4072
+ sql: `UPDATE memories
4073
+ SET importance = MAX(COALESCE(importance, 5), ?),
4074
+ parent_memory_id = COALESCE(parent_memory_id, ?)
4075
+ WHERE id = ?`,
4076
+ args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
4077
+ }
4078
+ ], "write");
4079
+ }
4080
+ } catch (err) {
4081
+ process.stderr.write(
4082
+ `[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
4083
+ `
4084
+ );
4085
+ }
4086
+ }
4087
+ function schedulePostWriteMemoryHygiene(memoryIds) {
4088
+ if (memoryIds.length === 0) return;
4089
+ const run = () => {
4090
+ void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
4091
+ };
4092
+ if (typeof setImmediate === "function") setImmediate(run);
4093
+ else setTimeout(run, 0);
4094
+ }
4095
+ var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
4096
+ var init_memory_write_governor = __esm({
4097
+ "src/lib/memory-write-governor.ts"() {
4098
+ "use strict";
4099
+ HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
4100
+ "decision",
4101
+ "adr",
4102
+ "behavior",
4103
+ "procedure"
4104
+ ]);
4105
+ NOISE_DROP_PATTERNS = [
4106
+ /^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
4107
+ /^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
4108
+ /^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
4109
+ /^\s*Intercom is a speedup, not delivery/im,
4110
+ /^\s*Context bar reads as USAGE not remaining/im
4111
+ ];
4112
+ SKIP_EMBED_PATTERNS = [
4113
+ /tmux capture-pane\b/i,
4114
+ /docker ps\b/i,
4115
+ /docker images\b/i,
4116
+ /git status\b/i,
4117
+ /grep .*node_modules/i,
4118
+ /npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
4119
+ ];
4120
+ }
4121
+ });
4122
+
3925
4123
  // src/lib/shard-manager.ts
3926
4124
  var shard_manager_exports = {};
3927
4125
  __export(shard_manager_exports, {
@@ -4086,7 +4284,8 @@ async function ensureShardSchema(client) {
4086
4284
  }
4087
4285
  for (const idx of [
4088
4286
  "CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
4089
- "CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
4287
+ "CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
4288
+ "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"
4090
4289
  ]) {
4091
4290
  try {
4092
4291
  await client.execute(idx);
@@ -4511,7 +4710,6 @@ __export(store_exports, {
4511
4710
  vectorToBlob: () => vectorToBlob,
4512
4711
  writeMemory: () => writeMemory
4513
4712
  });
4514
- import { createHash } from "crypto";
4515
4713
  function isBusyError2(err) {
4516
4714
  if (err instanceof Error) {
4517
4715
  const msg = err.message.toLowerCase();
@@ -4625,17 +4823,24 @@ async function writeMemory(record) {
4625
4823
  `Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
4626
4824
  );
4627
4825
  }
4628
- const contentHash = createHash("md5").update(record.raw_text).digest("hex");
4629
- if (_pendingRecords.some((r) => r.content_hash === contentHash && r.agent_id === record.agent_id)) {
4826
+ const governed = governMemoryRecord(record);
4827
+ if (governed.shouldDrop) return;
4828
+ record = governed.record;
4829
+ const contentHash = governed.contentHash;
4830
+ const memoryType = record.memory_type ?? "raw";
4831
+ if (_pendingRecords.some(
4832
+ (r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
4833
+ )) {
4630
4834
  return;
4631
4835
  }
4632
4836
  try {
4633
- const client = getClient();
4634
- const existing = await client.execute({
4635
- sql: "SELECT id FROM memories WHERE content_hash = ? AND agent_id = ? LIMIT 1",
4636
- args: [contentHash, record.agent_id]
4837
+ const existing = await findScopedDuplicate({
4838
+ contentHash,
4839
+ agentId: record.agent_id,
4840
+ projectName: record.project_name,
4841
+ memoryType
4637
4842
  });
4638
- if (existing.rows.length > 0) return;
4843
+ if (existing) return;
4639
4844
  } catch {
4640
4845
  }
4641
4846
  const dbRow = {
@@ -4666,7 +4871,7 @@ async function writeMemory(record) {
4666
4871
  tier: record.tier ?? classifyTier(record),
4667
4872
  supersedes_id: record.supersedes_id ?? null,
4668
4873
  draft: record.draft ? 1 : 0,
4669
- memory_type: record.memory_type ?? "raw",
4874
+ memory_type: memoryType,
4670
4875
  trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
4671
4876
  content_hash: contentHash,
4672
4877
  intent: record.intent ?? null,
@@ -4825,6 +5030,7 @@ async function flushBatch() {
4825
5030
  const globalClient = getClient();
4826
5031
  const globalStmts = batch.map(buildStmt);
4827
5032
  await globalClient.batch(globalStmts, "write");
5033
+ schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
4828
5034
  _pendingRecords.splice(0, batch.length);
4829
5035
  try {
4830
5036
  const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
@@ -5083,6 +5289,7 @@ var init_store = __esm({
5083
5289
  init_keychain();
5084
5290
  init_config();
5085
5291
  init_state_bus();
5292
+ init_memory_write_governor();
5086
5293
  INIT_MAX_RETRIES = 3;
5087
5294
  INIT_RETRY_DELAY_MS = 1e3;
5088
5295
  _pendingRecords = [];
@@ -2621,6 +2621,14 @@ async function ensureSchema() {
2621
2621
  );
2622
2622
  } catch {
2623
2623
  }
2624
+ try {
2625
+ await client.execute(
2626
+ `CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
2627
+ ON memories(content_hash, agent_id, project_name, memory_type)
2628
+ WHERE content_hash IS NOT NULL`
2629
+ );
2630
+ } catch {
2631
+ }
2624
2632
  await client.executeMultiple(`
2625
2633
  CREATE TABLE IF NOT EXISTS entities (
2626
2634
  id TEXT PRIMARY KEY,
@@ -3366,6 +3374,196 @@ var init_state_bus = __esm({
3366
3374
  }
3367
3375
  });
3368
3376
 
3377
+ // src/lib/memory-write-governor.ts
3378
+ import { createHash } from "crypto";
3379
+ function normalizeMemoryText(text) {
3380
+ return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
3381
+ }
3382
+ function classifyMemoryType(input2) {
3383
+ if (input2.memory_type && input2.memory_type.trim()) return input2.memory_type.trim();
3384
+ const tool = input2.tool_name.toLowerCase();
3385
+ const text = input2.raw_text.toLowerCase();
3386
+ if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
3387
+ if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
3388
+ if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
3389
+ if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
3390
+ if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
3391
+ if (tool === "store_memory" || tool === "manual") return "observation";
3392
+ return "raw";
3393
+ }
3394
+ function shouldDropMemory(text) {
3395
+ const normalized = normalizeMemoryText(text);
3396
+ if (normalized.length < 10) return { drop: true, reason: "too_short" };
3397
+ if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
3398
+ return { drop: true, reason: "known_boilerplate_noise" };
3399
+ }
3400
+ return { drop: false };
3401
+ }
3402
+ function shouldSkipEmbedding(input2) {
3403
+ const type = classifyMemoryType(input2);
3404
+ if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
3405
+ if (type === "raw" && input2.raw_text.length > 2e4) return true;
3406
+ if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input2.raw_text))) return true;
3407
+ return false;
3408
+ }
3409
+ function hashMemoryContent(text) {
3410
+ return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
3411
+ }
3412
+ function scopedDedupArgs(input2) {
3413
+ return [input2.contentHash, input2.agentId, input2.projectName, input2.memoryType];
3414
+ }
3415
+ function governMemoryRecord(record) {
3416
+ const normalized = normalizeMemoryText(record.raw_text);
3417
+ const memoryType = classifyMemoryType({
3418
+ raw_text: normalized,
3419
+ agent_id: record.agent_id,
3420
+ project_name: record.project_name,
3421
+ tool_name: record.tool_name,
3422
+ memory_type: record.memory_type
3423
+ });
3424
+ const drop = shouldDropMemory(normalized);
3425
+ const skipEmbedding = shouldSkipEmbedding({
3426
+ raw_text: normalized,
3427
+ agent_id: record.agent_id,
3428
+ project_name: record.project_name,
3429
+ tool_name: record.tool_name,
3430
+ memory_type: memoryType
3431
+ });
3432
+ return {
3433
+ record: {
3434
+ ...record,
3435
+ raw_text: normalized,
3436
+ memory_type: memoryType,
3437
+ vector: skipEmbedding ? null : record.vector
3438
+ },
3439
+ contentHash: hashMemoryContent(normalized),
3440
+ shouldDrop: drop.drop,
3441
+ dropReason: drop.reason,
3442
+ skipEmbedding,
3443
+ hygiene: {
3444
+ dedup: true,
3445
+ supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
3446
+ }
3447
+ };
3448
+ }
3449
+ async function findScopedDuplicate(input2) {
3450
+ const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
3451
+ const client = getClient2();
3452
+ const args = scopedDedupArgs(input2);
3453
+ let sql = `SELECT id FROM memories
3454
+ WHERE content_hash = ?
3455
+ AND agent_id = ?
3456
+ AND project_name = ?
3457
+ AND COALESCE(memory_type, 'raw') = ?
3458
+ AND COALESCE(status, 'active') != 'deleted'`;
3459
+ if (input2.excludeId) {
3460
+ sql += " AND id != ?";
3461
+ args.push(input2.excludeId);
3462
+ }
3463
+ sql += " ORDER BY timestamp DESC LIMIT 1";
3464
+ const result = await client.execute({ sql, args });
3465
+ return result.rows[0]?.id ? String(result.rows[0].id) : null;
3466
+ }
3467
+ async function runPostWriteMemoryHygiene(memoryId) {
3468
+ try {
3469
+ const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
3470
+ const client = getClient2();
3471
+ const current = await client.execute({
3472
+ sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
3473
+ importance, timestamp
3474
+ FROM memories
3475
+ WHERE id = ?
3476
+ LIMIT 1`,
3477
+ args: [memoryId]
3478
+ });
3479
+ const row = current.rows[0];
3480
+ if (!row) return;
3481
+ const memoryType = String(row.memory_type ?? "raw");
3482
+ const contentHash = row.content_hash ? String(row.content_hash) : null;
3483
+ const agentId = String(row.agent_id);
3484
+ const projectName = String(row.project_name);
3485
+ if (contentHash) {
3486
+ await client.execute({
3487
+ sql: `UPDATE memories
3488
+ SET status = 'deleted',
3489
+ outcome = COALESCE(outcome, 'superseded')
3490
+ WHERE id != ?
3491
+ AND content_hash = ?
3492
+ AND agent_id = ?
3493
+ AND project_name = ?
3494
+ AND COALESCE(memory_type, 'raw') = ?
3495
+ AND COALESCE(status, 'active') = 'active'`,
3496
+ args: [memoryId, contentHash, agentId, projectName, memoryType]
3497
+ });
3498
+ }
3499
+ const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
3500
+ if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
3501
+ const old = await client.execute({
3502
+ sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
3503
+ args: [supersedesId]
3504
+ });
3505
+ const oldImportance = Number(old.rows[0]?.importance ?? 0);
3506
+ const newImportance = Number(row.importance ?? 0);
3507
+ await client.batch([
3508
+ {
3509
+ sql: `UPDATE memories
3510
+ SET status = 'archived',
3511
+ outcome = COALESCE(outcome, 'superseded')
3512
+ WHERE id = ?`,
3513
+ args: [supersedesId]
3514
+ },
3515
+ {
3516
+ sql: `UPDATE memories
3517
+ SET importance = MAX(COALESCE(importance, 5), ?),
3518
+ parent_memory_id = COALESCE(parent_memory_id, ?)
3519
+ WHERE id = ?`,
3520
+ args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
3521
+ }
3522
+ ], "write");
3523
+ }
3524
+ } catch (err) {
3525
+ process.stderr.write(
3526
+ `[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
3527
+ `
3528
+ );
3529
+ }
3530
+ }
3531
+ function schedulePostWriteMemoryHygiene(memoryIds) {
3532
+ if (memoryIds.length === 0) return;
3533
+ const run = () => {
3534
+ void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
3535
+ };
3536
+ if (typeof setImmediate === "function") setImmediate(run);
3537
+ else setTimeout(run, 0);
3538
+ }
3539
+ var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
3540
+ var init_memory_write_governor = __esm({
3541
+ "src/lib/memory-write-governor.ts"() {
3542
+ "use strict";
3543
+ HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
3544
+ "decision",
3545
+ "adr",
3546
+ "behavior",
3547
+ "procedure"
3548
+ ]);
3549
+ NOISE_DROP_PATTERNS = [
3550
+ /^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
3551
+ /^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
3552
+ /^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
3553
+ /^\s*Intercom is a speedup, not delivery/im,
3554
+ /^\s*Context bar reads as USAGE not remaining/im
3555
+ ];
3556
+ SKIP_EMBED_PATTERNS = [
3557
+ /tmux capture-pane\b/i,
3558
+ /docker ps\b/i,
3559
+ /docker images\b/i,
3560
+ /git status\b/i,
3561
+ /grep .*node_modules/i,
3562
+ /npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
3563
+ ];
3564
+ }
3565
+ });
3566
+
3369
3567
  // src/lib/shard-manager.ts
3370
3568
  var shard_manager_exports = {};
3371
3569
  __export(shard_manager_exports, {
@@ -3530,7 +3728,8 @@ async function ensureShardSchema(client) {
3530
3728
  }
3531
3729
  for (const idx of [
3532
3730
  "CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
3533
- "CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
3731
+ "CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
3732
+ "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"
3534
3733
  ]) {
3535
3734
  try {
3536
3735
  await client.execute(idx);
@@ -3955,7 +4154,6 @@ __export(store_exports, {
3955
4154
  vectorToBlob: () => vectorToBlob,
3956
4155
  writeMemory: () => writeMemory
3957
4156
  });
3958
- import { createHash } from "crypto";
3959
4157
  function isBusyError2(err) {
3960
4158
  if (err instanceof Error) {
3961
4159
  const msg = err.message.toLowerCase();
@@ -4069,17 +4267,24 @@ async function writeMemory(record) {
4069
4267
  `Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
4070
4268
  );
4071
4269
  }
4072
- const contentHash = createHash("md5").update(record.raw_text).digest("hex");
4073
- if (_pendingRecords.some((r) => r.content_hash === contentHash && r.agent_id === record.agent_id)) {
4270
+ const governed = governMemoryRecord(record);
4271
+ if (governed.shouldDrop) return;
4272
+ record = governed.record;
4273
+ const contentHash = governed.contentHash;
4274
+ const memoryType = record.memory_type ?? "raw";
4275
+ if (_pendingRecords.some(
4276
+ (r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
4277
+ )) {
4074
4278
  return;
4075
4279
  }
4076
4280
  try {
4077
- const client = getClient();
4078
- const existing = await client.execute({
4079
- sql: "SELECT id FROM memories WHERE content_hash = ? AND agent_id = ? LIMIT 1",
4080
- args: [contentHash, record.agent_id]
4281
+ const existing = await findScopedDuplicate({
4282
+ contentHash,
4283
+ agentId: record.agent_id,
4284
+ projectName: record.project_name,
4285
+ memoryType
4081
4286
  });
4082
- if (existing.rows.length > 0) return;
4287
+ if (existing) return;
4083
4288
  } catch {
4084
4289
  }
4085
4290
  const dbRow = {
@@ -4110,7 +4315,7 @@ async function writeMemory(record) {
4110
4315
  tier: record.tier ?? classifyTier(record),
4111
4316
  supersedes_id: record.supersedes_id ?? null,
4112
4317
  draft: record.draft ? 1 : 0,
4113
- memory_type: record.memory_type ?? "raw",
4318
+ memory_type: memoryType,
4114
4319
  trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
4115
4320
  content_hash: contentHash,
4116
4321
  intent: record.intent ?? null,
@@ -4269,6 +4474,7 @@ async function flushBatch() {
4269
4474
  const globalClient = getClient();
4270
4475
  const globalStmts = batch.map(buildStmt);
4271
4476
  await globalClient.batch(globalStmts, "write");
4477
+ schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
4272
4478
  _pendingRecords.splice(0, batch.length);
4273
4479
  try {
4274
4480
  const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
@@ -4527,6 +4733,7 @@ var init_store = __esm({
4527
4733
  init_keychain();
4528
4734
  init_config();
4529
4735
  init_state_bus();
4736
+ init_memory_write_governor();
4530
4737
  INIT_MAX_RETRIES = 3;
4531
4738
  INIT_RETRY_DELAY_MS = 1e3;
4532
4739
  _pendingRecords = [];
@@ -9001,7 +9208,11 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
9001
9208
  }
9002
9209
  if (!useExeAgent && !useCodex && !useOpencode && !useBinSymlink) {
9003
9210
  if (agentRtConfig.runtime === "claude" && agentRtConfig.model) {
9004
- envPrefix = `${envPrefix} ANTHROPIC_MODEL=${agentRtConfig.model}`;
9211
+ let ccModel = agentRtConfig.model.replace(/(\d+)\.(\d+)/g, "$1-$2");
9212
+ if (/claude-(opus|sonnet)-4-[6-9]/.test(ccModel) && !ccModel.includes("[1m]")) {
9213
+ ccModel += "[1m]";
9214
+ }
9215
+ envPrefix = `${envPrefix} ANTHROPIC_MODEL=${ccModel}`;
9005
9216
  }
9006
9217
  }
9007
9218
  let spawnCommand;