@askexenow/exe-os 0.9.16 → 0.9.18

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 (97) hide show
  1. package/dist/bin/backfill-conversations.js +1242 -909
  2. package/dist/bin/backfill-responses.js +1245 -912
  3. package/dist/bin/backfill-vectors.js +1244 -906
  4. package/dist/bin/cleanup-stale-review-tasks.js +1870 -417
  5. package/dist/bin/cli.js +217 -107
  6. package/dist/bin/exe-agent-config.js +2 -2
  7. package/dist/bin/exe-agent.js +62 -0
  8. package/dist/bin/exe-assign.js +346 -10
  9. package/dist/bin/exe-boot.js +387 -32
  10. package/dist/bin/exe-call.js +72 -2
  11. package/dist/bin/exe-cloud.js +8 -0
  12. package/dist/bin/exe-dispatch.js +1821 -225
  13. package/dist/bin/exe-doctor.js +720 -52
  14. package/dist/bin/exe-export-behaviors.js +1429 -148
  15. package/dist/bin/exe-forget.js +1408 -34
  16. package/dist/bin/exe-gateway.js +1629 -1295
  17. package/dist/bin/exe-heartbeat.js +1899 -448
  18. package/dist/bin/exe-kill.js +1624 -346
  19. package/dist/bin/exe-launch-agent.js +726 -90
  20. package/dist/bin/exe-link.js +27 -8
  21. package/dist/bin/exe-new-employee.js +75 -9
  22. package/dist/bin/exe-pending-messages.js +2769 -1316
  23. package/dist/bin/exe-pending-notifications.js +2829 -1376
  24. package/dist/bin/exe-pending-reviews.js +2847 -1392
  25. package/dist/bin/exe-rename.js +89 -8
  26. package/dist/bin/exe-review.js +1494 -312
  27. package/dist/bin/exe-search.js +1608 -1300
  28. package/dist/bin/exe-session-cleanup.js +194 -91
  29. package/dist/bin/exe-settings.js +10 -2
  30. package/dist/bin/exe-start-codex.js +769 -120
  31. package/dist/bin/exe-start-opencode.js +763 -108
  32. package/dist/bin/exe-status.js +1887 -434
  33. package/dist/bin/exe-team.js +1782 -324
  34. package/dist/bin/git-sweep.js +408 -33
  35. package/dist/bin/graph-backfill.js +681 -27
  36. package/dist/bin/graph-export.js +1419 -141
  37. package/dist/bin/install.js +4 -8
  38. package/dist/bin/intercom-check.js +8641 -0
  39. package/dist/bin/scan-tasks.js +553 -38
  40. package/dist/bin/setup.js +82 -10
  41. package/dist/bin/shard-migrate.js +682 -28
  42. package/dist/gateway/index.js +1629 -1295
  43. package/dist/hooks/bug-report-worker.js +1136 -183
  44. package/dist/hooks/codex-stop-task-finalizer.js +1060 -107
  45. package/dist/hooks/commit-complete.js +408 -33
  46. package/dist/hooks/error-recall.js +1608 -1300
  47. package/dist/hooks/ingest-worker.js +250 -7966
  48. package/dist/hooks/ingest.js +707 -119
  49. package/dist/hooks/instructions-loaded.js +383 -20
  50. package/dist/hooks/notification.js +383 -20
  51. package/dist/hooks/post-compact.js +384 -21
  52. package/dist/hooks/{response-ingest-worker.js → post-tool-combined.js} +4157 -1716
  53. package/dist/hooks/pre-compact.js +486 -45
  54. package/dist/hooks/pre-tool-use.js +385 -22
  55. package/dist/hooks/prompt-submit.js +291 -96
  56. package/dist/hooks/session-end.js +490 -48
  57. package/dist/hooks/session-start.js +236 -22
  58. package/dist/hooks/stop.js +192 -50
  59. package/dist/hooks/subagent-stop.js +95 -18
  60. package/dist/hooks/summary-worker.js +205 -361
  61. package/dist/index.js +221 -105
  62. package/dist/lib/agent-config.js +2 -2
  63. package/dist/lib/cloud-sync.js +27 -8
  64. package/dist/lib/consolidation.js +437 -41
  65. package/dist/lib/database.js +20 -8
  66. package/dist/lib/db-daemon-client.js +2 -2
  67. package/dist/lib/db.js +20 -8
  68. package/dist/lib/device-registry.js +27 -8
  69. package/dist/lib/employee-templates.js +62 -0
  70. package/dist/lib/employees.js +2 -2
  71. package/dist/lib/exe-daemon.js +6703 -6259
  72. package/dist/lib/hybrid-search.js +1608 -1300
  73. package/dist/lib/identity.js +1 -1
  74. package/dist/lib/messaging.js +11 -3
  75. package/dist/lib/reminders.js +1 -1
  76. package/dist/lib/schedules.js +718 -26
  77. package/dist/lib/session-registry.js +11 -0
  78. package/dist/lib/skill-learning.js +639 -9
  79. package/dist/lib/store.js +676 -27
  80. package/dist/lib/tasks.js +665 -27
  81. package/dist/lib/tmux-routing.js +732 -94
  82. package/dist/lib/token-spend.js +1 -1
  83. package/dist/mcp/server.js +856 -383
  84. package/dist/mcp/tools/complete-reminder.js +1 -1
  85. package/dist/mcp/tools/create-reminder.js +1 -1
  86. package/dist/mcp/tools/create-task.js +616 -46
  87. package/dist/mcp/tools/deactivate-behavior.js +9 -1
  88. package/dist/mcp/tools/list-reminders.js +1 -1
  89. package/dist/mcp/tools/list-tasks.js +10 -2
  90. package/dist/mcp/tools/send-message.js +11 -3
  91. package/dist/mcp/tools/update-task.js +677 -39
  92. package/dist/runtime/index.js +425 -37
  93. package/dist/tui/App.js +365 -34
  94. package/package.json +5 -2
  95. package/src/commands/exe/intercom.md +6 -17
  96. package/dist/bin/wiki-sync.js +0 -2991
  97. package/dist/hooks/prompt-ingest-worker.js +0 -3979
@@ -83,9 +83,9 @@ var MAX_RETRIES, BASE_DELAY_MS, MAX_JITTER_MS;
83
83
  var init_db_retry = __esm({
84
84
  "src/lib/db-retry.ts"() {
85
85
  "use strict";
86
- MAX_RETRIES = 3;
87
- BASE_DELAY_MS = 200;
88
- MAX_JITTER_MS = 300;
86
+ MAX_RETRIES = 5;
87
+ BASE_DELAY_MS = 250;
88
+ MAX_JITTER_MS = 400;
89
89
  }
90
90
  });
91
91
 
@@ -1622,7 +1622,7 @@ function createDaemonDbClient(fallbackClient) {
1622
1622
  });
1623
1623
  if (response.error) {
1624
1624
  const errMsg = String(response.error);
1625
- if (errMsg === "Not connected" || errMsg === "Request timeout" || errMsg === "Write failed") {
1625
+ if (errMsg === "Not connected" || errMsg === "Request timeout" || errMsg === "Write failed" || errMsg === "DB not initialized") {
1626
1626
  process.stderr.write(`[db-daemon] Transport error (${errMsg}), falling back to direct
1627
1627
  `);
1628
1628
  return fallbackClient.execute(stmt);
@@ -1647,7 +1647,7 @@ function createDaemonDbClient(fallbackClient) {
1647
1647
  });
1648
1648
  if (response.error) {
1649
1649
  const errMsg = String(response.error);
1650
- if (errMsg === "Not connected" || errMsg === "Request timeout" || errMsg === "Write failed") {
1650
+ if (errMsg === "Not connected" || errMsg === "Request timeout" || errMsg === "Write failed" || errMsg === "DB not initialized") {
1651
1651
  process.stderr.write(`[db-daemon] Batch transport error (${errMsg}), falling back to direct
1652
1652
  `);
1653
1653
  return fallbackClient.batch(stmts, mode);
@@ -1770,7 +1770,7 @@ async function initDatabase(config) {
1770
1770
  });
1771
1771
  }, 3e4);
1772
1772
  _walCheckpointTimer.unref();
1773
- if (process.env.DATABASE_URL) {
1773
+ if (process.env.DATABASE_URL && process.env.EXE_USE_POSTGRES === "1") {
1774
1774
  _adapterClient = await createPrismaDbAdapter(_resilientClient);
1775
1775
  }
1776
1776
  }
@@ -1781,7 +1781,7 @@ function getClient() {
1781
1781
  if (!_adapterClient) {
1782
1782
  throw new Error("Database client not initialized. Call initDatabase() first.");
1783
1783
  }
1784
- if (process.env.DATABASE_URL) {
1784
+ if (process.env.DATABASE_URL && process.env.EXE_USE_POSTGRES === "1") {
1785
1785
  return _adapterClient;
1786
1786
  }
1787
1787
  if (process.env.EXE_IS_DAEMON === "1") {
@@ -1793,9 +1793,11 @@ function getClient() {
1793
1793
  return _resilientClient;
1794
1794
  }
1795
1795
  async function initDaemonClient() {
1796
- if (process.env.DATABASE_URL) return;
1796
+ if (process.env.DATABASE_URL && process.env.EXE_USE_POSTGRES === "1") return;
1797
1797
  if (process.env.EXE_IS_DAEMON === "1") return;
1798
+ if (process.env.VITEST) return;
1798
1799
  if (!_resilientClient) return;
1800
+ if (_daemonClient) return;
1799
1801
  try {
1800
1802
  const { initDaemonDbClient: initDaemonDbClient2 } = await Promise.resolve().then(() => (init_db_daemon_client(), db_daemon_client_exports));
1801
1803
  _daemonClient = await initDaemonDbClient2(_resilientClient);
@@ -1944,6 +1946,13 @@ async function ensureSchema() {
1944
1946
  });
1945
1947
  } catch {
1946
1948
  }
1949
+ try {
1950
+ await client.execute({
1951
+ sql: `ALTER TABLE behaviors ADD COLUMN vector F32_BLOB(${EMBEDDING_DIM})`,
1952
+ args: []
1953
+ });
1954
+ } catch {
1955
+ }
1947
1956
  try {
1948
1957
  await client.execute({
1949
1958
  sql: `ALTER TABLE tasks ADD COLUMN blocked_by TEXT`,
@@ -2814,6 +2823,7 @@ var init_database = __esm({
2814
2823
  init_db_retry();
2815
2824
  init_employees();
2816
2825
  init_database_adapter();
2826
+ init_memory();
2817
2827
  _client = null;
2818
2828
  _resilientClient = null;
2819
2829
  _walCheckpointTimer = null;
@@ -3432,6 +3442,68 @@ var init_platform_procedures = __esm({
3432
3442
  domain: "architecture",
3433
3443
  priority: "p0",
3434
3444
  content: "Tasks live in the DB. Intercom (tmux send-keys) is fire-and-forget \u2014 it may fail, get garbled, or arrive mid-work. Never rely on intercom for task delivery. The UserPromptSubmit hook checks the DB for new tasks on every prompt. Your operating procedures step 7 says check for next work. The daemon nudges idle agents as a speedup. If you have no tasks, you found them all."
3445
+ },
3446
+ // --- MCP is the ONLY data interface ---
3447
+ {
3448
+ title: "MCP disconnect \u2014 ask the user, never work around it",
3449
+ domain: "workflow",
3450
+ priority: "p0",
3451
+ content: "If MCP tools are unavailable, disconnected, or returning connection errors: STOP. Tell the user clearly: 'MCP server is disconnected. Please run /mcp to reconnect.' Do NOT attempt workarounds \u2014 no raw Node imports, no direct DB access, no CLI hacks, no daemon socket calls. MCP is the ONLY data interface. Working around it wastes time, hits bundling issues, and bypasses the contract boundary. Ask once, wait, proceed when reconnected."
3452
+ },
3453
+ // --- MCP Tool Catalog (Layer 0 — every agent knows what tools exist) ---
3454
+ {
3455
+ title: "MCP tools \u2014 memory and search",
3456
+ domain: "tool-use",
3457
+ priority: "p1",
3458
+ content: "recall_my_memory: search your own memories (semantic + FTS). ask_team_memory: search a colleague's memories by agent name. store_memory: persist a memory (decisions, summaries, context). commit_memory: high-importance memory that survives consolidation. search_everything: unified search across memories, tasks, entities, conversations. get_session_context: temporal window of memories around a timestamp. consolidate_memories: merge duplicate/related memories into insights. get_memory_cardinality: count memories per agent (health check)."
3459
+ },
3460
+ {
3461
+ title: "MCP tools \u2014 task orchestration",
3462
+ domain: "tool-use",
3463
+ priority: "p1",
3464
+ content: "create_task: dispatch work to an employee (auto-spawns session). The ONLY dispatch path. list_tasks: query tasks by status, assignee, project. get_task: fetch full task details by ID. update_task: change status (in_progress, done, blocked, cancelled) + add result summary. close_task: finalize a reviewed task (COO only). checkpoint_task: save progress state for crash recovery. resume_employee: re-spawn an employee session for an existing task."
3465
+ },
3466
+ {
3467
+ title: "MCP tools \u2014 knowledge graph (GraphRAG)",
3468
+ domain: "tool-use",
3469
+ priority: "p1",
3470
+ content: "query_relationships: find connections between entities in the knowledge graph. get_entity_neighbors: explore an entity's direct connections. get_hot_entities: find most-referenced entities (trending topics). get_graph_stats: graph health \u2014 entity/relationship counts, density. export_graph: export graph data for visualization. merge_entities: deduplicate entities (alias resolution). find_similar_trajectories: match tool-call patterns to past task solutions."
3471
+ },
3472
+ {
3473
+ title: "MCP tools \u2014 identity, behavior, and decisions",
3474
+ domain: "tool-use",
3475
+ priority: "p1",
3476
+ content: "get_identity: read an agent's exe.md (Layer 1 identity). update_identity: write an agent's exe.md. Identity > behavior \u2014 use for permanent rules. store_behavior: record a correction or pattern for an agent (Layer 2 expertise). list_behaviors: view an agent's active behaviors. deactivate_behavior: soft-delete a stale or conflicting behavior. store_decision: record an ADR (architectural decision record). get_decision: retrieve a past decision by query."
3477
+ },
3478
+ {
3479
+ title: "MCP tools \u2014 communication and messaging",
3480
+ domain: "tool-use",
3481
+ priority: "p1",
3482
+ content: "send_message: send supplementary context to another agent (NOT for actionable work \u2014 use create_task). acknowledge_messages: mark messages as read. send_whatsapp: send WhatsApp message via gateway (customer-facing alerts). query_conversations: search ingested conversations across all channels (WhatsApp, email, etc.)."
3483
+ },
3484
+ {
3485
+ title: "MCP tools \u2014 wiki, documents, and content",
3486
+ domain: "tool-use",
3487
+ priority: "p1",
3488
+ content: "create_wiki_page: create a wiki page in exe-wiki. list_wiki_pages: browse wiki pages. get_wiki_page: read a wiki page. update_wiki_page: edit a wiki page. ingest_document: import a file (PDF, MD, etc.) into memory as chunks. list_documents: browse ingested documents by workspace. purge_document: remove a document and its memory chunks. set_document_importance: adjust chunk importance scores. rerank_documents: re-score document relevance for a query."
3489
+ },
3490
+ {
3491
+ title: "MCP tools \u2014 system, operations, and admin",
3492
+ domain: "tool-use",
3493
+ priority: "p1",
3494
+ content: "get_agent_spend: token usage per agent/session (cost tracking). list_agent_sessions: view agent session history. get_session_kills: audit log of killed sessions. get_daemon_health: check exed daemon status. get_auto_wake_status: daemon auto-wake configuration. get_worker_gate: check worker deployment gates. run_memory_audit: health check \u2014 duplicates, null vectors, orphaned rows. run_consolidation: trigger sleep-time memory consolidation. cloud_sync: force a cloud sync cycle. backup_vps: trigger VPS backup."
3495
+ },
3496
+ {
3497
+ title: "MCP tools \u2014 config, licensing, and team",
3498
+ domain: "tool-use",
3499
+ priority: "p1",
3500
+ content: "set_agent_config: view/change per-agent runtime and model settings. list_employees: view the employee roster. add_person: add a person to the CRM contacts roster. list_people: browse CRM contacts. get_person: fetch contact details. get_license_status: check license validity. create_license: generate a new license key (admin). list_licenses: view all issued licenses. activate_license: activate a license on a device."
3501
+ },
3502
+ {
3503
+ title: "MCP tools \u2014 advanced (triggers, skills, orchestration)",
3504
+ domain: "tool-use",
3505
+ priority: "p1",
3506
+ 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 Layer 0 procedures (actions: store, list, deactivate). Legacy aliases: store_global_procedure, list_global_procedures, deactivate_global_procedure."
3435
3507
  }
3436
3508
  ];
3437
3509
  PLATFORM_PROCEDURE_TITLES = new Set(
@@ -3774,7 +3846,7 @@ var init_agent_config = __esm({
3774
3846
  init_secure_files();
3775
3847
  AGENT_CONFIG_PATH = path9.join(EXE_AI_DIR, "agent-config.json");
3776
3848
  DEFAULT_MODELS = {
3777
- claude: "claude-opus-4",
3849
+ claude: "claude-opus-4.6",
3778
3850
  codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
3779
3851
  opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
3780
3852
  };
@@ -4056,8 +4128,8 @@ async function validateLicense(apiKey, deviceId) {
4056
4128
  }
4057
4129
  function getCacheAgeMs() {
4058
4130
  try {
4059
- const { statSync: statSync2 } = __require("fs");
4060
- const s = statSync2(CACHE_PATH);
4131
+ const { statSync: statSync3 } = __require("fs");
4132
+ const s = statSync3(CACHE_PATH);
4061
4133
  return Date.now() - s.mtimeMs;
4062
4134
  } catch {
4063
4135
  return Infinity;
@@ -4559,10 +4631,10 @@ async function disposeEmbedder() {
4559
4631
  async function embedDirect(text) {
4560
4632
  const llamaCpp = await import("node-llama-cpp");
4561
4633
  const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
4562
- const { existsSync: existsSync18 } = await import("fs");
4563
- const path19 = await import("path");
4564
- const modelPath = path19.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
4565
- if (!existsSync18(modelPath)) {
4634
+ const { existsSync: existsSync19 } = await import("fs");
4635
+ const path20 = await import("path");
4636
+ const modelPath = path20.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
4637
+ if (!existsSync19(modelPath)) {
4566
4638
  throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
4567
4639
  }
4568
4640
  const llama = await llamaCpp.getLlama();
@@ -4600,13 +4672,13 @@ __export(worker_gate_exports, {
4600
4672
  tryAcquireBackfillLock: () => tryAcquireBackfillLock,
4601
4673
  tryAcquireWorkerSlot: () => tryAcquireWorkerSlot
4602
4674
  });
4603
- import { readdirSync as readdirSync4, writeFileSync as writeFileSync7, unlinkSync as unlinkSync4, mkdirSync as mkdirSync6, existsSync as existsSync14 } from "fs";
4604
- import path15 from "path";
4675
+ import { readdirSync as readdirSync4, writeFileSync as writeFileSync7, unlinkSync as unlinkSync5, mkdirSync as mkdirSync6, existsSync as existsSync15 } from "fs";
4676
+ import path16 from "path";
4605
4677
  function tryAcquireWorkerSlot() {
4606
4678
  try {
4607
4679
  mkdirSync6(WORKER_PID_DIR, { recursive: true });
4608
4680
  const reservationId = `res-${process.pid}-${Date.now()}`;
4609
- const reservationPath = path15.join(WORKER_PID_DIR, `${reservationId}.pid`);
4681
+ const reservationPath = path16.join(WORKER_PID_DIR, `${reservationId}.pid`);
4610
4682
  writeFileSync7(reservationPath, String(process.pid));
4611
4683
  const files = readdirSync4(WORKER_PID_DIR);
4612
4684
  let alive = 0;
@@ -4624,20 +4696,20 @@ function tryAcquireWorkerSlot() {
4624
4696
  alive++;
4625
4697
  } catch {
4626
4698
  try {
4627
- unlinkSync4(path15.join(WORKER_PID_DIR, f));
4699
+ unlinkSync5(path16.join(WORKER_PID_DIR, f));
4628
4700
  } catch {
4629
4701
  }
4630
4702
  }
4631
4703
  }
4632
4704
  if (alive > MAX_CONCURRENT_WORKERS) {
4633
4705
  try {
4634
- unlinkSync4(reservationPath);
4706
+ unlinkSync5(reservationPath);
4635
4707
  } catch {
4636
4708
  }
4637
4709
  return false;
4638
4710
  }
4639
4711
  try {
4640
- unlinkSync4(reservationPath);
4712
+ unlinkSync5(reservationPath);
4641
4713
  } catch {
4642
4714
  }
4643
4715
  return true;
@@ -4648,20 +4720,20 @@ function tryAcquireWorkerSlot() {
4648
4720
  function registerWorkerPid(pid) {
4649
4721
  try {
4650
4722
  mkdirSync6(WORKER_PID_DIR, { recursive: true });
4651
- writeFileSync7(path15.join(WORKER_PID_DIR, `worker-${pid}.pid`), String(pid));
4723
+ writeFileSync7(path16.join(WORKER_PID_DIR, `worker-${pid}.pid`), String(pid));
4652
4724
  } catch {
4653
4725
  }
4654
4726
  }
4655
4727
  function cleanupWorkerPid() {
4656
4728
  try {
4657
- unlinkSync4(path15.join(WORKER_PID_DIR, `worker-${process.pid}.pid`));
4729
+ unlinkSync5(path16.join(WORKER_PID_DIR, `worker-${process.pid}.pid`));
4658
4730
  } catch {
4659
4731
  }
4660
4732
  }
4661
4733
  function tryAcquireBackfillLock() {
4662
4734
  try {
4663
4735
  mkdirSync6(WORKER_PID_DIR, { recursive: true });
4664
- if (existsSync14(BACKFILL_LOCK)) {
4736
+ if (existsSync15(BACKFILL_LOCK)) {
4665
4737
  try {
4666
4738
  const pid = parseInt(
4667
4739
  __require("fs").readFileSync(BACKFILL_LOCK, "utf8").trim(),
@@ -4685,7 +4757,7 @@ function tryAcquireBackfillLock() {
4685
4757
  }
4686
4758
  function releaseBackfillLock() {
4687
4759
  try {
4688
- unlinkSync4(BACKFILL_LOCK);
4760
+ unlinkSync5(BACKFILL_LOCK);
4689
4761
  } catch {
4690
4762
  }
4691
4763
  }
@@ -4694,9 +4766,9 @@ var init_worker_gate = __esm({
4694
4766
  "src/lib/worker-gate.ts"() {
4695
4767
  "use strict";
4696
4768
  init_config();
4697
- WORKER_PID_DIR = path15.join(EXE_AI_DIR, "worker-pids");
4769
+ WORKER_PID_DIR = path16.join(EXE_AI_DIR, "worker-pids");
4698
4770
  MAX_CONCURRENT_WORKERS = 3;
4699
- BACKFILL_LOCK = path15.join(WORKER_PID_DIR, "backfill.lock");
4771
+ BACKFILL_LOCK = path16.join(WORKER_PID_DIR, "backfill.lock");
4700
4772
  }
4701
4773
  });
4702
4774
 
@@ -4798,8 +4870,8 @@ __export(crdt_sync_exports, {
4798
4870
  rebuildFromDb: () => rebuildFromDb
4799
4871
  });
4800
4872
  import * as Y from "yjs";
4801
- import { readFileSync as readFileSync11, writeFileSync as writeFileSync8, existsSync as existsSync15, mkdirSync as mkdirSync7, unlinkSync as unlinkSync5 } from "fs";
4802
- import path16 from "path";
4873
+ import { readFileSync as readFileSync12, writeFileSync as writeFileSync8, existsSync as existsSync16, mkdirSync as mkdirSync7, unlinkSync as unlinkSync6 } from "fs";
4874
+ import path17 from "path";
4803
4875
  import { homedir } from "os";
4804
4876
  function getStatePath() {
4805
4877
  return _statePathOverride ?? DEFAULT_STATE_PATH;
@@ -4811,14 +4883,14 @@ function initCrdtDoc() {
4811
4883
  if (doc) return doc;
4812
4884
  doc = new Y.Doc();
4813
4885
  const sp = getStatePath();
4814
- if (existsSync15(sp)) {
4886
+ if (existsSync16(sp)) {
4815
4887
  try {
4816
- const state = readFileSync11(sp);
4888
+ const state = readFileSync12(sp);
4817
4889
  Y.applyUpdate(doc, new Uint8Array(state));
4818
4890
  } catch {
4819
4891
  console.warn("[crdt-sync] WARN: corrupted state file, rebuilding from DB");
4820
4892
  try {
4821
- unlinkSync5(sp);
4893
+ unlinkSync6(sp);
4822
4894
  } catch {
4823
4895
  }
4824
4896
  rebuildFromDb().catch((err) => {
@@ -4955,8 +5027,8 @@ function persistState() {
4955
5027
  if (!doc) return;
4956
5028
  try {
4957
5029
  const sp = getStatePath();
4958
- const dir = path16.dirname(sp);
4959
- if (!existsSync15(dir)) mkdirSync7(dir, { recursive: true });
5030
+ const dir = path17.dirname(sp);
5031
+ if (!existsSync16(dir)) mkdirSync7(dir, { recursive: true });
4960
5032
  const state = Y.encodeStateAsUpdate(doc);
4961
5033
  writeFileSync8(sp, Buffer.from(state));
4962
5034
  } catch {
@@ -4999,7 +5071,7 @@ var DEFAULT_STATE_PATH, _statePathOverride, doc;
4999
5071
  var init_crdt_sync = __esm({
5000
5072
  "src/lib/crdt-sync.ts"() {
5001
5073
  "use strict";
5002
- DEFAULT_STATE_PATH = path16.join(homedir(), ".exe-os", "crdt-state.bin");
5074
+ DEFAULT_STATE_PATH = path17.join(homedir(), ".exe-os", "crdt-state.bin");
5003
5075
  _statePathOverride = null;
5004
5076
  doc = null;
5005
5077
  }
@@ -5034,17 +5106,17 @@ __export(cloud_sync_exports, {
5034
5106
  pushToPostgres: () => pushToPostgres,
5035
5107
  recordRosterDeletion: () => recordRosterDeletion
5036
5108
  });
5037
- import { readFileSync as readFileSync12, writeFileSync as writeFileSync9, existsSync as existsSync16, readdirSync as readdirSync5, mkdirSync as mkdirSync8, appendFileSync as appendFileSync2, unlinkSync as unlinkSync6, openSync as openSync2, closeSync as closeSync2 } from "fs";
5109
+ import { readFileSync as readFileSync13, writeFileSync as writeFileSync9, existsSync as existsSync17, readdirSync as readdirSync5, mkdirSync as mkdirSync8, appendFileSync as appendFileSync3, unlinkSync as unlinkSync7, openSync as openSync2, closeSync as closeSync2 } from "fs";
5038
5110
  import crypto4 from "crypto";
5039
- import path17 from "path";
5111
+ import path18 from "path";
5040
5112
  import { homedir as homedir2 } from "os";
5041
5113
  function sqlSafe(v) {
5042
5114
  return v === void 0 ? null : v;
5043
5115
  }
5044
5116
  function logError(msg) {
5045
5117
  try {
5046
- const logPath = path17.join(homedir2(), ".exe-os", "workers.log");
5047
- appendFileSync2(logPath, `${(/* @__PURE__ */ new Date()).toISOString()} ${msg}
5118
+ const logPath = path18.join(homedir2(), ".exe-os", "workers.log");
5119
+ appendFileSync3(logPath, `${(/* @__PURE__ */ new Date()).toISOString()} ${msg}
5048
5120
  `);
5049
5121
  } catch {
5050
5122
  }
@@ -5052,11 +5124,11 @@ function logError(msg) {
5052
5124
  function loadPgClient() {
5053
5125
  if (_pgFailed) return null;
5054
5126
  const postgresUrl = process.env.DATABASE_URL;
5055
- const configPath = path17.join(EXE_AI_DIR, "config.json");
5127
+ const configPath = path18.join(EXE_AI_DIR, "config.json");
5056
5128
  let cloudPostgresUrl;
5057
5129
  try {
5058
- if (existsSync16(configPath)) {
5059
- const cfg = JSON.parse(readFileSync12(configPath, "utf8"));
5130
+ if (existsSync17(configPath)) {
5131
+ const cfg = JSON.parse(readFileSync13(configPath, "utf8"));
5060
5132
  cloudPostgresUrl = cfg.cloud?.postgresUrl;
5061
5133
  if (cfg.cloud?.syncToPostgres === false) {
5062
5134
  _pgFailed = true;
@@ -5074,8 +5146,8 @@ function loadPgClient() {
5074
5146
  _pgPromise = (async () => {
5075
5147
  const { createRequire: createRequire3 } = await import("module");
5076
5148
  const { pathToFileURL: pathToFileURL3 } = await import("url");
5077
- const exeDbRoot = process.env.EXE_DB_ROOT ?? path17.join(homedir2(), "exe-db");
5078
- const req = createRequire3(path17.join(exeDbRoot, "package.json"));
5149
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path18.join(homedir2(), "exe-db");
5150
+ const req = createRequire3(path18.join(exeDbRoot, "package.json"));
5079
5151
  const entry = req.resolve("@prisma/client");
5080
5152
  const mod = await import(pathToFileURL3(entry).href);
5081
5153
  const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
@@ -5124,11 +5196,11 @@ async function withRosterLock(fn) {
5124
5196
  } catch (err) {
5125
5197
  if (err.code === "EEXIST") {
5126
5198
  try {
5127
- const ts = parseInt(readFileSync12(ROSTER_LOCK_PATH, "utf-8"), 10);
5199
+ const ts = parseInt(readFileSync13(ROSTER_LOCK_PATH, "utf-8"), 10);
5128
5200
  if (Date.now() - ts < LOCK_STALE_MS) {
5129
5201
  throw new Error("Roster merge already in progress \u2014 another sync is running");
5130
5202
  }
5131
- unlinkSync6(ROSTER_LOCK_PATH);
5203
+ unlinkSync7(ROSTER_LOCK_PATH);
5132
5204
  const fd = openSync2(ROSTER_LOCK_PATH, "wx");
5133
5205
  closeSync2(fd);
5134
5206
  writeFileSync9(ROSTER_LOCK_PATH, String(Date.now()));
@@ -5144,7 +5216,7 @@ async function withRosterLock(fn) {
5144
5216
  return await fn();
5145
5217
  } finally {
5146
5218
  try {
5147
- unlinkSync6(ROSTER_LOCK_PATH);
5219
+ unlinkSync7(ROSTER_LOCK_PATH);
5148
5220
  } catch {
5149
5221
  }
5150
5222
  }
@@ -5515,8 +5587,8 @@ async function cloudSync(config) {
5515
5587
  try {
5516
5588
  const employees = await loadEmployees();
5517
5589
  rosterResult.employees = employees.length;
5518
- const idDir = path17.join(EXE_AI_DIR, "identity");
5519
- if (existsSync16(idDir)) {
5590
+ const idDir = path18.join(EXE_AI_DIR, "identity");
5591
+ if (existsSync17(idDir)) {
5520
5592
  rosterResult.identities = readdirSync5(idDir).filter((f) => f.endsWith(".md")).length;
5521
5593
  }
5522
5594
  } catch {
@@ -5537,8 +5609,8 @@ async function cloudSync(config) {
5537
5609
  function recordRosterDeletion(name) {
5538
5610
  let deletions = [];
5539
5611
  try {
5540
- if (existsSync16(ROSTER_DELETIONS_PATH)) {
5541
- deletions = JSON.parse(readFileSync12(ROSTER_DELETIONS_PATH, "utf-8"));
5612
+ if (existsSync17(ROSTER_DELETIONS_PATH)) {
5613
+ deletions = JSON.parse(readFileSync13(ROSTER_DELETIONS_PATH, "utf-8"));
5542
5614
  }
5543
5615
  } catch {
5544
5616
  }
@@ -5547,8 +5619,8 @@ function recordRosterDeletion(name) {
5547
5619
  }
5548
5620
  function consumeRosterDeletions() {
5549
5621
  try {
5550
- if (!existsSync16(ROSTER_DELETIONS_PATH)) return [];
5551
- const deletions = JSON.parse(readFileSync12(ROSTER_DELETIONS_PATH, "utf-8"));
5622
+ if (!existsSync17(ROSTER_DELETIONS_PATH)) return [];
5623
+ const deletions = JSON.parse(readFileSync13(ROSTER_DELETIONS_PATH, "utf-8"));
5552
5624
  writeFileSync9(ROSTER_DELETIONS_PATH, "[]");
5553
5625
  return deletions;
5554
5626
  } catch {
@@ -5556,37 +5628,37 @@ function consumeRosterDeletions() {
5556
5628
  }
5557
5629
  }
5558
5630
  function buildRosterBlob(paths) {
5559
- const rosterPath = paths?.rosterPath ?? path17.join(EXE_AI_DIR, "exe-employees.json");
5560
- const identityDir = paths?.identityDir ?? path17.join(EXE_AI_DIR, "identity");
5561
- const configPath = paths?.configPath ?? path17.join(EXE_AI_DIR, "config.json");
5631
+ const rosterPath = paths?.rosterPath ?? path18.join(EXE_AI_DIR, "exe-employees.json");
5632
+ const identityDir = paths?.identityDir ?? path18.join(EXE_AI_DIR, "identity");
5633
+ const configPath = paths?.configPath ?? path18.join(EXE_AI_DIR, "config.json");
5562
5634
  let roster = [];
5563
- if (existsSync16(rosterPath)) {
5635
+ if (existsSync17(rosterPath)) {
5564
5636
  try {
5565
- roster = JSON.parse(readFileSync12(rosterPath, "utf-8"));
5637
+ roster = JSON.parse(readFileSync13(rosterPath, "utf-8"));
5566
5638
  } catch {
5567
5639
  }
5568
5640
  }
5569
5641
  const identities = {};
5570
- if (existsSync16(identityDir)) {
5642
+ if (existsSync17(identityDir)) {
5571
5643
  for (const file of readdirSync5(identityDir).filter((f) => f.endsWith(".md"))) {
5572
5644
  try {
5573
- identities[file] = readFileSync12(path17.join(identityDir, file), "utf-8");
5645
+ identities[file] = readFileSync13(path18.join(identityDir, file), "utf-8");
5574
5646
  } catch {
5575
5647
  }
5576
5648
  }
5577
5649
  }
5578
5650
  let config;
5579
- if (existsSync16(configPath)) {
5651
+ if (existsSync17(configPath)) {
5580
5652
  try {
5581
- config = JSON.parse(readFileSync12(configPath, "utf-8"));
5653
+ config = JSON.parse(readFileSync13(configPath, "utf-8"));
5582
5654
  } catch {
5583
5655
  }
5584
5656
  }
5585
5657
  let agentConfig;
5586
- const agentConfigPath = path17.join(EXE_AI_DIR, "agent-config.json");
5587
- if (existsSync16(agentConfigPath)) {
5658
+ const agentConfigPath = path18.join(EXE_AI_DIR, "agent-config.json");
5659
+ if (existsSync17(agentConfigPath)) {
5588
5660
  try {
5589
- agentConfig = JSON.parse(readFileSync12(agentConfigPath, "utf-8"));
5661
+ agentConfig = JSON.parse(readFileSync13(agentConfigPath, "utf-8"));
5590
5662
  } catch {
5591
5663
  }
5592
5664
  }
@@ -5662,16 +5734,16 @@ async function cloudPullRoster(config) {
5662
5734
  }
5663
5735
  }
5664
5736
  function mergeConfig(remoteConfig, configPath) {
5665
- const cfgPath = configPath ?? path17.join(EXE_AI_DIR, "config.json");
5737
+ const cfgPath = configPath ?? path18.join(EXE_AI_DIR, "config.json");
5666
5738
  let local = {};
5667
- if (existsSync16(cfgPath)) {
5739
+ if (existsSync17(cfgPath)) {
5668
5740
  try {
5669
- local = JSON.parse(readFileSync12(cfgPath, "utf-8"));
5741
+ local = JSON.parse(readFileSync13(cfgPath, "utf-8"));
5670
5742
  } catch {
5671
5743
  }
5672
5744
  }
5673
5745
  const merged = { ...remoteConfig, ...local };
5674
- const dir = path17.dirname(cfgPath);
5746
+ const dir = path18.dirname(cfgPath);
5675
5747
  ensurePrivateDirSync(dir);
5676
5748
  writeFileSync9(cfgPath, JSON.stringify(merged, null, 2), "utf-8");
5677
5749
  enforcePrivateFileSync(cfgPath);
@@ -5679,7 +5751,7 @@ function mergeConfig(remoteConfig, configPath) {
5679
5751
  async function mergeRosterFromRemote(remote, paths) {
5680
5752
  return withRosterLock(async () => {
5681
5753
  const rosterPath = paths?.rosterPath ?? void 0;
5682
- const identityDir = paths?.identityDir ?? path17.join(EXE_AI_DIR, "identity");
5754
+ const identityDir = paths?.identityDir ?? path18.join(EXE_AI_DIR, "identity");
5683
5755
  const localEmployees = await loadEmployees(rosterPath);
5684
5756
  const localNames = new Set(localEmployees.map((e) => e.name));
5685
5757
  let added = 0;
@@ -5700,11 +5772,11 @@ async function mergeRosterFromRemote(remote, paths) {
5700
5772
  ) ?? lookupKey;
5701
5773
  const remoteIdentity = remote.identities[matchedKey];
5702
5774
  if (remoteIdentity) {
5703
- if (!existsSync16(identityDir)) mkdirSync8(identityDir, { recursive: true });
5704
- const idPath = path17.join(identityDir, `${remoteEmp.name}.md`);
5775
+ if (!existsSync17(identityDir)) mkdirSync8(identityDir, { recursive: true });
5776
+ const idPath = path18.join(identityDir, `${remoteEmp.name}.md`);
5705
5777
  let localIdentity = null;
5706
5778
  try {
5707
- localIdentity = existsSync16(idPath) ? readFileSync12(idPath, "utf-8") : null;
5779
+ localIdentity = existsSync17(idPath) ? readFileSync13(idPath, "utf-8") : null;
5708
5780
  } catch {
5709
5781
  }
5710
5782
  if (localIdentity !== remoteIdentity) {
@@ -5734,16 +5806,16 @@ async function mergeRosterFromRemote(remote, paths) {
5734
5806
  }
5735
5807
  if (remote.agentConfig && Object.keys(remote.agentConfig).length > 0) {
5736
5808
  try {
5737
- const agentConfigPath = path17.join(EXE_AI_DIR, "agent-config.json");
5809
+ const agentConfigPath = path18.join(EXE_AI_DIR, "agent-config.json");
5738
5810
  let local = {};
5739
- if (existsSync16(agentConfigPath)) {
5811
+ if (existsSync17(agentConfigPath)) {
5740
5812
  try {
5741
- local = JSON.parse(readFileSync12(agentConfigPath, "utf-8"));
5813
+ local = JSON.parse(readFileSync13(agentConfigPath, "utf-8"));
5742
5814
  } catch {
5743
5815
  }
5744
5816
  }
5745
5817
  const merged = { ...remote.agentConfig, ...local };
5746
- ensurePrivateDirSync(path17.dirname(agentConfigPath));
5818
+ ensurePrivateDirSync(path18.dirname(agentConfigPath));
5747
5819
  writeFileSync9(agentConfigPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
5748
5820
  enforcePrivateFileSync(agentConfigPath);
5749
5821
  } catch {
@@ -6184,11 +6256,11 @@ var init_cloud_sync = __esm({
6184
6256
  LOCALHOST_PATTERNS = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
6185
6257
  FETCH_TIMEOUT_MS = 3e4;
6186
6258
  PUSH_BATCH_SIZE = 5e3;
6187
- ROSTER_LOCK_PATH = path17.join(EXE_AI_DIR, "roster-merge.lock");
6259
+ ROSTER_LOCK_PATH = path18.join(EXE_AI_DIR, "roster-merge.lock");
6188
6260
  LOCK_STALE_MS = 3e4;
6189
6261
  _pgPromise = null;
6190
6262
  _pgFailed = false;
6191
- ROSTER_DELETIONS_PATH = path17.join(EXE_AI_DIR, "roster-deletions.json");
6263
+ ROSTER_DELETIONS_PATH = path18.join(EXE_AI_DIR, "roster-deletions.json");
6192
6264
  }
6193
6265
  });
6194
6266
 
@@ -6258,6 +6330,11 @@ async function initStore(options) {
6258
6330
  encryptionKey: hexKey
6259
6331
  });
6260
6332
  await retryOnBusy2(() => ensureSchema(), "ensureSchema");
6333
+ try {
6334
+ const { initDaemonClient: initDaemonClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
6335
+ await initDaemonClient2();
6336
+ } catch {
6337
+ }
6261
6338
  if (!options?.lightweight) {
6262
6339
  try {
6263
6340
  const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
@@ -6277,288 +6354,59 @@ async function initStore(options) {
6277
6354
  }
6278
6355
  }
6279
6356
  }
6280
- function classifyTier(record) {
6281
- if (record.tool_name === "commit_to_long_term_memory" && (record.importance ?? 0) >= 8) return 1;
6282
- if (["store_memory", "manual"].includes(record.tool_name ?? "") && (record.importance ?? 0) >= 5) return 2;
6283
- return 3;
6284
- }
6285
- function inferFilePaths(record) {
6286
- if (!["Read", "Write", "Edit"].includes(record.tool_name)) return null;
6287
- const firstLine = record.raw_text.split("\n")[0] ?? "";
6288
- const match = firstLine.match(/(\/[\w./-]+\.\w+)/);
6289
- return match ? JSON.stringify([match[1]]) : null;
6290
- }
6291
- function inferCommitHash(record) {
6292
- if (record.tool_name !== "Bash") return null;
6293
- const match = record.raw_text.match(/\b([a-f0-9]{7,40})\b/);
6294
- return match ? match[1] : null;
6295
- }
6296
- function inferLanguageType(record) {
6297
- const text = record.raw_text;
6298
- if (!text || text.length < 10) return null;
6299
- const trimmed = text.trimStart();
6300
- if (trimmed.startsWith("{") || trimmed.startsWith("[")) return "json";
6301
- if (/\b(SELECT|INSERT|UPDATE|DELETE|CREATE TABLE|ALTER TABLE)\b/i.test(text)) return "sql";
6302
- if (/\b(function |const |import |export |class |def |async |=>)\b/.test(text)) return "code";
6303
- if (trimmed.startsWith("#") || trimmed.startsWith("*")) return "prose";
6304
- return "mixed";
6305
- }
6306
- function inferDomain(record) {
6307
- const proj = (record.project_name ?? "").toLowerCase();
6308
- if (proj.includes("marketing") || proj.includes("content")) return "marketing";
6309
- if (proj.includes("crm") || proj.includes("customer")) return "customer";
6310
- return null;
6357
+ function vectorToBlob(vector) {
6358
+ const f32 = vector instanceof Float32Array ? vector : new Float32Array(vector);
6359
+ return JSON.stringify(Array.from(f32));
6311
6360
  }
6312
- async function writeMemory(record) {
6313
- if (record.vector !== null && record.vector.length !== EMBEDDING_DIM) {
6314
- throw new Error(
6315
- `Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
6316
- );
6361
+
6362
+ // src/adapters/claude/hooks/summary-worker.ts
6363
+ init_database();
6364
+ init_notifications();
6365
+
6366
+ // src/lib/memory-queue-client.ts
6367
+ init_exe_daemon_client();
6368
+
6369
+ // src/lib/memory-queue.ts
6370
+ init_config();
6371
+ import { appendFileSync as appendFileSync2, readFileSync as readFileSync11, renameSync as renameSync4, unlinkSync as unlinkSync4, existsSync as existsSync14, statSync as statSync2 } from "fs";
6372
+ import path15 from "path";
6373
+ var QUEUE_PATH2 = path15.join(EXE_AI_DIR, "memory-queue.jsonl");
6374
+ var PROCESSING_PATH = QUEUE_PATH2 + ".processing";
6375
+ var TTL_MS2 = 24 * 60 * 60 * 1e3;
6376
+ function enqueueMemory(entry) {
6377
+ appendFileSync2(QUEUE_PATH2, JSON.stringify(entry) + "\n");
6378
+ }
6379
+
6380
+ // src/lib/memory-queue-client.ts
6381
+ async function writeMemoryViaDaemon(entry) {
6382
+ if (process.env.EXE_IS_DAEMON === "1") {
6383
+ enqueueMemory(entry);
6384
+ return false;
6317
6385
  }
6318
- const contentHash = createHash("md5").update(record.raw_text).digest("hex");
6319
- if (_pendingRecords.some((r) => r.content_hash === contentHash && r.agent_id === record.agent_id)) {
6320
- return;
6386
+ if (!isClientConnected()) {
6387
+ enqueueMemory(entry);
6388
+ return false;
6321
6389
  }
6322
6390
  try {
6323
- const client = getClient();
6324
- const existing = await client.execute({
6325
- sql: "SELECT id FROM memories WHERE content_hash = ? AND agent_id = ? LIMIT 1",
6326
- args: [contentHash, record.agent_id]
6391
+ const response = await sendDaemonRequest({
6392
+ type: "write-memory",
6393
+ entry
6327
6394
  });
6328
- if (existing.rows.length > 0) return;
6395
+ if (response.ok) return true;
6396
+ enqueueMemory(entry);
6397
+ return false;
6329
6398
  } catch {
6399
+ enqueueMemory(entry);
6400
+ return false;
6330
6401
  }
6331
- const dbRow = {
6332
- id: record.id,
6333
- agent_id: record.agent_id,
6334
- agent_role: record.agent_role,
6335
- session_id: record.session_id,
6336
- timestamp: record.timestamp,
6337
- tool_name: record.tool_name,
6338
- project_name: record.project_name,
6339
- has_error: record.has_error ? 1 : 0,
6340
- raw_text: record.raw_text,
6341
- vector: record.vector,
6342
- version: 0,
6343
- // Placeholder — assigned atomically at flush time
6344
- task_id: record.task_id ?? null,
6345
- importance: record.importance ?? 5,
6346
- status: record.status ?? "active",
6347
- confidence: record.confidence ?? 0.7,
6348
- last_accessed: record.last_accessed ?? record.timestamp,
6349
- workspace_id: record.workspace_id ?? null,
6350
- document_id: record.document_id ?? null,
6351
- user_id: record.user_id ?? null,
6352
- char_offset: record.char_offset ?? null,
6353
- page_number: record.page_number ?? null,
6354
- source_path: record.source_path ?? null,
6355
- source_type: record.source_type ?? null,
6356
- tier: record.tier ?? classifyTier(record),
6357
- supersedes_id: record.supersedes_id ?? null,
6358
- draft: record.draft ? 1 : 0,
6359
- memory_type: record.memory_type ?? "raw",
6360
- trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
6361
- content_hash: contentHash,
6362
- intent: record.intent ?? null,
6363
- outcome: record.outcome ?? null,
6364
- domain: record.domain ?? inferDomain(record),
6365
- referenced_entities: record.referenced_entities ?? null,
6366
- retrieval_count: record.retrieval_count ?? 0,
6367
- chain_position: record.chain_position ?? null,
6368
- review_status: record.review_status ?? null,
6369
- context_window_pct: record.context_window_pct ?? null,
6370
- file_paths: record.file_paths ?? inferFilePaths(record),
6371
- commit_hash: record.commit_hash ?? inferCommitHash(record),
6372
- duration_ms: record.duration_ms ?? null,
6373
- token_cost: record.token_cost ?? null,
6374
- audience: record.audience ?? null,
6375
- language_type: record.language_type ?? inferLanguageType(record),
6376
- parent_memory_id: record.parent_memory_id ?? null
6377
- };
6378
- _pendingRecords.push(dbRow);
6379
- orgBus.emit({
6380
- type: "memory_stored",
6381
- agentId: record.agent_id,
6382
- project: record.project_name,
6383
- timestamp: record.timestamp
6384
- });
6385
- const MAX_PENDING = 1e3;
6386
- if (_pendingRecords.length > MAX_PENDING) {
6387
- const dropped = _pendingRecords.length - MAX_PENDING;
6388
- _pendingRecords = _pendingRecords.slice(-MAX_PENDING);
6389
- console.warn(`[store] Dropped ${dropped} oldest pending records (overflow)`);
6390
- }
6391
- if (_flushTimer === null) {
6392
- _flushTimer = setInterval(() => {
6393
- void flushBatch();
6394
- }, _flushIntervalMs);
6395
- if (_flushTimer && typeof _flushTimer === "object" && "unref" in _flushTimer) {
6396
- _flushTimer.unref();
6397
- }
6398
- }
6399
- if (_pendingRecords.length >= _batchSize) {
6400
- await flushBatch();
6401
- }
6402
- }
6403
- async function flushBatch() {
6404
- if (_flushing || _pendingRecords.length === 0) return 0;
6405
- _flushing = true;
6406
- try {
6407
- const batch = _pendingRecords.slice(0);
6408
- const client = getClient();
6409
- const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
6410
- let baseVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
6411
- for (const row of batch) {
6412
- row.version = baseVersion++;
6413
- }
6414
- _nextVersion = baseVersion;
6415
- const buildStmt = (row) => {
6416
- const hasVector = row.vector !== null;
6417
- const taskId = row.task_id ?? null;
6418
- const importance = row.importance ?? 5;
6419
- const status = row.status ?? "active";
6420
- const confidence = row.confidence ?? 0.7;
6421
- const lastAccessed = row.last_accessed ?? row.timestamp;
6422
- const workspaceId = row.workspace_id ?? null;
6423
- const documentId = row.document_id ?? null;
6424
- const userId = row.user_id ?? null;
6425
- const charOffset = row.char_offset ?? null;
6426
- const pageNumber = row.page_number ?? null;
6427
- const sourcePath = row.source_path ?? null;
6428
- const sourceType = row.source_type ?? null;
6429
- const tier = row.tier ?? 3;
6430
- const supersedesId = row.supersedes_id ?? null;
6431
- const draft = row.draft ? 1 : 0;
6432
- const memoryType = row.memory_type ?? "raw";
6433
- const trajectory = row.trajectory ?? null;
6434
- const contentHash = row.content_hash ?? null;
6435
- const intent = row.intent ?? null;
6436
- const outcome = row.outcome ?? null;
6437
- const domain = row.domain ?? null;
6438
- const referencedEntities = row.referenced_entities ?? null;
6439
- const retrievalCount = row.retrieval_count ?? 0;
6440
- const chainPosition = row.chain_position ?? null;
6441
- const reviewStatus = row.review_status ?? null;
6442
- const contextWindowPct = row.context_window_pct ?? null;
6443
- const filePaths = row.file_paths ?? null;
6444
- const commitHash = row.commit_hash ?? null;
6445
- const durationMs = row.duration_ms ?? null;
6446
- const tokenCost = row.token_cost ?? null;
6447
- const audience = row.audience ?? null;
6448
- const languageType = row.language_type ?? null;
6449
- const parentMemoryId = row.parent_memory_id ?? null;
6450
- const cols = `id, agent_id, agent_role, session_id, timestamp,
6451
- tool_name, project_name,
6452
- has_error, raw_text, vector, version, task_id, importance, status,
6453
- confidence, last_accessed,
6454
- workspace_id, document_id, user_id, char_offset, page_number,
6455
- source_path, source_type, tier, supersedes_id, draft, memory_type, trajectory, content_hash,
6456
- intent, outcome, domain, referenced_entities, retrieval_count,
6457
- chain_position, review_status, context_window_pct, file_paths, commit_hash,
6458
- duration_ms, token_cost, audience, language_type, parent_memory_id`;
6459
- const metaArgs = [
6460
- intent,
6461
- outcome,
6462
- domain,
6463
- referencedEntities,
6464
- retrievalCount,
6465
- chainPosition,
6466
- reviewStatus,
6467
- contextWindowPct,
6468
- filePaths,
6469
- commitHash,
6470
- durationMs,
6471
- tokenCost,
6472
- audience,
6473
- languageType,
6474
- parentMemoryId
6475
- ];
6476
- const baseArgs = [
6477
- row.id,
6478
- row.agent_id,
6479
- row.agent_role,
6480
- row.session_id,
6481
- row.timestamp,
6482
- row.tool_name,
6483
- row.project_name,
6484
- row.has_error,
6485
- row.raw_text
6486
- ];
6487
- const sharedArgs = [
6488
- row.version,
6489
- taskId,
6490
- importance,
6491
- status,
6492
- confidence,
6493
- lastAccessed,
6494
- workspaceId,
6495
- documentId,
6496
- userId,
6497
- charOffset,
6498
- pageNumber,
6499
- sourcePath,
6500
- sourceType,
6501
- tier,
6502
- supersedesId,
6503
- draft,
6504
- memoryType,
6505
- trajectory,
6506
- contentHash
6507
- ];
6508
- return {
6509
- sql: hasVector ? `INSERT OR IGNORE INTO memories (${cols})
6510
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories (${cols})
6511
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
6512
- args: hasVector ? [...baseArgs, vectorToBlob(row.vector), ...sharedArgs, ...metaArgs] : [...baseArgs, ...sharedArgs, ...metaArgs]
6513
- };
6514
- };
6515
- const globalClient = getClient();
6516
- const globalStmts = batch.map(buildStmt);
6517
- await globalClient.batch(globalStmts, "write");
6518
- _pendingRecords.splice(0, batch.length);
6519
- try {
6520
- const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
6521
- if (isShardingEnabled2()) {
6522
- const byProject = /* @__PURE__ */ new Map();
6523
- for (const row of batch) {
6524
- const proj = row.project_name || "unknown";
6525
- if (!byProject.has(proj)) byProject.set(proj, []);
6526
- byProject.get(proj).push(row);
6527
- }
6528
- for (const [project, rows] of byProject) {
6529
- try {
6530
- const shardClient = await getReadyShardClient2(project);
6531
- const shardStmts = rows.map(buildStmt);
6532
- await shardClient.batch(shardStmts, "write");
6533
- } catch (err) {
6534
- process.stderr.write(
6535
- `[store] Shard write failed for ${project}: ${err instanceof Error ? err.message : String(err)}
6536
- `
6537
- );
6538
- }
6539
- }
6540
- }
6541
- } catch {
6542
- }
6543
- return batch.length;
6544
- } finally {
6545
- _flushing = false;
6546
- }
6547
- }
6548
- function vectorToBlob(vector) {
6549
- const f32 = vector instanceof Float32Array ? vector : new Float32Array(vector);
6550
- return JSON.stringify(Array.from(f32));
6551
6402
  }
6552
6403
 
6553
6404
  // src/adapters/claude/hooks/summary-worker.ts
6554
- init_database();
6555
- init_notifications();
6556
6405
  init_task_scope();
6557
6406
  init_employees();
6558
- import crypto5 from "crypto";
6559
6407
  import { execSync as execSync4 } from "child_process";
6560
- import { existsSync as existsSync17, mkdirSync as mkdirSync9, openSync as openSync3, closeSync as closeSync3 } from "fs";
6561
- import path18 from "path";
6408
+ import { existsSync as existsSync18, mkdirSync as mkdirSync9, openSync as openSync3, closeSync as closeSync3 } from "fs";
6409
+ import path19 from "path";
6562
6410
  async function main() {
6563
6411
  const agentId = process.env.AGENT_ID ?? "default";
6564
6412
  const agentRole = process.env.AGENT_ROLE ?? "employee";
@@ -6637,19 +6485,15 @@ async function main() {
6637
6485
  }
6638
6486
  if (limitReached) {
6639
6487
  } else {
6640
- await writeMemory({
6641
- id: crypto5.randomUUID(),
6488
+ await writeMemoryViaDaemon({
6489
+ raw_text: summaryText,
6642
6490
  agent_id: agentId,
6643
6491
  agent_role: agentRole,
6644
6492
  session_id: `auto-summary-${Date.now()}`,
6645
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
6646
6493
  tool_name: "auto-summary",
6647
6494
  project_name: primaryProject,
6648
- has_error: false,
6649
- raw_text: summaryText,
6650
- vector: null
6495
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
6651
6496
  });
6652
- await flushBatch();
6653
6497
  if (!canCoordinate(agentId, agentRole)) {
6654
6498
  let totalErrors = 0;
6655
6499
  for (const [, data] of projects) {
@@ -6693,8 +6537,8 @@ async function main() {
6693
6537
  }
6694
6538
  try {
6695
6539
  const { EXE_AI_DIR: EXE_AI_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
6696
- const flagPath = path18.join(EXE_AI_DIR2, "session-cache", "needs-backfill");
6697
- if (existsSync17(flagPath)) {
6540
+ const flagPath = path19.join(EXE_AI_DIR2, "session-cache", "needs-backfill");
6541
+ if (existsSync18(flagPath)) {
6698
6542
  const { tryAcquireWorkerSlot: tryAcquireWorkerSlot2, registerWorkerPid: registerWorkerPid2 } = await Promise.resolve().then(() => (init_worker_gate(), worker_gate_exports));
6699
6543
  if (!tryAcquireWorkerSlot2()) {
6700
6544
  process.stderr.write("[summary-worker] Backfill needed but worker gate full \u2014 skipping\n");
@@ -6702,11 +6546,11 @@ async function main() {
6702
6546
  const { spawn: spawn2 } = await import("child_process");
6703
6547
  const { fileURLToPath: fileURLToPath3 } = await import("url");
6704
6548
  const thisFile = fileURLToPath3(import.meta.url);
6705
- const backfillPath = path18.resolve(path18.dirname(thisFile), "backfill-vectors.js");
6706
- if (existsSync17(backfillPath)) {
6549
+ const backfillPath = path19.resolve(path19.dirname(thisFile), "backfill-vectors.js");
6550
+ if (existsSync18(backfillPath)) {
6707
6551
  const { EXE_AI_DIR: exeDir2 } = await Promise.resolve().then(() => (init_config(), config_exports));
6708
- const bLogPath = path18.join(exeDir2, "workers.log");
6709
- mkdirSync9(path18.dirname(bLogPath), { recursive: true });
6552
+ const bLogPath = path19.join(exeDir2, "workers.log");
6553
+ mkdirSync9(path19.dirname(bLogPath), { recursive: true });
6710
6554
  const bLogFd = openSync3(bLogPath, "a");
6711
6555
  const child = spawn2(process.execPath, [backfillPath], {
6712
6556
  detached: true,