@askexenow/exe-os 0.9.8 → 0.9.9

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 (101) hide show
  1. package/dist/bin/backfill-conversations.js +222 -49
  2. package/dist/bin/backfill-responses.js +221 -48
  3. package/dist/bin/backfill-vectors.js +225 -52
  4. package/dist/bin/cleanup-stale-review-tasks.js +150 -28
  5. package/dist/bin/cli.js +1295 -856
  6. package/dist/bin/exe-agent-config.js +36 -8
  7. package/dist/bin/exe-agent.js +14 -4
  8. package/dist/bin/exe-assign.js +221 -48
  9. package/dist/bin/exe-boot.js +778 -427
  10. package/dist/bin/exe-call.js +41 -13
  11. package/dist/bin/exe-cloud.js +163 -58
  12. package/dist/bin/exe-dispatch.js +276 -139
  13. package/dist/bin/exe-doctor.js +145 -27
  14. package/dist/bin/exe-export-behaviors.js +141 -23
  15. package/dist/bin/exe-forget.js +137 -19
  16. package/dist/bin/exe-gateway.js +677 -388
  17. package/dist/bin/exe-heartbeat.js +227 -108
  18. package/dist/bin/exe-kill.js +138 -20
  19. package/dist/bin/exe-launch-agent.js +172 -39
  20. package/dist/bin/exe-link.js +291 -100
  21. package/dist/bin/exe-new-employee.js +214 -106
  22. package/dist/bin/exe-pending-messages.js +395 -33
  23. package/dist/bin/exe-pending-notifications.js +684 -99
  24. package/dist/bin/exe-pending-reviews.js +420 -74
  25. package/dist/bin/exe-rename.js +147 -49
  26. package/dist/bin/exe-review.js +138 -20
  27. package/dist/bin/exe-search.js +240 -69
  28. package/dist/bin/exe-session-cleanup.js +440 -250
  29. package/dist/bin/exe-settings.js +61 -17
  30. package/dist/bin/exe-start-codex.js +158 -39
  31. package/dist/bin/exe-start-opencode.js +157 -38
  32. package/dist/bin/exe-status.js +151 -29
  33. package/dist/bin/exe-team.js +138 -20
  34. package/dist/bin/git-sweep.js +404 -212
  35. package/dist/bin/graph-backfill.js +137 -19
  36. package/dist/bin/graph-export.js +140 -22
  37. package/dist/bin/install.js +90 -61
  38. package/dist/bin/scan-tasks.js +412 -220
  39. package/dist/bin/setup.js +564 -293
  40. package/dist/bin/shard-migrate.js +139 -21
  41. package/dist/bin/update.js +138 -49
  42. package/dist/bin/wiki-sync.js +137 -19
  43. package/dist/gateway/index.js +533 -320
  44. package/dist/hooks/bug-report-worker.js +344 -193
  45. package/dist/hooks/codex-stop-task-finalizer.js +4678 -0
  46. package/dist/hooks/commit-complete.js +402 -210
  47. package/dist/hooks/error-recall.js +245 -74
  48. package/dist/hooks/exe-heartbeat-hook.js +16 -6
  49. package/dist/hooks/ingest-worker.js +3423 -3157
  50. package/dist/hooks/ingest.js +832 -97
  51. package/dist/hooks/instructions-loaded.js +227 -54
  52. package/dist/hooks/notification.js +216 -43
  53. package/dist/hooks/post-compact.js +239 -62
  54. package/dist/hooks/pre-compact.js +408 -216
  55. package/dist/hooks/pre-tool-use.js +268 -90
  56. package/dist/hooks/prompt-ingest-worker.js +352 -102
  57. package/dist/hooks/prompt-submit.js +541 -328
  58. package/dist/hooks/response-ingest-worker.js +372 -122
  59. package/dist/hooks/session-end.js +443 -240
  60. package/dist/hooks/session-start.js +313 -127
  61. package/dist/hooks/stop.js +293 -98
  62. package/dist/hooks/subagent-stop.js +239 -62
  63. package/dist/hooks/summary-worker.js +568 -236
  64. package/dist/index.js +538 -324
  65. package/dist/lib/agent-config.js +28 -6
  66. package/dist/lib/cloud-sync.js +284 -105
  67. package/dist/lib/config.js +30 -10
  68. package/dist/lib/consolidation.js +16 -6
  69. package/dist/lib/database.js +123 -25
  70. package/dist/lib/db-daemon-client.js +73 -19
  71. package/dist/lib/db.js +123 -25
  72. package/dist/lib/device-registry.js +133 -35
  73. package/dist/lib/embedder.js +107 -32
  74. package/dist/lib/employee-templates.js +14 -4
  75. package/dist/lib/employees.js +41 -13
  76. package/dist/lib/exe-daemon-client.js +88 -22
  77. package/dist/lib/exe-daemon.js +935 -587
  78. package/dist/lib/hybrid-search.js +240 -69
  79. package/dist/lib/identity.js +18 -8
  80. package/dist/lib/license.js +133 -48
  81. package/dist/lib/messaging.js +116 -56
  82. package/dist/lib/reminders.js +14 -4
  83. package/dist/lib/schedules.js +137 -19
  84. package/dist/lib/skill-learning.js +33 -6
  85. package/dist/lib/store.js +137 -19
  86. package/dist/lib/task-router.js +14 -4
  87. package/dist/lib/tasks.js +280 -234
  88. package/dist/lib/tmux-routing.js +172 -125
  89. package/dist/lib/token-spend.js +26 -8
  90. package/dist/mcp/server.js +1326 -609
  91. package/dist/mcp/tools/complete-reminder.js +14 -4
  92. package/dist/mcp/tools/create-reminder.js +14 -4
  93. package/dist/mcp/tools/create-task.js +306 -248
  94. package/dist/mcp/tools/deactivate-behavior.js +16 -6
  95. package/dist/mcp/tools/list-reminders.js +14 -4
  96. package/dist/mcp/tools/list-tasks.js +123 -107
  97. package/dist/mcp/tools/send-message.js +75 -29
  98. package/dist/mcp/tools/update-task.js +1848 -199
  99. package/dist/runtime/index.js +441 -248
  100. package/dist/tui/App.js +761 -424
  101. package/package.json +1 -1
@@ -1,5 +1,7 @@
1
1
  var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
2
3
  var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
3
5
  var __esm = (fn, res) => function __init() {
4
6
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
5
7
  };
@@ -7,6 +9,15 @@ var __export = (target, all) => {
7
9
  for (var name in all)
8
10
  __defProp(target, name, { get: all[name], enumerable: true });
9
11
  };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
10
21
 
11
22
  // src/lib/db-retry.ts
12
23
  function isBusyError(err) {
@@ -63,9 +74,34 @@ var init_db_retry = __esm({
63
74
  }
64
75
  });
65
76
 
77
+ // src/lib/secure-files.ts
78
+ import { chmodSync, existsSync, mkdirSync } from "fs";
79
+ import { chmod, mkdir } from "fs/promises";
80
+ async function ensurePrivateDir(dirPath) {
81
+ await mkdir(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
82
+ try {
83
+ await chmod(dirPath, PRIVATE_DIR_MODE);
84
+ } catch {
85
+ }
86
+ }
87
+ async function enforcePrivateFile(filePath) {
88
+ try {
89
+ await chmod(filePath, PRIVATE_FILE_MODE);
90
+ } catch {
91
+ }
92
+ }
93
+ var PRIVATE_DIR_MODE, PRIVATE_FILE_MODE;
94
+ var init_secure_files = __esm({
95
+ "src/lib/secure-files.ts"() {
96
+ "use strict";
97
+ PRIVATE_DIR_MODE = 448;
98
+ PRIVATE_FILE_MODE = 384;
99
+ }
100
+ });
101
+
66
102
  // src/lib/config.ts
67
- import { readFile, writeFile, mkdir, chmod } from "fs/promises";
68
- import { readFileSync, existsSync, renameSync } from "fs";
103
+ import { readFile, writeFile } from "fs/promises";
104
+ import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
69
105
  import path from "path";
70
106
  import os from "os";
71
107
  function resolveDataDir() {
@@ -73,7 +109,7 @@ function resolveDataDir() {
73
109
  if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
74
110
  const newDir = path.join(os.homedir(), ".exe-os");
75
111
  const legacyDir = path.join(os.homedir(), ".exe-mem");
76
- if (!existsSync(newDir) && existsSync(legacyDir)) {
112
+ if (!existsSync2(newDir) && existsSync2(legacyDir)) {
77
113
  try {
78
114
  renameSync(legacyDir, newDir);
79
115
  process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
@@ -136,9 +172,9 @@ function normalizeAutoUpdate(raw) {
136
172
  }
137
173
  async function loadConfig() {
138
174
  const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
139
- await mkdir(dir, { recursive: true });
175
+ await ensurePrivateDir(dir);
140
176
  const configPath = path.join(dir, "config.json");
141
- if (!existsSync(configPath)) {
177
+ if (!existsSync2(configPath)) {
142
178
  return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
143
179
  }
144
180
  const raw = await readFile(configPath, "utf-8");
@@ -151,6 +187,7 @@ async function loadConfig() {
151
187
  `);
152
188
  try {
153
189
  await writeFile(configPath, JSON.stringify(migratedCfg, null, 2) + "\n");
190
+ await enforcePrivateFile(configPath);
154
191
  } catch {
155
192
  }
156
193
  }
@@ -170,6 +207,7 @@ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CON
170
207
  var init_config = __esm({
171
208
  "src/lib/config.ts"() {
172
209
  "use strict";
210
+ init_secure_files();
173
211
  EXE_AI_DIR = resolveDataDir();
174
212
  DB_PATH = path.join(EXE_AI_DIR, "memories.db");
175
213
  MODELS_DIR = path.join(EXE_AI_DIR, "models");
@@ -248,7 +286,7 @@ var init_config = __esm({
248
286
 
249
287
  // src/lib/employees.ts
250
288
  import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
251
- import { existsSync as existsSync2, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
289
+ import { existsSync as existsSync3, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
252
290
  import { execSync } from "child_process";
253
291
  import path2 from "path";
254
292
  import os2 from "os";
@@ -265,7 +303,7 @@ function getCoordinatorName(employees = loadEmployeesSync()) {
265
303
  return getCoordinatorEmployee(employees)?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME;
266
304
  }
267
305
  function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
268
- if (!existsSync2(employeesPath)) return [];
306
+ if (!existsSync3(employeesPath)) return [];
269
307
  try {
270
308
  return JSON.parse(readFileSync2(employeesPath, "utf-8"));
271
309
  } catch {
@@ -1210,6 +1248,7 @@ async function ensureSchema() {
1210
1248
  project TEXT NOT NULL,
1211
1249
  summary TEXT NOT NULL,
1212
1250
  task_file TEXT,
1251
+ session_scope TEXT,
1213
1252
  read INTEGER NOT NULL DEFAULT 0,
1214
1253
  created_at TEXT NOT NULL
1215
1254
  );
@@ -1218,7 +1257,7 @@ async function ensureSchema() {
1218
1257
  ON notifications(read);
1219
1258
 
1220
1259
  CREATE INDEX IF NOT EXISTS idx_notifications_agent
1221
- ON notifications(agent_id);
1260
+ ON notifications(agent_id, session_scope);
1222
1261
 
1223
1262
  CREATE INDEX IF NOT EXISTS idx_notifications_task_file
1224
1263
  ON notifications(task_file);
@@ -1256,6 +1295,7 @@ async function ensureSchema() {
1256
1295
  target_agent TEXT NOT NULL,
1257
1296
  target_project TEXT,
1258
1297
  target_device TEXT NOT NULL DEFAULT 'local',
1298
+ session_scope TEXT,
1259
1299
  content TEXT NOT NULL,
1260
1300
  priority TEXT DEFAULT 'normal',
1261
1301
  status TEXT DEFAULT 'pending',
@@ -1269,10 +1309,31 @@ async function ensureSchema() {
1269
1309
  );
1270
1310
 
1271
1311
  CREATE INDEX IF NOT EXISTS idx_messages_target
1272
- ON messages(target_agent, status);
1312
+ ON messages(target_agent, session_scope, status);
1273
1313
 
1274
1314
  CREATE INDEX IF NOT EXISTS idx_messages_conversation_order
1275
- ON messages(target_agent, from_agent, server_seq);
1315
+ ON messages(target_agent, session_scope, from_agent, server_seq);
1316
+ `);
1317
+ try {
1318
+ await client.execute({
1319
+ sql: `ALTER TABLE notifications ADD COLUMN session_scope TEXT`,
1320
+ args: []
1321
+ });
1322
+ } catch {
1323
+ }
1324
+ try {
1325
+ await client.execute({
1326
+ sql: `ALTER TABLE messages ADD COLUMN session_scope TEXT`,
1327
+ args: []
1328
+ });
1329
+ } catch {
1330
+ }
1331
+ await client.executeMultiple(`
1332
+ CREATE INDEX IF NOT EXISTS idx_notifications_agent_scope_read
1333
+ ON notifications(agent_id, session_scope, read, created_at);
1334
+
1335
+ CREATE INDEX IF NOT EXISTS idx_messages_target_scope_status
1336
+ ON messages(target_agent, session_scope, status, created_at);
1276
1337
  `);
1277
1338
  try {
1278
1339
  await client.execute({
@@ -1856,6 +1917,13 @@ async function ensureSchema() {
1856
1917
  } catch {
1857
1918
  }
1858
1919
  }
1920
+ try {
1921
+ await client.execute({
1922
+ sql: `UPDATE tasks SET status = 'closed' WHERE status = 'done' AND result IS NOT NULL`,
1923
+ args: []
1924
+ });
1925
+ } catch {
1926
+ }
1859
1927
  }
1860
1928
  var _client, _resilientClient, _walCheckpointTimer, _daemonClient, _adapterClient, initTurso;
1861
1929
  var init_database = __esm({
@@ -1933,6 +2001,7 @@ var shard_manager_exports = {};
1933
2001
  __export(shard_manager_exports, {
1934
2002
  disposeShards: () => disposeShards,
1935
2003
  ensureShardSchema: () => ensureShardSchema,
2004
+ getOpenShardCount: () => getOpenShardCount,
1936
2005
  getReadyShardClient: () => getReadyShardClient,
1937
2006
  getShardClient: () => getShardClient,
1938
2007
  getShardsDir: () => getShardsDir,
@@ -1942,14 +2011,17 @@ __export(shard_manager_exports, {
1942
2011
  shardExists: () => shardExists
1943
2012
  });
1944
2013
  import path5 from "path";
1945
- import { existsSync as existsSync4, mkdirSync, readdirSync } from "fs";
2014
+ import { existsSync as existsSync5, mkdirSync as mkdirSync2, readdirSync } from "fs";
1946
2015
  import { createClient as createClient2 } from "@libsql/client";
1947
2016
  function initShardManager(encryptionKey) {
1948
2017
  _encryptionKey = encryptionKey;
1949
- if (!existsSync4(SHARDS_DIR)) {
1950
- mkdirSync(SHARDS_DIR, { recursive: true });
2018
+ if (!existsSync5(SHARDS_DIR)) {
2019
+ mkdirSync2(SHARDS_DIR, { recursive: true });
1951
2020
  }
1952
2021
  _shardingEnabled = true;
2022
+ if (_evictionTimer) clearInterval(_evictionTimer);
2023
+ _evictionTimer = setInterval(evictIdleShards, EVICTION_INTERVAL_MS);
2024
+ _evictionTimer.unref();
1953
2025
  }
1954
2026
  function isShardingEnabled() {
1955
2027
  return _shardingEnabled;
@@ -1966,21 +2038,28 @@ function getShardClient(projectName) {
1966
2038
  throw new Error(`Invalid project name for shard: "${projectName}"`);
1967
2039
  }
1968
2040
  const cached = _shards.get(safeName);
1969
- if (cached) return cached;
2041
+ if (cached) {
2042
+ _shardLastAccess.set(safeName, Date.now());
2043
+ return cached;
2044
+ }
2045
+ while (_shards.size >= MAX_OPEN_SHARDS) {
2046
+ evictLRU();
2047
+ }
1970
2048
  const dbPath = path5.join(SHARDS_DIR, `${safeName}.db`);
1971
2049
  const client = createClient2({
1972
2050
  url: `file:${dbPath}`,
1973
2051
  encryptionKey: _encryptionKey
1974
2052
  });
1975
2053
  _shards.set(safeName, client);
2054
+ _shardLastAccess.set(safeName, Date.now());
1976
2055
  return client;
1977
2056
  }
1978
2057
  function shardExists(projectName) {
1979
2058
  const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
1980
- return existsSync4(path5.join(SHARDS_DIR, `${safeName}.db`));
2059
+ return existsSync5(path5.join(SHARDS_DIR, `${safeName}.db`));
1981
2060
  }
1982
2061
  function listShards() {
1983
- if (!existsSync4(SHARDS_DIR)) return [];
2062
+ if (!existsSync5(SHARDS_DIR)) return [];
1984
2063
  return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
1985
2064
  }
1986
2065
  async function ensureShardSchema(client) {
@@ -2032,6 +2111,8 @@ async function ensureShardSchema(client) {
2032
2111
  for (const col of [
2033
2112
  "ALTER TABLE memories ADD COLUMN task_id TEXT",
2034
2113
  "ALTER TABLE memories ADD COLUMN consolidated INTEGER NOT NULL DEFAULT 0",
2114
+ "ALTER TABLE memories ADD COLUMN author_device_id TEXT",
2115
+ "ALTER TABLE memories ADD COLUMN scope TEXT NOT NULL DEFAULT 'business'",
2035
2116
  "ALTER TABLE memories ADD COLUMN importance INTEGER DEFAULT 5",
2036
2117
  "ALTER TABLE memories ADD COLUMN status TEXT DEFAULT 'active'",
2037
2118
  "ALTER TABLE memories ADD COLUMN wiki_synced INTEGER DEFAULT 0",
@@ -2169,21 +2250,69 @@ async function getReadyShardClient(projectName) {
2169
2250
  await ensureShardSchema(client);
2170
2251
  return client;
2171
2252
  }
2253
+ function evictLRU() {
2254
+ let oldest = null;
2255
+ let oldestTime = Infinity;
2256
+ for (const [name, time] of _shardLastAccess) {
2257
+ if (time < oldestTime) {
2258
+ oldestTime = time;
2259
+ oldest = name;
2260
+ }
2261
+ }
2262
+ if (oldest) {
2263
+ const client = _shards.get(oldest);
2264
+ if (client) {
2265
+ client.close();
2266
+ }
2267
+ _shards.delete(oldest);
2268
+ _shardLastAccess.delete(oldest);
2269
+ }
2270
+ }
2271
+ function evictIdleShards() {
2272
+ const now = Date.now();
2273
+ const toEvict = [];
2274
+ for (const [name, lastAccess] of _shardLastAccess) {
2275
+ if (now - lastAccess > SHARD_IDLE_MS) {
2276
+ toEvict.push(name);
2277
+ }
2278
+ }
2279
+ for (const name of toEvict) {
2280
+ const client = _shards.get(name);
2281
+ if (client) {
2282
+ client.close();
2283
+ }
2284
+ _shards.delete(name);
2285
+ _shardLastAccess.delete(name);
2286
+ }
2287
+ }
2288
+ function getOpenShardCount() {
2289
+ return _shards.size;
2290
+ }
2172
2291
  function disposeShards() {
2292
+ if (_evictionTimer) {
2293
+ clearInterval(_evictionTimer);
2294
+ _evictionTimer = null;
2295
+ }
2173
2296
  for (const [, client] of _shards) {
2174
2297
  client.close();
2175
2298
  }
2176
2299
  _shards.clear();
2300
+ _shardLastAccess.clear();
2177
2301
  _shardingEnabled = false;
2178
2302
  _encryptionKey = null;
2179
2303
  }
2180
- var SHARDS_DIR, _shards, _encryptionKey, _shardingEnabled;
2304
+ var SHARDS_DIR, SHARD_IDLE_MS, MAX_OPEN_SHARDS, EVICTION_INTERVAL_MS, _shards, _shardLastAccess, _evictionTimer, _encryptionKey, _shardingEnabled;
2181
2305
  var init_shard_manager = __esm({
2182
2306
  "src/lib/shard-manager.ts"() {
2183
2307
  "use strict";
2184
2308
  init_config();
2185
2309
  SHARDS_DIR = path5.join(EXE_AI_DIR, "shards");
2310
+ SHARD_IDLE_MS = 5 * 60 * 1e3;
2311
+ MAX_OPEN_SHARDS = 10;
2312
+ EVICTION_INTERVAL_MS = 60 * 1e3;
2186
2313
  _shards = /* @__PURE__ */ new Map();
2314
+ _shardLastAccess = /* @__PURE__ */ new Map();
2315
+ _evictionTimer = null;
2187
2316
  _encryptionKey = null;
2188
2317
  _shardingEnabled = false;
2189
2318
  }
@@ -2389,16 +2518,176 @@ var init_session_registry = __esm({
2389
2518
 
2390
2519
  // src/lib/session-key.ts
2391
2520
  import { execSync as execSync2 } from "child_process";
2521
+ function normalizeCommand(command) {
2522
+ const trimmed = command.trim().toLowerCase();
2523
+ const parts = trimmed.split(/[\\/]/);
2524
+ return parts[parts.length - 1] ?? trimmed;
2525
+ }
2526
+ function detectRuntimeFromCommand(command) {
2527
+ const normalized = normalizeCommand(command);
2528
+ for (const [runtime, commands] of Object.entries(RUNTIME_COMMANDS)) {
2529
+ if (commands.includes(normalized)) {
2530
+ return runtime;
2531
+ }
2532
+ }
2533
+ return null;
2534
+ }
2535
+ function resolveRuntimeProcess() {
2536
+ let pid = process.ppid;
2537
+ for (let i = 0; i < 10; i++) {
2538
+ try {
2539
+ const info = execSync2(`ps -p ${pid} -o ppid=,comm=`, {
2540
+ encoding: "utf8",
2541
+ timeout: 2e3
2542
+ }).trim();
2543
+ const match = info.match(/^\s*(\d+)\s+(.+)$/);
2544
+ if (!match) break;
2545
+ const [, ppid, cmd] = match;
2546
+ const runtime = detectRuntimeFromCommand(cmd ?? "");
2547
+ if (runtime) {
2548
+ return { pid: String(pid), runtime };
2549
+ }
2550
+ pid = parseInt(ppid, 10);
2551
+ if (pid <= 1) break;
2552
+ } catch {
2553
+ break;
2554
+ }
2555
+ }
2556
+ return null;
2557
+ }
2558
+ function getSessionKey() {
2559
+ if (_cached) return _cached;
2560
+ if (process.env.EXE_SESSION_KEY) {
2561
+ _cached = process.env.EXE_SESSION_KEY;
2562
+ return _cached;
2563
+ }
2564
+ const resolved = resolveRuntimeProcess();
2565
+ if (resolved) {
2566
+ _cachedRuntime = resolved.runtime;
2567
+ _cached = resolved.pid;
2568
+ return _cached;
2569
+ }
2570
+ _cached = process.env.CLAUDE_CODE_SSE_PORT ?? String(process.ppid);
2571
+ return _cached;
2572
+ }
2573
+ var _cached, _cachedRuntime, RUNTIME_COMMANDS;
2392
2574
  var init_session_key = __esm({
2393
2575
  "src/lib/session-key.ts"() {
2394
2576
  "use strict";
2577
+ _cached = null;
2578
+ _cachedRuntime = null;
2579
+ RUNTIME_COMMANDS = {
2580
+ claude: ["claude", "claude.exe", "claude-native"],
2581
+ codex: ["codex"],
2582
+ opencode: ["opencode"]
2583
+ };
2584
+ }
2585
+ });
2586
+
2587
+ // src/lib/tmux-transport.ts
2588
+ var tmux_transport_exports = {};
2589
+ __export(tmux_transport_exports, {
2590
+ TmuxTransport: () => TmuxTransport
2591
+ });
2592
+ import { execFileSync } from "child_process";
2593
+ var QUIET, TmuxTransport;
2594
+ var init_tmux_transport = __esm({
2595
+ "src/lib/tmux-transport.ts"() {
2596
+ "use strict";
2597
+ QUIET = {
2598
+ encoding: "utf8",
2599
+ stdio: ["pipe", "pipe", "pipe"]
2600
+ };
2601
+ TmuxTransport = class {
2602
+ getMySession() {
2603
+ try {
2604
+ return execFileSync("tmux", ["display-message", "-p", "#{session_name}"], QUIET).trim() || null;
2605
+ } catch {
2606
+ return null;
2607
+ }
2608
+ }
2609
+ listSessions() {
2610
+ try {
2611
+ return execFileSync("tmux", ["list-sessions", "-F", "#{session_name}"], QUIET).trim().split("\n").filter(Boolean);
2612
+ } catch {
2613
+ return [];
2614
+ }
2615
+ }
2616
+ isAlive(target) {
2617
+ try {
2618
+ const sessions = this.listSessions();
2619
+ if (!sessions.includes(target)) return false;
2620
+ const paneStatus = execFileSync(
2621
+ "tmux",
2622
+ ["list-panes", "-t", target, "-F", "#{pane_dead}"],
2623
+ QUIET
2624
+ ).trim();
2625
+ return paneStatus !== "1";
2626
+ } catch {
2627
+ return false;
2628
+ }
2629
+ }
2630
+ sendKeys(target, keys) {
2631
+ execFileSync("tmux", ["send-keys", "-t", target, keys, "Enter"], QUIET);
2632
+ }
2633
+ capturePane(target, lines) {
2634
+ const args = ["capture-pane", "-t", target, "-p"];
2635
+ if (lines) args.push("-S", `-${lines}`);
2636
+ return execFileSync("tmux", args, { ...QUIET, timeout: 3e3 });
2637
+ }
2638
+ isPaneInCopyMode(target) {
2639
+ try {
2640
+ const result = execFileSync(
2641
+ "tmux",
2642
+ ["display-message", "-p", "-t", target, "#{pane_in_mode}"],
2643
+ { ...QUIET, timeout: 3e3 }
2644
+ ).trim();
2645
+ return result === "1";
2646
+ } catch {
2647
+ return false;
2648
+ }
2649
+ }
2650
+ spawn(name, config) {
2651
+ try {
2652
+ const args = ["new-session", "-d", "-s", name];
2653
+ if (config.cwd) args.push("-c", config.cwd);
2654
+ args.push(config.command);
2655
+ execFileSync("tmux", args);
2656
+ return { sessionName: name };
2657
+ } catch (e) {
2658
+ return { sessionName: name, error: `spawn failed: ${e}` };
2659
+ }
2660
+ }
2661
+ kill(target) {
2662
+ try {
2663
+ execFileSync("tmux", ["kill-session", "-t", target], QUIET);
2664
+ } catch {
2665
+ }
2666
+ }
2667
+ pipeLog(target, logFile) {
2668
+ try {
2669
+ const safePath = logFile.replace(/'/g, "'\\''");
2670
+ execFileSync("tmux", ["pipe-pane", "-t", target, `cat >> '${safePath}'`], QUIET);
2671
+ } catch {
2672
+ }
2673
+ }
2674
+ };
2395
2675
  }
2396
2676
  });
2397
2677
 
2398
2678
  // src/lib/transport.ts
2679
+ function getTransport() {
2680
+ if (!_transport) {
2681
+ const { TmuxTransport: TmuxTransport2 } = (init_tmux_transport(), __toCommonJS(tmux_transport_exports));
2682
+ _transport = new TmuxTransport2();
2683
+ }
2684
+ return _transport;
2685
+ }
2686
+ var _transport;
2399
2687
  var init_transport = __esm({
2400
2688
  "src/lib/transport.ts"() {
2401
2689
  "use strict";
2690
+ _transport = null;
2402
2691
  }
2403
2692
  });
2404
2693
 
@@ -2458,7 +2747,7 @@ var init_runtime_table = __esm({
2458
2747
  });
2459
2748
 
2460
2749
  // src/lib/agent-config.ts
2461
- import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, existsSync as existsSync5, mkdirSync as mkdirSync2 } from "fs";
2750
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, existsSync as existsSync6 } from "fs";
2462
2751
  import path7 from "path";
2463
2752
  var AGENT_CONFIG_PATH, DEFAULT_MODELS;
2464
2753
  var init_agent_config = __esm({
@@ -2466,6 +2755,7 @@ var init_agent_config = __esm({
2466
2755
  "use strict";
2467
2756
  init_config();
2468
2757
  init_runtime_table();
2758
+ init_secure_files();
2469
2759
  AGENT_CONFIG_PATH = path7.join(EXE_AI_DIR, "agent-config.json");
2470
2760
  DEFAULT_MODELS = {
2471
2761
  claude: "claude-opus-4",
@@ -2476,7 +2766,7 @@ var init_agent_config = __esm({
2476
2766
  });
2477
2767
 
2478
2768
  // src/lib/intercom-queue.ts
2479
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, renameSync as renameSync3, existsSync as existsSync6, mkdirSync as mkdirSync3 } from "fs";
2769
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, renameSync as renameSync3, existsSync as existsSync7, mkdirSync as mkdirSync3 } from "fs";
2480
2770
  import path8 from "path";
2481
2771
  import os6 from "os";
2482
2772
  var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
@@ -2490,8 +2780,11 @@ var init_intercom_queue = __esm({
2490
2780
  });
2491
2781
 
2492
2782
  // src/lib/license.ts
2493
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, existsSync as existsSync7, mkdirSync as mkdirSync4 } from "fs";
2783
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, existsSync as existsSync8, mkdirSync as mkdirSync4 } from "fs";
2494
2784
  import { randomUUID as randomUUID2 } from "crypto";
2785
+ import { createRequire as createRequire2 } from "module";
2786
+ import { pathToFileURL as pathToFileURL2 } from "url";
2787
+ import os7 from "os";
2495
2788
  import path9 from "path";
2496
2789
  import { jwtVerify, importSPKI } from "jose";
2497
2790
  var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH;
@@ -2506,7 +2799,7 @@ var init_license = __esm({
2506
2799
  });
2507
2800
 
2508
2801
  // src/lib/plan-limits.ts
2509
- import { readFileSync as readFileSync6, existsSync as existsSync8 } from "fs";
2802
+ import { readFileSync as readFileSync6, existsSync as existsSync9 } from "fs";
2510
2803
  import path10 from "path";
2511
2804
  var CACHE_PATH2;
2512
2805
  var init_plan_limits = __esm({
@@ -2521,9 +2814,49 @@ var init_plan_limits = __esm({
2521
2814
  });
2522
2815
 
2523
2816
  // src/lib/tmux-routing.ts
2817
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, existsSync as existsSync10, appendFileSync, readdirSync as readdirSync2 } from "fs";
2524
2818
  import path11 from "path";
2525
- import os7 from "os";
2819
+ import os8 from "os";
2526
2820
  import { fileURLToPath } from "url";
2821
+ function getMySession() {
2822
+ return getTransport().getMySession();
2823
+ }
2824
+ function extractRootExe(name) {
2825
+ if (!name) return null;
2826
+ if (!name.includes("-")) return name;
2827
+ const parts = name.split("-").filter(Boolean);
2828
+ return parts.length > 0 ? parts[parts.length - 1] : null;
2829
+ }
2830
+ function getParentExe(sessionKey) {
2831
+ try {
2832
+ const data = JSON.parse(readFileSync7(path11.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
2833
+ return data.parentExe || null;
2834
+ } catch {
2835
+ return null;
2836
+ }
2837
+ }
2838
+ function resolveExeSession() {
2839
+ const mySession = getMySession();
2840
+ if (!mySession) return null;
2841
+ const fromSessionName = extractRootExe(mySession);
2842
+ try {
2843
+ const key = getSessionKey();
2844
+ const parentExe = getParentExe(key);
2845
+ if (parentExe) {
2846
+ const fromCache = extractRootExe(parentExe) ?? parentExe;
2847
+ if (fromSessionName && fromCache !== fromSessionName) {
2848
+ process.stderr.write(
2849
+ `[tmux-routing] WARN: cache says "${fromCache}" but session name says "${fromSessionName}". Trusting session name.
2850
+ `
2851
+ );
2852
+ return fromSessionName;
2853
+ }
2854
+ return fromCache;
2855
+ }
2856
+ } catch {
2857
+ }
2858
+ return fromSessionName ?? mySession;
2859
+ }
2527
2860
  var SPAWN_LOCK_DIR, SESSION_CACHE, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS;
2528
2861
  var init_tmux_routing = __esm({
2529
2862
  "src/lib/tmux-routing.ts"() {
@@ -2539,21 +2872,45 @@ var init_tmux_routing = __esm({
2539
2872
  init_intercom_queue();
2540
2873
  init_plan_limits();
2541
2874
  init_employees();
2542
- SPAWN_LOCK_DIR = path11.join(os7.homedir(), ".exe-os", "spawn-locks");
2543
- SESSION_CACHE = path11.join(os7.homedir(), ".exe-os", "session-cache");
2544
- INTERCOM_LOG2 = path11.join(os7.homedir(), ".exe-os", "intercom.log");
2875
+ SPAWN_LOCK_DIR = path11.join(os8.homedir(), ".exe-os", "spawn-locks");
2876
+ SESSION_CACHE = path11.join(os8.homedir(), ".exe-os", "session-cache");
2877
+ INTERCOM_LOG2 = path11.join(os8.homedir(), ".exe-os", "intercom.log");
2545
2878
  DEBOUNCE_FILE = path11.join(SESSION_CACHE, "intercom-debounce.json");
2546
2879
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
2547
2880
  }
2548
2881
  });
2549
2882
 
2883
+ // src/lib/task-scope.ts
2884
+ function getCurrentSessionScope() {
2885
+ try {
2886
+ return resolveExeSession();
2887
+ } catch {
2888
+ return null;
2889
+ }
2890
+ }
2891
+ function strictSessionScopeFilter(sessionScope, tableAlias) {
2892
+ const scope = sessionScope !== void 0 ? sessionScope : getCurrentSessionScope();
2893
+ if (!scope) return { sql: "", args: [] };
2894
+ const col = tableAlias ? `${tableAlias}.session_scope` : "session_scope";
2895
+ return {
2896
+ sql: ` AND ${col} = ?`,
2897
+ args: [scope]
2898
+ };
2899
+ }
2900
+ var init_task_scope = __esm({
2901
+ "src/lib/task-scope.ts"() {
2902
+ "use strict";
2903
+ init_tmux_routing();
2904
+ }
2905
+ });
2906
+
2550
2907
  // src/lib/store.ts
2551
2908
  import { createHash } from "crypto";
2552
2909
  init_database();
2553
2910
 
2554
2911
  // src/lib/keychain.ts
2555
2912
  import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
2556
- import { existsSync as existsSync3 } from "fs";
2913
+ import { existsSync as existsSync4 } from "fs";
2557
2914
  import path4 from "path";
2558
2915
  import os4 from "os";
2559
2916
  var SERVICE = "exe-mem";
@@ -2583,7 +2940,7 @@ async function getMasterKey() {
2583
2940
  }
2584
2941
  }
2585
2942
  const keyPath = getKeyPath();
2586
- if (!existsSync3(keyPath)) {
2943
+ if (!existsSync4(keyPath)) {
2587
2944
  process.stderr.write(
2588
2945
  `[keychain] Key not found at ${keyPath} (HOME=${os4.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
2589
2946
  `
@@ -2687,6 +3044,7 @@ async function initStore(options) {
2687
3044
  // src/lib/messaging.ts
2688
3045
  init_database();
2689
3046
  init_tmux_routing();
3047
+ init_task_scope();
2690
3048
  import crypto from "crypto";
2691
3049
  function rowToMessage(row) {
2692
3050
  return {
@@ -2696,6 +3054,7 @@ function rowToMessage(row) {
2696
3054
  targetAgent: row.target_agent,
2697
3055
  targetProject: row.target_project ?? null,
2698
3056
  targetDevice: row.target_device,
3057
+ sessionScope: row.session_scope ?? null,
2699
3058
  content: row.content,
2700
3059
  priority: row.priority ?? "normal",
2701
3060
  status: row.status ?? "pending",
@@ -2708,21 +3067,24 @@ function rowToMessage(row) {
2708
3067
  failureReason: row.failure_reason ?? null
2709
3068
  };
2710
3069
  }
2711
- async function getPendingMessages(targetAgent) {
3070
+ async function getPendingMessages(targetAgent, sessionScope) {
2712
3071
  const client = getClient();
3072
+ const scope = strictSessionScopeFilter(sessionScope);
2713
3073
  const result = await client.execute({
2714
3074
  sql: `SELECT * FROM messages
2715
- WHERE target_agent = ? AND status IN ('pending', 'delivered')
3075
+ WHERE target_agent = ? AND status IN ('pending', 'delivered')${scope.sql}
2716
3076
  ORDER BY id`,
2717
- args: [targetAgent]
3077
+ args: [targetAgent, ...scope.args]
2718
3078
  });
2719
3079
  return result.rows.map((row) => rowToMessage(row));
2720
3080
  }
2721
- async function markRead(messageId) {
3081
+ async function markRead(messageId, sessionScope) {
2722
3082
  const client = getClient();
3083
+ const scope = strictSessionScopeFilter(sessionScope);
2723
3084
  await client.execute({
2724
- sql: "UPDATE messages SET status = 'read' WHERE id = ? AND status IN ('pending', 'delivered')",
2725
- args: [messageId]
3085
+ sql: `UPDATE messages SET status = 'read'
3086
+ WHERE id = ? AND status IN ('pending', 'delivered')${scope.sql}`,
3087
+ args: [messageId, ...scope.args]
2726
3088
  });
2727
3089
  }
2728
3090