@askexenow/exe-os 0.8.83 → 0.8.86

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/dist/bin/backfill-conversations.js +746 -595
  2. package/dist/bin/backfill-responses.js +745 -594
  3. package/dist/bin/backfill-vectors.js +312 -226
  4. package/dist/bin/cleanup-stale-review-tasks.js +154 -21
  5. package/dist/bin/cli.js +14678 -12676
  6. package/dist/bin/exe-agent-config.js +242 -0
  7. package/dist/bin/exe-agent.js +100 -91
  8. package/dist/bin/exe-assign.js +1003 -854
  9. package/dist/bin/exe-boot.js +1420 -485
  10. package/dist/bin/exe-call.js +10 -0
  11. package/dist/bin/exe-cloud.js +29 -6
  12. package/dist/bin/exe-dispatch.js +572 -271
  13. package/dist/bin/exe-doctor.js +403 -6
  14. package/dist/bin/exe-export-behaviors.js +175 -72
  15. package/dist/bin/exe-forget.js +102 -3
  16. package/dist/bin/exe-gateway.js +796 -292
  17. package/dist/bin/exe-healthcheck.js +134 -1
  18. package/dist/bin/exe-heartbeat.js +172 -36
  19. package/dist/bin/exe-kill.js +175 -72
  20. package/dist/bin/exe-launch-agent.js +189 -76
  21. package/dist/bin/exe-link.js +927 -82
  22. package/dist/bin/exe-new-employee.js +60 -8
  23. package/dist/bin/exe-pending-messages.js +151 -19
  24. package/dist/bin/exe-pending-notifications.js +97 -2
  25. package/dist/bin/exe-pending-reviews.js +155 -22
  26. package/dist/bin/exe-rename.js +564 -23
  27. package/dist/bin/exe-review.js +231 -73
  28. package/dist/bin/exe-search.js +995 -228
  29. package/dist/bin/exe-session-cleanup.js +4930 -1664
  30. package/dist/bin/exe-settings.js +20 -5
  31. package/dist/bin/exe-start-codex.js +2598 -0
  32. package/dist/bin/exe-start.sh +15 -3
  33. package/dist/bin/exe-status.js +154 -21
  34. package/dist/bin/exe-team.js +97 -2
  35. package/dist/bin/git-sweep.js +1180 -363
  36. package/dist/bin/graph-backfill.js +175 -72
  37. package/dist/bin/graph-export.js +175 -72
  38. package/dist/bin/install.js +60 -7
  39. package/dist/bin/list-providers.js +1 -0
  40. package/dist/bin/scan-tasks.js +1185 -367
  41. package/dist/bin/setup.js +914 -270
  42. package/dist/bin/shard-migrate.js +175 -72
  43. package/dist/bin/update.js +1 -0
  44. package/dist/bin/wiki-sync.js +175 -72
  45. package/dist/gateway/index.js +792 -285
  46. package/dist/hooks/bug-report-worker.js +445 -135
  47. package/dist/hooks/commit-complete.js +1178 -361
  48. package/dist/hooks/error-recall.js +994 -228
  49. package/dist/hooks/ingest-worker.js +1799 -1234
  50. package/dist/hooks/ingest.js +3 -0
  51. package/dist/hooks/instructions-loaded.js +707 -97
  52. package/dist/hooks/notification.js +699 -89
  53. package/dist/hooks/post-compact.js +757 -109
  54. package/dist/hooks/pre-compact.js +1061 -244
  55. package/dist/hooks/pre-tool-use.js +787 -130
  56. package/dist/hooks/prompt-ingest-worker.js +242 -101
  57. package/dist/hooks/prompt-submit.js +1121 -299
  58. package/dist/hooks/response-ingest-worker.js +242 -101
  59. package/dist/hooks/session-end.js +4063 -397
  60. package/dist/hooks/session-start.js +1071 -254
  61. package/dist/hooks/stop.js +768 -120
  62. package/dist/hooks/subagent-stop.js +757 -109
  63. package/dist/hooks/summary-worker.js +1706 -1011
  64. package/dist/index.js +1821 -1098
  65. package/dist/lib/agent-config.js +167 -0
  66. package/dist/lib/cloud-sync.js +932 -88
  67. package/dist/lib/consolidation.js +2 -1
  68. package/dist/lib/database.js +642 -87
  69. package/dist/lib/db-daemon-client.js +503 -0
  70. package/dist/lib/device-registry.js +547 -7
  71. package/dist/lib/embedder.js +14 -28
  72. package/dist/lib/employee-templates.js +84 -74
  73. package/dist/lib/employees.js +9 -0
  74. package/dist/lib/exe-daemon-client.js +16 -29
  75. package/dist/lib/exe-daemon.js +2733 -1575
  76. package/dist/lib/hybrid-search.js +995 -228
  77. package/dist/lib/identity.js +87 -67
  78. package/dist/lib/keychain.js +9 -1
  79. package/dist/lib/messaging.js +103 -40
  80. package/dist/lib/reminders.js +91 -74
  81. package/dist/lib/runtime-table.js +16 -0
  82. package/dist/lib/schedules.js +96 -2
  83. package/dist/lib/session-wrappers.js +22 -0
  84. package/dist/lib/skill-learning.js +103 -85
  85. package/dist/lib/store.js +234 -73
  86. package/dist/lib/tasks.js +348 -134
  87. package/dist/lib/tmux-routing.js +422 -208
  88. package/dist/lib/token-spend.js +273 -0
  89. package/dist/lib/ws-client.js +11 -0
  90. package/dist/mcp/server.js +5742 -696
  91. package/dist/mcp/tools/complete-reminder.js +94 -77
  92. package/dist/mcp/tools/create-reminder.js +94 -77
  93. package/dist/mcp/tools/create-task.js +375 -152
  94. package/dist/mcp/tools/deactivate-behavior.js +95 -77
  95. package/dist/mcp/tools/list-reminders.js +94 -77
  96. package/dist/mcp/tools/list-tasks.js +99 -31
  97. package/dist/mcp/tools/send-message.js +108 -45
  98. package/dist/mcp/tools/update-task.js +162 -77
  99. package/dist/runtime/index.js +1075 -258
  100. package/dist/tui/App.js +1333 -506
  101. package/package.json +6 -1
  102. package/src/commands/exe/agent-config.md +27 -0
  103. package/src/commands/exe/cc-doctor.md +10 -0
@@ -55,7 +55,16 @@ if [ "${1:-}" = "go" ]; then
55
55
  fi
56
56
 
57
57
  if tmux has-session -t "$SESSION" 2>/dev/null; then
58
- exec tmux attach -t "$SESSION"
58
+ # Detect if already inside the target session — prevent double-attach
59
+ CURRENT_SESSION="$(tmux display-message -p '#{session_name}' 2>/dev/null || true)"
60
+ if [ "$CURRENT_SESSION" = "$SESSION" ]; then
61
+ echo " Already inside ${SESSION}."
62
+ exit 0
63
+ fi
64
+ # Unset TMUX to allow attach from auto-tmux shells (common zsh/bash configs).
65
+ # -d detaches other clients to prevent status-bar-in-body rendering bug.
66
+ unset TMUX
67
+ exec tmux attach -d -t "$SESSION"
59
68
  else
60
69
  if [ "$NAME" = "$COO_NAME" ]; then
61
70
  echo " ${SESSION} is not running. Use ${COO_NAME}${NUM} to start a new session."
@@ -125,9 +134,12 @@ if tmux has-session -t "$SESSION" 2>/dev/null; then
125
134
  exit 1
126
135
  fi
127
136
 
137
+ # Unset TMUX to allow creating sessions from auto-tmux shells
138
+ unset TMUX
139
+
128
140
  # Create session and launch COO with full identity + behaviors + bypass permissions
129
141
  tmux new-session -d -s "$SESSION" -c "$PWD"
130
142
  tmux send-keys -t "$SESSION" "exe-launch-agent --agent $COO_NAME" Enter
131
143
 
132
- # Attach user to session
133
- exec tmux attach -t "$SESSION"
144
+ # Attach user to session (-d detaches other clients to prevent rendering conflict)
145
+ exec tmux attach -d -t "$SESSION"
@@ -327,6 +327,12 @@ function getClient() {
327
327
  if (!_resilientClient) {
328
328
  throw new Error("Database client not initialized. Call initDatabase() first.");
329
329
  }
330
+ if (process.env.EXE_IS_DAEMON === "1") {
331
+ return _resilientClient;
332
+ }
333
+ if (_daemonClient && _daemonClient._isDaemonActive()) {
334
+ return _daemonClient;
335
+ }
330
336
  return _resilientClient;
331
337
  }
332
338
  function getRawClient() {
@@ -815,6 +821,12 @@ async function ensureSchema() {
815
821
  } catch {
816
822
  }
817
823
  }
824
+ try {
825
+ await client.execute(
826
+ `CREATE INDEX IF NOT EXISTS idx_memories_content_hash ON memories(content_hash, agent_id)`
827
+ );
828
+ } catch {
829
+ }
818
830
  await client.executeMultiple(`
819
831
  CREATE TABLE IF NOT EXISTS entities (
820
832
  id TEXT PRIMARY KEY,
@@ -867,7 +879,30 @@ async function ensureSchema() {
867
879
  entity_id TEXT NOT NULL,
868
880
  PRIMARY KEY (hyperedge_id, entity_id)
869
881
  );
882
+
883
+ CREATE VIRTUAL TABLE IF NOT EXISTS entities_fts USING fts5(
884
+ name,
885
+ content=entities,
886
+ content_rowid=rowid
887
+ );
888
+
889
+ CREATE TRIGGER IF NOT EXISTS entities_fts_ai AFTER INSERT ON entities BEGIN
890
+ INSERT INTO entities_fts(rowid, name) VALUES (new.rowid, new.name);
891
+ END;
892
+
893
+ CREATE TRIGGER IF NOT EXISTS entities_fts_ad AFTER DELETE ON entities BEGIN
894
+ INSERT INTO entities_fts(entities_fts, rowid, name) VALUES('delete', old.rowid, old.name);
895
+ END;
896
+
897
+ CREATE TRIGGER IF NOT EXISTS entities_fts_au AFTER UPDATE ON entities BEGIN
898
+ INSERT INTO entities_fts(entities_fts, rowid, name) VALUES('delete', old.rowid, old.name);
899
+ INSERT INTO entities_fts(rowid, name) VALUES (new.rowid, new.name);
900
+ END;
870
901
  `);
902
+ try {
903
+ await client.execute("INSERT INTO entities_fts(entities_fts) VALUES('rebuild')");
904
+ } catch {
905
+ }
871
906
  await client.executeMultiple(`
872
907
  CREATE TABLE IF NOT EXISTS entity_aliases (
873
908
  alias TEXT NOT NULL PRIMARY KEY,
@@ -1048,6 +1083,33 @@ async function ensureSchema() {
1048
1083
  CREATE INDEX IF NOT EXISTS idx_conversations_channel
1049
1084
  ON conversations(channel_id);
1050
1085
  `);
1086
+ await client.executeMultiple(`
1087
+ CREATE TABLE IF NOT EXISTS session_agent_map (
1088
+ session_uuid TEXT PRIMARY KEY,
1089
+ agent_id TEXT NOT NULL,
1090
+ session_name TEXT,
1091
+ task_id TEXT,
1092
+ project_name TEXT,
1093
+ started_at TEXT NOT NULL
1094
+ );
1095
+
1096
+ CREATE INDEX IF NOT EXISTS idx_session_agent_map_agent
1097
+ ON session_agent_map(agent_id);
1098
+ `);
1099
+ try {
1100
+ const mapCount = await client.execute({ sql: `SELECT COUNT(*) as cnt FROM session_agent_map`, args: [] });
1101
+ if (Number(mapCount.rows[0]?.cnt ?? 0) === 0) {
1102
+ await client.execute({
1103
+ sql: `INSERT OR IGNORE INTO session_agent_map (session_uuid, agent_id, session_name, started_at)
1104
+ SELECT session_id, agent_id, '', MIN(timestamp)
1105
+ FROM memories
1106
+ WHERE session_id IS NOT NULL AND session_id != '' AND agent_id IS NOT NULL AND agent_id != ''
1107
+ GROUP BY session_id, agent_id`,
1108
+ args: []
1109
+ });
1110
+ }
1111
+ } catch {
1112
+ }
1051
1113
  try {
1052
1114
  await client.execute({
1053
1115
  sql: `ALTER TABLE tasks ADD COLUMN budget_tokens INTEGER`,
@@ -1181,8 +1243,30 @@ async function ensureSchema() {
1181
1243
  });
1182
1244
  } catch {
1183
1245
  }
1246
+ for (const col of [
1247
+ "ALTER TABLE memories ADD COLUMN intent TEXT",
1248
+ "ALTER TABLE memories ADD COLUMN outcome TEXT",
1249
+ "ALTER TABLE memories ADD COLUMN domain TEXT",
1250
+ "ALTER TABLE memories ADD COLUMN referenced_entities TEXT",
1251
+ "ALTER TABLE memories ADD COLUMN retrieval_count INTEGER DEFAULT 0",
1252
+ "ALTER TABLE memories ADD COLUMN chain_position TEXT",
1253
+ "ALTER TABLE memories ADD COLUMN review_status TEXT",
1254
+ "ALTER TABLE memories ADD COLUMN context_window_pct INTEGER",
1255
+ "ALTER TABLE memories ADD COLUMN file_paths TEXT",
1256
+ "ALTER TABLE memories ADD COLUMN commit_hash TEXT",
1257
+ "ALTER TABLE memories ADD COLUMN duration_ms INTEGER",
1258
+ "ALTER TABLE memories ADD COLUMN token_cost REAL",
1259
+ "ALTER TABLE memories ADD COLUMN audience TEXT",
1260
+ "ALTER TABLE memories ADD COLUMN language_type TEXT",
1261
+ "ALTER TABLE memories ADD COLUMN parent_memory_id TEXT"
1262
+ ]) {
1263
+ try {
1264
+ await client.execute(col);
1265
+ } catch {
1266
+ }
1267
+ }
1184
1268
  }
1185
- var _client, _resilientClient, initTurso;
1269
+ var _client, _resilientClient, _daemonClient, initTurso;
1186
1270
  var init_database = __esm({
1187
1271
  "src/lib/database.ts"() {
1188
1272
  "use strict";
@@ -1190,6 +1274,7 @@ var init_database = __esm({
1190
1274
  init_employees();
1191
1275
  _client = null;
1192
1276
  _resilientClient = null;
1277
+ _daemonClient = null;
1193
1278
  initTurso = initDatabase;
1194
1279
  }
1195
1280
  });
@@ -1857,39 +1942,75 @@ var init_provider_table = __esm({
1857
1942
  }
1858
1943
  });
1859
1944
 
1860
- // src/lib/intercom-queue.ts
1861
- import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, renameSync as renameSync3, existsSync as existsSync5, mkdirSync as mkdirSync2 } from "fs";
1945
+ // src/lib/runtime-table.ts
1946
+ var RUNTIME_TABLE;
1947
+ var init_runtime_table = __esm({
1948
+ "src/lib/runtime-table.ts"() {
1949
+ "use strict";
1950
+ RUNTIME_TABLE = {
1951
+ codex: {
1952
+ binary: "codex",
1953
+ launchMode: "exec",
1954
+ autoApproveFlag: "--full-auto",
1955
+ inlineFlag: "--no-alt-screen",
1956
+ apiKeyEnv: "OPENAI_API_KEY",
1957
+ defaultModel: "gpt-5.4"
1958
+ }
1959
+ };
1960
+ }
1961
+ });
1962
+
1963
+ // src/lib/agent-config.ts
1964
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, existsSync as existsSync5, mkdirSync as mkdirSync2 } from "fs";
1862
1965
  import path6 from "path";
1966
+ var AGENT_CONFIG_PATH, DEFAULT_MODELS;
1967
+ var init_agent_config = __esm({
1968
+ "src/lib/agent-config.ts"() {
1969
+ "use strict";
1970
+ init_config();
1971
+ init_runtime_table();
1972
+ AGENT_CONFIG_PATH = path6.join(EXE_AI_DIR, "agent-config.json");
1973
+ DEFAULT_MODELS = {
1974
+ claude: "claude-opus-4",
1975
+ codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
1976
+ opencode: "minimax-m2.7"
1977
+ };
1978
+ }
1979
+ });
1980
+
1981
+ // src/lib/intercom-queue.ts
1982
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, renameSync as renameSync3, existsSync as existsSync6, mkdirSync as mkdirSync3 } from "fs";
1983
+ import path7 from "path";
1863
1984
  import os5 from "os";
1864
1985
  var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
1865
1986
  var init_intercom_queue = __esm({
1866
1987
  "src/lib/intercom-queue.ts"() {
1867
1988
  "use strict";
1868
- QUEUE_PATH = path6.join(os5.homedir(), ".exe-os", "intercom-queue.json");
1989
+ QUEUE_PATH = path7.join(os5.homedir(), ".exe-os", "intercom-queue.json");
1869
1990
  TTL_MS = 60 * 60 * 1e3;
1870
- INTERCOM_LOG = path6.join(os5.homedir(), ".exe-os", "intercom.log");
1991
+ INTERCOM_LOG = path7.join(os5.homedir(), ".exe-os", "intercom.log");
1871
1992
  }
1872
1993
  });
1873
1994
 
1874
1995
  // src/lib/license.ts
1875
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync6, mkdirSync as mkdirSync3 } from "fs";
1996
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, existsSync as existsSync7, mkdirSync as mkdirSync4 } from "fs";
1876
1997
  import { randomUUID as randomUUID2 } from "crypto";
1877
- import path7 from "path";
1998
+ import path8 from "path";
1878
1999
  import { jwtVerify, importSPKI } from "jose";
1879
2000
  var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH;
1880
2001
  var init_license = __esm({
1881
2002
  "src/lib/license.ts"() {
1882
2003
  "use strict";
1883
2004
  init_config();
1884
- LICENSE_PATH = path7.join(EXE_AI_DIR, "license.key");
1885
- CACHE_PATH = path7.join(EXE_AI_DIR, "license-cache.json");
1886
- DEVICE_ID_PATH = path7.join(EXE_AI_DIR, "device-id");
2005
+ LICENSE_PATH = path8.join(EXE_AI_DIR, "license.key");
2006
+ CACHE_PATH = path8.join(EXE_AI_DIR, "license-cache.json");
2007
+ DEVICE_ID_PATH = path8.join(EXE_AI_DIR, "device-id");
1887
2008
  }
1888
2009
  });
1889
2010
 
1890
2011
  // src/lib/plan-limits.ts
1891
- import { readFileSync as readFileSync5, existsSync as existsSync7 } from "fs";
1892
- import path8 from "path";
2012
+ import { readFileSync as readFileSync6, existsSync as existsSync8 } from "fs";
2013
+ import path9 from "path";
1893
2014
  var CACHE_PATH2;
1894
2015
  var init_plan_limits = __esm({
1895
2016
  "src/lib/plan-limits.ts"() {
@@ -1898,13 +2019,13 @@ var init_plan_limits = __esm({
1898
2019
  init_employees();
1899
2020
  init_license();
1900
2021
  init_config();
1901
- CACHE_PATH2 = path8.join(EXE_AI_DIR, "license-cache.json");
2022
+ CACHE_PATH2 = path9.join(EXE_AI_DIR, "license-cache.json");
1902
2023
  }
1903
2024
  });
1904
2025
 
1905
2026
  // src/lib/tmux-routing.ts
1906
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4, existsSync as existsSync8, appendFileSync } from "fs";
1907
- import path9 from "path";
2027
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, existsSync as existsSync9, appendFileSync } from "fs";
2028
+ import path10 from "path";
1908
2029
  import os6 from "os";
1909
2030
  import { fileURLToPath as fileURLToPath2 } from "url";
1910
2031
  function getMySession() {
@@ -1918,7 +2039,7 @@ function extractRootExe(name) {
1918
2039
  }
1919
2040
  function getParentExe(sessionKey) {
1920
2041
  try {
1921
- const data = JSON.parse(readFileSync6(path9.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
2042
+ const data = JSON.parse(readFileSync7(path10.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
1922
2043
  return data.parentExe || null;
1923
2044
  } catch {
1924
2045
  return null;
@@ -1947,13 +2068,15 @@ var init_tmux_routing = __esm({
1947
2068
  init_cc_agent_support();
1948
2069
  init_mcp_prefix();
1949
2070
  init_provider_table();
2071
+ init_agent_config();
2072
+ init_runtime_table();
1950
2073
  init_intercom_queue();
1951
2074
  init_plan_limits();
1952
2075
  init_employees();
1953
- SPAWN_LOCK_DIR = path9.join(os6.homedir(), ".exe-os", "spawn-locks");
1954
- SESSION_CACHE = path9.join(os6.homedir(), ".exe-os", "session-cache");
1955
- INTERCOM_LOG2 = path9.join(os6.homedir(), ".exe-os", "intercom.log");
1956
- DEBOUNCE_FILE = path9.join(SESSION_CACHE, "intercom-debounce.json");
2076
+ SPAWN_LOCK_DIR = path10.join(os6.homedir(), ".exe-os", "spawn-locks");
2077
+ SESSION_CACHE = path10.join(os6.homedir(), ".exe-os", "session-cache");
2078
+ INTERCOM_LOG2 = path10.join(os6.homedir(), ".exe-os", "intercom.log");
2079
+ DEBOUNCE_FILE = path10.join(SESSION_CACHE, "intercom-debounce.json");
1957
2080
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
1958
2081
  }
1959
2082
  });
@@ -1986,6 +2109,7 @@ var init_task_scope = __esm({
1986
2109
  init_employees();
1987
2110
 
1988
2111
  // src/lib/store.ts
2112
+ import { createHash } from "crypto";
1989
2113
  init_database();
1990
2114
 
1991
2115
  // src/lib/keychain.ts
@@ -2021,12 +2145,20 @@ async function getMasterKey() {
2021
2145
  }
2022
2146
  const keyPath = getKeyPath();
2023
2147
  if (!existsSync3(keyPath)) {
2148
+ process.stderr.write(
2149
+ `[keychain] Key not found at ${keyPath} (HOME=${os3.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
2150
+ `
2151
+ );
2024
2152
  return null;
2025
2153
  }
2026
2154
  try {
2027
2155
  const content = await readFile3(keyPath, "utf-8");
2028
2156
  return Buffer.from(content.trim(), "base64");
2029
- } catch {
2157
+ } catch (err) {
2158
+ process.stderr.write(
2159
+ `[keychain] Key read failed at ${keyPath}: ${err instanceof Error ? err.message : String(err)}
2160
+ `
2161
+ );
2030
2162
  return null;
2031
2163
  }
2032
2164
  }
@@ -2340,6 +2472,7 @@ import { realpathSync } from "fs";
2340
2472
  import { fileURLToPath } from "url";
2341
2473
  function isMainModule(importMetaUrl) {
2342
2474
  if (process.argv[1] == null) return false;
2475
+ if (process.argv[1].includes("mcp/server")) return false;
2343
2476
  try {
2344
2477
  const scriptPath = realpathSync(process.argv[1]);
2345
2478
  const modulePath = realpathSync(fileURLToPath(importMetaUrl));
@@ -316,6 +316,12 @@ function getClient() {
316
316
  if (!_resilientClient) {
317
317
  throw new Error("Database client not initialized. Call initDatabase() first.");
318
318
  }
319
+ if (process.env.EXE_IS_DAEMON === "1") {
320
+ return _resilientClient;
321
+ }
322
+ if (_daemonClient && _daemonClient._isDaemonActive()) {
323
+ return _daemonClient;
324
+ }
319
325
  return _resilientClient;
320
326
  }
321
327
  function getRawClient() {
@@ -804,6 +810,12 @@ async function ensureSchema() {
804
810
  } catch {
805
811
  }
806
812
  }
813
+ try {
814
+ await client.execute(
815
+ `CREATE INDEX IF NOT EXISTS idx_memories_content_hash ON memories(content_hash, agent_id)`
816
+ );
817
+ } catch {
818
+ }
807
819
  await client.executeMultiple(`
808
820
  CREATE TABLE IF NOT EXISTS entities (
809
821
  id TEXT PRIMARY KEY,
@@ -856,7 +868,30 @@ async function ensureSchema() {
856
868
  entity_id TEXT NOT NULL,
857
869
  PRIMARY KEY (hyperedge_id, entity_id)
858
870
  );
871
+
872
+ CREATE VIRTUAL TABLE IF NOT EXISTS entities_fts USING fts5(
873
+ name,
874
+ content=entities,
875
+ content_rowid=rowid
876
+ );
877
+
878
+ CREATE TRIGGER IF NOT EXISTS entities_fts_ai AFTER INSERT ON entities BEGIN
879
+ INSERT INTO entities_fts(rowid, name) VALUES (new.rowid, new.name);
880
+ END;
881
+
882
+ CREATE TRIGGER IF NOT EXISTS entities_fts_ad AFTER DELETE ON entities BEGIN
883
+ INSERT INTO entities_fts(entities_fts, rowid, name) VALUES('delete', old.rowid, old.name);
884
+ END;
885
+
886
+ CREATE TRIGGER IF NOT EXISTS entities_fts_au AFTER UPDATE ON entities BEGIN
887
+ INSERT INTO entities_fts(entities_fts, rowid, name) VALUES('delete', old.rowid, old.name);
888
+ INSERT INTO entities_fts(rowid, name) VALUES (new.rowid, new.name);
889
+ END;
859
890
  `);
891
+ try {
892
+ await client.execute("INSERT INTO entities_fts(entities_fts) VALUES('rebuild')");
893
+ } catch {
894
+ }
860
895
  await client.executeMultiple(`
861
896
  CREATE TABLE IF NOT EXISTS entity_aliases (
862
897
  alias TEXT NOT NULL PRIMARY KEY,
@@ -1037,6 +1072,33 @@ async function ensureSchema() {
1037
1072
  CREATE INDEX IF NOT EXISTS idx_conversations_channel
1038
1073
  ON conversations(channel_id);
1039
1074
  `);
1075
+ await client.executeMultiple(`
1076
+ CREATE TABLE IF NOT EXISTS session_agent_map (
1077
+ session_uuid TEXT PRIMARY KEY,
1078
+ agent_id TEXT NOT NULL,
1079
+ session_name TEXT,
1080
+ task_id TEXT,
1081
+ project_name TEXT,
1082
+ started_at TEXT NOT NULL
1083
+ );
1084
+
1085
+ CREATE INDEX IF NOT EXISTS idx_session_agent_map_agent
1086
+ ON session_agent_map(agent_id);
1087
+ `);
1088
+ try {
1089
+ const mapCount = await client.execute({ sql: `SELECT COUNT(*) as cnt FROM session_agent_map`, args: [] });
1090
+ if (Number(mapCount.rows[0]?.cnt ?? 0) === 0) {
1091
+ await client.execute({
1092
+ sql: `INSERT OR IGNORE INTO session_agent_map (session_uuid, agent_id, session_name, started_at)
1093
+ SELECT session_id, agent_id, '', MIN(timestamp)
1094
+ FROM memories
1095
+ WHERE session_id IS NOT NULL AND session_id != '' AND agent_id IS NOT NULL AND agent_id != ''
1096
+ GROUP BY session_id, agent_id`,
1097
+ args: []
1098
+ });
1099
+ }
1100
+ } catch {
1101
+ }
1040
1102
  try {
1041
1103
  await client.execute({
1042
1104
  sql: `ALTER TABLE tasks ADD COLUMN budget_tokens INTEGER`,
@@ -1170,8 +1232,30 @@ async function ensureSchema() {
1170
1232
  });
1171
1233
  } catch {
1172
1234
  }
1235
+ for (const col of [
1236
+ "ALTER TABLE memories ADD COLUMN intent TEXT",
1237
+ "ALTER TABLE memories ADD COLUMN outcome TEXT",
1238
+ "ALTER TABLE memories ADD COLUMN domain TEXT",
1239
+ "ALTER TABLE memories ADD COLUMN referenced_entities TEXT",
1240
+ "ALTER TABLE memories ADD COLUMN retrieval_count INTEGER DEFAULT 0",
1241
+ "ALTER TABLE memories ADD COLUMN chain_position TEXT",
1242
+ "ALTER TABLE memories ADD COLUMN review_status TEXT",
1243
+ "ALTER TABLE memories ADD COLUMN context_window_pct INTEGER",
1244
+ "ALTER TABLE memories ADD COLUMN file_paths TEXT",
1245
+ "ALTER TABLE memories ADD COLUMN commit_hash TEXT",
1246
+ "ALTER TABLE memories ADD COLUMN duration_ms INTEGER",
1247
+ "ALTER TABLE memories ADD COLUMN token_cost REAL",
1248
+ "ALTER TABLE memories ADD COLUMN audience TEXT",
1249
+ "ALTER TABLE memories ADD COLUMN language_type TEXT",
1250
+ "ALTER TABLE memories ADD COLUMN parent_memory_id TEXT"
1251
+ ]) {
1252
+ try {
1253
+ await client.execute(col);
1254
+ } catch {
1255
+ }
1256
+ }
1173
1257
  }
1174
- var _client, _resilientClient, initTurso;
1258
+ var _client, _resilientClient, _daemonClient, initTurso;
1175
1259
  var init_database = __esm({
1176
1260
  "src/lib/database.ts"() {
1177
1261
  "use strict";
@@ -1179,6 +1263,7 @@ var init_database = __esm({
1179
1263
  init_employees();
1180
1264
  _client = null;
1181
1265
  _resilientClient = null;
1266
+ _daemonClient = null;
1182
1267
  initTurso = initDatabase;
1183
1268
  }
1184
1269
  });
@@ -1613,6 +1698,7 @@ ${p.content}`).join("\n\n");
1613
1698
  init_employees();
1614
1699
 
1615
1700
  // src/lib/store.ts
1701
+ import { createHash } from "crypto";
1616
1702
  init_database();
1617
1703
 
1618
1704
  // src/lib/keychain.ts
@@ -1648,12 +1734,20 @@ async function getMasterKey() {
1648
1734
  }
1649
1735
  const keyPath = getKeyPath();
1650
1736
  if (!existsSync3(keyPath)) {
1737
+ process.stderr.write(
1738
+ `[keychain] Key not found at ${keyPath} (HOME=${os3.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
1739
+ `
1740
+ );
1651
1741
  return null;
1652
1742
  }
1653
1743
  try {
1654
1744
  const content = await readFile3(keyPath, "utf-8");
1655
1745
  return Buffer.from(content.trim(), "base64");
1656
- } catch {
1746
+ } catch (err) {
1747
+ process.stderr.write(
1748
+ `[keychain] Key read failed at ${keyPath}: ${err instanceof Error ? err.message : String(err)}
1749
+ `
1750
+ );
1657
1751
  return null;
1658
1752
  }
1659
1753
  }
@@ -1796,6 +1890,7 @@ import { realpathSync } from "fs";
1796
1890
  import { fileURLToPath } from "url";
1797
1891
  function isMainModule(importMetaUrl) {
1798
1892
  if (process.argv[1] == null) return false;
1893
+ if (process.argv[1].includes("mcp/server")) return false;
1799
1894
  try {
1800
1895
  const scriptPath = realpathSync(process.argv[1]);
1801
1896
  const modulePath = realpathSync(fileURLToPath(importMetaUrl));