@askexenow/exe-os 0.9.34 → 0.9.36

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 (76) hide show
  1. package/dist/bin/backfill-conversations.js +212 -11
  2. package/dist/bin/backfill-responses.js +212 -11
  3. package/dist/bin/backfill-vectors.js +14 -3
  4. package/dist/bin/cleanup-stale-review-tasks.js +224 -12
  5. package/dist/bin/cli.js +265 -23
  6. package/dist/bin/exe-agent.js +1 -1
  7. package/dist/bin/exe-assign.js +217 -12
  8. package/dist/bin/exe-boot.js +25 -4
  9. package/dist/bin/exe-call.js +7 -5
  10. package/dist/bin/exe-dispatch.js +229 -13
  11. package/dist/bin/exe-doctor.js +14 -3
  12. package/dist/bin/exe-export-behaviors.js +301 -14
  13. package/dist/bin/exe-forget.js +245 -21
  14. package/dist/bin/exe-gateway.js +229 -13
  15. package/dist/bin/exe-heartbeat.js +224 -12
  16. package/dist/bin/exe-kill.js +224 -12
  17. package/dist/bin/exe-launch-agent.js +177 -9
  18. package/dist/bin/exe-link.js +8 -0
  19. package/dist/bin/exe-new-employee.js +26 -2
  20. package/dist/bin/exe-pending-messages.js +224 -12
  21. package/dist/bin/exe-pending-notifications.js +224 -12
  22. package/dist/bin/exe-pending-reviews.js +224 -12
  23. package/dist/bin/exe-rename.js +9 -1
  24. package/dist/bin/exe-review.js +224 -12
  25. package/dist/bin/exe-search.js +246 -21
  26. package/dist/bin/exe-session-cleanup.js +229 -13
  27. package/dist/bin/exe-start-codex.js +161 -5
  28. package/dist/bin/exe-start-opencode.js +172 -5
  29. package/dist/bin/exe-status.js +224 -12
  30. package/dist/bin/exe-team.js +224 -12
  31. package/dist/bin/git-sweep.js +229 -13
  32. package/dist/bin/graph-backfill.js +94 -3
  33. package/dist/bin/graph-export.js +224 -12
  34. package/dist/bin/install.js +25 -1
  35. package/dist/bin/intercom-check.js +229 -13
  36. package/dist/bin/scan-tasks.js +229 -13
  37. package/dist/bin/setup.js +15 -5
  38. package/dist/bin/shard-migrate.js +94 -3
  39. package/dist/gateway/index.js +229 -13
  40. package/dist/hooks/bug-report-worker.js +229 -13
  41. package/dist/hooks/codex-stop-task-finalizer.js +224 -12
  42. package/dist/hooks/commit-complete.js +229 -13
  43. package/dist/hooks/error-recall.js +246 -21
  44. package/dist/hooks/ingest.js +224 -12
  45. package/dist/hooks/instructions-loaded.js +224 -12
  46. package/dist/hooks/notification.js +224 -12
  47. package/dist/hooks/post-compact.js +224 -12
  48. package/dist/hooks/post-tool-combined.js +246 -21
  49. package/dist/hooks/pre-compact.js +229 -13
  50. package/dist/hooks/pre-tool-use.js +234 -18
  51. package/dist/hooks/prompt-submit.js +346 -23
  52. package/dist/hooks/session-end.js +229 -13
  53. package/dist/hooks/session-start.js +418 -42
  54. package/dist/hooks/stop.js +224 -12
  55. package/dist/hooks/subagent-stop.js +224 -12
  56. package/dist/hooks/summary-worker.js +15 -2
  57. package/dist/index.js +229 -13
  58. package/dist/lib/cloud-sync.js +8 -0
  59. package/dist/lib/consolidation.js +3 -1
  60. package/dist/lib/database.js +8 -0
  61. package/dist/lib/db.js +8 -0
  62. package/dist/lib/device-registry.js +8 -0
  63. package/dist/lib/employee-templates.js +7 -5
  64. package/dist/lib/exe-daemon.js +1776 -1433
  65. package/dist/lib/hybrid-search.js +246 -21
  66. package/dist/lib/schedules.js +14 -3
  67. package/dist/lib/store.js +217 -12
  68. package/dist/lib/tasks.js +5 -1
  69. package/dist/lib/tmux-routing.js +5 -1
  70. package/dist/mcp/server.js +331 -37
  71. package/dist/mcp/tools/create-task.js +5 -1
  72. package/dist/mcp/tools/update-task.js +5 -1
  73. package/dist/runtime/index.js +229 -13
  74. package/dist/tui/App.js +229 -13
  75. package/package.json +1 -1
  76. package/src/commands/exe/save.md +48 -0
@@ -1,6 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
4
6
  var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
5
7
  get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
6
8
  }) : x)(function(x) {
@@ -14,6 +16,15 @@ var __export = (target, all) => {
14
16
  for (var name in all)
15
17
  __defProp(target, name, { get: all[name], enumerable: true });
16
18
  };
19
+ var __copyProps = (to, from, except, desc) => {
20
+ if (from && typeof from === "object" || typeof from === "function") {
21
+ for (let key of __getOwnPropNames(from))
22
+ if (!__hasOwnProp.call(to, key) && key !== except)
23
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
24
+ }
25
+ return to;
26
+ };
27
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
17
28
 
18
29
  // src/types/memory.ts
19
30
  var EMBEDDING_DIM;
@@ -118,6 +129,21 @@ var init_secure_files = __esm({
118
129
  });
119
130
 
120
131
  // src/lib/config.ts
132
+ var config_exports = {};
133
+ __export(config_exports, {
134
+ CONFIG_MIGRATIONS: () => CONFIG_MIGRATIONS,
135
+ CONFIG_PATH: () => CONFIG_PATH,
136
+ CURRENT_CONFIG_VERSION: () => CURRENT_CONFIG_VERSION,
137
+ DB_PATH: () => DB_PATH,
138
+ EXE_AI_DIR: () => EXE_AI_DIR,
139
+ LEGACY_LANCE_PATH: () => LEGACY_LANCE_PATH,
140
+ MODELS_DIR: () => MODELS_DIR,
141
+ loadConfig: () => loadConfig,
142
+ loadConfigFrom: () => loadConfigFrom,
143
+ loadConfigSync: () => loadConfigSync,
144
+ migrateConfig: () => migrateConfig,
145
+ saveConfig: () => saveConfig
146
+ });
121
147
  import { readFile, writeFile } from "fs/promises";
122
148
  import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
123
149
  import path from "path";
@@ -221,6 +247,46 @@ async function loadConfig() {
221
247
  return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
222
248
  }
223
249
  }
250
+ function loadConfigSync() {
251
+ const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
252
+ const configPath = path.join(dir, "config.json");
253
+ if (!existsSync2(configPath)) {
254
+ return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
255
+ }
256
+ try {
257
+ const raw = readFileSync(configPath, "utf-8");
258
+ let parsed = JSON.parse(raw);
259
+ parsed = migrateLegacyConfig(parsed);
260
+ const { config: migratedCfg } = migrateConfig(parsed);
261
+ normalizeScalingRoadmap(migratedCfg);
262
+ normalizeSessionLifecycle(migratedCfg);
263
+ normalizeAutoUpdate(migratedCfg);
264
+ return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db"), ...migratedCfg };
265
+ } catch {
266
+ return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
267
+ }
268
+ }
269
+ async function saveConfig(config) {
270
+ const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
271
+ await ensurePrivateDir(dir);
272
+ const configPath = path.join(dir, "config.json");
273
+ await writeFile(configPath, JSON.stringify(config, null, 2) + "\n");
274
+ await enforcePrivateFile(configPath);
275
+ }
276
+ async function loadConfigFrom(configPath) {
277
+ const raw = await readFile(configPath, "utf-8");
278
+ try {
279
+ let parsed = JSON.parse(raw);
280
+ parsed = migrateLegacyConfig(parsed);
281
+ const { config: migratedCfg } = migrateConfig(parsed);
282
+ normalizeScalingRoadmap(migratedCfg);
283
+ normalizeSessionLifecycle(migratedCfg);
284
+ normalizeAutoUpdate(migratedCfg);
285
+ return { ...DEFAULT_CONFIG, ...migratedCfg };
286
+ } catch {
287
+ return { ...DEFAULT_CONFIG };
288
+ }
289
+ }
224
290
  var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
225
291
  var init_config = __esm({
226
292
  "src/lib/config.ts"() {
@@ -2084,6 +2150,14 @@ async function ensureSchema() {
2084
2150
  );
2085
2151
  } catch {
2086
2152
  }
2153
+ try {
2154
+ await client.execute(
2155
+ `CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
2156
+ ON memories(content_hash, agent_id, project_name, memory_type)
2157
+ WHERE content_hash IS NOT NULL`
2158
+ );
2159
+ } catch {
2160
+ }
2087
2161
  await client.executeMultiple(`
2088
2162
  CREATE TABLE IF NOT EXISTS entities (
2089
2163
  id TEXT PRIMARY KEY,
@@ -2753,7 +2827,8 @@ async function ensureShardSchema(client) {
2753
2827
  }
2754
2828
  for (const idx of [
2755
2829
  "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"
2830
+ "CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
2831
+ "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
2832
  ]) {
2758
2833
  try {
2759
2834
  await client.execute(idx);
@@ -2947,7 +3022,7 @@ var init_platform_procedures = __esm({
2947
3022
  title: "Chain of command \u2014 who talks to whom",
2948
3023
  domain: "workflow",
2949
3024
  priority: "p0",
2950
- content: "Founder -> COO -> CTO/CMO. CTO -> engineers. CMO -> content production. Never skip levels: the COO does not bypass managers for specialist work. Specialists report to their manager. If you need cross-team info, use ask_team_memory \u2014 don't read other agents' task folders. Each level owns dispatch downward and review upward."
3025
+ content: "Founder -> coordinator (the executive agent, internally routed as 'COO') -> CTO/CMO. CTO -> engineers. CMO -> content production. Never skip levels: the coordinator does not bypass managers for specialist work. Specialists report to their manager. If you need cross-team info, use ask_team_memory \u2014 don't read other agents' task folders. Each level owns dispatch downward and review upward."
2951
3026
  },
2952
3027
  {
2953
3028
  title: "Single dispatch path \u2014 create_task only",
@@ -3490,6 +3565,7 @@ var init_preferences = __esm({
3490
3565
  // src/adapters/claude/installer.ts
3491
3566
  import { readFile as readFile4, writeFile as writeFile4, mkdir as mkdir4, readdir } from "fs/promises";
3492
3567
  import { existsSync as existsSync11, readFileSync as readFileSync7, writeFileSync as writeFileSync6, copyFileSync, mkdirSync as mkdirSync6 } from "fs";
3568
+ import { createHash as createHash2 } from "crypto";
3493
3569
  import path12 from "path";
3494
3570
  import os9 from "os";
3495
3571
  import { execSync as execSync5 } from "child_process";
@@ -3827,7 +3903,6 @@ import { spawnSync } from "child_process";
3827
3903
  // src/lib/store.ts
3828
3904
  init_memory();
3829
3905
  init_database();
3830
- import { createHash } from "crypto";
3831
3906
 
3832
3907
  // src/lib/keychain.ts
3833
3908
  import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
@@ -4060,6 +4135,88 @@ var StateBus = class {
4060
4135
  };
4061
4136
  var orgBus = new StateBus();
4062
4137
 
4138
+ // src/lib/memory-write-governor.ts
4139
+ import { createHash } from "crypto";
4140
+ var HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
4141
+ "decision",
4142
+ "adr",
4143
+ "behavior",
4144
+ "procedure"
4145
+ ]);
4146
+ async function runPostWriteMemoryHygiene(memoryId) {
4147
+ try {
4148
+ const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
4149
+ const client = getClient2();
4150
+ const current = await client.execute({
4151
+ sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
4152
+ importance, timestamp
4153
+ FROM memories
4154
+ WHERE id = ?
4155
+ LIMIT 1`,
4156
+ args: [memoryId]
4157
+ });
4158
+ const row = current.rows[0];
4159
+ if (!row) return;
4160
+ const memoryType = String(row.memory_type ?? "raw");
4161
+ const contentHash = row.content_hash ? String(row.content_hash) : null;
4162
+ const agentId = String(row.agent_id);
4163
+ const projectName = String(row.project_name);
4164
+ if (contentHash) {
4165
+ await client.execute({
4166
+ sql: `UPDATE memories
4167
+ SET status = 'deleted',
4168
+ outcome = COALESCE(outcome, 'superseded')
4169
+ WHERE id != ?
4170
+ AND content_hash = ?
4171
+ AND agent_id = ?
4172
+ AND project_name = ?
4173
+ AND COALESCE(memory_type, 'raw') = ?
4174
+ AND COALESCE(status, 'active') = 'active'`,
4175
+ args: [memoryId, contentHash, agentId, projectName, memoryType]
4176
+ });
4177
+ }
4178
+ const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
4179
+ if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
4180
+ const old = await client.execute({
4181
+ sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
4182
+ args: [supersedesId]
4183
+ });
4184
+ const oldImportance = Number(old.rows[0]?.importance ?? 0);
4185
+ const newImportance = Number(row.importance ?? 0);
4186
+ await client.batch([
4187
+ {
4188
+ sql: `UPDATE memories
4189
+ SET status = 'archived',
4190
+ outcome = COALESCE(outcome, 'superseded')
4191
+ WHERE id = ?`,
4192
+ args: [supersedesId]
4193
+ },
4194
+ {
4195
+ sql: `UPDATE memories
4196
+ SET importance = MAX(COALESCE(importance, 5), ?),
4197
+ parent_memory_id = COALESCE(parent_memory_id, ?)
4198
+ WHERE id = ?`,
4199
+ args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
4200
+ }
4201
+ ], "write");
4202
+ }
4203
+ } catch (err) {
4204
+ process.stderr.write(
4205
+ `[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
4206
+ `
4207
+ );
4208
+ }
4209
+ }
4210
+ function schedulePostWriteMemoryHygiene(memoryIds) {
4211
+ if (process.env.EXE_SKIP_MEMORY_HYGIENE === "1") return;
4212
+ if (memoryIds.length === 0) return;
4213
+ const run = () => {
4214
+ void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
4215
+ };
4216
+ if (typeof setImmediate === "function") setImmediate(run);
4217
+ else setTimeout(run, 0);
4218
+ }
4219
+
4063
4220
  // src/lib/store.ts
4064
4221
  var INIT_MAX_RETRIES = 3;
4065
4222
  var INIT_RETRY_DELAY_MS = 1e3;
@@ -4259,6 +4416,7 @@ async function flushBatch() {
4259
4416
  const globalClient = getClient();
4260
4417
  const globalStmts = batch.map(buildStmt);
4261
4418
  await globalClient.batch(globalStmts, "write");
4419
+ schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
4262
4420
  _pendingRecords.splice(0, batch.length);
4263
4421
  try {
4264
4422
  const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
@@ -4370,7 +4528,16 @@ var BEHAVIORS_EXPORT_DIR = path8.join(
4370
4528
  "behaviors-export"
4371
4529
  );
4372
4530
  var STALE_EXPORT_AGE_MS = 60 * 60 * 1e3;
4373
- var EXPORT_BEHAVIOR_LIMIT = 30;
4531
+ var DEFAULT_BEHAVIOR_LIMIT = 30;
4532
+ function getBehaviorLimit() {
4533
+ try {
4534
+ const { loadConfigSync: loadConfigSync2 } = (init_config(), __toCommonJS(config_exports));
4535
+ const cfg = loadConfigSync2();
4536
+ return cfg.behaviorExportLimit ?? DEFAULT_BEHAVIOR_LIMIT;
4537
+ } catch {
4538
+ return DEFAULT_BEHAVIOR_LIMIT;
4539
+ }
4540
+ }
4374
4541
  function sweepStaleBehaviorExports(now = Date.now()) {
4375
4542
  if (!existsSync8(BEHAVIORS_EXPORT_DIR)) return;
4376
4543
  let entries;
@@ -4424,7 +4591,7 @@ function exportFilePath(agentId, projectName, sessionKey) {
4424
4591
  async function exportBehaviorsForAgent(agentId, projectName, sessionKey) {
4425
4592
  mkdirSync3(BEHAVIORS_EXPORT_DIR, { recursive: true });
4426
4593
  sweepStaleBehaviorExports();
4427
- const behaviors = await listBehaviors(agentId, projectName, EXPORT_BEHAVIOR_LIMIT);
4594
+ const behaviors = await listBehaviors(agentId, projectName, getBehaviorLimit());
4428
4595
  if (behaviors.length === 0) return null;
4429
4596
  const body = renderBehaviorExport(behaviors);
4430
4597
  const target = exportFilePath(agentId, projectName, sessionKey);
@@ -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,197 @@ 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 (process.env.EXE_SKIP_MEMORY_HYGIENE === "1") return;
3222
+ if (memoryIds.length === 0) return;
3223
+ const run = () => {
3224
+ void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
3225
+ };
3226
+ if (typeof setImmediate === "function") setImmediate(run);
3227
+ else setTimeout(run, 0);
3228
+ }
3229
+ var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
3230
+ var init_memory_write_governor = __esm({
3231
+ "src/lib/memory-write-governor.ts"() {
3232
+ "use strict";
3233
+ HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
3234
+ "decision",
3235
+ "adr",
3236
+ "behavior",
3237
+ "procedure"
3238
+ ]);
3239
+ NOISE_DROP_PATTERNS = [
3240
+ /^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
3241
+ /^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
3242
+ /^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
3243
+ /^\s*Intercom is a speedup, not delivery/im,
3244
+ /^\s*Context bar reads as USAGE not remaining/im
3245
+ ];
3246
+ SKIP_EMBED_PATTERNS = [
3247
+ /tmux capture-pane\b/i,
3248
+ /docker ps\b/i,
3249
+ /docker images\b/i,
3250
+ /git status\b/i,
3251
+ /grep .*node_modules/i,
3252
+ /npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
3253
+ ];
3254
+ }
3255
+ });
3256
+
3058
3257
  // src/lib/shard-manager.ts
3059
3258
  var shard_manager_exports = {};
3060
3259
  __export(shard_manager_exports, {
@@ -3219,7 +3418,8 @@ async function ensureShardSchema(client) {
3219
3418
  }
3220
3419
  for (const idx of [
3221
3420
  "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"
3421
+ "CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
3422
+ "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
3423
  ]) {
3224
3424
  try {
3225
3425
  await client.execute(idx);
@@ -3413,7 +3613,7 @@ var init_platform_procedures = __esm({
3413
3613
  title: "Chain of command \u2014 who talks to whom",
3414
3614
  domain: "workflow",
3415
3615
  priority: "p0",
3416
- content: "Founder -> COO -> CTO/CMO. CTO -> engineers. CMO -> content production. Never skip levels: the COO does not bypass managers for specialist work. Specialists report to their manager. If you need cross-team info, use ask_team_memory \u2014 don't read other agents' task folders. Each level owns dispatch downward and review upward."
3616
+ content: "Founder -> coordinator (the executive agent, internally routed as 'COO') -> CTO/CMO. CTO -> engineers. CMO -> content production. Never skip levels: the coordinator does not bypass managers for specialist work. Specialists report to their manager. If you need cross-team info, use ask_team_memory \u2014 don't read other agents' task folders. Each level owns dispatch downward and review upward."
3417
3617
  },
3418
3618
  {
3419
3619
  title: "Single dispatch path \u2014 create_task only",
@@ -3644,7 +3844,6 @@ __export(store_exports, {
3644
3844
  vectorToBlob: () => vectorToBlob,
3645
3845
  writeMemory: () => writeMemory
3646
3846
  });
3647
- import { createHash } from "crypto";
3648
3847
  function isBusyError2(err) {
3649
3848
  if (err instanceof Error) {
3650
3849
  const msg = err.message.toLowerCase();
@@ -3758,17 +3957,24 @@ async function writeMemory(record) {
3758
3957
  `Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
3759
3958
  );
3760
3959
  }
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)) {
3960
+ const governed = governMemoryRecord(record);
3961
+ if (governed.shouldDrop) return;
3962
+ record = governed.record;
3963
+ const contentHash = governed.contentHash;
3964
+ const memoryType = record.memory_type ?? "raw";
3965
+ if (_pendingRecords.some(
3966
+ (r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
3967
+ )) {
3763
3968
  return;
3764
3969
  }
3765
3970
  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]
3971
+ const existing = await findScopedDuplicate({
3972
+ contentHash,
3973
+ agentId: record.agent_id,
3974
+ projectName: record.project_name,
3975
+ memoryType
3770
3976
  });
3771
- if (existing.rows.length > 0) return;
3977
+ if (existing) return;
3772
3978
  } catch {
3773
3979
  }
3774
3980
  const dbRow = {
@@ -3799,7 +4005,7 @@ async function writeMemory(record) {
3799
4005
  tier: record.tier ?? classifyTier(record),
3800
4006
  supersedes_id: record.supersedes_id ?? null,
3801
4007
  draft: record.draft ? 1 : 0,
3802
- memory_type: record.memory_type ?? "raw",
4008
+ memory_type: memoryType,
3803
4009
  trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
3804
4010
  content_hash: contentHash,
3805
4011
  intent: record.intent ?? null,
@@ -3958,6 +4164,7 @@ async function flushBatch() {
3958
4164
  const globalClient = getClient();
3959
4165
  const globalStmts = batch.map(buildStmt);
3960
4166
  await globalClient.batch(globalStmts, "write");
4167
+ schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
3961
4168
  _pendingRecords.splice(0, batch.length);
3962
4169
  try {
3963
4170
  const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
@@ -4078,7 +4285,11 @@ async function searchMemories(queryVector, agentId, options) {
4078
4285
  sql += ` AND timestamp >= ?`;
4079
4286
  args.push(options.since);
4080
4287
  }
4081
- if (options?.memoryType) {
4288
+ if (options?.memoryTypes && options.memoryTypes.length > 0) {
4289
+ const uniqueTypes = [...new Set(options.memoryTypes)];
4290
+ sql += ` AND memory_type IN (${uniqueTypes.map(() => "?").join(",")})`;
4291
+ args.push(...uniqueTypes);
4292
+ } else if (options?.memoryType) {
4082
4293
  sql += ` AND memory_type = ?`;
4083
4294
  args.push(options.memoryType);
4084
4295
  }
@@ -4216,6 +4427,7 @@ var init_store = __esm({
4216
4427
  init_keychain();
4217
4428
  init_config();
4218
4429
  init_state_bus();
4430
+ init_memory_write_governor();
4219
4431
  INIT_MAX_RETRIES = 3;
4220
4432
  INIT_RETRY_DELAY_MS = 1e3;
4221
4433
  _pendingRecords = [];