@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"() {
@@ -2288,6 +2354,14 @@ async function ensureSchema() {
2288
2354
  );
2289
2355
  } catch {
2290
2356
  }
2357
+ try {
2358
+ await client.execute(
2359
+ `CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
2360
+ ON memories(content_hash, agent_id, project_name, memory_type)
2361
+ WHERE content_hash IS NOT NULL`
2362
+ );
2363
+ } catch {
2364
+ }
2291
2365
  await client.executeMultiple(`
2292
2366
  CREATE TABLE IF NOT EXISTS entities (
2293
2367
  id TEXT PRIMARY KEY,
@@ -3033,6 +3107,197 @@ var init_state_bus = __esm({
3033
3107
  }
3034
3108
  });
3035
3109
 
3110
+ // src/lib/memory-write-governor.ts
3111
+ import { createHash } from "crypto";
3112
+ function normalizeMemoryText(text) {
3113
+ return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
3114
+ }
3115
+ function classifyMemoryType(input) {
3116
+ if (input.memory_type && input.memory_type.trim()) return input.memory_type.trim();
3117
+ const tool = input.tool_name.toLowerCase();
3118
+ const text = input.raw_text.toLowerCase();
3119
+ if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
3120
+ if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
3121
+ if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
3122
+ if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
3123
+ if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
3124
+ if (tool === "store_memory" || tool === "manual") return "observation";
3125
+ return "raw";
3126
+ }
3127
+ function shouldDropMemory(text) {
3128
+ const normalized = normalizeMemoryText(text);
3129
+ if (normalized.length < 10) return { drop: true, reason: "too_short" };
3130
+ if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
3131
+ return { drop: true, reason: "known_boilerplate_noise" };
3132
+ }
3133
+ return { drop: false };
3134
+ }
3135
+ function shouldSkipEmbedding(input) {
3136
+ const type = classifyMemoryType(input);
3137
+ if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
3138
+ if (type === "raw" && input.raw_text.length > 2e4) return true;
3139
+ if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input.raw_text))) return true;
3140
+ return false;
3141
+ }
3142
+ function hashMemoryContent(text) {
3143
+ return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
3144
+ }
3145
+ function scopedDedupArgs(input) {
3146
+ return [input.contentHash, input.agentId, input.projectName, input.memoryType];
3147
+ }
3148
+ function governMemoryRecord(record) {
3149
+ const normalized = normalizeMemoryText(record.raw_text);
3150
+ const memoryType = classifyMemoryType({
3151
+ raw_text: normalized,
3152
+ agent_id: record.agent_id,
3153
+ project_name: record.project_name,
3154
+ tool_name: record.tool_name,
3155
+ memory_type: record.memory_type
3156
+ });
3157
+ const drop = shouldDropMemory(normalized);
3158
+ const skipEmbedding = shouldSkipEmbedding({
3159
+ raw_text: normalized,
3160
+ agent_id: record.agent_id,
3161
+ project_name: record.project_name,
3162
+ tool_name: record.tool_name,
3163
+ memory_type: memoryType
3164
+ });
3165
+ return {
3166
+ record: {
3167
+ ...record,
3168
+ raw_text: normalized,
3169
+ memory_type: memoryType,
3170
+ vector: skipEmbedding ? null : record.vector
3171
+ },
3172
+ contentHash: hashMemoryContent(normalized),
3173
+ shouldDrop: drop.drop,
3174
+ dropReason: drop.reason,
3175
+ skipEmbedding,
3176
+ hygiene: {
3177
+ dedup: true,
3178
+ supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
3179
+ }
3180
+ };
3181
+ }
3182
+ async function findScopedDuplicate(input) {
3183
+ const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
3184
+ const client = getClient2();
3185
+ const args = scopedDedupArgs(input);
3186
+ let sql = `SELECT id FROM memories
3187
+ WHERE content_hash = ?
3188
+ AND agent_id = ?
3189
+ AND project_name = ?
3190
+ AND COALESCE(memory_type, 'raw') = ?
3191
+ AND COALESCE(status, 'active') != 'deleted'`;
3192
+ if (input.excludeId) {
3193
+ sql += " AND id != ?";
3194
+ args.push(input.excludeId);
3195
+ }
3196
+ sql += " ORDER BY timestamp DESC LIMIT 1";
3197
+ const result = await client.execute({ sql, args });
3198
+ return result.rows[0]?.id ? String(result.rows[0].id) : null;
3199
+ }
3200
+ async function runPostWriteMemoryHygiene(memoryId) {
3201
+ try {
3202
+ const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
3203
+ const client = getClient2();
3204
+ const current = await client.execute({
3205
+ sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
3206
+ importance, timestamp
3207
+ FROM memories
3208
+ WHERE id = ?
3209
+ LIMIT 1`,
3210
+ args: [memoryId]
3211
+ });
3212
+ const row = current.rows[0];
3213
+ if (!row) return;
3214
+ const memoryType = String(row.memory_type ?? "raw");
3215
+ const contentHash = row.content_hash ? String(row.content_hash) : null;
3216
+ const agentId2 = String(row.agent_id);
3217
+ const projectName2 = String(row.project_name);
3218
+ if (contentHash) {
3219
+ await client.execute({
3220
+ sql: `UPDATE memories
3221
+ SET status = 'deleted',
3222
+ outcome = COALESCE(outcome, 'superseded')
3223
+ WHERE id != ?
3224
+ AND content_hash = ?
3225
+ AND agent_id = ?
3226
+ AND project_name = ?
3227
+ AND COALESCE(memory_type, 'raw') = ?
3228
+ AND COALESCE(status, 'active') = 'active'`,
3229
+ args: [memoryId, contentHash, agentId2, projectName2, memoryType]
3230
+ });
3231
+ }
3232
+ const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
3233
+ if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
3234
+ const old = await client.execute({
3235
+ sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
3236
+ args: [supersedesId]
3237
+ });
3238
+ const oldImportance = Number(old.rows[0]?.importance ?? 0);
3239
+ const newImportance = Number(row.importance ?? 0);
3240
+ await client.batch([
3241
+ {
3242
+ sql: `UPDATE memories
3243
+ SET status = 'archived',
3244
+ outcome = COALESCE(outcome, 'superseded')
3245
+ WHERE id = ?`,
3246
+ args: [supersedesId]
3247
+ },
3248
+ {
3249
+ sql: `UPDATE memories
3250
+ SET importance = MAX(COALESCE(importance, 5), ?),
3251
+ parent_memory_id = COALESCE(parent_memory_id, ?)
3252
+ WHERE id = ?`,
3253
+ args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
3254
+ }
3255
+ ], "write");
3256
+ }
3257
+ } catch (err) {
3258
+ process.stderr.write(
3259
+ `[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
3260
+ `
3261
+ );
3262
+ }
3263
+ }
3264
+ function schedulePostWriteMemoryHygiene(memoryIds) {
3265
+ if (process.env.EXE_SKIP_MEMORY_HYGIENE === "1") return;
3266
+ if (memoryIds.length === 0) return;
3267
+ const run = () => {
3268
+ void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
3269
+ };
3270
+ if (typeof setImmediate === "function") setImmediate(run);
3271
+ else setTimeout(run, 0);
3272
+ }
3273
+ var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
3274
+ var init_memory_write_governor = __esm({
3275
+ "src/lib/memory-write-governor.ts"() {
3276
+ "use strict";
3277
+ HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
3278
+ "decision",
3279
+ "adr",
3280
+ "behavior",
3281
+ "procedure"
3282
+ ]);
3283
+ NOISE_DROP_PATTERNS = [
3284
+ /^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
3285
+ /^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
3286
+ /^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
3287
+ /^\s*Intercom is a speedup, not delivery/im,
3288
+ /^\s*Context bar reads as USAGE not remaining/im
3289
+ ];
3290
+ SKIP_EMBED_PATTERNS = [
3291
+ /tmux capture-pane\b/i,
3292
+ /docker ps\b/i,
3293
+ /docker images\b/i,
3294
+ /git status\b/i,
3295
+ /grep .*node_modules/i,
3296
+ /npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
3297
+ ];
3298
+ }
3299
+ });
3300
+
3036
3301
  // src/lib/shard-manager.ts
3037
3302
  var shard_manager_exports = {};
3038
3303
  __export(shard_manager_exports, {
@@ -3197,7 +3462,8 @@ async function ensureShardSchema(client) {
3197
3462
  }
3198
3463
  for (const idx of [
3199
3464
  "CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
3200
- "CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
3465
+ "CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
3466
+ "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"
3201
3467
  ]) {
3202
3468
  try {
3203
3469
  await client.execute(idx);
@@ -3391,7 +3657,7 @@ var init_platform_procedures = __esm({
3391
3657
  title: "Chain of command \u2014 who talks to whom",
3392
3658
  domain: "workflow",
3393
3659
  priority: "p0",
3394
- 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."
3660
+ 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."
3395
3661
  },
3396
3662
  {
3397
3663
  title: "Single dispatch path \u2014 create_task only",
@@ -3622,7 +3888,6 @@ __export(store_exports, {
3622
3888
  vectorToBlob: () => vectorToBlob,
3623
3889
  writeMemory: () => writeMemory
3624
3890
  });
3625
- import { createHash } from "crypto";
3626
3891
  function isBusyError2(err) {
3627
3892
  if (err instanceof Error) {
3628
3893
  const msg = err.message.toLowerCase();
@@ -3736,17 +4001,24 @@ async function writeMemory(record) {
3736
4001
  `Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
3737
4002
  );
3738
4003
  }
3739
- const contentHash = createHash("md5").update(record.raw_text).digest("hex");
3740
- if (_pendingRecords.some((r) => r.content_hash === contentHash && r.agent_id === record.agent_id)) {
4004
+ const governed = governMemoryRecord(record);
4005
+ if (governed.shouldDrop) return;
4006
+ record = governed.record;
4007
+ const contentHash = governed.contentHash;
4008
+ const memoryType = record.memory_type ?? "raw";
4009
+ if (_pendingRecords.some(
4010
+ (r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
4011
+ )) {
3741
4012
  return;
3742
4013
  }
3743
4014
  try {
3744
- const client = getClient();
3745
- const existing = await client.execute({
3746
- sql: "SELECT id FROM memories WHERE content_hash = ? AND agent_id = ? LIMIT 1",
3747
- args: [contentHash, record.agent_id]
4015
+ const existing = await findScopedDuplicate({
4016
+ contentHash,
4017
+ agentId: record.agent_id,
4018
+ projectName: record.project_name,
4019
+ memoryType
3748
4020
  });
3749
- if (existing.rows.length > 0) return;
4021
+ if (existing) return;
3750
4022
  } catch {
3751
4023
  }
3752
4024
  const dbRow = {
@@ -3777,7 +4049,7 @@ async function writeMemory(record) {
3777
4049
  tier: record.tier ?? classifyTier(record),
3778
4050
  supersedes_id: record.supersedes_id ?? null,
3779
4051
  draft: record.draft ? 1 : 0,
3780
- memory_type: record.memory_type ?? "raw",
4052
+ memory_type: memoryType,
3781
4053
  trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
3782
4054
  content_hash: contentHash,
3783
4055
  intent: record.intent ?? null,
@@ -3936,6 +4208,7 @@ async function flushBatch() {
3936
4208
  const globalClient = getClient();
3937
4209
  const globalStmts = batch.map(buildStmt);
3938
4210
  await globalClient.batch(globalStmts, "write");
4211
+ schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
3939
4212
  _pendingRecords.splice(0, batch.length);
3940
4213
  try {
3941
4214
  const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
@@ -4056,7 +4329,11 @@ async function searchMemories(queryVector, agentId2, options) {
4056
4329
  sql += ` AND timestamp >= ?`;
4057
4330
  args.push(options.since);
4058
4331
  }
4059
- if (options?.memoryType) {
4332
+ if (options?.memoryTypes && options.memoryTypes.length > 0) {
4333
+ const uniqueTypes = [...new Set(options.memoryTypes)];
4334
+ sql += ` AND memory_type IN (${uniqueTypes.map(() => "?").join(",")})`;
4335
+ args.push(...uniqueTypes);
4336
+ } else if (options?.memoryType) {
4060
4337
  sql += ` AND memory_type = ?`;
4061
4338
  args.push(options.memoryType);
4062
4339
  }
@@ -4194,6 +4471,7 @@ var init_store = __esm({
4194
4471
  init_keychain();
4195
4472
  init_config();
4196
4473
  init_state_bus();
4474
+ init_memory_write_governor();
4197
4475
  INIT_MAX_RETRIES = 3;
4198
4476
  INIT_RETRY_DELAY_MS = 1e3;
4199
4477
  _pendingRecords = [];
@@ -4322,7 +4600,16 @@ var BEHAVIORS_EXPORT_DIR = path8.join(
4322
4600
  "behaviors-export"
4323
4601
  );
4324
4602
  var STALE_EXPORT_AGE_MS = 60 * 60 * 1e3;
4325
- var EXPORT_BEHAVIOR_LIMIT = 30;
4603
+ var DEFAULT_BEHAVIOR_LIMIT = 30;
4604
+ function getBehaviorLimit() {
4605
+ try {
4606
+ const { loadConfigSync: loadConfigSync2 } = (init_config(), __toCommonJS(config_exports));
4607
+ const cfg = loadConfigSync2();
4608
+ return cfg.behaviorExportLimit ?? DEFAULT_BEHAVIOR_LIMIT;
4609
+ } catch {
4610
+ return DEFAULT_BEHAVIOR_LIMIT;
4611
+ }
4612
+ }
4326
4613
  function sweepStaleBehaviorExports(now = Date.now()) {
4327
4614
  if (!existsSync8(BEHAVIORS_EXPORT_DIR)) return;
4328
4615
  let entries;
@@ -4376,7 +4663,7 @@ function exportFilePath(agentId2, projectName2, sessionKey2) {
4376
4663
  async function exportBehaviorsForAgent(agentId2, projectName2, sessionKey2) {
4377
4664
  mkdirSync3(BEHAVIORS_EXPORT_DIR, { recursive: true });
4378
4665
  sweepStaleBehaviorExports();
4379
- const behaviors = await listBehaviors(agentId2, projectName2, EXPORT_BEHAVIOR_LIMIT);
4666
+ const behaviors = await listBehaviors(agentId2, projectName2, getBehaviorLimit());
4380
4667
  if (behaviors.length === 0) return null;
4381
4668
  const body = renderBehaviorExport(behaviors);
4382
4669
  const target = exportFilePath(agentId2, projectName2, sessionKey2);