@askexenow/exe-os 0.9.61 → 0.9.63

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 (103) hide show
  1. package/deploy/stack-manifests/v0.9.json +62 -13
  2. package/dist/bin/backfill-conversations.js +210 -2
  3. package/dist/bin/backfill-responses.js +210 -2
  4. package/dist/bin/backfill-vectors.js +47 -2
  5. package/dist/bin/cleanup-stale-review-tasks.js +210 -2
  6. package/dist/bin/cli.js +488 -25
  7. package/dist/bin/exe-agent-config.js +2 -2
  8. package/dist/bin/exe-agent.js +2 -2
  9. package/dist/bin/exe-assign.js +210 -2
  10. package/dist/bin/exe-boot.js +47 -2
  11. package/dist/bin/exe-call.js +2 -2
  12. package/dist/bin/exe-cloud.js +2 -2
  13. package/dist/bin/exe-dispatch.js +210 -2
  14. package/dist/bin/exe-doctor.js +2691 -1212
  15. package/dist/bin/exe-export-behaviors.js +210 -2
  16. package/dist/bin/exe-forget.js +264 -2
  17. package/dist/bin/exe-gateway.js +210 -2
  18. package/dist/bin/exe-heartbeat.js +212 -4
  19. package/dist/bin/exe-kill.js +210 -2
  20. package/dist/bin/exe-launch-agent.js +210 -2
  21. package/dist/bin/exe-link.js +47 -2
  22. package/dist/bin/exe-new-employee.js +113 -3
  23. package/dist/bin/exe-pending-messages.js +210 -2
  24. package/dist/bin/exe-pending-notifications.js +210 -2
  25. package/dist/bin/exe-pending-reviews.js +210 -2
  26. package/dist/bin/exe-rename.js +210 -2
  27. package/dist/bin/exe-review.js +210 -2
  28. package/dist/bin/exe-search.js +234 -3
  29. package/dist/bin/exe-session-cleanup.js +210 -2
  30. package/dist/bin/exe-settings.js +2 -2
  31. package/dist/bin/exe-start-codex.js +322 -4
  32. package/dist/bin/exe-start-opencode.js +336 -4
  33. package/dist/bin/exe-status.js +210 -2
  34. package/dist/bin/exe-team.js +210 -2
  35. package/dist/bin/git-sweep.js +210 -2
  36. package/dist/bin/graph-backfill.js +210 -2
  37. package/dist/bin/graph-export.js +210 -2
  38. package/dist/bin/install.js +113 -3
  39. package/dist/bin/intercom-check.js +210 -2
  40. package/dist/bin/scan-tasks.js +210 -2
  41. package/dist/bin/setup.js +47 -2
  42. package/dist/bin/shard-migrate.js +210 -2
  43. package/dist/bin/stack-update.js +158 -13
  44. package/dist/bin/update.js +2 -2
  45. package/dist/gateway/index.js +210 -2
  46. package/dist/hooks/bug-report-worker.js +321 -44
  47. package/dist/hooks/codex-stop-task-finalizer.js +210 -2
  48. package/dist/hooks/commit-complete.js +210 -2
  49. package/dist/hooks/error-recall.js +234 -3
  50. package/dist/hooks/exe-heartbeat-hook.js +2 -2
  51. package/dist/hooks/ingest-worker.js +2 -2
  52. package/dist/hooks/ingest.js +210 -2
  53. package/dist/hooks/instructions-loaded.js +210 -2
  54. package/dist/hooks/notification.js +210 -2
  55. package/dist/hooks/post-compact.js +210 -2
  56. package/dist/hooks/post-tool-combined.js +234 -3
  57. package/dist/hooks/pre-compact.js +446 -111
  58. package/dist/hooks/pre-tool-use.js +210 -2
  59. package/dist/hooks/prompt-submit.js +234 -3
  60. package/dist/hooks/session-end.js +347 -124
  61. package/dist/hooks/session-start.js +236 -5
  62. package/dist/hooks/stop.js +210 -2
  63. package/dist/hooks/subagent-stop.js +210 -2
  64. package/dist/hooks/summary-worker.js +171 -74
  65. package/dist/index.js +210 -2
  66. package/dist/lib/agent-config.js +2 -2
  67. package/dist/lib/cloud-sync.js +47 -2
  68. package/dist/lib/config.js +2 -2
  69. package/dist/lib/consolidation.js +2 -2
  70. package/dist/lib/database.js +47 -2
  71. package/dist/lib/db-daemon-client.js +2 -2
  72. package/dist/lib/db.js +47 -2
  73. package/dist/lib/device-registry.js +47 -2
  74. package/dist/lib/embedder.js +2 -2
  75. package/dist/lib/employee-templates.js +2 -2
  76. package/dist/lib/employees.js +2 -2
  77. package/dist/lib/exe-daemon-client.js +2 -2
  78. package/dist/lib/exe-daemon.js +592 -10
  79. package/dist/lib/hybrid-search.js +234 -3
  80. package/dist/lib/identity.js +2 -2
  81. package/dist/lib/license.js +2 -2
  82. package/dist/lib/messaging.js +2 -2
  83. package/dist/lib/reminders.js +2 -2
  84. package/dist/lib/schedules.js +47 -2
  85. package/dist/lib/skill-learning.js +2 -2
  86. package/dist/lib/store.js +210 -2
  87. package/dist/lib/task-router.js +2 -2
  88. package/dist/lib/tasks.js +2 -2
  89. package/dist/lib/tmux-routing.js +2 -2
  90. package/dist/lib/token-spend.js +2 -2
  91. package/dist/mcp/server.js +608 -10
  92. package/dist/mcp/tools/complete-reminder.js +2 -2
  93. package/dist/mcp/tools/create-reminder.js +2 -2
  94. package/dist/mcp/tools/create-task.js +2 -2
  95. package/dist/mcp/tools/deactivate-behavior.js +2 -2
  96. package/dist/mcp/tools/list-reminders.js +2 -2
  97. package/dist/mcp/tools/list-tasks.js +2 -2
  98. package/dist/mcp/tools/send-message.js +2 -2
  99. package/dist/mcp/tools/update-task.js +2 -2
  100. package/dist/runtime/index.js +210 -2
  101. package/dist/tui/App.js +210 -2
  102. package/package.json +3 -2
  103. package/stack.release.json +23 -7
@@ -313,8 +313,8 @@ var init_config = __esm({
313
313
  rerankerAutoTrigger: {
314
314
  enabled: true,
315
315
  broadQueryMinCardinality: 5e4,
316
- fetchTopK: 150,
317
- returnTopK: 5
316
+ fetchTopK: 200,
317
+ returnTopK: 20
318
318
  }
319
319
  },
320
320
  graphRagEnabled: true,
@@ -2820,6 +2820,51 @@ async function ensureSchema() {
2820
2820
  VALUES (new.rowid, new.content_text, new.sender_name, new.agent_response);
2821
2821
  END;
2822
2822
  `);
2823
+ await client.executeMultiple(`
2824
+ CREATE TABLE IF NOT EXISTS memory_cards (
2825
+ id TEXT PRIMARY KEY,
2826
+ memory_id TEXT NOT NULL,
2827
+ agent_id TEXT NOT NULL,
2828
+ session_id TEXT NOT NULL,
2829
+ project_name TEXT,
2830
+ timestamp TEXT NOT NULL,
2831
+ card_type TEXT NOT NULL,
2832
+ subject TEXT,
2833
+ predicate TEXT,
2834
+ object TEXT,
2835
+ content TEXT NOT NULL,
2836
+ source_ref TEXT,
2837
+ confidence REAL DEFAULT 0.6,
2838
+ active INTEGER DEFAULT 1,
2839
+ created_at TEXT NOT NULL
2840
+ );
2841
+
2842
+ CREATE INDEX IF NOT EXISTS idx_memory_cards_agent
2843
+ ON memory_cards(agent_id, active, timestamp);
2844
+
2845
+ CREATE INDEX IF NOT EXISTS idx_memory_cards_memory
2846
+ ON memory_cards(memory_id);
2847
+
2848
+ CREATE VIRTUAL TABLE IF NOT EXISTS memory_cards_fts
2849
+ USING fts5(content, subject, predicate, object, content='memory_cards', content_rowid='rowid');
2850
+
2851
+ CREATE TRIGGER IF NOT EXISTS memory_cards_fts_ai AFTER INSERT ON memory_cards BEGIN
2852
+ INSERT INTO memory_cards_fts(rowid, content, subject, predicate, object)
2853
+ VALUES (new.rowid, new.content, new.subject, new.predicate, new.object);
2854
+ END;
2855
+
2856
+ CREATE TRIGGER IF NOT EXISTS memory_cards_fts_ad AFTER DELETE ON memory_cards BEGIN
2857
+ INSERT INTO memory_cards_fts(memory_cards_fts, rowid, content, subject, predicate, object)
2858
+ VALUES('delete', old.rowid, old.content, old.subject, old.predicate, old.object);
2859
+ END;
2860
+
2861
+ CREATE TRIGGER IF NOT EXISTS memory_cards_fts_au AFTER UPDATE ON memory_cards BEGIN
2862
+ INSERT INTO memory_cards_fts(memory_cards_fts, rowid, content, subject, predicate, object)
2863
+ VALUES('delete', old.rowid, old.content, old.subject, old.predicate, old.object);
2864
+ INSERT INTO memory_cards_fts(rowid, content, subject, predicate, object)
2865
+ VALUES (new.rowid, new.content, new.subject, new.predicate, new.object);
2866
+ END;
2867
+ `);
2823
2868
  try {
2824
2869
  await client.execute({
2825
2870
  sql: `ALTER TABLE memories ADD COLUMN tier INTEGER DEFAULT 3`,
@@ -4109,6 +4154,164 @@ ${p.content}`).join("\n\n");
4109
4154
  }
4110
4155
  });
4111
4156
 
4157
+ // src/lib/memory-cards.ts
4158
+ var memory_cards_exports = {};
4159
+ __export(memory_cards_exports, {
4160
+ extractMemoryCards: () => extractMemoryCards,
4161
+ insertMemoryCardsForBatch: () => insertMemoryCardsForBatch,
4162
+ searchMemoryCards: () => searchMemoryCards
4163
+ });
4164
+ import { createHash as createHash2 } from "crypto";
4165
+ function stableId(memoryId, type, content) {
4166
+ return createHash2("sha256").update(`${memoryId}:${type}:${content}`).digest("hex").slice(0, 32);
4167
+ }
4168
+ function cleanText(text) {
4169
+ return text.replace(/```[\s\S]*?```/g, " ").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
4170
+ }
4171
+ function splitSentences(text) {
4172
+ return cleanText(text).split(/(?<=[.!?])\s+|\n+/).map((s) => s.trim()).filter((s) => s.length >= 24 && s.length <= MAX_SENTENCE_CHARS);
4173
+ }
4174
+ function inferCardType(sentence, toolName) {
4175
+ const lower = sentence.toLowerCase();
4176
+ if (toolName === "store_decision" || /\b(decided|decision|adr|approved|rejected)\b/.test(lower)) return "decision";
4177
+ if (/\b(prefers|preference|likes|dislikes|wants|doesn't want|does not want)\b/.test(lower)) return "preference";
4178
+ if (/\b(changed|updated|replaced|now|no longer|instead|supersedes)\b/.test(lower)) return "belief_update";
4179
+ if (toolName && ["Read", "Write", "Edit", "Bash"].includes(toolName)) return "code";
4180
+ if (/\b(meeting|deadline|shipped|launched|completed|failed|blocked|assigned|created)\b/.test(lower)) return "event";
4181
+ return "fact";
4182
+ }
4183
+ function extractSubject(sentence, agentId) {
4184
+ const explicit = sentence.match(/\b([A-Z][a-zA-Z0-9_-]{2,}(?:\s+[A-Z][a-zA-Z0-9_-]{2,})?)\b/);
4185
+ return explicit?.[1] ?? agentId;
4186
+ }
4187
+ function predicateFor(type) {
4188
+ switch (type) {
4189
+ case "preference":
4190
+ return "prefers";
4191
+ case "belief_update":
4192
+ return "updated";
4193
+ case "decision":
4194
+ return "decided";
4195
+ case "event":
4196
+ return "happened";
4197
+ case "code":
4198
+ return "implemented";
4199
+ default:
4200
+ return "states";
4201
+ }
4202
+ }
4203
+ function extractMemoryCards(row) {
4204
+ const sentences = splitSentences(row.raw_text);
4205
+ const cards = [];
4206
+ for (const sentence of sentences) {
4207
+ const type = inferCardType(sentence, row.tool_name);
4208
+ const subject = extractSubject(sentence, row.agent_id);
4209
+ const content = sentence.length > MAX_SENTENCE_CHARS ? `${sentence.slice(0, MAX_SENTENCE_CHARS - 1)}\u2026` : sentence;
4210
+ cards.push({
4211
+ id: stableId(row.id, type, content),
4212
+ memory_id: row.id,
4213
+ agent_id: row.agent_id,
4214
+ session_id: row.session_id,
4215
+ project_name: row.project_name ?? null,
4216
+ timestamp: row.timestamp,
4217
+ card_type: type,
4218
+ subject,
4219
+ predicate: predicateFor(type),
4220
+ object: content,
4221
+ content,
4222
+ source_ref: row.id,
4223
+ confidence: type === "fact" ? 0.55 : 0.65
4224
+ });
4225
+ if (cards.length >= MAX_CARDS_PER_MEMORY) break;
4226
+ }
4227
+ return cards;
4228
+ }
4229
+ async function insertMemoryCardsForBatch(rows) {
4230
+ const cards = rows.flatMap(extractMemoryCards);
4231
+ if (cards.length === 0) return 0;
4232
+ const now = (/* @__PURE__ */ new Date()).toISOString();
4233
+ const client = getClient();
4234
+ const stmts = cards.map((card) => ({
4235
+ sql: `INSERT OR IGNORE INTO memory_cards
4236
+ (id, memory_id, agent_id, session_id, project_name, timestamp, card_type,
4237
+ subject, predicate, object, content, source_ref, confidence, active, created_at)
4238
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?)`,
4239
+ args: [
4240
+ card.id,
4241
+ card.memory_id,
4242
+ card.agent_id,
4243
+ card.session_id,
4244
+ card.project_name,
4245
+ card.timestamp,
4246
+ card.card_type,
4247
+ card.subject,
4248
+ card.predicate,
4249
+ card.object,
4250
+ card.content,
4251
+ card.source_ref,
4252
+ card.confidence,
4253
+ now
4254
+ ]
4255
+ }));
4256
+ await client.batch(stmts, "write");
4257
+ return cards.length;
4258
+ }
4259
+ function buildMatchExpr(queryText) {
4260
+ const terms = queryText.toLowerCase().split(/\s+/).filter((t) => t.length >= 3).map((t) => t.replace(/[^a-z0-9_]/g, "")).filter((t) => t.length >= 3).slice(0, 12);
4261
+ if (terms.length === 0) return null;
4262
+ return terms.map((t) => `${t}*`).join(terms.length >= 3 ? " AND " : " OR ");
4263
+ }
4264
+ async function searchMemoryCards(queryText, agentId, options) {
4265
+ const limit = options?.limit ?? 10;
4266
+ const matchExpr = buildMatchExpr(queryText);
4267
+ if (!matchExpr) return [];
4268
+ let sql = `SELECT c.id, c.memory_id, c.agent_id, c.session_id, c.project_name,
4269
+ c.timestamp, c.card_type, c.content, c.source_ref, c.confidence
4270
+ FROM memory_cards c
4271
+ JOIN memory_cards_fts fts ON c.rowid = fts.rowid
4272
+ WHERE memory_cards_fts MATCH ?
4273
+ AND c.agent_id = ?
4274
+ AND COALESCE(c.active, 1) = 1`;
4275
+ const args = [matchExpr, agentId];
4276
+ if (options?.projectName) {
4277
+ sql += ` AND c.project_name = ?`;
4278
+ args.push(options.projectName);
4279
+ }
4280
+ if (options?.since) {
4281
+ sql += ` AND c.timestamp >= ?`;
4282
+ args.push(options.since);
4283
+ }
4284
+ sql += ` ORDER BY rank LIMIT ?`;
4285
+ args.push(limit);
4286
+ const result = await getClient().execute({ sql, args });
4287
+ return result.rows.map((row) => ({
4288
+ id: `card:${String(row.id)}`,
4289
+ agent_id: String(row.agent_id),
4290
+ agent_role: "memory_card",
4291
+ session_id: String(row.session_id),
4292
+ timestamp: String(row.timestamp),
4293
+ tool_name: `memory_card:${String(row.card_type)}`,
4294
+ project_name: row.project_name == null ? "" : String(row.project_name),
4295
+ has_error: false,
4296
+ raw_text: `[${String(row.card_type)}] ${String(row.content)}
4297
+ Source memory: ${String(row.source_ref ?? row.memory_id)}`,
4298
+ vector: [],
4299
+ importance: 6,
4300
+ status: "active",
4301
+ confidence: Number(row.confidence ?? 0.6),
4302
+ last_accessed: String(row.timestamp)
4303
+ }));
4304
+ }
4305
+ var MAX_CARDS_PER_MEMORY, MAX_SENTENCE_CHARS;
4306
+ var init_memory_cards = __esm({
4307
+ "src/lib/memory-cards.ts"() {
4308
+ "use strict";
4309
+ init_database();
4310
+ MAX_CARDS_PER_MEMORY = 6;
4311
+ MAX_SENTENCE_CHARS = 360;
4312
+ }
4313
+ });
4314
+
4112
4315
  // src/lib/store.ts
4113
4316
  var store_exports = {};
4114
4317
  __export(store_exports, {
@@ -4447,6 +4650,11 @@ async function flushBatch() {
4447
4650
  const globalClient = getClient();
4448
4651
  const globalStmts = batch.map(buildStmt);
4449
4652
  await globalClient.batch(globalStmts, "write");
4653
+ try {
4654
+ const { insertMemoryCardsForBatch: insertMemoryCardsForBatch2 } = await Promise.resolve().then(() => (init_memory_cards(), memory_cards_exports));
4655
+ await insertMemoryCardsForBatch2(batch);
4656
+ } catch {
4657
+ }
4450
4658
  schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
4451
4659
  _pendingRecords.splice(0, batch.length);
4452
4660
  try {
@@ -562,8 +562,8 @@ var init_config = __esm({
562
562
  rerankerAutoTrigger: {
563
563
  enabled: true,
564
564
  broadQueryMinCardinality: 5e4,
565
- fetchTopK: 150,
566
- returnTopK: 5
565
+ fetchTopK: 200,
566
+ returnTopK: 20
567
567
  }
568
568
  },
569
569
  graphRagEnabled: true,
@@ -3312,6 +3312,51 @@ async function ensureSchema() {
3312
3312
  VALUES (new.rowid, new.content_text, new.sender_name, new.agent_response);
3313
3313
  END;
3314
3314
  `);
3315
+ await client.executeMultiple(`
3316
+ CREATE TABLE IF NOT EXISTS memory_cards (
3317
+ id TEXT PRIMARY KEY,
3318
+ memory_id TEXT NOT NULL,
3319
+ agent_id TEXT NOT NULL,
3320
+ session_id TEXT NOT NULL,
3321
+ project_name TEXT,
3322
+ timestamp TEXT NOT NULL,
3323
+ card_type TEXT NOT NULL,
3324
+ subject TEXT,
3325
+ predicate TEXT,
3326
+ object TEXT,
3327
+ content TEXT NOT NULL,
3328
+ source_ref TEXT,
3329
+ confidence REAL DEFAULT 0.6,
3330
+ active INTEGER DEFAULT 1,
3331
+ created_at TEXT NOT NULL
3332
+ );
3333
+
3334
+ CREATE INDEX IF NOT EXISTS idx_memory_cards_agent
3335
+ ON memory_cards(agent_id, active, timestamp);
3336
+
3337
+ CREATE INDEX IF NOT EXISTS idx_memory_cards_memory
3338
+ ON memory_cards(memory_id);
3339
+
3340
+ CREATE VIRTUAL TABLE IF NOT EXISTS memory_cards_fts
3341
+ USING fts5(content, subject, predicate, object, content='memory_cards', content_rowid='rowid');
3342
+
3343
+ CREATE TRIGGER IF NOT EXISTS memory_cards_fts_ai AFTER INSERT ON memory_cards BEGIN
3344
+ INSERT INTO memory_cards_fts(rowid, content, subject, predicate, object)
3345
+ VALUES (new.rowid, new.content, new.subject, new.predicate, new.object);
3346
+ END;
3347
+
3348
+ CREATE TRIGGER IF NOT EXISTS memory_cards_fts_ad AFTER DELETE ON memory_cards BEGIN
3349
+ INSERT INTO memory_cards_fts(memory_cards_fts, rowid, content, subject, predicate, object)
3350
+ VALUES('delete', old.rowid, old.content, old.subject, old.predicate, old.object);
3351
+ END;
3352
+
3353
+ CREATE TRIGGER IF NOT EXISTS memory_cards_fts_au AFTER UPDATE ON memory_cards BEGIN
3354
+ INSERT INTO memory_cards_fts(memory_cards_fts, rowid, content, subject, predicate, object)
3355
+ VALUES('delete', old.rowid, old.content, old.subject, old.predicate, old.object);
3356
+ INSERT INTO memory_cards_fts(rowid, content, subject, predicate, object)
3357
+ VALUES (new.rowid, new.content, new.subject, new.predicate, new.object);
3358
+ END;
3359
+ `);
3315
3360
  try {
3316
3361
  await client.execute({
3317
3362
  sql: `ALTER TABLE memories ADD COLUMN tier INTEGER DEFAULT 3`,
@@ -7787,6 +7832,164 @@ ${p.content}`).join("\n\n");
7787
7832
  }
7788
7833
  });
7789
7834
 
7835
+ // src/lib/memory-cards.ts
7836
+ var memory_cards_exports = {};
7837
+ __export(memory_cards_exports, {
7838
+ extractMemoryCards: () => extractMemoryCards,
7839
+ insertMemoryCardsForBatch: () => insertMemoryCardsForBatch,
7840
+ searchMemoryCards: () => searchMemoryCards
7841
+ });
7842
+ import { createHash as createHash2 } from "crypto";
7843
+ function stableId(memoryId, type, content) {
7844
+ return createHash2("sha256").update(`${memoryId}:${type}:${content}`).digest("hex").slice(0, 32);
7845
+ }
7846
+ function cleanText(text) {
7847
+ return text.replace(/```[\s\S]*?```/g, " ").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
7848
+ }
7849
+ function splitSentences(text) {
7850
+ return cleanText(text).split(/(?<=[.!?])\s+|\n+/).map((s) => s.trim()).filter((s) => s.length >= 24 && s.length <= MAX_SENTENCE_CHARS);
7851
+ }
7852
+ function inferCardType(sentence, toolName) {
7853
+ const lower = sentence.toLowerCase();
7854
+ if (toolName === "store_decision" || /\b(decided|decision|adr|approved|rejected)\b/.test(lower)) return "decision";
7855
+ if (/\b(prefers|preference|likes|dislikes|wants|doesn't want|does not want)\b/.test(lower)) return "preference";
7856
+ if (/\b(changed|updated|replaced|now|no longer|instead|supersedes)\b/.test(lower)) return "belief_update";
7857
+ if (toolName && ["Read", "Write", "Edit", "Bash"].includes(toolName)) return "code";
7858
+ if (/\b(meeting|deadline|shipped|launched|completed|failed|blocked|assigned|created)\b/.test(lower)) return "event";
7859
+ return "fact";
7860
+ }
7861
+ function extractSubject(sentence, agentId) {
7862
+ const explicit = sentence.match(/\b([A-Z][a-zA-Z0-9_-]{2,}(?:\s+[A-Z][a-zA-Z0-9_-]{2,})?)\b/);
7863
+ return explicit?.[1] ?? agentId;
7864
+ }
7865
+ function predicateFor(type) {
7866
+ switch (type) {
7867
+ case "preference":
7868
+ return "prefers";
7869
+ case "belief_update":
7870
+ return "updated";
7871
+ case "decision":
7872
+ return "decided";
7873
+ case "event":
7874
+ return "happened";
7875
+ case "code":
7876
+ return "implemented";
7877
+ default:
7878
+ return "states";
7879
+ }
7880
+ }
7881
+ function extractMemoryCards(row) {
7882
+ const sentences = splitSentences(row.raw_text);
7883
+ const cards = [];
7884
+ for (const sentence of sentences) {
7885
+ const type = inferCardType(sentence, row.tool_name);
7886
+ const subject = extractSubject(sentence, row.agent_id);
7887
+ const content = sentence.length > MAX_SENTENCE_CHARS ? `${sentence.slice(0, MAX_SENTENCE_CHARS - 1)}\u2026` : sentence;
7888
+ cards.push({
7889
+ id: stableId(row.id, type, content),
7890
+ memory_id: row.id,
7891
+ agent_id: row.agent_id,
7892
+ session_id: row.session_id,
7893
+ project_name: row.project_name ?? null,
7894
+ timestamp: row.timestamp,
7895
+ card_type: type,
7896
+ subject,
7897
+ predicate: predicateFor(type),
7898
+ object: content,
7899
+ content,
7900
+ source_ref: row.id,
7901
+ confidence: type === "fact" ? 0.55 : 0.65
7902
+ });
7903
+ if (cards.length >= MAX_CARDS_PER_MEMORY) break;
7904
+ }
7905
+ return cards;
7906
+ }
7907
+ async function insertMemoryCardsForBatch(rows) {
7908
+ const cards = rows.flatMap(extractMemoryCards);
7909
+ if (cards.length === 0) return 0;
7910
+ const now = (/* @__PURE__ */ new Date()).toISOString();
7911
+ const client = getClient();
7912
+ const stmts = cards.map((card) => ({
7913
+ sql: `INSERT OR IGNORE INTO memory_cards
7914
+ (id, memory_id, agent_id, session_id, project_name, timestamp, card_type,
7915
+ subject, predicate, object, content, source_ref, confidence, active, created_at)
7916
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?)`,
7917
+ args: [
7918
+ card.id,
7919
+ card.memory_id,
7920
+ card.agent_id,
7921
+ card.session_id,
7922
+ card.project_name,
7923
+ card.timestamp,
7924
+ card.card_type,
7925
+ card.subject,
7926
+ card.predicate,
7927
+ card.object,
7928
+ card.content,
7929
+ card.source_ref,
7930
+ card.confidence,
7931
+ now
7932
+ ]
7933
+ }));
7934
+ await client.batch(stmts, "write");
7935
+ return cards.length;
7936
+ }
7937
+ function buildMatchExpr(queryText) {
7938
+ const terms = queryText.toLowerCase().split(/\s+/).filter((t) => t.length >= 3).map((t) => t.replace(/[^a-z0-9_]/g, "")).filter((t) => t.length >= 3).slice(0, 12);
7939
+ if (terms.length === 0) return null;
7940
+ return terms.map((t) => `${t}*`).join(terms.length >= 3 ? " AND " : " OR ");
7941
+ }
7942
+ async function searchMemoryCards(queryText, agentId, options) {
7943
+ const limit = options?.limit ?? 10;
7944
+ const matchExpr = buildMatchExpr(queryText);
7945
+ if (!matchExpr) return [];
7946
+ let sql = `SELECT c.id, c.memory_id, c.agent_id, c.session_id, c.project_name,
7947
+ c.timestamp, c.card_type, c.content, c.source_ref, c.confidence
7948
+ FROM memory_cards c
7949
+ JOIN memory_cards_fts fts ON c.rowid = fts.rowid
7950
+ WHERE memory_cards_fts MATCH ?
7951
+ AND c.agent_id = ?
7952
+ AND COALESCE(c.active, 1) = 1`;
7953
+ const args = [matchExpr, agentId];
7954
+ if (options?.projectName) {
7955
+ sql += ` AND c.project_name = ?`;
7956
+ args.push(options.projectName);
7957
+ }
7958
+ if (options?.since) {
7959
+ sql += ` AND c.timestamp >= ?`;
7960
+ args.push(options.since);
7961
+ }
7962
+ sql += ` ORDER BY rank LIMIT ?`;
7963
+ args.push(limit);
7964
+ const result = await getClient().execute({ sql, args });
7965
+ return result.rows.map((row) => ({
7966
+ id: `card:${String(row.id)}`,
7967
+ agent_id: String(row.agent_id),
7968
+ agent_role: "memory_card",
7969
+ session_id: String(row.session_id),
7970
+ timestamp: String(row.timestamp),
7971
+ tool_name: `memory_card:${String(row.card_type)}`,
7972
+ project_name: row.project_name == null ? "" : String(row.project_name),
7973
+ has_error: false,
7974
+ raw_text: `[${String(row.card_type)}] ${String(row.content)}
7975
+ Source memory: ${String(row.source_ref ?? row.memory_id)}`,
7976
+ vector: [],
7977
+ importance: 6,
7978
+ status: "active",
7979
+ confidence: Number(row.confidence ?? 0.6),
7980
+ last_accessed: String(row.timestamp)
7981
+ }));
7982
+ }
7983
+ var MAX_CARDS_PER_MEMORY, MAX_SENTENCE_CHARS;
7984
+ var init_memory_cards = __esm({
7985
+ "src/lib/memory-cards.ts"() {
7986
+ "use strict";
7987
+ init_database();
7988
+ MAX_CARDS_PER_MEMORY = 6;
7989
+ MAX_SENTENCE_CHARS = 360;
7990
+ }
7991
+ });
7992
+
7790
7993
  // src/lib/store.ts
7791
7994
  var store_exports = {};
7792
7995
  __export(store_exports, {
@@ -8125,6 +8328,11 @@ async function flushBatch() {
8125
8328
  const globalClient = getClient();
8126
8329
  const globalStmts = batch.map(buildStmt);
8127
8330
  await globalClient.batch(globalStmts, "write");
8331
+ try {
8332
+ const { insertMemoryCardsForBatch: insertMemoryCardsForBatch2 } = await Promise.resolve().then(() => (init_memory_cards(), memory_cards_exports));
8333
+ await insertMemoryCardsForBatch2(batch);
8334
+ } catch {
8335
+ }
8128
8336
  schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
8129
8337
  _pendingRecords.splice(0, batch.length);
8130
8338
  try {
package/dist/bin/setup.js CHANGED
@@ -247,8 +247,8 @@ var init_config = __esm({
247
247
  rerankerAutoTrigger: {
248
248
  enabled: true,
249
249
  broadQueryMinCardinality: 5e4,
250
- fetchTopK: 150,
251
- returnTopK: 5
250
+ fetchTopK: 200,
251
+ returnTopK: 20
252
252
  }
253
253
  },
254
254
  graphRagEnabled: true,
@@ -3959,6 +3959,51 @@ async function ensureSchema() {
3959
3959
  VALUES (new.rowid, new.content_text, new.sender_name, new.agent_response);
3960
3960
  END;
3961
3961
  `);
3962
+ await client.executeMultiple(`
3963
+ CREATE TABLE IF NOT EXISTS memory_cards (
3964
+ id TEXT PRIMARY KEY,
3965
+ memory_id TEXT NOT NULL,
3966
+ agent_id TEXT NOT NULL,
3967
+ session_id TEXT NOT NULL,
3968
+ project_name TEXT,
3969
+ timestamp TEXT NOT NULL,
3970
+ card_type TEXT NOT NULL,
3971
+ subject TEXT,
3972
+ predicate TEXT,
3973
+ object TEXT,
3974
+ content TEXT NOT NULL,
3975
+ source_ref TEXT,
3976
+ confidence REAL DEFAULT 0.6,
3977
+ active INTEGER DEFAULT 1,
3978
+ created_at TEXT NOT NULL
3979
+ );
3980
+
3981
+ CREATE INDEX IF NOT EXISTS idx_memory_cards_agent
3982
+ ON memory_cards(agent_id, active, timestamp);
3983
+
3984
+ CREATE INDEX IF NOT EXISTS idx_memory_cards_memory
3985
+ ON memory_cards(memory_id);
3986
+
3987
+ CREATE VIRTUAL TABLE IF NOT EXISTS memory_cards_fts
3988
+ USING fts5(content, subject, predicate, object, content='memory_cards', content_rowid='rowid');
3989
+
3990
+ CREATE TRIGGER IF NOT EXISTS memory_cards_fts_ai AFTER INSERT ON memory_cards BEGIN
3991
+ INSERT INTO memory_cards_fts(rowid, content, subject, predicate, object)
3992
+ VALUES (new.rowid, new.content, new.subject, new.predicate, new.object);
3993
+ END;
3994
+
3995
+ CREATE TRIGGER IF NOT EXISTS memory_cards_fts_ad AFTER DELETE ON memory_cards BEGIN
3996
+ INSERT INTO memory_cards_fts(memory_cards_fts, rowid, content, subject, predicate, object)
3997
+ VALUES('delete', old.rowid, old.content, old.subject, old.predicate, old.object);
3998
+ END;
3999
+
4000
+ CREATE TRIGGER IF NOT EXISTS memory_cards_fts_au AFTER UPDATE ON memory_cards BEGIN
4001
+ INSERT INTO memory_cards_fts(memory_cards_fts, rowid, content, subject, predicate, object)
4002
+ VALUES('delete', old.rowid, old.content, old.subject, old.predicate, old.object);
4003
+ INSERT INTO memory_cards_fts(rowid, content, subject, predicate, object)
4004
+ VALUES (new.rowid, new.content, new.subject, new.predicate, new.object);
4005
+ END;
4006
+ `);
3962
4007
  try {
3963
4008
  await client.execute({
3964
4009
  sql: `ALTER TABLE memories ADD COLUMN tier INTEGER DEFAULT 3`,