@askexenow/exe-os 0.9.60 → 0.9.62

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 (106) hide show
  1. package/deploy/stack-manifests/v0.9.json +62 -13
  2. package/dist/bin/backfill-conversations.js +282 -7
  3. package/dist/bin/backfill-responses.js +282 -7
  4. package/dist/bin/backfill-vectors.js +119 -7
  5. package/dist/bin/cc-doctor.js +376 -0
  6. package/dist/bin/cleanup-stale-review-tasks.js +282 -7
  7. package/dist/bin/cli.js +455 -77
  8. package/dist/bin/customer-readiness.js +33 -0
  9. package/dist/bin/exe-agent-config.js +2 -2
  10. package/dist/bin/exe-agent.js +3 -3
  11. package/dist/bin/exe-assign.js +282 -7
  12. package/dist/bin/exe-boot.js +125 -13
  13. package/dist/bin/exe-call.js +3 -3
  14. package/dist/bin/exe-cloud.js +2 -2
  15. package/dist/bin/exe-dispatch.js +282 -7
  16. package/dist/bin/exe-doctor.js +119 -7
  17. package/dist/bin/exe-export-behaviors.js +282 -7
  18. package/dist/bin/exe-forget.js +336 -7
  19. package/dist/bin/exe-gateway.js +282 -7
  20. package/dist/bin/exe-heartbeat.js +284 -9
  21. package/dist/bin/exe-kill.js +282 -7
  22. package/dist/bin/exe-launch-agent.js +282 -7
  23. package/dist/bin/exe-link.js +121 -9
  24. package/dist/bin/exe-new-employee.js +50 -21
  25. package/dist/bin/exe-pending-messages.js +282 -7
  26. package/dist/bin/exe-pending-notifications.js +282 -7
  27. package/dist/bin/exe-pending-reviews.js +282 -7
  28. package/dist/bin/exe-rename.js +282 -7
  29. package/dist/bin/exe-review.js +282 -7
  30. package/dist/bin/exe-search.js +306 -8
  31. package/dist/bin/exe-session-cleanup.js +282 -7
  32. package/dist/bin/exe-settings.js +2 -2
  33. package/dist/bin/exe-start-codex.js +326 -21
  34. package/dist/bin/exe-start-opencode.js +304 -10
  35. package/dist/bin/exe-status.js +282 -7
  36. package/dist/bin/exe-team.js +282 -7
  37. package/dist/bin/git-sweep.js +282 -7
  38. package/dist/bin/graph-backfill.js +282 -7
  39. package/dist/bin/graph-export.js +282 -7
  40. package/dist/bin/install.js +58 -33
  41. package/dist/bin/intercom-check.js +282 -7
  42. package/dist/bin/pre-build-guard.js +98 -0
  43. package/dist/bin/scan-tasks.js +282 -7
  44. package/dist/bin/setup.js +122 -10
  45. package/dist/bin/shard-migrate.js +282 -7
  46. package/dist/bin/stack-update.js +79 -11
  47. package/dist/bin/update.js +2 -2
  48. package/dist/gateway/index.js +288 -13
  49. package/dist/hooks/bug-report-worker.js +282 -7
  50. package/dist/hooks/codex-stop-task-finalizer.js +282 -7
  51. package/dist/hooks/commit-complete.js +282 -7
  52. package/dist/hooks/error-recall.js +306 -8
  53. package/dist/hooks/exe-heartbeat-hook.js +2 -2
  54. package/dist/hooks/ingest-worker.js +2 -2
  55. package/dist/hooks/ingest.js +282 -7
  56. package/dist/hooks/instructions-loaded.js +282 -7
  57. package/dist/hooks/notification.js +282 -7
  58. package/dist/hooks/post-compact.js +282 -7
  59. package/dist/hooks/post-tool-combined.js +306 -8
  60. package/dist/hooks/pre-compact.js +282 -7
  61. package/dist/hooks/pre-tool-use.js +282 -7
  62. package/dist/hooks/prompt-submit.js +306 -8
  63. package/dist/hooks/session-end.js +282 -7
  64. package/dist/hooks/session-start.js +308 -10
  65. package/dist/hooks/stop.js +282 -7
  66. package/dist/hooks/subagent-stop.js +282 -7
  67. package/dist/hooks/summary-worker.js +125 -13
  68. package/dist/index.js +288 -13
  69. package/dist/lib/agent-config.js +2 -2
  70. package/dist/lib/cloud-sync.js +121 -9
  71. package/dist/lib/config.js +2 -2
  72. package/dist/lib/consolidation.js +2 -2
  73. package/dist/lib/database.js +115 -3
  74. package/dist/lib/db-daemon-client.js +2 -2
  75. package/dist/lib/db.js +115 -3
  76. package/dist/lib/device-registry.js +115 -3
  77. package/dist/lib/embedder.js +2 -2
  78. package/dist/lib/employee-templates.js +3 -3
  79. package/dist/lib/employees.js +2 -2
  80. package/dist/lib/exe-daemon-client.js +2 -2
  81. package/dist/lib/exe-daemon.js +339 -31
  82. package/dist/lib/hybrid-search.js +306 -8
  83. package/dist/lib/identity.js +2 -2
  84. package/dist/lib/license.js +2 -2
  85. package/dist/lib/messaging.js +2 -2
  86. package/dist/lib/reminders.js +2 -2
  87. package/dist/lib/schedules.js +119 -7
  88. package/dist/lib/skill-learning.js +2 -2
  89. package/dist/lib/store.js +282 -7
  90. package/dist/lib/task-router.js +2 -2
  91. package/dist/lib/tasks.js +2 -2
  92. package/dist/lib/tmux-routing.js +2 -2
  93. package/dist/lib/token-spend.js +2 -2
  94. package/dist/mcp/server.js +339 -31
  95. package/dist/mcp/tools/complete-reminder.js +2 -2
  96. package/dist/mcp/tools/create-reminder.js +2 -2
  97. package/dist/mcp/tools/create-task.js +2 -2
  98. package/dist/mcp/tools/deactivate-behavior.js +2 -2
  99. package/dist/mcp/tools/list-reminders.js +2 -2
  100. package/dist/mcp/tools/list-tasks.js +2 -2
  101. package/dist/mcp/tools/send-message.js +2 -2
  102. package/dist/mcp/tools/update-task.js +2 -2
  103. package/dist/runtime/index.js +282 -7
  104. package/dist/tui/App.js +290 -15
  105. package/package.json +3 -2
  106. package/stack.release.json +23 -7
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "latest": "0.9.0",
3
+ "latest": "0.9.1",
4
4
  "stacks": {
5
5
  "0.9.0": {
6
6
  "version": "0.9.0",
7
7
  "releasedAt": "2026-05-10T00:00:00Z",
8
- "notes": "Initial omnibus stack manifest for self-hosted Exe OS v0.9 customer VPS deployments.",
8
+ "notes": "Private/customer pilot omnibus stack manifest for self-hosted Exe OS v0.9 VPS deployments. v1.0.0 is the public-beta/stable contract line.",
9
9
  "breakingChanges": [
10
10
  {
11
11
  "id": "whatsapp_relink_required",
@@ -19,36 +19,85 @@
19
19
  "services": {
20
20
  "crm": {
21
21
  "env": "CRM_IMAGE_TAG",
22
- "image": "ghcr.io/askexe/exe-crm:v0.2.1",
22
+ "image": "ghcr.io/askexe/exe-crm:v0.9.0",
23
23
  "healthUrl": "http://127.0.0.1:3000/healthz"
24
24
  },
25
25
  "wiki": {
26
26
  "env": "WIKI_IMAGE_TAG",
27
- "image": "ghcr.io/askexe/exe-wiki:v1.11.1",
27
+ "image": "ghcr.io/askexe/exe-wiki:v0.9.0",
28
28
  "healthUrl": "http://127.0.0.1:3001/api/ping"
29
29
  },
30
30
  "exed": {
31
31
  "env": "EXED_IMAGE_TAG",
32
- "image": "ghcr.io/askexe/exed:v0.9.37",
32
+ "image": "ghcr.io/askexe/exed:v0.9.61",
33
33
  "healthUrl": "http://127.0.0.1:8765/health"
34
34
  },
35
35
  "gateway": {
36
36
  "env": "GATEWAY_IMAGE_TAG",
37
- "image": "ghcr.io/askexe/exe-gateway:v0.3.0",
37
+ "image": "ghcr.io/askexe/exe-gateway:v0.9.0",
38
38
  "healthUrl": "http://127.0.0.1:3100/health"
39
39
  },
40
40
  "monitorAgent": {
41
41
  "env": "MONITOR_AGENT_IMAGE_TAG",
42
- "image": "ghcr.io/askexe/exe-monitor-agent:v0.1.0"
42
+ "image": "ghcr.io/askexe/exe-monitor-agent:v0.9.0"
43
43
  }
44
44
  },
45
45
  "releaseDescriptors": {
46
- "exed": "AskExe/exe-os@0.9.37:stack.release.json",
47
- "crm": "AskExe/exe-crm@0.2.1:stack.release.json",
48
- "wiki": "AskExe/exe-wiki@1.11.1:stack.release.json",
49
- "gateway": "AskExe/exe-gateway@0.3.0:stack.release.json",
50
- "db": "AskExe/exe-db@0.1.0:stack.release.json",
51
- "monitorAgent": "AskExe/exe-monitor@0.1.0:stack.release.json"
46
+ "exed": "AskExe/exe-os@0.9.61:stack.release.json",
47
+ "crm": "AskExe/exe-crm@0.9.0:stack.release.json",
48
+ "wiki": "AskExe/exe-wiki@0.9.0:stack.release.json",
49
+ "gateway": "AskExe/exe-gateway@0.9.0:stack.release.json",
50
+ "db": "AskExe/exe-db@0.9.0:stack.release.json",
51
+ "monitorAgent": "AskExe/exe-monitor@0.9.0:stack.release.json"
52
+ }
53
+ },
54
+ "0.9.1": {
55
+ "version": "0.9.1",
56
+ "releasedAt": "2026-05-10T00:00:00Z",
57
+ "notes": "High Ghost private/customer pilot stack release. Pins exact tested service images; v1.0.0 remains the public-beta/stable contract line.",
58
+ "breakingChanges": [
59
+ {
60
+ "id": "whatsapp_relink_required",
61
+ "title": "WhatsApp QR re-link required for Baileys v7",
62
+ "description": "exe-gateway uses Baileys v7. Existing WhatsApp 6.x linked-device auth state must be backed up and re-linked once with a new QR code.",
63
+ "requiredAction": "Open https://<gateway-domain>/pair/default?token=<admin-token> and scan from WhatsApp \u2192 Linked Devices after the update.",
64
+ "expectedDowntimeMinutes": "2-5",
65
+ "requiresConfirmation": true
66
+ }
67
+ ],
68
+ "services": {
69
+ "crm": {
70
+ "env": "CRM_IMAGE_TAG",
71
+ "image": "ghcr.io/askexe/exe-crm:v0.9.1",
72
+ "healthUrl": "http://127.0.0.1:3000/healthz"
73
+ },
74
+ "wiki": {
75
+ "env": "WIKI_IMAGE_TAG",
76
+ "image": "ghcr.io/askexe/exe-wiki:v0.9.1",
77
+ "healthUrl": "http://127.0.0.1:3001/api/ping"
78
+ },
79
+ "exed": {
80
+ "env": "EXED_IMAGE_TAG",
81
+ "image": "ghcr.io/askexe/exed:v0.9.61",
82
+ "healthUrl": "http://127.0.0.1:8765/health"
83
+ },
84
+ "gateway": {
85
+ "env": "GATEWAY_IMAGE_TAG",
86
+ "image": "ghcr.io/askexe/exe-gateway:v0.9.1",
87
+ "healthUrl": "http://127.0.0.1:3100/health"
88
+ },
89
+ "monitorAgent": {
90
+ "env": "MONITOR_AGENT_IMAGE_TAG",
91
+ "image": "ghcr.io/askexe/exe-monitor-agent:v0.9.1"
92
+ }
93
+ },
94
+ "releaseDescriptors": {
95
+ "exed": "AskExe/exe-os@0.9.61:stack.release.json",
96
+ "crm": "AskExe/exe-crm@0.9.1:stack.release.json",
97
+ "wiki": "AskExe/exe-wiki@0.9.1:stack.release.json",
98
+ "gateway": "AskExe/exe-gateway@0.9.1:stack.release.json",
99
+ "db": "AskExe/exe-db@0.9.1:stack.release.json",
100
+ "monitorAgent": "AskExe/exe-monitor@0.9.1:stack.release.json"
52
101
  }
53
102
  }
54
103
  }
@@ -256,8 +256,8 @@ var init_config = __esm({
256
256
  rerankerAutoTrigger: {
257
257
  enabled: true,
258
258
  broadQueryMinCardinality: 5e4,
259
- fetchTopK: 150,
260
- returnTopK: 5
259
+ fetchTopK: 200,
260
+ returnTopK: 20
261
261
  }
262
262
  },
263
263
  graphRagEnabled: true,
@@ -2429,7 +2429,7 @@ async function ensureSchema() {
2429
2429
  ON session_kills(agent_id);
2430
2430
  `);
2431
2431
  await client.execute(`
2432
- CREATE TABLE IF NOT EXISTS global_procedures (
2432
+ CREATE TABLE IF NOT EXISTS company_procedures (
2433
2433
  id TEXT PRIMARY KEY,
2434
2434
  title TEXT NOT NULL,
2435
2435
  content TEXT NOT NULL,
@@ -2440,6 +2440,73 @@ async function ensureSchema() {
2440
2440
  updated_at TEXT NOT NULL
2441
2441
  )
2442
2442
  `);
2443
+ const legacyProcedureObject = await client.execute({
2444
+ sql: "SELECT type FROM sqlite_master WHERE name = 'global_procedures'",
2445
+ args: []
2446
+ });
2447
+ const legacyProcedureType = legacyProcedureObject.rows[0]?.type == null ? null : String(legacyProcedureObject.rows[0].type);
2448
+ if (legacyProcedureType === "table") {
2449
+ await client.execute(`
2450
+ INSERT OR IGNORE INTO company_procedures
2451
+ (id, title, content, priority, domain, active, created_at, updated_at)
2452
+ SELECT id, title, content, priority, domain, active, created_at, updated_at
2453
+ FROM global_procedures
2454
+ `);
2455
+ await client.executeMultiple(`
2456
+ CREATE TRIGGER IF NOT EXISTS global_procedures_mirror_insert
2457
+ AFTER INSERT ON global_procedures
2458
+ BEGIN
2459
+ INSERT OR IGNORE INTO company_procedures
2460
+ (id, title, content, priority, domain, active, created_at, updated_at)
2461
+ VALUES
2462
+ (NEW.id, NEW.title, NEW.content, NEW.priority, NEW.domain, NEW.active, NEW.created_at, NEW.updated_at);
2463
+ END;
2464
+
2465
+ CREATE TRIGGER IF NOT EXISTS global_procedures_mirror_update
2466
+ AFTER UPDATE ON global_procedures
2467
+ BEGIN
2468
+ UPDATE company_procedures
2469
+ SET title = NEW.title,
2470
+ content = NEW.content,
2471
+ priority = NEW.priority,
2472
+ domain = NEW.domain,
2473
+ active = NEW.active,
2474
+ created_at = NEW.created_at,
2475
+ updated_at = NEW.updated_at
2476
+ WHERE id = OLD.id;
2477
+ END;
2478
+ `);
2479
+ } else {
2480
+ await client.execute(`
2481
+ CREATE VIEW IF NOT EXISTS global_procedures AS
2482
+ SELECT id, title, content, priority, domain, active, created_at, updated_at
2483
+ FROM company_procedures
2484
+ `);
2485
+ await client.executeMultiple(`
2486
+ CREATE TRIGGER IF NOT EXISTS global_procedures_insert
2487
+ INSTEAD OF INSERT ON global_procedures
2488
+ BEGIN
2489
+ INSERT INTO company_procedures
2490
+ (id, title, content, priority, domain, active, created_at, updated_at)
2491
+ VALUES
2492
+ (NEW.id, NEW.title, NEW.content, NEW.priority, NEW.domain, NEW.active, NEW.created_at, NEW.updated_at);
2493
+ END;
2494
+
2495
+ CREATE TRIGGER IF NOT EXISTS global_procedures_update
2496
+ INSTEAD OF UPDATE ON global_procedures
2497
+ BEGIN
2498
+ UPDATE company_procedures
2499
+ SET title = NEW.title,
2500
+ content = NEW.content,
2501
+ priority = NEW.priority,
2502
+ domain = NEW.domain,
2503
+ active = NEW.active,
2504
+ created_at = NEW.created_at,
2505
+ updated_at = NEW.updated_at
2506
+ WHERE id = OLD.id;
2507
+ END;
2508
+ `);
2509
+ }
2443
2510
  await client.executeMultiple(`
2444
2511
  CREATE TABLE IF NOT EXISTS conversations (
2445
2512
  id TEXT PRIMARY KEY,
@@ -2579,6 +2646,51 @@ async function ensureSchema() {
2579
2646
  VALUES (new.rowid, new.content_text, new.sender_name, new.agent_response);
2580
2647
  END;
2581
2648
  `);
2649
+ await client.executeMultiple(`
2650
+ CREATE TABLE IF NOT EXISTS memory_cards (
2651
+ id TEXT PRIMARY KEY,
2652
+ memory_id TEXT NOT NULL,
2653
+ agent_id TEXT NOT NULL,
2654
+ session_id TEXT NOT NULL,
2655
+ project_name TEXT,
2656
+ timestamp TEXT NOT NULL,
2657
+ card_type TEXT NOT NULL,
2658
+ subject TEXT,
2659
+ predicate TEXT,
2660
+ object TEXT,
2661
+ content TEXT NOT NULL,
2662
+ source_ref TEXT,
2663
+ confidence REAL DEFAULT 0.6,
2664
+ active INTEGER DEFAULT 1,
2665
+ created_at TEXT NOT NULL
2666
+ );
2667
+
2668
+ CREATE INDEX IF NOT EXISTS idx_memory_cards_agent
2669
+ ON memory_cards(agent_id, active, timestamp);
2670
+
2671
+ CREATE INDEX IF NOT EXISTS idx_memory_cards_memory
2672
+ ON memory_cards(memory_id);
2673
+
2674
+ CREATE VIRTUAL TABLE IF NOT EXISTS memory_cards_fts
2675
+ USING fts5(content, subject, predicate, object, content='memory_cards', content_rowid='rowid');
2676
+
2677
+ CREATE TRIGGER IF NOT EXISTS memory_cards_fts_ai AFTER INSERT ON memory_cards BEGIN
2678
+ INSERT INTO memory_cards_fts(rowid, content, subject, predicate, object)
2679
+ VALUES (new.rowid, new.content, new.subject, new.predicate, new.object);
2680
+ END;
2681
+
2682
+ CREATE TRIGGER IF NOT EXISTS memory_cards_fts_ad AFTER DELETE ON memory_cards BEGIN
2683
+ INSERT INTO memory_cards_fts(memory_cards_fts, rowid, content, subject, predicate, object)
2684
+ VALUES('delete', old.rowid, old.content, old.subject, old.predicate, old.object);
2685
+ END;
2686
+
2687
+ CREATE TRIGGER IF NOT EXISTS memory_cards_fts_au AFTER UPDATE ON memory_cards BEGIN
2688
+ INSERT INTO memory_cards_fts(memory_cards_fts, rowid, content, subject, predicate, object)
2689
+ VALUES('delete', old.rowid, old.content, old.subject, old.predicate, old.object);
2690
+ INSERT INTO memory_cards_fts(rowid, content, subject, predicate, object)
2691
+ VALUES (new.rowid, new.content, new.subject, new.predicate, new.object);
2692
+ END;
2693
+ `);
2582
2694
  try {
2583
2695
  await client.execute({
2584
2696
  sql: `ALTER TABLE memories ADD COLUMN tier INTEGER DEFAULT 3`,
@@ -3316,7 +3428,7 @@ var init_platform_procedures = __esm({
3316
3428
  title: "MCP tools \u2014 advanced (triggers, skills, orchestration)",
3317
3429
  domain: "tool-use",
3318
3430
  priority: "p1",
3319
- content: "create_trigger: set up a scheduled recurring agent job (cron). list_triggers: view active triggers. load_skill: load a slash-command skill dynamically. apply_starter_pack: import a pre-built behavior + identity pack for a role. export_orchestration: export full org state (tasks, behaviors, identities) as portable JSON. import_orchestration: import org state into a new instance. deploy_client: deploy a customer client instance. query_company_brain: unified RAG query across all company knowledge. create_reminder: set a text reminder (shown in boot brief). list_reminders: view pending reminders. complete_reminder: mark a reminder done. global_procedure: manage customer-owned company procedures (Layer 0; actions: store, list, deactivate). Legacy aliases: store_global_procedure, list_global_procedures, deactivate_global_procedure."
3431
+ content: "create_trigger: set up a scheduled recurring agent job (cron). list_triggers: view active triggers. load_skill: load a slash-command skill dynamically. apply_starter_pack: import a pre-built behavior + identity pack for a role. export_orchestration: export full org state (tasks, behaviors, identities) as portable JSON. import_orchestration: import org state into a new instance. deploy_client: deploy a customer client instance. query_company_brain: unified RAG query across all company knowledge. create_reminder: set a text reminder (shown in boot brief). list_reminders: view pending reminders. complete_reminder: mark a reminder done. company_procedure: manage customer-owned company procedures (Layer 0; actions: store, list, deactivate). Legacy aliases: global_procedure, store_global_procedure, list_global_procedures, deactivate_global_procedure."
3320
3432
  }
3321
3433
  ];
3322
3434
  PLATFORM_PROCEDURE_TITLES = new Set(
@@ -3337,7 +3449,7 @@ import { randomUUID as randomUUID2 } from "crypto";
3337
3449
  async function loadGlobalProcedures() {
3338
3450
  const client = getClient();
3339
3451
  const result = await client.execute({
3340
- sql: "SELECT * FROM global_procedures WHERE active = 1 ORDER BY priority ASC, created_at ASC",
3452
+ sql: "SELECT * FROM company_procedures WHERE active = 1 ORDER BY priority ASC, created_at ASC",
3341
3453
  args: []
3342
3454
  });
3343
3455
  const allRows = result.rows;
@@ -3366,7 +3478,7 @@ async function storeGlobalProcedure(input) {
3366
3478
  const now = (/* @__PURE__ */ new Date()).toISOString();
3367
3479
  const client = getClient();
3368
3480
  await client.execute({
3369
- sql: `INSERT INTO global_procedures (id, title, content, priority, domain, active, created_at, updated_at)
3481
+ sql: `INSERT INTO company_procedures (id, title, content, priority, domain, active, created_at, updated_at)
3370
3482
  VALUES (?, ?, ?, ?, ?, 1, ?, ?)`,
3371
3483
  args: [id, input.title, input.content, input.priority ?? "p0", input.domain ?? null, now, now]
3372
3484
  });
@@ -3377,7 +3489,7 @@ async function deactivateGlobalProcedure(id) {
3377
3489
  const now = (/* @__PURE__ */ new Date()).toISOString();
3378
3490
  const client = getClient();
3379
3491
  const result = await client.execute({
3380
- sql: "UPDATE global_procedures SET active = 0, updated_at = ? WHERE id = ?",
3492
+ sql: "UPDATE company_procedures SET active = 0, updated_at = ? WHERE id = ?",
3381
3493
  args: [now, id]
3382
3494
  });
3383
3495
  await loadGlobalProcedures();
@@ -3396,6 +3508,164 @@ ${p.content}`).join("\n\n");
3396
3508
  }
3397
3509
  });
3398
3510
 
3511
+ // src/lib/memory-cards.ts
3512
+ var memory_cards_exports = {};
3513
+ __export(memory_cards_exports, {
3514
+ extractMemoryCards: () => extractMemoryCards,
3515
+ insertMemoryCardsForBatch: () => insertMemoryCardsForBatch,
3516
+ searchMemoryCards: () => searchMemoryCards
3517
+ });
3518
+ import { createHash as createHash2 } from "crypto";
3519
+ function stableId(memoryId, type, content) {
3520
+ return createHash2("sha256").update(`${memoryId}:${type}:${content}`).digest("hex").slice(0, 32);
3521
+ }
3522
+ function cleanText(text) {
3523
+ return text.replace(/```[\s\S]*?```/g, " ").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
3524
+ }
3525
+ function splitSentences(text) {
3526
+ return cleanText(text).split(/(?<=[.!?])\s+|\n+/).map((s) => s.trim()).filter((s) => s.length >= 24 && s.length <= MAX_SENTENCE_CHARS);
3527
+ }
3528
+ function inferCardType(sentence, toolName) {
3529
+ const lower = sentence.toLowerCase();
3530
+ if (toolName === "store_decision" || /\b(decided|decision|adr|approved|rejected)\b/.test(lower)) return "decision";
3531
+ if (/\b(prefers|preference|likes|dislikes|wants|doesn't want|does not want)\b/.test(lower)) return "preference";
3532
+ if (/\b(changed|updated|replaced|now|no longer|instead|supersedes)\b/.test(lower)) return "belief_update";
3533
+ if (toolName && ["Read", "Write", "Edit", "Bash"].includes(toolName)) return "code";
3534
+ if (/\b(meeting|deadline|shipped|launched|completed|failed|blocked|assigned|created)\b/.test(lower)) return "event";
3535
+ return "fact";
3536
+ }
3537
+ function extractSubject(sentence, agentId) {
3538
+ const explicit = sentence.match(/\b([A-Z][a-zA-Z0-9_-]{2,}(?:\s+[A-Z][a-zA-Z0-9_-]{2,})?)\b/);
3539
+ return explicit?.[1] ?? agentId;
3540
+ }
3541
+ function predicateFor(type) {
3542
+ switch (type) {
3543
+ case "preference":
3544
+ return "prefers";
3545
+ case "belief_update":
3546
+ return "updated";
3547
+ case "decision":
3548
+ return "decided";
3549
+ case "event":
3550
+ return "happened";
3551
+ case "code":
3552
+ return "implemented";
3553
+ default:
3554
+ return "states";
3555
+ }
3556
+ }
3557
+ function extractMemoryCards(row) {
3558
+ const sentences = splitSentences(row.raw_text);
3559
+ const cards = [];
3560
+ for (const sentence of sentences) {
3561
+ const type = inferCardType(sentence, row.tool_name);
3562
+ const subject = extractSubject(sentence, row.agent_id);
3563
+ const content = sentence.length > MAX_SENTENCE_CHARS ? `${sentence.slice(0, MAX_SENTENCE_CHARS - 1)}\u2026` : sentence;
3564
+ cards.push({
3565
+ id: stableId(row.id, type, content),
3566
+ memory_id: row.id,
3567
+ agent_id: row.agent_id,
3568
+ session_id: row.session_id,
3569
+ project_name: row.project_name ?? null,
3570
+ timestamp: row.timestamp,
3571
+ card_type: type,
3572
+ subject,
3573
+ predicate: predicateFor(type),
3574
+ object: content,
3575
+ content,
3576
+ source_ref: row.id,
3577
+ confidence: type === "fact" ? 0.55 : 0.65
3578
+ });
3579
+ if (cards.length >= MAX_CARDS_PER_MEMORY) break;
3580
+ }
3581
+ return cards;
3582
+ }
3583
+ async function insertMemoryCardsForBatch(rows) {
3584
+ const cards = rows.flatMap(extractMemoryCards);
3585
+ if (cards.length === 0) return 0;
3586
+ const now = (/* @__PURE__ */ new Date()).toISOString();
3587
+ const client = getClient();
3588
+ const stmts = cards.map((card) => ({
3589
+ sql: `INSERT OR IGNORE INTO memory_cards
3590
+ (id, memory_id, agent_id, session_id, project_name, timestamp, card_type,
3591
+ subject, predicate, object, content, source_ref, confidence, active, created_at)
3592
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?)`,
3593
+ args: [
3594
+ card.id,
3595
+ card.memory_id,
3596
+ card.agent_id,
3597
+ card.session_id,
3598
+ card.project_name,
3599
+ card.timestamp,
3600
+ card.card_type,
3601
+ card.subject,
3602
+ card.predicate,
3603
+ card.object,
3604
+ card.content,
3605
+ card.source_ref,
3606
+ card.confidence,
3607
+ now
3608
+ ]
3609
+ }));
3610
+ await client.batch(stmts, "write");
3611
+ return cards.length;
3612
+ }
3613
+ function buildMatchExpr(queryText) {
3614
+ 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);
3615
+ if (terms.length === 0) return null;
3616
+ return terms.map((t) => `${t}*`).join(terms.length >= 3 ? " AND " : " OR ");
3617
+ }
3618
+ async function searchMemoryCards(queryText, agentId, options) {
3619
+ const limit = options?.limit ?? 10;
3620
+ const matchExpr = buildMatchExpr(queryText);
3621
+ if (!matchExpr) return [];
3622
+ let sql = `SELECT c.id, c.memory_id, c.agent_id, c.session_id, c.project_name,
3623
+ c.timestamp, c.card_type, c.content, c.source_ref, c.confidence
3624
+ FROM memory_cards c
3625
+ JOIN memory_cards_fts fts ON c.rowid = fts.rowid
3626
+ WHERE memory_cards_fts MATCH ?
3627
+ AND c.agent_id = ?
3628
+ AND COALESCE(c.active, 1) = 1`;
3629
+ const args = [matchExpr, agentId];
3630
+ if (options?.projectName) {
3631
+ sql += ` AND c.project_name = ?`;
3632
+ args.push(options.projectName);
3633
+ }
3634
+ if (options?.since) {
3635
+ sql += ` AND c.timestamp >= ?`;
3636
+ args.push(options.since);
3637
+ }
3638
+ sql += ` ORDER BY rank LIMIT ?`;
3639
+ args.push(limit);
3640
+ const result = await getClient().execute({ sql, args });
3641
+ return result.rows.map((row) => ({
3642
+ id: `card:${String(row.id)}`,
3643
+ agent_id: String(row.agent_id),
3644
+ agent_role: "memory_card",
3645
+ session_id: String(row.session_id),
3646
+ timestamp: String(row.timestamp),
3647
+ tool_name: `memory_card:${String(row.card_type)}`,
3648
+ project_name: row.project_name == null ? "" : String(row.project_name),
3649
+ has_error: false,
3650
+ raw_text: `[${String(row.card_type)}] ${String(row.content)}
3651
+ Source memory: ${String(row.source_ref ?? row.memory_id)}`,
3652
+ vector: [],
3653
+ importance: 6,
3654
+ status: "active",
3655
+ confidence: Number(row.confidence ?? 0.6),
3656
+ last_accessed: String(row.timestamp)
3657
+ }));
3658
+ }
3659
+ var MAX_CARDS_PER_MEMORY, MAX_SENTENCE_CHARS;
3660
+ var init_memory_cards = __esm({
3661
+ "src/lib/memory-cards.ts"() {
3662
+ "use strict";
3663
+ init_database();
3664
+ MAX_CARDS_PER_MEMORY = 6;
3665
+ MAX_SENTENCE_CHARS = 360;
3666
+ }
3667
+ });
3668
+
3399
3669
  // src/bin/backfill-conversations.ts
3400
3670
  import crypto2 from "crypto";
3401
3671
  import { createReadStream } from "fs";
@@ -4195,6 +4465,11 @@ async function flushBatch() {
4195
4465
  const globalClient = getClient();
4196
4466
  const globalStmts = batch.map(buildStmt);
4197
4467
  await globalClient.batch(globalStmts, "write");
4468
+ try {
4469
+ const { insertMemoryCardsForBatch: insertMemoryCardsForBatch2 } = await Promise.resolve().then(() => (init_memory_cards(), memory_cards_exports));
4470
+ await insertMemoryCardsForBatch2(batch);
4471
+ } catch {
4472
+ }
4198
4473
  schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
4199
4474
  _pendingRecords.splice(0, batch.length);
4200
4475
  try {