@askexenow/exe-os 0.9.21 → 0.9.22

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 (60) hide show
  1. package/dist/bin/backfill-conversations.js +17 -4
  2. package/dist/bin/backfill-responses.js +17 -4
  3. package/dist/bin/backfill-vectors.js +2 -2
  4. package/dist/bin/cleanup-stale-review-tasks.js +17 -4
  5. package/dist/bin/cli.js +378 -171
  6. package/dist/bin/exe-assign.js +17 -4
  7. package/dist/bin/exe-boot.js +2 -2
  8. package/dist/bin/exe-dispatch.js +17 -4
  9. package/dist/bin/exe-doctor.js +2 -2
  10. package/dist/bin/exe-export-behaviors.js +17 -4
  11. package/dist/bin/exe-forget.js +17 -4
  12. package/dist/bin/exe-gateway.js +17 -4
  13. package/dist/bin/exe-heartbeat.js +17 -4
  14. package/dist/bin/exe-kill.js +17 -4
  15. package/dist/bin/exe-launch-agent.js +17 -4
  16. package/dist/bin/exe-pending-messages.js +17 -4
  17. package/dist/bin/exe-pending-notifications.js +17 -4
  18. package/dist/bin/exe-pending-reviews.js +17 -4
  19. package/dist/bin/exe-review.js +17 -4
  20. package/dist/bin/exe-search.js +23 -8
  21. package/dist/bin/exe-session-cleanup.js +17 -4
  22. package/dist/bin/exe-start-codex.js +209 -32
  23. package/dist/bin/exe-start-opencode.js +17 -4
  24. package/dist/bin/exe-status.js +17 -4
  25. package/dist/bin/exe-team.js +17 -4
  26. package/dist/bin/git-sweep.js +17 -4
  27. package/dist/bin/graph-backfill.js +17 -4
  28. package/dist/bin/graph-export.js +17 -4
  29. package/dist/bin/install.js +42 -0
  30. package/dist/bin/intercom-check.js +17 -4
  31. package/dist/bin/scan-tasks.js +17 -4
  32. package/dist/bin/shard-migrate.js +17 -4
  33. package/dist/bin/update.js +187 -42
  34. package/dist/gateway/index.js +17 -4
  35. package/dist/hooks/bug-report-worker.js +793 -150
  36. package/dist/hooks/codex-stop-task-finalizer.js +3020 -2375
  37. package/dist/hooks/commit-complete.js +156 -6
  38. package/dist/hooks/error-recall.js +23 -8
  39. package/dist/hooks/ingest.js +17 -4
  40. package/dist/hooks/instructions-loaded.js +17 -4
  41. package/dist/hooks/notification.js +17 -4
  42. package/dist/hooks/post-compact.js +17 -4
  43. package/dist/hooks/post-tool-combined.js +23 -8
  44. package/dist/hooks/pre-compact.js +156 -8
  45. package/dist/hooks/pre-tool-use.js +21 -12
  46. package/dist/hooks/prompt-submit.js +23 -8
  47. package/dist/hooks/session-end.js +156 -8
  48. package/dist/hooks/session-start.js +23 -8
  49. package/dist/hooks/stop.js +306 -9
  50. package/dist/hooks/subagent-stop.js +306 -9
  51. package/dist/hooks/summary-worker.js +2 -2
  52. package/dist/index.js +17 -4
  53. package/dist/lib/exe-daemon.js +17 -4
  54. package/dist/lib/hybrid-search.js +23 -8
  55. package/dist/lib/schedules.js +2 -2
  56. package/dist/lib/store.js +17 -4
  57. package/dist/mcp/server.js +36 -10
  58. package/dist/runtime/index.js +17 -4
  59. package/dist/tui/App.js +17 -4
  60. package/package.json +1 -1
@@ -1285,6 +1285,17 @@ var init_daemon_auth = __esm({
1285
1285
  });
1286
1286
 
1287
1287
  // src/lib/exe-daemon-client.ts
1288
+ var exe_daemon_client_exports = {};
1289
+ __export(exe_daemon_client_exports, {
1290
+ connectEmbedDaemon: () => connectEmbedDaemon,
1291
+ disconnectClient: () => disconnectClient,
1292
+ embedBatchViaClient: () => embedBatchViaClient,
1293
+ embedViaClient: () => embedViaClient,
1294
+ isClientConnected: () => isClientConnected,
1295
+ pingDaemon: () => pingDaemon,
1296
+ sendDaemonRequest: () => sendDaemonRequest,
1297
+ sendIngestRequest: () => sendIngestRequest
1298
+ });
1288
1299
  import net from "net";
1289
1300
  import os6 from "os";
1290
1301
  import { spawn } from "child_process";
@@ -1521,6 +1532,9 @@ async function connectEmbedDaemon() {
1521
1532
  }
1522
1533
  return false;
1523
1534
  }
1535
+ function sendRequest(texts, priority) {
1536
+ return sendDaemonRequest({ texts, priority });
1537
+ }
1524
1538
  function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
1525
1539
  return new Promise((resolve) => {
1526
1540
  if (!_socket || !_connected) {
@@ -1543,10 +1557,170 @@ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
1543
1557
  }
1544
1558
  });
1545
1559
  }
1560
+ async function pingDaemon() {
1561
+ if (!_socket || !_connected) return null;
1562
+ const response = await sendDaemonRequest({ type: "health" }, 5e3);
1563
+ if (response.health) {
1564
+ return response.health;
1565
+ }
1566
+ return null;
1567
+ }
1568
+ function killAndRespawnDaemon() {
1569
+ if (!acquireSpawnLock()) {
1570
+ process.stderr.write("[exed-client] Another process is already restarting daemon \u2014 skipping\n");
1571
+ if (_socket) {
1572
+ _socket.destroy();
1573
+ _socket = null;
1574
+ }
1575
+ _connected = false;
1576
+ _buffer = "";
1577
+ return;
1578
+ }
1579
+ try {
1580
+ process.stderr.write("[exed-client] Killing daemon for restart...\n");
1581
+ if (existsSync7(PID_PATH)) {
1582
+ try {
1583
+ const pid = parseInt(readFileSync7(PID_PATH, "utf8").trim(), 10);
1584
+ if (pid > 0) {
1585
+ try {
1586
+ process.kill(pid, "SIGKILL");
1587
+ } catch {
1588
+ }
1589
+ }
1590
+ } catch {
1591
+ }
1592
+ }
1593
+ if (_socket) {
1594
+ _socket.destroy();
1595
+ _socket = null;
1596
+ }
1597
+ _connected = false;
1598
+ _buffer = "";
1599
+ try {
1600
+ unlinkSync3(PID_PATH);
1601
+ } catch {
1602
+ }
1603
+ try {
1604
+ unlinkSync3(SOCKET_PATH);
1605
+ } catch {
1606
+ }
1607
+ spawnDaemon();
1608
+ } finally {
1609
+ releaseSpawnLock();
1610
+ }
1611
+ }
1612
+ function isDaemonTooYoung() {
1613
+ try {
1614
+ const stat = statSync(PID_PATH);
1615
+ return Date.now() - stat.mtimeMs < MIN_DAEMON_AGE_MS;
1616
+ } catch {
1617
+ return false;
1618
+ }
1619
+ }
1620
+ async function retryThenRestart(doRequest, label) {
1621
+ const result = await doRequest();
1622
+ if (!result.error) {
1623
+ _consecutiveFailures = 0;
1624
+ return result;
1625
+ }
1626
+ _consecutiveFailures++;
1627
+ for (let i = 0; i < MAX_RETRIES_BEFORE_RESTART; i++) {
1628
+ const delayMs = RETRY_DELAYS_MS[i] ?? 5e3;
1629
+ process.stderr.write(`[exed-client] ${label} failed (${result.error}), retry ${i + 1}/${MAX_RETRIES_BEFORE_RESTART} in ${delayMs}ms
1630
+ `);
1631
+ await new Promise((r) => setTimeout(r, delayMs));
1632
+ if (!_connected) {
1633
+ if (!await connectToSocket()) continue;
1634
+ }
1635
+ const retry = await doRequest();
1636
+ if (!retry.error) {
1637
+ _consecutiveFailures = 0;
1638
+ return retry;
1639
+ }
1640
+ _consecutiveFailures++;
1641
+ }
1642
+ if (isDaemonTooYoung()) {
1643
+ process.stderr.write(`[exed-client] ${label}: daemon too young (< ${MIN_DAEMON_AGE_MS / 1e3}s) \u2014 skipping restart
1644
+ `);
1645
+ return { error: result.error };
1646
+ }
1647
+ process.stderr.write(`[exed-client] ${label}: ${_consecutiveFailures} consecutive failures \u2014 restarting daemon
1648
+ `);
1649
+ killAndRespawnDaemon();
1650
+ const start = Date.now();
1651
+ let delay2 = 200;
1652
+ while (Date.now() - start < CONNECT_TIMEOUT_MS) {
1653
+ await new Promise((r) => setTimeout(r, delay2));
1654
+ if (await connectToSocket()) break;
1655
+ delay2 = Math.min(delay2 * 2, 3e3);
1656
+ }
1657
+ if (!_connected) return { error: "Daemon restart failed" };
1658
+ const final = await doRequest();
1659
+ if (!final.error) _consecutiveFailures = 0;
1660
+ return final;
1661
+ }
1662
+ async function embedViaClient(text, priority = "high") {
1663
+ if (!_connected && !await connectEmbedDaemon()) return null;
1664
+ _requestCount++;
1665
+ if (_requestCount % HEALTH_CHECK_INTERVAL === 0) {
1666
+ const health = await pingDaemon();
1667
+ if (!health && !isDaemonTooYoung()) {
1668
+ process.stderr.write(`[exed-client] Periodic health check failed at request ${_requestCount} \u2014 restarting daemon
1669
+ `);
1670
+ killAndRespawnDaemon();
1671
+ const start = Date.now();
1672
+ let d = 200;
1673
+ while (Date.now() - start < CONNECT_TIMEOUT_MS) {
1674
+ await new Promise((r) => setTimeout(r, d));
1675
+ if (await connectToSocket()) break;
1676
+ d = Math.min(d * 2, 3e3);
1677
+ }
1678
+ if (!_connected) return null;
1679
+ }
1680
+ }
1681
+ const result = await retryThenRestart(
1682
+ () => sendRequest([text], priority),
1683
+ "Embed"
1684
+ );
1685
+ return !result.error && result.vectors?.[0] ? result.vectors[0] : null;
1686
+ }
1687
+ async function embedBatchViaClient(texts, priority = "high") {
1688
+ if (!_connected && !await connectEmbedDaemon()) return null;
1689
+ _requestCount++;
1690
+ const result = await retryThenRestart(
1691
+ () => sendRequest(texts, priority),
1692
+ "Batch embed"
1693
+ );
1694
+ return !result.error && result.vectors ? result.vectors : null;
1695
+ }
1696
+ function disconnectClient() {
1697
+ if (_socket) {
1698
+ _socket.destroy();
1699
+ _socket = null;
1700
+ }
1701
+ _connected = false;
1702
+ _buffer = "";
1703
+ for (const [id, entry] of _pending) {
1704
+ clearTimeout(entry.timer);
1705
+ _pending.delete(id);
1706
+ entry.resolve({ error: "Client disconnected" });
1707
+ }
1708
+ }
1546
1709
  function isClientConnected() {
1547
1710
  return _connected;
1548
1711
  }
1549
- var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, DAEMON_TOKEN_ENV, _socket, _connected, _buffer, _pending, MAX_BUFFER;
1712
+ function sendIngestRequest(payload) {
1713
+ if (!_socket || !_connected) return false;
1714
+ try {
1715
+ const id = randomUUID();
1716
+ const token = process.env[DAEMON_TOKEN_ENV] ?? readDaemonToken();
1717
+ _socket.write(JSON.stringify({ id, token, type: "ingest", ...payload }) + "\n");
1718
+ return true;
1719
+ } catch {
1720
+ return false;
1721
+ }
1722
+ }
1723
+ var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, DAEMON_TOKEN_ENV, _socket, _connected, _buffer, _requestCount, _consecutiveFailures, HEALTH_CHECK_INTERVAL, MAX_RETRIES_BEFORE_RESTART, RETRY_DELAYS_MS, MIN_DAEMON_AGE_MS, _pending, MAX_BUFFER;
1550
1724
  var init_exe_daemon_client = __esm({
1551
1725
  "src/lib/exe-daemon-client.ts"() {
1552
1726
  "use strict";
@@ -1562,12 +1736,27 @@ var init_exe_daemon_client = __esm({
1562
1736
  _socket = null;
1563
1737
  _connected = false;
1564
1738
  _buffer = "";
1739
+ _requestCount = 0;
1740
+ _consecutiveFailures = 0;
1741
+ HEALTH_CHECK_INTERVAL = 100;
1742
+ MAX_RETRIES_BEFORE_RESTART = 3;
1743
+ RETRY_DELAYS_MS = [1e3, 3e3, 5e3];
1744
+ MIN_DAEMON_AGE_MS = 3e4;
1565
1745
  _pending = /* @__PURE__ */ new Map();
1566
1746
  MAX_BUFFER = 1e7;
1567
1747
  }
1568
1748
  });
1569
1749
 
1570
1750
  // src/lib/daemon-protocol.ts
1751
+ var daemon_protocol_exports = {};
1752
+ __export(daemon_protocol_exports, {
1753
+ deserializeArgs: () => deserializeArgs,
1754
+ deserializeResultSet: () => deserializeResultSet,
1755
+ deserializeValue: () => deserializeValue,
1756
+ serializeArgs: () => serializeArgs,
1757
+ serializeResultSet: () => serializeResultSet,
1758
+ serializeValue: () => serializeValue
1759
+ });
1571
1760
  function serializeValue(v) {
1572
1761
  if (v === null || v === void 0) return null;
1573
1762
  if (typeof v === "bigint") return Number(v);
@@ -1592,6 +1781,32 @@ function deserializeValue(v) {
1592
1781
  }
1593
1782
  return v;
1594
1783
  }
1784
+ function serializeArgs(args) {
1785
+ return args.map(serializeValue);
1786
+ }
1787
+ function deserializeArgs(args) {
1788
+ return args.map(deserializeValue);
1789
+ }
1790
+ function serializeResultSet(rs) {
1791
+ const rows = [];
1792
+ for (const row of rs.rows) {
1793
+ const obj = {};
1794
+ for (let i = 0; i < rs.columns.length; i++) {
1795
+ const col = rs.columns[i];
1796
+ if (col !== void 0) {
1797
+ obj[col] = serializeValue(row[i]);
1798
+ }
1799
+ }
1800
+ rows.push(obj);
1801
+ }
1802
+ return {
1803
+ columns: [...rs.columns],
1804
+ columnTypes: [...rs.columnTypes ?? []],
1805
+ rows,
1806
+ rowsAffected: typeof rs.rowsAffected === "bigint" ? Number(rs.rowsAffected) : rs.rowsAffected ?? 0,
1807
+ lastInsertRowid: rs.lastInsertRowid != null ? typeof rs.lastInsertRowid === "bigint" ? Number(rs.lastInsertRowid) : rs.lastInsertRowid : null
1808
+ };
1809
+ }
1595
1810
  function deserializeResultSet(srs) {
1596
1811
  const rows = srs.rows.map((obj) => {
1597
1812
  const values = srs.columns.map(
@@ -3156,8 +3371,8 @@ function getShardClient(projectName) {
3156
3371
  throw new Error("Shard manager not initialized. Call initShardManager() first.");
3157
3372
  }
3158
3373
  const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
3159
- if (!safeName) {
3160
- throw new Error(`Invalid project name for shard: "${projectName}"`);
3374
+ if (!safeName || safeName === "unknown") {
3375
+ throw new Error(`Invalid project name for shard: "${projectName}" (resolved to "${safeName}")`);
3161
3376
  }
3162
3377
  const cached = _shards.get(safeName);
3163
3378
  if (cached) {
@@ -4026,19 +4241,32 @@ async function flushBatch() {
4026
4241
  const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
4027
4242
  if (isShardingEnabled2()) {
4028
4243
  const byProject = /* @__PURE__ */ new Map();
4244
+ let skippedUnknown = 0;
4029
4245
  for (const row of batch) {
4030
- const proj = row.project_name || "unknown";
4246
+ const proj = row.project_name?.trim();
4247
+ if (!proj) {
4248
+ skippedUnknown++;
4249
+ continue;
4250
+ }
4031
4251
  if (!byProject.has(proj)) byProject.set(proj, []);
4032
4252
  byProject.get(proj).push(row);
4033
4253
  }
4254
+ if (skippedUnknown > 0) {
4255
+ process.stderr.write(
4256
+ `[store] Shard skip: ${skippedUnknown} record(s) with empty project_name (kept in main DB only)
4257
+ `
4258
+ );
4259
+ }
4034
4260
  for (const [project, rows] of byProject) {
4035
4261
  try {
4036
4262
  const shardClient = await getReadyShardClient2(project);
4037
4263
  const shardStmts = rows.map(buildStmt);
4038
4264
  await shardClient.batch(shardStmts, "write");
4039
4265
  } catch (err) {
4266
+ const fullError = err instanceof Error ? `${err.name}: ${err.message}${err.stack ? `
4267
+ ${err.stack.split("\n").slice(1, 3).join("\n")}` : ""}` : String(err);
4040
4268
  process.stderr.write(
4041
- `[store] Shard write failed for ${project}: ${err instanceof Error ? err.message : String(err)}
4269
+ `[store] Shard write failed for ${project} (${rows.length} records): ${fullError}
4042
4270
  `
4043
4271
  );
4044
4272
  }
@@ -4277,6 +4505,77 @@ var init_store = __esm({
4277
4505
  }
4278
4506
  });
4279
4507
 
4508
+ // src/bin/fast-db-init.ts
4509
+ var fast_db_init_exports = {};
4510
+ __export(fast_db_init_exports, {
4511
+ fastDbInit: () => fastDbInit
4512
+ });
4513
+ async function fastDbInit() {
4514
+ const { isInitialized: isInitialized2, getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
4515
+ if (isInitialized2()) {
4516
+ return getClient2();
4517
+ }
4518
+ try {
4519
+ const { connectEmbedDaemon: connectEmbedDaemon2, sendDaemonRequest: sendDaemonRequest2, isClientConnected: isClientConnected2 } = await Promise.resolve().then(() => (init_exe_daemon_client(), exe_daemon_client_exports));
4520
+ const { deserializeResultSet: deserializeResultSet2 } = await Promise.resolve().then(() => (init_daemon_protocol(), daemon_protocol_exports));
4521
+ await connectEmbedDaemon2();
4522
+ if (isClientConnected2()) {
4523
+ const daemonClient = {
4524
+ async execute(stmt) {
4525
+ const sql = typeof stmt === "string" ? stmt : stmt.sql;
4526
+ const args = typeof stmt === "string" ? [] : Array.isArray(stmt.args) ? stmt.args : [];
4527
+ const resp = await sendDaemonRequest2({ type: "db-execute", sql, args });
4528
+ if (resp.error) throw new Error(String(resp.error));
4529
+ if (resp.db) return deserializeResultSet2(resp.db);
4530
+ throw new Error("Unexpected daemon response");
4531
+ },
4532
+ async batch(stmts, mode) {
4533
+ const statements = stmts.map((s) => {
4534
+ const sql = typeof s === "string" ? s : s.sql;
4535
+ const args = typeof s === "string" ? [] : Array.isArray(s.args) ? s.args : [];
4536
+ return { sql, args };
4537
+ });
4538
+ const resp = await sendDaemonRequest2({ type: "db-batch", statements, mode: mode ?? "deferred" });
4539
+ if (resp.error) throw new Error(String(resp.error));
4540
+ const batchResults = resp["db-batch"];
4541
+ if (batchResults) return batchResults.map(deserializeResultSet2);
4542
+ throw new Error("Unexpected daemon batch response");
4543
+ },
4544
+ async transaction(_mode) {
4545
+ throw new Error("Transactions not supported via daemon socket");
4546
+ },
4547
+ async executeMultiple(_sql) {
4548
+ throw new Error("executeMultiple not supported via daemon socket");
4549
+ },
4550
+ async migrate(_stmts) {
4551
+ throw new Error("migrate not supported via daemon socket");
4552
+ },
4553
+ sync() {
4554
+ return Promise.resolve(void 0);
4555
+ },
4556
+ close() {
4557
+ },
4558
+ get closed() {
4559
+ return false;
4560
+ },
4561
+ get protocol() {
4562
+ return "file";
4563
+ }
4564
+ };
4565
+ return daemonClient;
4566
+ }
4567
+ } catch {
4568
+ }
4569
+ const { initStore: initStore2 } = await Promise.resolve().then(() => (init_store(), store_exports));
4570
+ await initStore2({ lightweight: true });
4571
+ return getClient2();
4572
+ }
4573
+ var init_fast_db_init = __esm({
4574
+ "src/bin/fast-db-init.ts"() {
4575
+ "use strict";
4576
+ }
4577
+ });
4578
+
4280
4579
  // src/lib/memory-queue.ts
4281
4580
  import { appendFileSync as appendFileSync2, readFileSync as readFileSync11, renameSync as renameSync4, unlinkSync as unlinkSync4, existsSync as existsSync13, statSync as statSync2 } from "fs";
4282
4581
  import path15 from "path";
@@ -4538,8 +4837,7 @@ process.stdin.on("end", () => {
4538
4837
  }
4539
4838
  const CAPACITY_SIGNALS = /context[- ]?full|hit capacity|conversation is too long|maximum context length|context window.*(?:limit|exceed|full)/i;
4540
4839
  if (!canCoordinate(agent.agentId, agent.agentRole) && CAPACITY_SIGNALS.test(message)) {
4541
- Promise.resolve().then(() => (init_store(), store_exports)).then(({ initStore: initStore2 }) => initStore2()).then(() => Promise.resolve().then(() => (init_database(), database_exports))).then(async ({ getClient: getClient2 }) => {
4542
- const client = getClient2();
4840
+ Promise.resolve().then(() => (init_fast_db_init(), fast_db_init_exports)).then(({ fastDbInit: fastDbInit2 }) => fastDbInit2()).then(async (client) => {
4543
4841
  const stScope = sessionScopeFilter();
4544
4842
  const tasks = await client.execute({
4545
4843
  sql: `SELECT title, status, task_file FROM tasks
@@ -4573,8 +4871,7 @@ process.stdin.on("end", () => {
4573
4871
  });
4574
4872
  }
4575
4873
  if (!canCoordinate(agent.agentId, agent.agentRole)) {
4576
- Promise.resolve().then(() => (init_store(), store_exports)).then(({ initStore: initStore2 }) => initStore2()).then(() => Promise.resolve().then(() => (init_database(), database_exports))).then(({ getClient: getClient2 }) => {
4577
- const client = getClient2();
4874
+ Promise.resolve().then(() => (init_fast_db_init(), fast_db_init_exports)).then(({ fastDbInit: fastDbInit2 }) => fastDbInit2()).then(async (client) => {
4578
4875
  const orScope = sessionScopeFilter();
4579
4876
  return client.execute({
4580
4877
  sql: `SELECT title, status FROM tasks