@askexenow/exe-os 0.8.55 → 0.8.56

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.
@@ -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 __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
4
6
  get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
5
7
  }) : x)(function(x) {
@@ -13,6 +15,15 @@ var __export = (target, all) => {
13
15
  for (var name in all)
14
16
  __defProp(target, name, { get: all[name], enumerable: true });
15
17
  };
18
+ var __copyProps = (to, from, except, desc) => {
19
+ if (from && typeof from === "object" || typeof from === "function") {
20
+ for (let key of __getOwnPropNames(from))
21
+ if (!__hasOwnProp.call(to, key) && key !== except)
22
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
23
+ }
24
+ return to;
25
+ };
26
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
16
27
 
17
28
  // src/types/memory.ts
18
29
  var EMBEDDING_DIM;
@@ -1347,6 +1358,61 @@ var init_config = __esm({
1347
1358
  }
1348
1359
  });
1349
1360
 
1361
+ // src/lib/state-bus.ts
1362
+ var StateBus, orgBus;
1363
+ var init_state_bus = __esm({
1364
+ "src/lib/state-bus.ts"() {
1365
+ "use strict";
1366
+ StateBus = class {
1367
+ handlers = /* @__PURE__ */ new Map();
1368
+ globalHandlers = /* @__PURE__ */ new Set();
1369
+ /** Emit an event to all subscribers */
1370
+ emit(event) {
1371
+ const typeHandlers = this.handlers.get(event.type);
1372
+ if (typeHandlers) {
1373
+ for (const handler of typeHandlers) {
1374
+ try {
1375
+ handler(event);
1376
+ } catch {
1377
+ }
1378
+ }
1379
+ }
1380
+ for (const handler of this.globalHandlers) {
1381
+ try {
1382
+ handler(event);
1383
+ } catch {
1384
+ }
1385
+ }
1386
+ }
1387
+ /** Subscribe to a specific event type */
1388
+ on(type, handler) {
1389
+ if (!this.handlers.has(type)) {
1390
+ this.handlers.set(type, /* @__PURE__ */ new Set());
1391
+ }
1392
+ this.handlers.get(type).add(handler);
1393
+ }
1394
+ /** Subscribe to ALL events */
1395
+ onAny(handler) {
1396
+ this.globalHandlers.add(handler);
1397
+ }
1398
+ /** Unsubscribe from a specific event type */
1399
+ off(type, handler) {
1400
+ this.handlers.get(type)?.delete(handler);
1401
+ }
1402
+ /** Unsubscribe from ALL events */
1403
+ offAny(handler) {
1404
+ this.globalHandlers.delete(handler);
1405
+ }
1406
+ /** Remove all listeners */
1407
+ clear() {
1408
+ this.handlers.clear();
1409
+ this.globalHandlers.clear();
1410
+ }
1411
+ };
1412
+ orgBus = new StateBus();
1413
+ }
1414
+ });
1415
+
1350
1416
  // src/lib/shard-manager.ts
1351
1417
  var shard_manager_exports = {};
1352
1418
  __export(shard_manager_exports, {
@@ -1756,13 +1822,251 @@ ${p.content}`).join("\n\n");
1756
1822
  }
1757
1823
  });
1758
1824
 
1825
+ // src/lib/notifications.ts
1826
+ import crypto2 from "crypto";
1827
+ import path4 from "path";
1828
+ import os3 from "os";
1829
+ import {
1830
+ readFileSync as readFileSync2,
1831
+ readdirSync as readdirSync2,
1832
+ unlinkSync,
1833
+ existsSync as existsSync4,
1834
+ rmdirSync
1835
+ } from "fs";
1836
+ async function writeNotification(notification) {
1837
+ try {
1838
+ const client = getClient();
1839
+ const id = crypto2.randomUUID();
1840
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1841
+ await client.execute({
1842
+ sql: `INSERT INTO notifications (id, agent_id, agent_role, event, project, summary, task_file, read, created_at)
1843
+ VALUES (?, ?, ?, ?, ?, ?, ?, 0, ?)`,
1844
+ args: [
1845
+ id,
1846
+ notification.agentId,
1847
+ notification.agentRole,
1848
+ notification.event,
1849
+ notification.project,
1850
+ notification.summary,
1851
+ notification.taskFile ?? null,
1852
+ now
1853
+ ]
1854
+ });
1855
+ } catch (err) {
1856
+ process.stderr.write(`[notifications] WRITE FAILED: ${err instanceof Error ? err.message : String(err)}
1857
+ `);
1858
+ }
1859
+ }
1860
+ var init_notifications = __esm({
1861
+ "src/lib/notifications.ts"() {
1862
+ "use strict";
1863
+ init_database();
1864
+ }
1865
+ });
1866
+
1867
+ // src/lib/session-registry.ts
1868
+ import path5 from "path";
1869
+ import os4 from "os";
1870
+ var REGISTRY_PATH;
1871
+ var init_session_registry = __esm({
1872
+ "src/lib/session-registry.ts"() {
1873
+ "use strict";
1874
+ REGISTRY_PATH = path5.join(os4.homedir(), ".exe-os", "session-registry.json");
1875
+ }
1876
+ });
1877
+
1878
+ // src/lib/session-key.ts
1879
+ import { execSync } from "child_process";
1880
+ function getSessionKey() {
1881
+ if (_cached) return _cached;
1882
+ let pid = process.ppid;
1883
+ for (let i = 0; i < 10; i++) {
1884
+ try {
1885
+ const info = execSync(`ps -p ${pid} -o ppid=,comm=`, {
1886
+ encoding: "utf8",
1887
+ timeout: 2e3
1888
+ }).trim();
1889
+ const match = info.match(/^\s*(\d+)\s+(.+)$/);
1890
+ if (!match) break;
1891
+ const [, ppid, cmd] = match;
1892
+ if (cmd === "claude" || cmd.endsWith("/claude")) {
1893
+ _cached = String(pid);
1894
+ return _cached;
1895
+ }
1896
+ pid = parseInt(ppid, 10);
1897
+ if (pid <= 1) break;
1898
+ } catch {
1899
+ break;
1900
+ }
1901
+ }
1902
+ _cached = process.env.CLAUDE_CODE_SSE_PORT ?? String(process.ppid);
1903
+ return _cached;
1904
+ }
1905
+ var _cached;
1906
+ var init_session_key = __esm({
1907
+ "src/lib/session-key.ts"() {
1908
+ "use strict";
1909
+ _cached = null;
1910
+ }
1911
+ });
1912
+
1913
+ // src/lib/tmux-transport.ts
1914
+ var tmux_transport_exports = {};
1915
+ __export(tmux_transport_exports, {
1916
+ TmuxTransport: () => TmuxTransport
1917
+ });
1918
+ import { execFileSync } from "child_process";
1919
+ var QUIET, TmuxTransport;
1920
+ var init_tmux_transport = __esm({
1921
+ "src/lib/tmux-transport.ts"() {
1922
+ "use strict";
1923
+ QUIET = {
1924
+ encoding: "utf8",
1925
+ stdio: ["pipe", "pipe", "pipe"]
1926
+ };
1927
+ TmuxTransport = class {
1928
+ getMySession() {
1929
+ try {
1930
+ return execFileSync("tmux", ["display-message", "-p", "#{session_name}"], QUIET).trim() || null;
1931
+ } catch {
1932
+ return null;
1933
+ }
1934
+ }
1935
+ listSessions() {
1936
+ try {
1937
+ return execFileSync("tmux", ["list-sessions", "-F", "#{session_name}"], QUIET).trim().split("\n").filter(Boolean);
1938
+ } catch {
1939
+ return [];
1940
+ }
1941
+ }
1942
+ isAlive(target) {
1943
+ try {
1944
+ const sessions = this.listSessions();
1945
+ if (!sessions.includes(target)) return false;
1946
+ const paneStatus = execFileSync(
1947
+ "tmux",
1948
+ ["list-panes", "-t", target, "-F", "#{pane_dead}"],
1949
+ QUIET
1950
+ ).trim();
1951
+ return paneStatus !== "1";
1952
+ } catch {
1953
+ return false;
1954
+ }
1955
+ }
1956
+ sendKeys(target, keys) {
1957
+ execFileSync("tmux", ["send-keys", "-t", target, keys, "Enter"], QUIET);
1958
+ }
1959
+ capturePane(target, lines) {
1960
+ const args = ["capture-pane", "-t", target, "-p"];
1961
+ if (lines) args.push("-S", `-${lines}`);
1962
+ return execFileSync("tmux", args, { ...QUIET, timeout: 3e3 });
1963
+ }
1964
+ isPaneInCopyMode(target) {
1965
+ try {
1966
+ const result = execFileSync(
1967
+ "tmux",
1968
+ ["display-message", "-p", "-t", target, "#{pane_in_mode}"],
1969
+ { ...QUIET, timeout: 3e3 }
1970
+ ).trim();
1971
+ return result === "1";
1972
+ } catch {
1973
+ return false;
1974
+ }
1975
+ }
1976
+ spawn(name, config) {
1977
+ try {
1978
+ const args = ["new-session", "-d", "-s", name];
1979
+ if (config.cwd) args.push("-c", config.cwd);
1980
+ args.push(config.command);
1981
+ execFileSync("tmux", args);
1982
+ return { sessionName: name };
1983
+ } catch (e) {
1984
+ return { sessionName: name, error: `spawn failed: ${e}` };
1985
+ }
1986
+ }
1987
+ kill(target) {
1988
+ try {
1989
+ execFileSync("tmux", ["kill-session", "-t", target], QUIET);
1990
+ } catch {
1991
+ }
1992
+ }
1993
+ pipeLog(target, logFile) {
1994
+ try {
1995
+ const safePath = logFile.replace(/'/g, "'\\''");
1996
+ execFileSync("tmux", ["pipe-pane", "-t", target, `cat >> '${safePath}'`], QUIET);
1997
+ } catch {
1998
+ }
1999
+ }
2000
+ };
2001
+ }
2002
+ });
2003
+
2004
+ // src/lib/transport.ts
2005
+ function getTransport() {
2006
+ if (!_transport) {
2007
+ const { TmuxTransport: TmuxTransport2 } = (init_tmux_transport(), __toCommonJS(tmux_transport_exports));
2008
+ _transport = new TmuxTransport2();
2009
+ }
2010
+ return _transport;
2011
+ }
2012
+ var _transport;
2013
+ var init_transport = __esm({
2014
+ "src/lib/transport.ts"() {
2015
+ "use strict";
2016
+ _transport = null;
2017
+ }
2018
+ });
2019
+
2020
+ // src/lib/cc-agent-support.ts
2021
+ import { execSync as execSync2 } from "child_process";
2022
+ var init_cc_agent_support = __esm({
2023
+ "src/lib/cc-agent-support.ts"() {
2024
+ "use strict";
2025
+ }
2026
+ });
2027
+
2028
+ // src/lib/mcp-prefix.ts
2029
+ var MCP_PRIMARY_KEY, MCP_LEGACY_KEY, MCP_TOOL_PREFIXES;
2030
+ var init_mcp_prefix = __esm({
2031
+ "src/lib/mcp-prefix.ts"() {
2032
+ "use strict";
2033
+ MCP_PRIMARY_KEY = "exe-os";
2034
+ MCP_LEGACY_KEY = "exe-mem";
2035
+ MCP_TOOL_PREFIXES = [
2036
+ `mcp__${MCP_PRIMARY_KEY}__`,
2037
+ `mcp__${MCP_LEGACY_KEY}__`
2038
+ ];
2039
+ }
2040
+ });
2041
+
2042
+ // src/lib/provider-table.ts
2043
+ var init_provider_table = __esm({
2044
+ "src/lib/provider-table.ts"() {
2045
+ "use strict";
2046
+ }
2047
+ });
2048
+
2049
+ // src/lib/intercom-queue.ts
2050
+ import { readFileSync as readFileSync3, writeFileSync, renameSync as renameSync2, existsSync as existsSync5, mkdirSync as mkdirSync2 } from "fs";
2051
+ import path6 from "path";
2052
+ import os5 from "os";
2053
+ var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
2054
+ var init_intercom_queue = __esm({
2055
+ "src/lib/intercom-queue.ts"() {
2056
+ "use strict";
2057
+ QUEUE_PATH = path6.join(os5.homedir(), ".exe-os", "intercom-queue.json");
2058
+ TTL_MS = 60 * 60 * 1e3;
2059
+ INTERCOM_LOG = path6.join(os5.homedir(), ".exe-os", "intercom.log");
2060
+ }
2061
+ });
2062
+
1759
2063
  // src/lib/employees.ts
1760
2064
  import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
1761
- import { existsSync as existsSync5, symlinkSync, readlinkSync, readFileSync as readFileSync3 } from "fs";
1762
- import { execSync } from "child_process";
1763
- import path5 from "path";
2065
+ import { existsSync as existsSync6, symlinkSync, readlinkSync, readFileSync as readFileSync4 } from "fs";
2066
+ import { execSync as execSync3 } from "child_process";
2067
+ import path7 from "path";
1764
2068
  async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
1765
- if (!existsSync5(employeesPath)) {
2069
+ if (!existsSync6(employeesPath)) {
1766
2070
  return [];
1767
2071
  }
1768
2072
  const raw = await readFile3(employeesPath, "utf-8");
@@ -1773,12 +2077,12 @@ async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
1773
2077
  }
1774
2078
  }
1775
2079
  async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
1776
- await mkdir3(path5.dirname(employeesPath), { recursive: true });
2080
+ await mkdir3(path7.dirname(employeesPath), { recursive: true });
1777
2081
  await writeFile3(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
1778
2082
  }
1779
2083
  function findExeBin() {
1780
2084
  try {
1781
- return execSync(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
2085
+ return execSync3(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
1782
2086
  } catch {
1783
2087
  return null;
1784
2088
  }
@@ -1792,7 +2096,7 @@ function registerBinSymlinks(name) {
1792
2096
  errors.push("Could not find 'exe-os' in PATH");
1793
2097
  return { created, skipped, errors };
1794
2098
  }
1795
- const binDir = path5.dirname(exeBinPath);
2099
+ const binDir = path7.dirname(exeBinPath);
1796
2100
  let target;
1797
2101
  try {
1798
2102
  target = readlinkSync(exeBinPath);
@@ -1802,8 +2106,8 @@ function registerBinSymlinks(name) {
1802
2106
  }
1803
2107
  for (const suffix of ["", "-opencode"]) {
1804
2108
  const linkName = `${name}${suffix}`;
1805
- const linkPath = path5.join(binDir, linkName);
1806
- if (existsSync5(linkPath)) {
2109
+ const linkPath = path7.join(binDir, linkName);
2110
+ if (existsSync6(linkPath)) {
1807
2111
  skipped.push(linkName);
1808
2112
  continue;
1809
2113
  }
@@ -1821,14 +2125,30 @@ var init_employees = __esm({
1821
2125
  "src/lib/employees.ts"() {
1822
2126
  "use strict";
1823
2127
  init_config();
1824
- EMPLOYEES_PATH = path5.join(EXE_AI_DIR, "exe-employees.json");
2128
+ EMPLOYEES_PATH = path7.join(EXE_AI_DIR, "exe-employees.json");
1825
2129
  }
1826
2130
  });
1827
2131
 
1828
2132
  // src/lib/license.ts
1829
- import { readFileSync as readFileSync4, writeFileSync, existsSync as existsSync6, mkdirSync as mkdirSync2 } from "fs";
2133
+ var license_exports = {};
2134
+ __export(license_exports, {
2135
+ LICENSE_PUBLIC_KEY_PEM: () => LICENSE_PUBLIC_KEY_PEM,
2136
+ PLAN_LIMITS: () => PLAN_LIMITS,
2137
+ assertVpsLicense: () => assertVpsLicense,
2138
+ checkLicense: () => checkLicense,
2139
+ getCachedLicense: () => getCachedLicense,
2140
+ isFeatureAllowed: () => isFeatureAllowed,
2141
+ loadDeviceId: () => loadDeviceId,
2142
+ loadLicense: () => loadLicense,
2143
+ mirrorLicenseKey: () => mirrorLicenseKey,
2144
+ saveLicense: () => saveLicense,
2145
+ startLicenseRevalidation: () => startLicenseRevalidation,
2146
+ stopLicenseRevalidation: () => stopLicenseRevalidation,
2147
+ validateLicense: () => validateLicense
2148
+ });
2149
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync2, existsSync as existsSync7, mkdirSync as mkdirSync3 } from "fs";
1830
2150
  import { randomUUID as randomUUID2 } from "crypto";
1831
- import path6 from "path";
2151
+ import path8 from "path";
1832
2152
  import { jwtVerify, importSPKI } from "jose";
1833
2153
  async function fetchRetry(url, init) {
1834
2154
  try {
@@ -1839,37 +2159,37 @@ async function fetchRetry(url, init) {
1839
2159
  }
1840
2160
  }
1841
2161
  function loadDeviceId() {
1842
- const deviceJsonPath = path6.join(EXE_AI_DIR, "device.json");
2162
+ const deviceJsonPath = path8.join(EXE_AI_DIR, "device.json");
1843
2163
  try {
1844
- if (existsSync6(deviceJsonPath)) {
1845
- const data = JSON.parse(readFileSync4(deviceJsonPath, "utf8"));
2164
+ if (existsSync7(deviceJsonPath)) {
2165
+ const data = JSON.parse(readFileSync5(deviceJsonPath, "utf8"));
1846
2166
  if (data.deviceId) return data.deviceId;
1847
2167
  }
1848
2168
  } catch {
1849
2169
  }
1850
2170
  try {
1851
- if (existsSync6(DEVICE_ID_PATH)) {
1852
- const id2 = readFileSync4(DEVICE_ID_PATH, "utf8").trim();
2171
+ if (existsSync7(DEVICE_ID_PATH)) {
2172
+ const id2 = readFileSync5(DEVICE_ID_PATH, "utf8").trim();
1853
2173
  if (id2) return id2;
1854
2174
  }
1855
2175
  } catch {
1856
2176
  }
1857
2177
  const id = randomUUID2();
1858
- mkdirSync2(EXE_AI_DIR, { recursive: true });
1859
- writeFileSync(DEVICE_ID_PATH, id, "utf8");
2178
+ mkdirSync3(EXE_AI_DIR, { recursive: true });
2179
+ writeFileSync2(DEVICE_ID_PATH, id, "utf8");
1860
2180
  return id;
1861
2181
  }
1862
2182
  function loadLicense() {
1863
2183
  try {
1864
- if (!existsSync6(LICENSE_PATH)) return null;
1865
- return readFileSync4(LICENSE_PATH, "utf8").trim();
2184
+ if (!existsSync7(LICENSE_PATH)) return null;
2185
+ return readFileSync5(LICENSE_PATH, "utf8").trim();
1866
2186
  } catch {
1867
2187
  return null;
1868
2188
  }
1869
2189
  }
1870
2190
  function saveLicense(apiKey) {
1871
- mkdirSync2(EXE_AI_DIR, { recursive: true });
1872
- writeFileSync(LICENSE_PATH, apiKey.trim(), { encoding: "utf8", mode: 384 });
2191
+ mkdirSync3(EXE_AI_DIR, { recursive: true });
2192
+ writeFileSync2(LICENSE_PATH, apiKey.trim(), { encoding: "utf8", mode: 384 });
1873
2193
  }
1874
2194
  async function verifyLicenseJwt(token) {
1875
2195
  try {
@@ -1895,17 +2215,26 @@ async function verifyLicenseJwt(token) {
1895
2215
  }
1896
2216
  async function getCachedLicense() {
1897
2217
  try {
1898
- if (!existsSync6(CACHE_PATH)) return null;
1899
- const raw = JSON.parse(readFileSync4(CACHE_PATH, "utf8"));
2218
+ if (!existsSync7(CACHE_PATH)) return null;
2219
+ const raw = JSON.parse(readFileSync5(CACHE_PATH, "utf8"));
1900
2220
  if (!raw.token || typeof raw.token !== "string") return null;
1901
2221
  return await verifyLicenseJwt(raw.token);
1902
2222
  } catch {
1903
2223
  return null;
1904
2224
  }
1905
2225
  }
2226
+ function readCachedToken() {
2227
+ try {
2228
+ if (!existsSync7(CACHE_PATH)) return null;
2229
+ const raw = JSON.parse(readFileSync5(CACHE_PATH, "utf8"));
2230
+ return typeof raw.token === "string" ? raw.token : null;
2231
+ } catch {
2232
+ return null;
2233
+ }
2234
+ }
1906
2235
  function cacheResponse(token) {
1907
2236
  try {
1908
- writeFileSync(CACHE_PATH, JSON.stringify({ token }), "utf8");
2237
+ writeFileSync2(CACHE_PATH, JSON.stringify({ token }), "utf8");
1909
2238
  } catch {
1910
2239
  }
1911
2240
  }
@@ -1963,9 +2292,9 @@ async function checkLicense() {
1963
2292
  let key = loadLicense();
1964
2293
  if (!key) {
1965
2294
  try {
1966
- const configPath = path6.join(EXE_AI_DIR, "config.json");
1967
- if (existsSync6(configPath)) {
1968
- const raw = JSON.parse(readFileSync4(configPath, "utf8"));
2295
+ const configPath = path8.join(EXE_AI_DIR, "config.json");
2296
+ if (existsSync7(configPath)) {
2297
+ const raw = JSON.parse(readFileSync5(configPath, "utf8"));
1969
2298
  const cloud = raw.cloud;
1970
2299
  if (cloud?.apiKey) {
1971
2300
  key = cloud.apiKey;
@@ -1991,14 +2320,142 @@ function isFeatureAllowed(license, feature) {
1991
2320
  return license.plan === "team" || license.plan === "agency" || license.plan === "enterprise";
1992
2321
  }
1993
2322
  }
1994
- var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, API_BASE, RETRY_DELAY_MS, LICENSE_PUBLIC_KEY_PEM, LICENSE_JWT_ALG, PLAN_LIMITS, FREE_LICENSE, CACHE_MAX_AGE_MS;
2323
+ function mirrorLicenseKey(apiKey) {
2324
+ const trimmed = apiKey.trim();
2325
+ if (!trimmed) return;
2326
+ saveLicense(trimmed);
2327
+ }
2328
+ async function assertVpsLicense(opts) {
2329
+ const env = opts?.env ?? process.env;
2330
+ const inProduction = env.NODE_ENV === "production";
2331
+ if (!opts?.force && !inProduction) {
2332
+ return { ...FREE_LICENSE, plan: "free" };
2333
+ }
2334
+ const envKey = env.EXE_LICENSE_KEY?.trim();
2335
+ if (envKey) {
2336
+ saveLicense(envKey);
2337
+ }
2338
+ const apiKey = envKey ?? loadLicense();
2339
+ if (!apiKey) {
2340
+ throw new Error(
2341
+ "License required: set EXE_LICENSE_KEY env var with your exe_sk_* key. Purchase at https://askexe.com. This VPS image refuses to boot without a valid license."
2342
+ );
2343
+ }
2344
+ const deviceId = loadDeviceId();
2345
+ let backendResponse = null;
2346
+ let explicitRejection = false;
2347
+ let transientFailure = false;
2348
+ try {
2349
+ const res = await fetchRetry(`${API_BASE}/auth/activate`, {
2350
+ method: "POST",
2351
+ headers: { "Content-Type": "application/json" },
2352
+ body: JSON.stringify({ apiKey, deviceId }),
2353
+ signal: AbortSignal.timeout(1e4)
2354
+ });
2355
+ if (res.ok) {
2356
+ backendResponse = await res.json();
2357
+ if (!backendResponse.valid) explicitRejection = true;
2358
+ } else if (res.status === 401 || res.status === 403) {
2359
+ explicitRejection = true;
2360
+ } else {
2361
+ transientFailure = true;
2362
+ }
2363
+ } catch {
2364
+ transientFailure = true;
2365
+ }
2366
+ if (backendResponse?.valid) {
2367
+ if (backendResponse.token) {
2368
+ cacheResponse(backendResponse.token);
2369
+ const verified = await verifyLicenseJwt(backendResponse.token);
2370
+ if (verified) return verified;
2371
+ }
2372
+ const limits = PLAN_LIMITS[backendResponse.plan] ?? PLAN_LIMITS.free;
2373
+ return {
2374
+ valid: true,
2375
+ plan: backendResponse.plan,
2376
+ email: backendResponse.email,
2377
+ expiresAt: backendResponse.expiresAt,
2378
+ deviceLimit: limits.devices,
2379
+ employeeLimit: limits.employees,
2380
+ memoryLimit: limits.memories
2381
+ };
2382
+ }
2383
+ if (explicitRejection) {
2384
+ throw new Error(
2385
+ `License invalid or expired. Renew at https://askexe.com, then restart. Backend rejected key ending in ...${apiKey.slice(-4)}.`
2386
+ );
2387
+ }
2388
+ if (!transientFailure) {
2389
+ throw new Error(
2390
+ "License validation failed: unknown backend state. Restore network connectivity to https://askexe.com/cloud and retry."
2391
+ );
2392
+ }
2393
+ const fresh = await getCachedLicense();
2394
+ if (fresh && fresh.valid) return fresh;
2395
+ const graceDays = opts?.offlineGraceDays ?? 7;
2396
+ const graceMs = graceDays * 24 * 60 * 60 * 1e3;
2397
+ try {
2398
+ const token = readCachedToken();
2399
+ if (token) {
2400
+ const payloadB64 = token.split(".")[1];
2401
+ if (payloadB64) {
2402
+ const payload = JSON.parse(Buffer.from(payloadB64, "base64url").toString());
2403
+ const expMs = (payload.exp ?? 0) * 1e3;
2404
+ if (Date.now() < expMs + graceMs) {
2405
+ const key = await importSPKI(LICENSE_PUBLIC_KEY_PEM, LICENSE_JWT_ALG);
2406
+ const { payload: verified } = await jwtVerify(token, key, {
2407
+ algorithms: [LICENSE_JWT_ALG],
2408
+ clockTolerance: graceDays * 24 * 60 * 60
2409
+ });
2410
+ const plan = verified.plan ?? "free";
2411
+ const limits = PLAN_LIMITS[plan] ?? PLAN_LIMITS.free;
2412
+ return {
2413
+ valid: true,
2414
+ plan,
2415
+ email: verified.sub ?? "",
2416
+ expiresAt: verified.exp ? new Date(verified.exp * 1e3).toISOString() : null,
2417
+ deviceLimit: limits.devices,
2418
+ employeeLimit: limits.employees,
2419
+ memoryLimit: limits.memories
2420
+ };
2421
+ }
2422
+ }
2423
+ }
2424
+ } catch {
2425
+ }
2426
+ throw new Error(
2427
+ `License validation unreachable for more than ${graceDays} days. Restore network connectivity to https://askexe.com/cloud and retry. This VPS image refuses to boot after the offline grace window.`
2428
+ );
2429
+ }
2430
+ function startLicenseRevalidation(intervalMs = 36e5) {
2431
+ if (_revalTimer) return;
2432
+ _revalTimer = setInterval(async () => {
2433
+ try {
2434
+ const license = await checkLicense();
2435
+ if (!license.valid) {
2436
+ process.stderr.write("[exe-os] License expired or invalid \u2014 features may be restricted\n");
2437
+ }
2438
+ } catch {
2439
+ }
2440
+ }, intervalMs);
2441
+ if (_revalTimer && typeof _revalTimer === "object" && "unref" in _revalTimer) {
2442
+ _revalTimer.unref();
2443
+ }
2444
+ }
2445
+ function stopLicenseRevalidation() {
2446
+ if (_revalTimer) {
2447
+ clearInterval(_revalTimer);
2448
+ _revalTimer = null;
2449
+ }
2450
+ }
2451
+ var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, API_BASE, RETRY_DELAY_MS, LICENSE_PUBLIC_KEY_PEM, LICENSE_JWT_ALG, PLAN_LIMITS, FREE_LICENSE, CACHE_MAX_AGE_MS, _revalTimer;
1995
2452
  var init_license = __esm({
1996
2453
  "src/lib/license.ts"() {
1997
2454
  "use strict";
1998
2455
  init_config();
1999
- LICENSE_PATH = path6.join(EXE_AI_DIR, "license.key");
2000
- CACHE_PATH = path6.join(EXE_AI_DIR, "license-cache.json");
2001
- DEVICE_ID_PATH = path6.join(EXE_AI_DIR, "device-id");
2456
+ LICENSE_PATH = path8.join(EXE_AI_DIR, "license.key");
2457
+ CACHE_PATH = path8.join(EXE_AI_DIR, "license-cache.json");
2458
+ DEVICE_ID_PATH = path8.join(EXE_AI_DIR, "device-id");
2002
2459
  API_BASE = "https://askexe.com/cloud";
2003
2460
  RETRY_DELAY_MS = 500;
2004
2461
  LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
@@ -2023,6 +2480,7 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
2023
2480
  memoryLimit: 5e3
2024
2481
  };
2025
2482
  CACHE_MAX_AGE_MS = 36e5;
2483
+ _revalTimer = null;
2026
2484
  }
2027
2485
  });
2028
2486
 
@@ -2037,12 +2495,12 @@ __export(plan_limits_exports, {
2037
2495
  countActiveMemories: () => countActiveMemories,
2038
2496
  getLicenseSync: () => getLicenseSync
2039
2497
  });
2040
- import { readFileSync as readFileSync5, existsSync as existsSync7 } from "fs";
2041
- import path7 from "path";
2498
+ import { readFileSync as readFileSync6, existsSync as existsSync8 } from "fs";
2499
+ import path9 from "path";
2042
2500
  function getLicenseSync() {
2043
2501
  try {
2044
- if (!existsSync7(CACHE_PATH2)) return freeLicense();
2045
- const raw = JSON.parse(readFileSync5(CACHE_PATH2, "utf8"));
2502
+ if (!existsSync8(CACHE_PATH2)) return freeLicense();
2503
+ const raw = JSON.parse(readFileSync6(CACHE_PATH2, "utf8"));
2046
2504
  if (!raw.token || typeof raw.token !== "string") return freeLicense();
2047
2505
  const parts = raw.token.split(".");
2048
2506
  if (parts.length !== 3) return freeLicense();
@@ -2109,8 +2567,8 @@ function assertEmployeeLimitSync(rosterPath) {
2109
2567
  const filePath = rosterPath ?? EMPLOYEES_PATH;
2110
2568
  let count = 0;
2111
2569
  try {
2112
- if (existsSync7(filePath)) {
2113
- const raw = readFileSync5(filePath, "utf8");
2570
+ if (existsSync8(filePath)) {
2571
+ const raw = readFileSync6(filePath, "utf8");
2114
2572
  const employees = JSON.parse(raw);
2115
2573
  count = Array.isArray(employees) ? employees.length : 0;
2116
2574
  }
@@ -2147,7 +2605,60 @@ var init_plan_limits = __esm({
2147
2605
  this.name = "PlanLimitError";
2148
2606
  }
2149
2607
  };
2150
- CACHE_PATH2 = path7.join(EXE_AI_DIR, "license-cache.json");
2608
+ CACHE_PATH2 = path9.join(EXE_AI_DIR, "license-cache.json");
2609
+ }
2610
+ });
2611
+
2612
+ // src/lib/tmux-routing.ts
2613
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4, existsSync as existsSync9, appendFileSync } from "fs";
2614
+ import path10 from "path";
2615
+ import os6 from "os";
2616
+ import { fileURLToPath } from "url";
2617
+ function getMySession() {
2618
+ return getTransport().getMySession();
2619
+ }
2620
+ function extractRootExe(name) {
2621
+ const match = name.match(/(exe\d+)$/);
2622
+ return match?.[1] ?? null;
2623
+ }
2624
+ function getParentExe(sessionKey) {
2625
+ try {
2626
+ const data = JSON.parse(readFileSync7(path10.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
2627
+ return data.parentExe || null;
2628
+ } catch {
2629
+ return null;
2630
+ }
2631
+ }
2632
+ function resolveExeSession() {
2633
+ const mySession = getMySession();
2634
+ if (!mySession) return null;
2635
+ try {
2636
+ const key = getSessionKey();
2637
+ const parentExe = getParentExe(key);
2638
+ if (parentExe) {
2639
+ return extractRootExe(parentExe) ?? parentExe;
2640
+ }
2641
+ } catch {
2642
+ }
2643
+ return extractRootExe(mySession) ?? mySession;
2644
+ }
2645
+ var SPAWN_LOCK_DIR, SESSION_CACHE, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS;
2646
+ var init_tmux_routing = __esm({
2647
+ "src/lib/tmux-routing.ts"() {
2648
+ "use strict";
2649
+ init_session_registry();
2650
+ init_session_key();
2651
+ init_transport();
2652
+ init_cc_agent_support();
2653
+ init_mcp_prefix();
2654
+ init_provider_table();
2655
+ init_intercom_queue();
2656
+ init_plan_limits();
2657
+ SPAWN_LOCK_DIR = path10.join(os6.homedir(), ".exe-os", "spawn-locks");
2658
+ SESSION_CACHE = path10.join(os6.homedir(), ".exe-os", "session-cache");
2659
+ INTERCOM_LOG2 = path10.join(os6.homedir(), ".exe-os", "intercom.log");
2660
+ DEBOUNCE_FILE = path10.join(SESSION_CACHE, "intercom-debounce.json");
2661
+ DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
2151
2662
  }
2152
2663
  });
2153
2664
 
@@ -2155,9 +2666,9 @@ var init_plan_limits = __esm({
2155
2666
  import net from "net";
2156
2667
  import { spawn } from "child_process";
2157
2668
  import { randomUUID as randomUUID3 } from "crypto";
2158
- import { existsSync as existsSync8, unlinkSync as unlinkSync2, readFileSync as readFileSync6, openSync, closeSync, statSync } from "fs";
2159
- import path8 from "path";
2160
- import { fileURLToPath } from "url";
2669
+ import { existsSync as existsSync10, unlinkSync as unlinkSync2, readFileSync as readFileSync8, openSync, closeSync, statSync } from "fs";
2670
+ import path11 from "path";
2671
+ import { fileURLToPath as fileURLToPath2 } from "url";
2161
2672
  function handleData(chunk) {
2162
2673
  _buffer += chunk.toString();
2163
2674
  if (_buffer.length > MAX_BUFFER) {
@@ -2182,9 +2693,9 @@ function handleData(chunk) {
2182
2693
  }
2183
2694
  }
2184
2695
  function cleanupStaleFiles() {
2185
- if (existsSync8(PID_PATH)) {
2696
+ if (existsSync10(PID_PATH)) {
2186
2697
  try {
2187
- const pid = parseInt(readFileSync6(PID_PATH, "utf8").trim(), 10);
2698
+ const pid = parseInt(readFileSync8(PID_PATH, "utf8").trim(), 10);
2188
2699
  if (pid > 0) {
2189
2700
  try {
2190
2701
  process.kill(pid, 0);
@@ -2205,11 +2716,11 @@ function cleanupStaleFiles() {
2205
2716
  }
2206
2717
  }
2207
2718
  function findPackageRoot() {
2208
- let dir = path8.dirname(fileURLToPath(import.meta.url));
2209
- const { root } = path8.parse(dir);
2719
+ let dir = path11.dirname(fileURLToPath2(import.meta.url));
2720
+ const { root } = path11.parse(dir);
2210
2721
  while (dir !== root) {
2211
- if (existsSync8(path8.join(dir, "package.json"))) return dir;
2212
- dir = path8.dirname(dir);
2722
+ if (existsSync10(path11.join(dir, "package.json"))) return dir;
2723
+ dir = path11.dirname(dir);
2213
2724
  }
2214
2725
  return null;
2215
2726
  }
@@ -2219,8 +2730,8 @@ function spawnDaemon() {
2219
2730
  process.stderr.write("[exed-client] WARN: cannot find package root\n");
2220
2731
  return;
2221
2732
  }
2222
- const daemonPath = path8.join(pkgRoot, "dist", "lib", "exe-daemon.js");
2223
- if (!existsSync8(daemonPath)) {
2733
+ const daemonPath = path11.join(pkgRoot, "dist", "lib", "exe-daemon.js");
2734
+ if (!existsSync10(daemonPath)) {
2224
2735
  process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
2225
2736
  `);
2226
2737
  return;
@@ -2228,7 +2739,7 @@ function spawnDaemon() {
2228
2739
  const resolvedPath = daemonPath;
2229
2740
  process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
2230
2741
  `);
2231
- const logPath = path8.join(path8.dirname(SOCKET_PATH), "exed.log");
2742
+ const logPath = path11.join(path11.dirname(SOCKET_PATH), "exed.log");
2232
2743
  let stderrFd = "ignore";
2233
2744
  try {
2234
2745
  stderrFd = openSync(logPath, "a");
@@ -2390,9 +2901,9 @@ async function pingDaemon() {
2390
2901
  }
2391
2902
  function killAndRespawnDaemon() {
2392
2903
  process.stderr.write("[exed-client] Killing daemon for restart...\n");
2393
- if (existsSync8(PID_PATH)) {
2904
+ if (existsSync10(PID_PATH)) {
2394
2905
  try {
2395
- const pid = parseInt(readFileSync6(PID_PATH, "utf8").trim(), 10);
2906
+ const pid = parseInt(readFileSync8(PID_PATH, "utf8").trim(), 10);
2396
2907
  if (pid > 0) {
2397
2908
  try {
2398
2909
  process.kill(pid, "SIGKILL");
@@ -2476,9 +2987,9 @@ var init_exe_daemon_client = __esm({
2476
2987
  "src/lib/exe-daemon-client.ts"() {
2477
2988
  "use strict";
2478
2989
  init_config();
2479
- SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path8.join(EXE_AI_DIR, "exed.sock");
2480
- PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path8.join(EXE_AI_DIR, "exed.pid");
2481
- SPAWN_LOCK_PATH = path8.join(EXE_AI_DIR, "exed-spawn.lock");
2990
+ SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path11.join(EXE_AI_DIR, "exed.sock");
2991
+ PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path11.join(EXE_AI_DIR, "exed.pid");
2992
+ SPAWN_LOCK_PATH = path11.join(EXE_AI_DIR, "exed-spawn.lock");
2482
2993
  SPAWN_LOCK_STALE_MS = 3e4;
2483
2994
  CONNECT_TIMEOUT_MS = 15e3;
2484
2995
  REQUEST_TIMEOUT_MS = 3e4;
@@ -2529,10 +3040,10 @@ async function disposeEmbedder() {
2529
3040
  async function embedDirect(text) {
2530
3041
  const llamaCpp = await import("node-llama-cpp");
2531
3042
  const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
2532
- const { existsSync: existsSync11 } = await import("fs");
2533
- const path12 = await import("path");
2534
- const modelPath = path12.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
2535
- if (!existsSync11(modelPath)) {
3043
+ const { existsSync: existsSync13 } = await import("fs");
3044
+ const path15 = await import("path");
3045
+ const modelPath = path15.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
3046
+ if (!existsSync13(modelPath)) {
2536
3047
  throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
2537
3048
  }
2538
3049
  const llama = await llamaCpp.getLlama();
@@ -2568,11 +3079,11 @@ __export(worker_gate_exports, {
2568
3079
  registerWorkerPid: () => registerWorkerPid,
2569
3080
  tryAcquireWorkerSlot: () => tryAcquireWorkerSlot
2570
3081
  });
2571
- import { readdirSync as readdirSync3, writeFileSync as writeFileSync2, unlinkSync as unlinkSync3, mkdirSync as mkdirSync3 } from "fs";
2572
- import path9 from "path";
3082
+ import { readdirSync as readdirSync3, writeFileSync as writeFileSync4, unlinkSync as unlinkSync3, mkdirSync as mkdirSync5 } from "fs";
3083
+ import path12 from "path";
2573
3084
  function tryAcquireWorkerSlot() {
2574
3085
  try {
2575
- mkdirSync3(WORKER_PID_DIR, { recursive: true });
3086
+ mkdirSync5(WORKER_PID_DIR, { recursive: true });
2576
3087
  const files = readdirSync3(WORKER_PID_DIR);
2577
3088
  let alive = 0;
2578
3089
  for (const f of files) {
@@ -2585,7 +3096,7 @@ function tryAcquireWorkerSlot() {
2585
3096
  alive++;
2586
3097
  } catch {
2587
3098
  try {
2588
- unlinkSync3(path9.join(WORKER_PID_DIR, f));
3099
+ unlinkSync3(path12.join(WORKER_PID_DIR, f));
2589
3100
  } catch {
2590
3101
  }
2591
3102
  }
@@ -2597,14 +3108,14 @@ function tryAcquireWorkerSlot() {
2597
3108
  }
2598
3109
  function registerWorkerPid(pid) {
2599
3110
  try {
2600
- mkdirSync3(WORKER_PID_DIR, { recursive: true });
2601
- writeFileSync2(path9.join(WORKER_PID_DIR, `worker-${pid}.pid`), String(pid));
3111
+ mkdirSync5(WORKER_PID_DIR, { recursive: true });
3112
+ writeFileSync4(path12.join(WORKER_PID_DIR, `worker-${pid}.pid`), String(pid));
2602
3113
  } catch {
2603
3114
  }
2604
3115
  }
2605
3116
  function cleanupWorkerPid() {
2606
3117
  try {
2607
- unlinkSync3(path9.join(WORKER_PID_DIR, `worker-${process.pid}.pid`));
3118
+ unlinkSync3(path12.join(WORKER_PID_DIR, `worker-${process.pid}.pid`));
2608
3119
  } catch {
2609
3120
  }
2610
3121
  }
@@ -2613,7 +3124,7 @@ var init_worker_gate = __esm({
2613
3124
  "src/lib/worker-gate.ts"() {
2614
3125
  "use strict";
2615
3126
  init_config();
2616
- WORKER_PID_DIR = path9.join(EXE_AI_DIR, "worker-pids");
3127
+ WORKER_PID_DIR = path12.join(EXE_AI_DIR, "worker-pids");
2617
3128
  MAX_CONCURRENT_WORKERS = 3;
2618
3129
  }
2619
3130
  });
@@ -2725,14 +3236,14 @@ __export(cloud_sync_exports, {
2725
3236
  mergeRosterFromRemote: () => mergeRosterFromRemote,
2726
3237
  recordRosterDeletion: () => recordRosterDeletion
2727
3238
  });
2728
- import { readFileSync as readFileSync7, writeFileSync as writeFileSync3, existsSync as existsSync9, readdirSync as readdirSync4, mkdirSync as mkdirSync4, appendFileSync, unlinkSync as unlinkSync4, openSync as openSync2, closeSync as closeSync2 } from "fs";
3239
+ import { readFileSync as readFileSync9, writeFileSync as writeFileSync5, existsSync as existsSync11, readdirSync as readdirSync4, mkdirSync as mkdirSync6, appendFileSync as appendFileSync2, unlinkSync as unlinkSync4, openSync as openSync2, closeSync as closeSync2 } from "fs";
2729
3240
  import crypto4 from "crypto";
2730
- import path10 from "path";
3241
+ import path13 from "path";
2731
3242
  import { homedir } from "os";
2732
3243
  function logError(msg) {
2733
3244
  try {
2734
- const logPath = path10.join(homedir(), ".exe-os", "workers.log");
2735
- appendFileSync(logPath, `${(/* @__PURE__ */ new Date()).toISOString()} ${msg}
3245
+ const logPath = path13.join(homedir(), ".exe-os", "workers.log");
3246
+ appendFileSync2(logPath, `${(/* @__PURE__ */ new Date()).toISOString()} ${msg}
2736
3247
  `);
2737
3248
  } catch {
2738
3249
  }
@@ -2741,18 +3252,18 @@ async function withRosterLock(fn) {
2741
3252
  try {
2742
3253
  const fd = openSync2(ROSTER_LOCK_PATH, "wx");
2743
3254
  closeSync2(fd);
2744
- writeFileSync3(ROSTER_LOCK_PATH, String(Date.now()));
3255
+ writeFileSync5(ROSTER_LOCK_PATH, String(Date.now()));
2745
3256
  } catch (err) {
2746
3257
  if (err.code === "EEXIST") {
2747
3258
  try {
2748
- const ts = parseInt(readFileSync7(ROSTER_LOCK_PATH, "utf-8"), 10);
3259
+ const ts = parseInt(readFileSync9(ROSTER_LOCK_PATH, "utf-8"), 10);
2749
3260
  if (Date.now() - ts < LOCK_STALE_MS) {
2750
3261
  throw new Error("Roster merge already in progress \u2014 another sync is running");
2751
3262
  }
2752
3263
  unlinkSync4(ROSTER_LOCK_PATH);
2753
3264
  const fd = openSync2(ROSTER_LOCK_PATH, "wx");
2754
3265
  closeSync2(fd);
2755
- writeFileSync3(ROSTER_LOCK_PATH, String(Date.now()));
3266
+ writeFileSync5(ROSTER_LOCK_PATH, String(Date.now()));
2756
3267
  } catch (retryErr) {
2757
3268
  if (retryErr instanceof Error && retryErr.message.includes("already in progress")) throw retryErr;
2758
3269
  throw new Error("Roster merge already in progress \u2014 another sync is running");
@@ -3059,48 +3570,48 @@ async function cloudSync(config) {
3059
3570
  function recordRosterDeletion(name) {
3060
3571
  let deletions = [];
3061
3572
  try {
3062
- if (existsSync9(ROSTER_DELETIONS_PATH)) {
3063
- deletions = JSON.parse(readFileSync7(ROSTER_DELETIONS_PATH, "utf-8"));
3573
+ if (existsSync11(ROSTER_DELETIONS_PATH)) {
3574
+ deletions = JSON.parse(readFileSync9(ROSTER_DELETIONS_PATH, "utf-8"));
3064
3575
  }
3065
3576
  } catch {
3066
3577
  }
3067
3578
  if (!deletions.includes(name)) deletions.push(name);
3068
- writeFileSync3(ROSTER_DELETIONS_PATH, JSON.stringify(deletions));
3579
+ writeFileSync5(ROSTER_DELETIONS_PATH, JSON.stringify(deletions));
3069
3580
  }
3070
3581
  function consumeRosterDeletions() {
3071
3582
  try {
3072
- if (!existsSync9(ROSTER_DELETIONS_PATH)) return [];
3073
- const deletions = JSON.parse(readFileSync7(ROSTER_DELETIONS_PATH, "utf-8"));
3074
- writeFileSync3(ROSTER_DELETIONS_PATH, "[]");
3583
+ if (!existsSync11(ROSTER_DELETIONS_PATH)) return [];
3584
+ const deletions = JSON.parse(readFileSync9(ROSTER_DELETIONS_PATH, "utf-8"));
3585
+ writeFileSync5(ROSTER_DELETIONS_PATH, "[]");
3075
3586
  return deletions;
3076
3587
  } catch {
3077
3588
  return [];
3078
3589
  }
3079
3590
  }
3080
3591
  function buildRosterBlob(paths) {
3081
- const rosterPath = paths?.rosterPath ?? path10.join(EXE_AI_DIR, "exe-employees.json");
3082
- const identityDir = paths?.identityDir ?? path10.join(EXE_AI_DIR, "identity");
3083
- const configPath = paths?.configPath ?? path10.join(EXE_AI_DIR, "config.json");
3592
+ const rosterPath = paths?.rosterPath ?? path13.join(EXE_AI_DIR, "exe-employees.json");
3593
+ const identityDir = paths?.identityDir ?? path13.join(EXE_AI_DIR, "identity");
3594
+ const configPath = paths?.configPath ?? path13.join(EXE_AI_DIR, "config.json");
3084
3595
  let roster = [];
3085
- if (existsSync9(rosterPath)) {
3596
+ if (existsSync11(rosterPath)) {
3086
3597
  try {
3087
- roster = JSON.parse(readFileSync7(rosterPath, "utf-8"));
3598
+ roster = JSON.parse(readFileSync9(rosterPath, "utf-8"));
3088
3599
  } catch {
3089
3600
  }
3090
3601
  }
3091
3602
  const identities = {};
3092
- if (existsSync9(identityDir)) {
3603
+ if (existsSync11(identityDir)) {
3093
3604
  for (const file of readdirSync4(identityDir).filter((f) => f.endsWith(".md"))) {
3094
3605
  try {
3095
- identities[file] = readFileSync7(path10.join(identityDir, file), "utf-8");
3606
+ identities[file] = readFileSync9(path13.join(identityDir, file), "utf-8");
3096
3607
  } catch {
3097
3608
  }
3098
3609
  }
3099
3610
  }
3100
3611
  let config;
3101
- if (existsSync9(configPath)) {
3612
+ if (existsSync11(configPath)) {
3102
3613
  try {
3103
- config = JSON.parse(readFileSync7(configPath, "utf-8"));
3614
+ config = JSON.parse(readFileSync9(configPath, "utf-8"));
3104
3615
  } catch {
3105
3616
  }
3106
3617
  }
@@ -3176,23 +3687,23 @@ async function cloudPullRoster(config) {
3176
3687
  }
3177
3688
  }
3178
3689
  function mergeConfig(remoteConfig, configPath) {
3179
- const cfgPath = configPath ?? path10.join(EXE_AI_DIR, "config.json");
3690
+ const cfgPath = configPath ?? path13.join(EXE_AI_DIR, "config.json");
3180
3691
  let local = {};
3181
- if (existsSync9(cfgPath)) {
3692
+ if (existsSync11(cfgPath)) {
3182
3693
  try {
3183
- local = JSON.parse(readFileSync7(cfgPath, "utf-8"));
3694
+ local = JSON.parse(readFileSync9(cfgPath, "utf-8"));
3184
3695
  } catch {
3185
3696
  }
3186
3697
  }
3187
3698
  const merged = { ...remoteConfig, ...local };
3188
- const dir = path10.dirname(cfgPath);
3189
- if (!existsSync9(dir)) mkdirSync4(dir, { recursive: true });
3190
- writeFileSync3(cfgPath, JSON.stringify(merged, null, 2), "utf-8");
3699
+ const dir = path13.dirname(cfgPath);
3700
+ if (!existsSync11(dir)) mkdirSync6(dir, { recursive: true });
3701
+ writeFileSync5(cfgPath, JSON.stringify(merged, null, 2), "utf-8");
3191
3702
  }
3192
3703
  async function mergeRosterFromRemote(remote, paths) {
3193
3704
  return withRosterLock(async () => {
3194
3705
  const rosterPath = paths?.rosterPath ?? void 0;
3195
- const identityDir = paths?.identityDir ?? path10.join(EXE_AI_DIR, "identity");
3706
+ const identityDir = paths?.identityDir ?? path13.join(EXE_AI_DIR, "identity");
3196
3707
  const localEmployees = await loadEmployees(rosterPath);
3197
3708
  const localNames = new Set(localEmployees.map((e) => e.name));
3198
3709
  let added = 0;
@@ -3202,10 +3713,10 @@ async function mergeRosterFromRemote(remote, paths) {
3202
3713
  localNames.add(remoteEmp.name);
3203
3714
  added++;
3204
3715
  if (remote.identities[`${remoteEmp.name}.md`]) {
3205
- if (!existsSync9(identityDir)) mkdirSync4(identityDir, { recursive: true });
3206
- const idPath = path10.join(identityDir, `${remoteEmp.name}.md`);
3207
- if (!existsSync9(idPath)) {
3208
- writeFileSync3(idPath, remote.identities[`${remoteEmp.name}.md`], "utf-8");
3716
+ if (!existsSync11(identityDir)) mkdirSync6(identityDir, { recursive: true });
3717
+ const idPath = path13.join(identityDir, `${remoteEmp.name}.md`);
3718
+ if (!existsSync11(idPath)) {
3719
+ writeFileSync5(idPath, remote.identities[`${remoteEmp.name}.md`], "utf-8");
3209
3720
  }
3210
3721
  }
3211
3722
  try {
@@ -3665,9 +4176,9 @@ var init_cloud_sync = __esm({
3665
4176
  LOCALHOST_PATTERNS = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
3666
4177
  FETCH_TIMEOUT_MS = 3e4;
3667
4178
  PUSH_BATCH_SIZE = 5e3;
3668
- ROSTER_LOCK_PATH = path10.join(EXE_AI_DIR, "roster-merge.lock");
4179
+ ROSTER_LOCK_PATH = path13.join(EXE_AI_DIR, "roster-merge.lock");
3669
4180
  LOCK_STALE_MS = 3e4;
3670
- ROSTER_DELETIONS_PATH = path10.join(EXE_AI_DIR, "roster-deletions.json");
4181
+ ROSTER_DELETIONS_PATH = path13.join(EXE_AI_DIR, "roster-deletions.json");
3671
4182
  }
3672
4183
  });
3673
4184
 
@@ -3676,57 +4187,7 @@ init_memory();
3676
4187
  init_database();
3677
4188
  init_keychain();
3678
4189
  init_config();
3679
-
3680
- // src/lib/state-bus.ts
3681
- var StateBus = class {
3682
- handlers = /* @__PURE__ */ new Map();
3683
- globalHandlers = /* @__PURE__ */ new Set();
3684
- /** Emit an event to all subscribers */
3685
- emit(event) {
3686
- const typeHandlers = this.handlers.get(event.type);
3687
- if (typeHandlers) {
3688
- for (const handler of typeHandlers) {
3689
- try {
3690
- handler(event);
3691
- } catch {
3692
- }
3693
- }
3694
- }
3695
- for (const handler of this.globalHandlers) {
3696
- try {
3697
- handler(event);
3698
- } catch {
3699
- }
3700
- }
3701
- }
3702
- /** Subscribe to a specific event type */
3703
- on(type, handler) {
3704
- if (!this.handlers.has(type)) {
3705
- this.handlers.set(type, /* @__PURE__ */ new Set());
3706
- }
3707
- this.handlers.get(type).add(handler);
3708
- }
3709
- /** Subscribe to ALL events */
3710
- onAny(handler) {
3711
- this.globalHandlers.add(handler);
3712
- }
3713
- /** Unsubscribe from a specific event type */
3714
- off(type, handler) {
3715
- this.handlers.get(type)?.delete(handler);
3716
- }
3717
- /** Unsubscribe from ALL events */
3718
- offAny(handler) {
3719
- this.globalHandlers.delete(handler);
3720
- }
3721
- /** Remove all listeners */
3722
- clear() {
3723
- this.handlers.clear();
3724
- this.globalHandlers.clear();
3725
- }
3726
- };
3727
- var orgBus = new StateBus();
3728
-
3729
- // src/lib/store.ts
4190
+ init_state_bus();
3730
4191
  var INIT_MAX_RETRIES = 3;
3731
4192
  var INIT_RETRY_DELAY_MS = 1e3;
3732
4193
  function isBusyError2(err) {
@@ -4005,49 +4466,32 @@ function vectorToBlob(vector) {
4005
4466
 
4006
4467
  // src/adapters/claude/hooks/summary-worker.ts
4007
4468
  init_database();
4469
+ init_notifications();
4008
4470
  import crypto5 from "crypto";
4471
+ import { execSync as execSync4 } from "child_process";
4472
+ import { existsSync as existsSync12, mkdirSync as mkdirSync7, openSync as openSync3, closeSync as closeSync3 } from "fs";
4473
+ import path14 from "path";
4009
4474
 
4010
- // src/lib/notifications.ts
4011
- init_database();
4012
- import crypto2 from "crypto";
4013
- import path4 from "path";
4014
- import os3 from "os";
4015
- import {
4016
- readFileSync as readFileSync2,
4017
- readdirSync as readdirSync2,
4018
- unlinkSync,
4019
- existsSync as existsSync4,
4020
- rmdirSync
4021
- } from "fs";
4022
- async function writeNotification(notification) {
4475
+ // src/lib/task-scope.ts
4476
+ init_tmux_routing();
4477
+ function getCurrentSessionScope() {
4023
4478
  try {
4024
- const client = getClient();
4025
- const id = crypto2.randomUUID();
4026
- const now = (/* @__PURE__ */ new Date()).toISOString();
4027
- await client.execute({
4028
- sql: `INSERT INTO notifications (id, agent_id, agent_role, event, project, summary, task_file, read, created_at)
4029
- VALUES (?, ?, ?, ?, ?, ?, ?, 0, ?)`,
4030
- args: [
4031
- id,
4032
- notification.agentId,
4033
- notification.agentRole,
4034
- notification.event,
4035
- notification.project,
4036
- notification.summary,
4037
- notification.taskFile ?? null,
4038
- now
4039
- ]
4040
- });
4041
- } catch (err) {
4042
- process.stderr.write(`[notifications] WRITE FAILED: ${err instanceof Error ? err.message : String(err)}
4043
- `);
4479
+ return resolveExeSession();
4480
+ } catch {
4481
+ return null;
4044
4482
  }
4045
4483
  }
4484
+ function sessionScopeFilter(sessionScope, tableAlias) {
4485
+ const scope = sessionScope !== void 0 ? sessionScope : getCurrentSessionScope();
4486
+ if (!scope) return { sql: "", args: [] };
4487
+ const col = tableAlias ? `${tableAlias}.session_scope` : "session_scope";
4488
+ return {
4489
+ sql: ` AND (${col} IS NULL OR ${col} = ?)`,
4490
+ args: [scope]
4491
+ };
4492
+ }
4046
4493
 
4047
4494
  // src/adapters/claude/hooks/summary-worker.ts
4048
- import { execSync as execSync2 } from "child_process";
4049
- import { existsSync as existsSync10, mkdirSync as mkdirSync5, openSync as openSync3, closeSync as closeSync3 } from "fs";
4050
- import path11 from "path";
4051
4495
  async function main() {
4052
4496
  const agentId = process.env.AGENT_ID ?? "default";
4053
4497
  const agentRole = process.env.AGENT_ROLE ?? "employee";
@@ -4171,20 +4615,20 @@ async function main() {
4171
4615
  }
4172
4616
  try {
4173
4617
  const { EXE_AI_DIR: EXE_AI_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
4174
- const flagPath = path11.join(EXE_AI_DIR2, "session-cache", "needs-backfill");
4175
- if (existsSync10(flagPath)) {
4618
+ const flagPath = path14.join(EXE_AI_DIR2, "session-cache", "needs-backfill");
4619
+ if (existsSync12(flagPath)) {
4176
4620
  const { tryAcquireWorkerSlot: tryAcquireWorkerSlot2, registerWorkerPid: registerWorkerPid2 } = await Promise.resolve().then(() => (init_worker_gate(), worker_gate_exports));
4177
4621
  if (!tryAcquireWorkerSlot2()) {
4178
4622
  process.stderr.write("[summary-worker] Backfill needed but worker gate full \u2014 skipping\n");
4179
4623
  } else {
4180
4624
  const { spawn: spawn2 } = await import("child_process");
4181
- const { fileURLToPath: fileURLToPath2 } = await import("url");
4182
- const thisFile = fileURLToPath2(import.meta.url);
4183
- const backfillPath = path11.resolve(path11.dirname(thisFile), "backfill-vectors.js");
4184
- if (existsSync10(backfillPath)) {
4625
+ const { fileURLToPath: fileURLToPath3 } = await import("url");
4626
+ const thisFile = fileURLToPath3(import.meta.url);
4627
+ const backfillPath = path14.resolve(path14.dirname(thisFile), "backfill-vectors.js");
4628
+ if (existsSync12(backfillPath)) {
4185
4629
  const { EXE_AI_DIR: exeDir2 } = await Promise.resolve().then(() => (init_config(), config_exports));
4186
- const bLogPath = path11.join(exeDir2, "workers.log");
4187
- mkdirSync5(path11.dirname(bLogPath), { recursive: true });
4630
+ const bLogPath = path14.join(exeDir2, "workers.log");
4631
+ mkdirSync7(path14.dirname(bLogPath), { recursive: true });
4188
4632
  const bLogFd = openSync3(bLogPath, "a");
4189
4633
  const child = spawn2(process.execPath, [backfillPath], {
4190
4634
  detached: true,
@@ -4210,26 +4654,33 @@ async function main() {
4210
4654
  const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
4211
4655
  const cfg = await loadConfig2();
4212
4656
  if (cfg.cloud?.apiKey) {
4213
- const { initSyncCrypto: initSyncCrypto2, isSyncCryptoInitialized: isSyncCryptoInitialized2 } = await Promise.resolve().then(() => (init_crypto(), crypto_exports));
4214
- if (!isSyncCryptoInitialized2()) {
4215
- const { getMasterKey: getMasterKey2 } = await Promise.resolve().then(() => (init_keychain(), keychain_exports));
4216
- const mk = await getMasterKey2();
4217
- if (mk) initSyncCrypto2(mk);
4657
+ const { checkLicense: checkLicense2, isFeatureAllowed: isFeatureAllowed2 } = await Promise.resolve().then(() => (init_license(), license_exports));
4658
+ const license = await checkLicense2();
4659
+ if (!isFeatureAllowed2(license, "cloud_sync")) {
4660
+ } else {
4661
+ const { initSyncCrypto: initSyncCrypto2, isSyncCryptoInitialized: isSyncCryptoInitialized2 } = await Promise.resolve().then(() => (init_crypto(), crypto_exports));
4662
+ if (!isSyncCryptoInitialized2()) {
4663
+ const { getMasterKey: getMasterKey2 } = await Promise.resolve().then(() => (init_keychain(), keychain_exports));
4664
+ const mk = await getMasterKey2();
4665
+ if (mk) initSyncCrypto2(mk);
4666
+ }
4667
+ const { cloudSync: cloudSync2 } = await Promise.resolve().then(() => (init_cloud_sync(), cloud_sync_exports));
4668
+ await cloudSync2({ apiKey: cfg.cloud.apiKey, endpoint: cfg.cloud.endpoint });
4218
4669
  }
4219
- const { cloudSync: cloudSync2 } = await Promise.resolve().then(() => (init_cloud_sync(), cloud_sync_exports));
4220
- await cloudSync2({ apiKey: cfg.cloud.apiKey, endpoint: cfg.cloud.endpoint });
4221
4670
  }
4222
4671
  } catch (err) {
4223
4672
  const msg = err instanceof Error ? err.message : String(err);
4224
- const stack = err instanceof Error && err.stack ? "\n " + err.stack.split("\n").slice(1, 4).join("\n ") : "";
4225
- process.stderr.write(`[summary-worker] cloud sync failed: ${msg}${stack}
4673
+ if (!msg.includes("requires a paid plan")) {
4674
+ process.stderr.write(`[summary-worker] cloud sync failed: ${msg}
4226
4675
  `);
4676
+ }
4227
4677
  }
4228
4678
  if (agentId !== "exe" && agentId !== "default") {
4229
4679
  try {
4680
+ const swScope = sessionScopeFilter();
4230
4681
  const inProgressResult = await client.execute({
4231
- sql: "SELECT id, title, task_file FROM tasks WHERE assigned_to = ? AND status = 'in_progress'",
4232
- args: [agentId]
4682
+ sql: `SELECT id, title, task_file FROM tasks WHERE assigned_to = ? AND status = 'in_progress'${swScope.sql}`,
4683
+ args: [agentId, ...swScope.args]
4233
4684
  });
4234
4685
  for (const taskRow of inProgressResult.rows) {
4235
4686
  const taskFile = String(taskRow.task_file);
@@ -4237,7 +4688,7 @@ async function main() {
4237
4688
  const taskTitle = String(taskRow.title);
4238
4689
  let lastCommit = "";
4239
4690
  try {
4240
- lastCommit = execSync2("git log --oneline -1 --since='30 minutes ago'", {
4691
+ lastCommit = execSync4("git log --oneline -1 --since='30 minutes ago'", {
4241
4692
  encoding: "utf8",
4242
4693
  timeout: 5e3
4243
4694
  }).trim();