@askexenow/exe-os 0.9.166 → 0.9.167
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.
- package/deploy/compose/backup.sh +45 -7
- package/deploy/compose/setup.sh +7 -0
- package/deploy/stack-manifests/v0.9.json +40 -1
- package/dist/{active-agent-R2KMWMR6.js → active-agent-DGTIJN2U.js} +2 -2
- package/dist/{active-agent-CYMM3QQA.js → active-agent-HVMLG6FH.js} +2 -2
- package/dist/{agentic-ontology-GKAKYNPE.js → agentic-ontology-S54AFODT.js} +1 -1
- package/dist/{backfill-metadata-Z5SYUWAV.js → backfill-metadata-74IWETRF.js} +4 -3
- package/dist/{behaviors-QGU6XI5R.js → behaviors-LZVAVHTC.js} +2 -2
- package/dist/bin/agentic-ontology-backfill.js +5 -4
- package/dist/bin/agentic-reflection-backfill.js +6 -5
- package/dist/bin/agentic-semantic-label.js +5 -4
- package/dist/bin/backfill-conversations.js +5 -4
- package/dist/bin/backfill-responses.js +5 -4
- package/dist/bin/backfill-vectors.js +6 -5
- package/dist/bin/bulk-sync-postgres.js +6 -5
- package/dist/bin/cleanup-stale-review-tasks.js +9 -9
- package/dist/bin/cli.js +16 -13
- package/dist/bin/daily-summary.js +0 -217
- package/dist/bin/deferred-daemon-restart.js +8 -0
- package/dist/bin/exe-agent-config.js +1 -1
- package/dist/bin/exe-agent.js +10 -10
- package/dist/bin/exe-assign.js +7 -6
- package/dist/bin/exe-boot.js +20 -19
- package/dist/bin/exe-call.js +4 -4
- package/dist/bin/exe-cloud.js +3 -3
- package/dist/bin/exe-dispatch.js +9 -9
- package/dist/bin/exe-doctor.js +1 -1
- package/dist/bin/exe-export-behaviors.js +7 -6
- package/dist/bin/exe-forget.js +6 -5
- package/dist/bin/exe-gateway.js +5 -5
- package/dist/bin/exe-heartbeat.js +9 -9
- package/dist/bin/exe-kill.js +13 -12
- package/dist/bin/exe-launch-agent.js +11 -10
- package/dist/bin/exe-new-employee.js +6 -6
- package/dist/bin/exe-pending-messages.js +10 -10
- package/dist/bin/exe-pending-notifications.js +9 -9
- package/dist/bin/exe-pending-reviews.js +9 -9
- package/dist/bin/exe-rename.js +4 -4
- package/dist/bin/exe-review.js +12 -11
- package/dist/bin/exe-search.js +5 -4
- package/dist/bin/exe-session-cleanup.js +19 -14
- package/dist/bin/exe-settings.js +3 -3
- package/dist/bin/exe-start-codex.js +11 -10
- package/dist/bin/exe-start-opencode.js +8 -7
- package/dist/bin/exe-status.js +10 -10
- package/dist/bin/exe-team.js +2 -2
- package/dist/bin/git-sweep.js +9 -9
- package/dist/bin/graph-backfill.js +4 -3
- package/dist/bin/graph-export.js +5 -4
- package/dist/bin/import-history.js +171 -0
- package/dist/bin/install-launchd.js +41 -0
- package/dist/bin/install.js +50 -74
- package/dist/bin/intercom-check.js +4 -4
- package/dist/bin/postgres-agentic-reflection-backfill.js +2 -2
- package/dist/bin/postgres-agentic-semantic-backfill.js +4 -4
- package/dist/bin/pre-publish.js +1 -1
- package/dist/bin/scan-tasks.js +22 -12
- package/dist/bin/setup.js +1 -1
- package/dist/bin/shard-migrate.js +4 -3
- package/dist/bin/stack-update.js +61 -857
- package/dist/bin/vps-backup.js +170 -0
- package/dist/bin/vps-health-gate.js +232 -0
- package/dist/{capacity-monitor-ZEAE4WP2.js → capacity-monitor-JBZB2S4P.js} +10 -10
- package/dist/{catchup-brief-OGWCHENC.js → catchup-brief-HE2EMZS5.js} +12 -11
- package/dist/{chunk-DJJNB47C.js → chunk-27DO3EZO.js} +1 -1
- package/dist/{chunk-45FYZIHI.js → chunk-32YUET3Y.js} +2 -2
- package/dist/{chunk-Y75ECPO5.js → chunk-3FW5LUGI.js} +2 -2
- package/dist/{chunk-4OZGQZ4U.js → chunk-3M3O56VT.js} +636 -179
- package/dist/{chunk-77WQOD6J.js → chunk-4CXUZ4NI.js} +2 -2
- package/dist/{chunk-PBXWPHEK.js → chunk-4VEHJZ6R.js} +1 -1
- package/dist/{chunk-TH22QIEC.js → chunk-6A4COFDG.js} +1 -1
- package/dist/{chunk-ACBTCC2L.js → chunk-7OJH2A6I.js} +1 -1
- package/dist/{chunk-NHCOTCI6.js → chunk-A7SGEBXJ.js} +2 -2
- package/dist/{chunk-5MPQSNZF.js → chunk-AUTCT6AY.js} +1 -1
- package/dist/{chunk-OEKSTOTE.js → chunk-AZAZ2C75.js} +1 -1
- package/dist/chunk-CHCA3ZM2.js +167 -0
- package/dist/{chunk-X347L57O.js → chunk-CSTJQDOE.js} +4 -3
- package/dist/{chunk-B234R3VW.js → chunk-D7WLV6WD.js} +2 -2
- package/dist/{chunk-GMXF3AHJ.js → chunk-DGAONW36.js} +1 -1
- package/dist/chunk-EAT5YL3W.js +229 -0
- package/dist/{chunk-OD4H5YCJ.js → chunk-EKTQE2R5.js} +8 -8
- package/dist/{chunk-Z44PC42G.js → chunk-ELUBA7XL.js} +2 -2
- package/dist/{chunk-ZWS6XQER.js → chunk-F5AKOE4P.js} +7 -7
- package/dist/{chunk-T5YULDDO.js → chunk-FVI4UBKO.js} +27 -4
- package/dist/{chunk-ESRI7MFI.js → chunk-GAN7PW6G.js} +28 -24
- package/dist/{chunk-K4OWYJSP.js → chunk-GM2WZTG3.js} +2 -2
- package/dist/{chunk-TAB5QGIK.js → chunk-GZYQTPTF.js} +3 -3
- package/dist/{chunk-CXDU5DE3.js → chunk-IAUNGATJ.js} +1 -1
- package/dist/{chunk-YS63NS6M.js → chunk-IHSM5GR4.js} +1 -1
- package/dist/{chunk-23PTS2ZD.js → chunk-IP7KJAUW.js} +117 -15
- package/dist/{chunk-D6IMJAV2.js → chunk-J64P2LB2.js} +2 -2
- package/dist/{chunk-CXAVSQZM.js → chunk-JXMSCKRM.js} +1 -1
- package/dist/{chunk-RQMK3IQH.js → chunk-K4OTJP6N.js} +14 -7
- package/dist/{chunk-L7ROZR2H.js → chunk-KXAUMIOX.js} +1 -1
- package/dist/{chunk-TPC3LAP7.js → chunk-LGY2BIOT.js} +13 -0
- package/dist/{chunk-RPIDSBK7.js → chunk-LLHRJEE4.js} +3 -3
- package/dist/{chunk-6WG2VIKC.js → chunk-LM7H6XU4.js} +1 -1
- package/dist/{chunk-Y6GMKZZ2.js → chunk-LOFFGJSY.js} +150 -23
- package/dist/{chunk-W7SDGBEC.js → chunk-MFI5OXYW.js} +52 -84
- package/dist/{chunk-KNPEVPYG.js → chunk-MSSQWF6X.js} +2 -2
- package/dist/{chunk-QIQAO3VG.js → chunk-NEFFFKMD.js} +3 -3
- package/dist/{chunk-YUC552KZ.js → chunk-NEHONJJC.js} +3 -3
- package/dist/{chunk-KZ7SXZ2V.js → chunk-NFMQRLCD.js} +1 -1
- package/dist/{chunk-52HCNDPG.js → chunk-O4TATDOV.js} +1 -1
- package/dist/{chunk-AR3OYGLB.js → chunk-PEFBRL4S.js} +28 -6
- package/dist/{chunk-AEUXUEJG.js → chunk-PEXVU3HU.js} +5 -3
- package/dist/chunk-Q2G5C3HV.js +217 -0
- package/dist/{chunk-KOO56JVC.js → chunk-Q6N6LDEJ.js} +1 -1
- package/dist/{chunk-TXSJ2L5O.js → chunk-QI4IXJN7.js} +1 -1
- package/dist/{chunk-HLVQ5Y7B.js → chunk-RE4VLK45.js} +1 -1
- package/dist/{chunk-TF6SZGDT.js → chunk-SA2PH6WY.js} +1 -1
- package/dist/{chunk-5RSYY7BE.js → chunk-SJYOPYXH.js} +117 -9
- package/dist/{chunk-PJGHBANY.js → chunk-TTJE7CCU.js} +1 -1
- package/dist/{chunk-A7KEWR6S.js → chunk-TXWQPL2U.js} +1 -1
- package/dist/{chunk-XXSJ35J5.js → chunk-U5ZH52FB.js} +2 -2
- package/dist/{chunk-G4FDG3LK.js → chunk-UVNDLF74.js} +63 -40
- package/dist/{chunk-5OD3AFRW.js → chunk-V6RCZ25F.js} +1 -1
- package/dist/{chunk-LHMBIFKD.js → chunk-VYNNN2S3.js} +4 -4
- package/dist/chunk-WCYT54XP.js +934 -0
- package/dist/{chunk-5AMSQRHT.js → chunk-XGYSTVUH.js} +1 -1
- package/dist/{chunk-MKZBHM6A.js → chunk-XLWF3C4R.js} +4 -4
- package/dist/{chunk-YL36L2SN.js → chunk-Y7YHLV57.js} +1 -1
- package/dist/{chunk-HZC4MR4H.js → chunk-YBKB2PXY.js} +1 -1
- package/dist/{chunk-NWM3A4TK.js → chunk-ZDNLKXZA.js} +1 -1
- package/dist/{chunk-O7KW6QMH.js → chunk-ZW4TKQUM.js} +15 -5
- package/dist/{chunk-6BURHBE6.js → chunk-ZXB44R3E.js} +32 -11
- package/dist/co-occurrence-WCED475N.js +73 -0
- package/dist/{code-context-index-B6VIWPSF.js → code-context-index-LSZ3DKTJ.js} +2 -2
- package/dist/{crdt-sync-XA22KI3S.js → crdt-sync-PBXZTHZC.js} +1 -1
- package/dist/{crm-webhook-CIZNOEY4.js → crm-webhook-W7Q25VZU.js} +2 -2
- package/dist/{cto-delegation-gate-H5IULFRC.js → cto-delegation-gate-JKULOLMC.js} +8 -8
- package/dist/{daemon-orchestration-VO5XQIJL.js → daemon-orchestration-CHV6MB42.js} +13 -11
- package/dist/{exe-drift-DMT75WR3.js → exe-drift-PW36OULT.js} +2 -2
- package/dist/{exe-export-2RZWOSX6.js → exe-export-XQOD3KE6.js} +6 -5
- package/dist/{exe-import-NFNYATHL.js → exe-import-QOFP67LW.js} +6 -5
- package/dist/{exe-key-4D7CF3BU.js → exe-key-WQ34UZR6.js} +1 -1
- package/dist/{fast-db-init-LAEISZQ2.js → fast-db-init-UKETGWQI.js} +1 -1
- package/dist/gateway/index.js +6 -6
- package/dist/{git-staleness-M46AYLPP.js → git-staleness-ATV5CGAP.js} +1 -1
- package/dist/{git-task-sweep-PXOS56YT.js → git-task-sweep-KXZRIP4T.js} +9 -9
- package/dist/{global-procedures-KROQQX54.js → global-procedures-G6IKCYKM.js} +3 -3
- package/dist/{graph-auto-extract-QJ2BBJM2.js → graph-auto-extract-ZJXJOLE2.js} +1 -1
- package/dist/hooks/bug-report-worker.js +10 -10
- package/dist/hooks/codex-stop-task-finalizer.js +10 -10
- package/dist/hooks/commit-complete.js +11 -11
- package/dist/hooks/error-recall.js +8 -7
- package/dist/hooks/exe-heartbeat-hook.js +2 -2
- package/dist/hooks/ingest-worker.js +3 -3
- package/dist/hooks/ingest.js +9 -9
- package/dist/hooks/instructions-loaded.js +3 -3
- package/dist/hooks/notification.js +3 -3
- package/dist/hooks/post-compact.js +10 -10
- package/dist/hooks/post-tool-combined.js +5 -5
- package/dist/hooks/pre-compact.js +16 -16
- package/dist/hooks/pre-tool-use.js +14 -14
- package/dist/hooks/prompt-submit.js +30 -29
- package/dist/hooks/session-end.js +46 -25
- package/dist/hooks/session-start.js +48 -10
- package/dist/hooks/stop.js +17 -17
- package/dist/hooks/subagent-stop.js +10 -10
- package/dist/hooks/summary-worker.js +17 -16
- package/dist/index.js +17 -17
- package/dist/{installer-SDBLJBAB.js → installer-DE2LH5EC.js} +4 -4
- package/dist/{installer-ZA6QNQ4P.js → installer-M2MDS7HC.js} +4 -4
- package/dist/{installer-6KAY6LD6.js → installer-VE23YFXU.js} +4 -4
- package/dist/{intercom-queue-K3DVKSPJ.js → intercom-queue-RNM6EPGA.js} +1 -1
- package/dist/keyword-extractor-UJHFWVZE.js +11 -0
- package/dist/lib/cloud-sync.js +3 -3
- package/dist/lib/consolidation.js +5 -4
- package/dist/lib/database.js +1 -1
- package/dist/lib/db-daemon-client.js +1 -1
- package/dist/lib/db.js +1 -1
- package/dist/lib/embed-worker.js +98 -0
- package/dist/lib/embedder.js +2 -2
- package/dist/lib/employee-templates.js +4 -4
- package/dist/lib/employees.js +1 -1
- package/dist/lib/exe-daemon-client.js +1 -1
- package/dist/lib/exe-daemon.js +523 -500
- package/dist/lib/hybrid-search.js +5 -6
- package/dist/lib/identity.js +1 -1
- package/dist/lib/messaging.js +9 -9
- package/dist/lib/reminders.js +2 -2
- package/dist/lib/schedules.js +5 -4
- package/dist/lib/skill-learning.js +3 -3
- package/dist/lib/store.js +4 -3
- package/dist/lib/task-router.js +2 -2
- package/dist/lib/tasks.js +9 -9
- package/dist/lib/tmux-routing.js +8 -8
- package/dist/lib/tmux-transport.js +1 -1
- package/dist/lib/token-spend.js +2 -2
- package/dist/lib/transport.js +2 -2
- package/dist/lib/ws-client.js +3 -1
- package/dist/mcp/register-tools.js +54 -51
- package/dist/mcp/server.js +58 -55
- package/dist/mcp/tools/complete-reminder.js +3 -3
- package/dist/mcp/tools/create-reminder.js +3 -3
- package/dist/mcp/tools/create-task.js +11 -11
- package/dist/mcp/tools/deactivate-behavior.js +4 -4
- package/dist/mcp/tools/list-reminders.js +3 -3
- package/dist/mcp/tools/list-tasks.js +11 -11
- package/dist/mcp/tools/send-message.js +11 -11
- package/dist/mcp/tools/update-task.js +10 -10
- package/dist/{mcp-http-config-LK2EDOEJ.js → mcp-http-config-Z2E4VUOF.js} +2 -2
- package/dist/{memory-cards-V3DKSRWL.js → memory-cards-SFDKDIAW.js} +1 -1
- package/dist/memory-graph-extractor-YD4GNH7T.js +16 -0
- package/dist/{memory-poisoning-defense-3B75HS74.js → memory-poisoning-defense-VEGNFELN.js} +1 -1
- package/dist/{memory-queue-client-LFPZPPQA.js → memory-queue-client-5HB2XUH7.js} +2 -2
- package/dist/{memory-reflection-HTDAUUE5.js → memory-reflection-MTPRQNI6.js} +2 -2
- package/dist/{notifications-76VCYXWW.js → notifications-6TCE6OBG.js} +8 -8
- package/dist/{orchestrator-CBNSBI5P.js → orchestrator-W2GYJR23.js} +10 -10
- package/dist/{plan-limits-SOR3QXKV.js → plan-limits-4EP46323.js} +2 -2
- package/dist/{projection-worker-FK5YOEIL.js → projection-worker-EBUYNMU2.js} +1 -1
- package/dist/{review-polling-ZLNDUKL4.js → review-polling-2N7KQFZZ.js} +9 -9
- package/dist/runtime/index.js +15 -15
- package/dist/{session-events-CUSPL25D.js → session-events-K47FHAXJ.js} +9 -9
- package/dist/{session-kill-telemetry-FLBRHBDP.js → session-kill-telemetry-275YUXM5.js} +2 -2
- package/dist/{session-scope-PX2ABSJO.js → session-scope-XSFJZEER.js} +8 -8
- package/dist/{setup-wizard-Y6PBZGFX.js → setup-wizard-UEO7HYLQ.js} +1 -1
- package/dist/{skill-refinement-L7PGKCYO.js → skill-refinement-WXBTANDQ.js} +1 -1
- package/dist/stack-update-2B2UXREV.js +50 -0
- package/dist/{task-enforcement-7FUILB63.js → task-enforcement-2JIJSXPU.js} +14 -16
- package/dist/{task-scope-2N45TE32.js → task-scope-W73Z3XWE.js} +8 -8
- package/dist/{tasks-crud-ADLCGHGH.js → tasks-crud-HPJKI3QQ.js} +8 -8
- package/dist/{tasks-review-PJ2DUI6N.js → tasks-review-MXLMPGNZ.js} +8 -8
- package/dist/{token-budget-T5DFXVTM.js → token-budget-BA46CVHX.js} +1 -1
- package/dist/{tool-capability-index-6JJN6ZRC.js → tool-capability-index-42VVN5BS.js} +1 -1
- package/dist/{tool-telemetry-72PVO5HV.js → tool-telemetry-GZ5E2AUL.js} +1 -1
- package/dist/tui/App.js +22 -18
- package/dist/{tui-data-63JHE6EZ.js → tui-data-PVXWQCJX.js} +8 -8
- package/dist/{worker-gate-REVBJUZ6.js → worker-gate-WTTK64TK.js} +1 -1
- package/dist/{workflow-engine-W2WNHJG5.js → workflow-engine-LT3WTT7V.js} +2 -2
- package/package.json +1 -1
- package/release-notes.json +209 -209
- /package/dist/{chunk-BNOZUS6J.js → chunk-6VVCAVRT.js} +0 -0
- /package/dist/{chunk-IC7GKK6I.js → chunk-CWQZZ7X3.js} +0 -0
- /package/dist/{chunk-ZI2ZVERO.js → chunk-EIW5GOBW.js} +0 -0
- /package/dist/{chunk-2BGGDNRD.js → chunk-IPPJEM26.js} +0 -0
- /package/dist/{chunk-4ISDU5KR.js → chunk-K5UR73PM.js} +0 -0
- /package/dist/{chunk-ZWRTVUQ6.js → chunk-KIMO5S45.js} +0 -0
- /package/dist/{chunk-S2FX5KJ4.js → chunk-WBLILGAP.js} +0 -0
- /package/dist/{core-memory-PCJ3L46L.js → core-memory-RAC6M67J.js} +0 -0
- /package/dist/{entity-boost-GHFPE6A2.js → entity-boost-5FIRFRDC.js} +0 -0
- /package/dist/{message-queue-client-CHRQYBH5.js → message-queue-client-PTQ2S7D7.js} +0 -0
- /package/dist/{wiki-acl-QYRAYYVQ.js → wiki-acl-MSDRCIAI.js} +0 -0
package/dist/lib/exe-daemon.js
CHANGED
|
@@ -17,15 +17,10 @@ import {
|
|
|
17
17
|
findScopedDuplicate,
|
|
18
18
|
governMemoryRecord,
|
|
19
19
|
schedulePostWriteMemoryHygiene
|
|
20
|
-
} from "../chunk-
|
|
20
|
+
} from "../chunk-WBLILGAP.js";
|
|
21
21
|
import {
|
|
22
22
|
logRestartEvent
|
|
23
23
|
} from "../chunk-PWMMIGVQ.js";
|
|
24
|
-
import {
|
|
25
|
-
getClient,
|
|
26
|
-
isInitialized
|
|
27
|
-
} from "../chunk-5RSYY7BE.js";
|
|
28
|
-
import "../chunk-RVSGXGWG.js";
|
|
29
24
|
import {
|
|
30
25
|
EMBEDDING_DIM
|
|
31
26
|
} from "../chunk-FXU7JOXK.js";
|
|
@@ -43,8 +38,10 @@ import v82 from "v8";
|
|
|
43
38
|
import { createServer as createHttpServer } from "http";
|
|
44
39
|
import { randomUUID } from "crypto";
|
|
45
40
|
import { writeFileSync as writeFileSync2, unlinkSync, mkdirSync as mkdirSync2, existsSync as existsSync3, readFileSync as readFileSync2, chmodSync } from "fs";
|
|
46
|
-
import { execFileSync as execFileSyncNode, execSync as execSyncNode } from "child_process";
|
|
41
|
+
import { execFileSync as execFileSyncNode, execSync as execSyncNode, execFile as execFileNode } from "child_process";
|
|
42
|
+
import { promisify } from "util";
|
|
47
43
|
import path3 from "path";
|
|
44
|
+
import { fork } from "child_process";
|
|
48
45
|
|
|
49
46
|
// src/lib/orchestration-metrics.ts
|
|
50
47
|
import { writeFileSync, readFileSync, existsSync } from "fs";
|
|
@@ -391,9 +388,9 @@ var MODEL_FILE = "jina-embeddings-v5-small-q4_k_m.gguf";
|
|
|
391
388
|
var IDLE_TIMEOUT_MS = parseInt(process.env.EXE_DAEMON_IDLE_TIMEOUT_MS || "0", 10);
|
|
392
389
|
var REVIEW_POLL_INTERVAL_MS = 60 * 1e3;
|
|
393
390
|
var DAEMON_TOKEN_ENV = "EXE_DAEMON_TOKEN";
|
|
394
|
-
var
|
|
395
|
-
var
|
|
396
|
-
var
|
|
391
|
+
var _embedWorker = null;
|
|
392
|
+
var _embedWorkerReady = false;
|
|
393
|
+
var _embedPendingCallbacks = /* @__PURE__ */ new Map();
|
|
397
394
|
var _shuttingDown = false;
|
|
398
395
|
var _drainingWrites = false;
|
|
399
396
|
var _activeWrites = 0;
|
|
@@ -420,6 +417,58 @@ function getTmuxSessions() {
|
|
|
420
417
|
}
|
|
421
418
|
return _tmuxSessionCache;
|
|
422
419
|
}
|
|
420
|
+
var _paneCacheMap = /* @__PURE__ */ new Map();
|
|
421
|
+
var PANE_CACHE_TTL_MS = 1e4;
|
|
422
|
+
var execFileAsync = promisify(execFileNode);
|
|
423
|
+
async function capturePaneAsync(session, lines = 20) {
|
|
424
|
+
const cacheKey = `${session}:${lines}`;
|
|
425
|
+
const cached = _paneCacheMap.get(cacheKey);
|
|
426
|
+
if (cached && Date.now() - cached.ts < PANE_CACHE_TTL_MS) return cached.text;
|
|
427
|
+
try {
|
|
428
|
+
const args = ["capture-pane", "-t", session, "-p", "-S", `-${lines}`];
|
|
429
|
+
const { stdout } = await execFileAsync("tmux", args, { encoding: "utf8", timeout: 3e3 });
|
|
430
|
+
const text = stdout ?? "";
|
|
431
|
+
_paneCacheMap.set(cacheKey, { text, ts: Date.now() });
|
|
432
|
+
return text;
|
|
433
|
+
} catch {
|
|
434
|
+
return "";
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
async function batchCapturePanes(sessions, lines = 20) {
|
|
438
|
+
const results = /* @__PURE__ */ new Map();
|
|
439
|
+
const CONCURRENCY = 4;
|
|
440
|
+
for (let i = 0; i < sessions.length; i += CONCURRENCY) {
|
|
441
|
+
const batch = sessions.slice(i, i + CONCURRENCY);
|
|
442
|
+
const settled = await Promise.allSettled(
|
|
443
|
+
batch.map(async (s) => ({ session: s, text: await capturePaneAsync(s, lines) }))
|
|
444
|
+
);
|
|
445
|
+
for (const r of settled) {
|
|
446
|
+
if (r.status === "fulfilled") results.set(r.value.session, r.value.text);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
return results;
|
|
450
|
+
}
|
|
451
|
+
setInterval(() => {
|
|
452
|
+
const now = Date.now();
|
|
453
|
+
for (const [key, entry] of _paneCacheMap) {
|
|
454
|
+
if (now - entry.ts > PANE_CACHE_TTL_MS * 3) _paneCacheMap.delete(key);
|
|
455
|
+
}
|
|
456
|
+
}, 6e4).unref();
|
|
457
|
+
var _configCache = null;
|
|
458
|
+
var _configCacheTs = 0;
|
|
459
|
+
var CONFIG_CACHE_TTL_MS = 6e4;
|
|
460
|
+
var _loadConfigSyncFn = null;
|
|
461
|
+
async function getCachedConfig() {
|
|
462
|
+
const now = Date.now();
|
|
463
|
+
if (_configCache && now - _configCacheTs < CONFIG_CACHE_TTL_MS) return _configCache;
|
|
464
|
+
if (!_loadConfigSyncFn) {
|
|
465
|
+
const mod = await import("./config.js");
|
|
466
|
+
_loadConfigSyncFn = mod.loadConfigSync;
|
|
467
|
+
}
|
|
468
|
+
_configCache = _loadConfigSyncFn();
|
|
469
|
+
_configCacheTs = now;
|
|
470
|
+
return _configCache;
|
|
471
|
+
}
|
|
423
472
|
var highQueue = [];
|
|
424
473
|
var lowQueue = [];
|
|
425
474
|
var _processing = false;
|
|
@@ -454,41 +503,151 @@ async function loadModel() {
|
|
|
454
503
|
return;
|
|
455
504
|
}
|
|
456
505
|
if (existsSync3(OOM_MARKER_PATH)) {
|
|
457
|
-
process.stderr.write(
|
|
458
|
-
|
|
459
|
-
`
|
|
460
|
-
);
|
|
506
|
+
process.stderr.write(`[exed] Skipping embeddings \u2014 OOM marker exists. Delete ${OOM_MARKER_PATH} to retry.
|
|
507
|
+
`);
|
|
461
508
|
return;
|
|
462
509
|
}
|
|
463
510
|
const totalMem = os2.totalmem();
|
|
464
|
-
const freeMem = os2.freemem();
|
|
465
511
|
if (totalMem <= MAX_SMALL_TOTAL_BYTES) {
|
|
466
|
-
process.stderr.write(
|
|
467
|
-
|
|
468
|
-
`
|
|
469
|
-
);
|
|
512
|
+
process.stderr.write(`[exed] Skipping embeddings \u2014 total RAM ${(totalMem / 1024 ** 3).toFixed(1)} GB <= 8 GB.
|
|
513
|
+
`);
|
|
470
514
|
return;
|
|
471
515
|
}
|
|
472
|
-
if (
|
|
473
|
-
process.stderr.write(
|
|
474
|
-
|
|
475
|
-
`
|
|
476
|
-
);
|
|
516
|
+
if (os2.freemem() < MIN_FREE_MEM_BYTES) {
|
|
517
|
+
process.stderr.write(`[exed] Skipping embeddings \u2014 free RAM < 2 GB.
|
|
518
|
+
`);
|
|
477
519
|
return;
|
|
478
520
|
}
|
|
479
|
-
process.stderr.write("[exed]
|
|
521
|
+
process.stderr.write("[exed] Spawning embed worker process...\n");
|
|
480
522
|
try {
|
|
481
|
-
const
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
523
|
+
const workerPath = path3.join(path3.dirname(new URL(import.meta.url).pathname), "embed-worker.js");
|
|
524
|
+
if (!existsSync3(workerPath)) {
|
|
525
|
+
process.stderr.write(`[exed] Embed worker not found at ${workerPath} \u2014 running without embeddings.
|
|
526
|
+
`);
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
_embedWorker = fork(workerPath, [], {
|
|
530
|
+
stdio: ["ignore", "ignore", "inherit", "ipc"],
|
|
531
|
+
env: { ...process.env, EXE_EMBED_MODEL: MODEL_FILE }
|
|
532
|
+
});
|
|
533
|
+
_embedWorker.on("message", (msg) => {
|
|
534
|
+
if (msg.type === "ready") {
|
|
535
|
+
if (msg.dim && msg.dim > 0) {
|
|
536
|
+
_embedWorkerReady = true;
|
|
537
|
+
process.stderr.write(`[exed] Embed worker ready (dim=${msg.dim}, pid=${_embedWorker?.pid}).
|
|
538
|
+
`);
|
|
539
|
+
} else {
|
|
540
|
+
process.stderr.write(`[exed] Embed worker failed to load: ${msg.error ?? "unknown"}
|
|
541
|
+
`);
|
|
542
|
+
}
|
|
543
|
+
return;
|
|
544
|
+
}
|
|
545
|
+
if (msg.id) {
|
|
546
|
+
const cb = _embedPendingCallbacks.get(msg.id);
|
|
547
|
+
if (cb) {
|
|
548
|
+
_embedPendingCallbacks.delete(msg.id);
|
|
549
|
+
if (msg.error) cb.reject(new Error(msg.error));
|
|
550
|
+
else if (msg.vectors) cb.resolve(msg.vectors);
|
|
551
|
+
else cb.reject(new Error("Invalid worker response"));
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
});
|
|
555
|
+
_embedWorker.on("error", (err) => {
|
|
556
|
+
process.stderr.write(`[exed] Embed worker error: ${err.message}
|
|
557
|
+
`);
|
|
558
|
+
_embedWorkerReady = false;
|
|
559
|
+
for (const [id, cb] of _embedPendingCallbacks) {
|
|
560
|
+
cb.reject(new Error("Worker crashed"));
|
|
561
|
+
_embedPendingCallbacks.delete(id);
|
|
562
|
+
}
|
|
563
|
+
});
|
|
564
|
+
_embedWorker.on("exit", (code) => {
|
|
565
|
+
process.stderr.write(`[exed] Embed worker exited (code=${code}).
|
|
566
|
+
`);
|
|
567
|
+
_embedWorkerReady = false;
|
|
568
|
+
_embedWorker = null;
|
|
569
|
+
_embedRespawnCount++;
|
|
570
|
+
if (_embedRespawnCount <= 3) {
|
|
571
|
+
const delay = _embedRespawnCount * 1e4;
|
|
572
|
+
process.stderr.write(`[exed] Respawning embed worker in ${delay / 1e3}s (attempt ${_embedRespawnCount}/3).
|
|
573
|
+
`);
|
|
574
|
+
setTimeout(() => loadModel(), delay);
|
|
575
|
+
} else {
|
|
576
|
+
process.stderr.write(`[exed] Embed worker failed 3 times \u2014 running without embeddings.
|
|
577
|
+
`);
|
|
578
|
+
}
|
|
579
|
+
});
|
|
486
580
|
} catch (err) {
|
|
487
|
-
process.stderr.write(`[exed]
|
|
581
|
+
process.stderr.write(`[exed] Embed worker spawn failed: ${err instanceof Error ? err.message : String(err)}
|
|
488
582
|
`);
|
|
489
|
-
process.stderr.write("[exed] Running without embeddings \u2014 memories will have null vectors.\n");
|
|
490
583
|
}
|
|
491
584
|
}
|
|
585
|
+
var _embedRespawnCount = 0;
|
|
586
|
+
setInterval(() => {
|
|
587
|
+
if (!_embedWorker || !_embedWorkerReady) return;
|
|
588
|
+
let onMsg = null;
|
|
589
|
+
const healthTimeout = setTimeout(() => {
|
|
590
|
+
process.stderr.write("[exed] Embed worker UNRESPONSIVE (10s health timeout) \u2014 killing.\n");
|
|
591
|
+
if (onMsg) try {
|
|
592
|
+
_embedWorker?.removeListener("message", onMsg);
|
|
593
|
+
} catch {
|
|
594
|
+
}
|
|
595
|
+
try {
|
|
596
|
+
_embedWorker?.kill("SIGKILL");
|
|
597
|
+
} catch {
|
|
598
|
+
}
|
|
599
|
+
}, 1e4);
|
|
600
|
+
try {
|
|
601
|
+
_embedWorker.send({ type: "health" });
|
|
602
|
+
onMsg = (msg) => {
|
|
603
|
+
if (msg.type === "health") {
|
|
604
|
+
clearTimeout(healthTimeout);
|
|
605
|
+
if (onMsg) try {
|
|
606
|
+
_embedWorker?.removeListener("message", onMsg);
|
|
607
|
+
} catch {
|
|
608
|
+
}
|
|
609
|
+
if (msg.rss && msg.rss > 4096) {
|
|
610
|
+
process.stderr.write(`[exed] Embed worker RSS ${msg.rss}MB > 4GB \u2014 killing to prevent OOM.
|
|
611
|
+
`);
|
|
612
|
+
try {
|
|
613
|
+
_embedWorker?.kill("SIGKILL");
|
|
614
|
+
} catch {
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
};
|
|
619
|
+
_embedWorker.on("message", onMsg);
|
|
620
|
+
} catch {
|
|
621
|
+
clearTimeout(healthTimeout);
|
|
622
|
+
}
|
|
623
|
+
}, 6e4);
|
|
624
|
+
function embedTexts(texts) {
|
|
625
|
+
return new Promise((resolve, reject) => {
|
|
626
|
+
if (!_embedWorker || !_embedWorkerReady) {
|
|
627
|
+
reject(new Error("Embed worker not available"));
|
|
628
|
+
return;
|
|
629
|
+
}
|
|
630
|
+
if (texts.length > 100) {
|
|
631
|
+
texts = texts.slice(0, 100);
|
|
632
|
+
}
|
|
633
|
+
const id = randomUUID();
|
|
634
|
+
const timeout = setTimeout(() => {
|
|
635
|
+
_embedPendingCallbacks.delete(id);
|
|
636
|
+
reject(new Error("Embed timeout (30s)"));
|
|
637
|
+
}, 3e4);
|
|
638
|
+
_embedPendingCallbacks.set(id, {
|
|
639
|
+
resolve: (v) => {
|
|
640
|
+
clearTimeout(timeout);
|
|
641
|
+
resolve(v);
|
|
642
|
+
},
|
|
643
|
+
reject: (e) => {
|
|
644
|
+
clearTimeout(timeout);
|
|
645
|
+
reject(e);
|
|
646
|
+
}
|
|
647
|
+
});
|
|
648
|
+
_embedWorker.send({ id, type: "embed", texts });
|
|
649
|
+
});
|
|
650
|
+
}
|
|
492
651
|
var RSS_BACKPRESSURE_HIGH = 0.8;
|
|
493
652
|
var RSS_BACKPRESSURE_LOW = 0.7;
|
|
494
653
|
var _lowQueuePaused = false;
|
|
@@ -519,23 +678,14 @@ async function processQueue() {
|
|
|
519
678
|
if (!entry) break;
|
|
520
679
|
if (entry.socket.destroyed) continue;
|
|
521
680
|
try {
|
|
522
|
-
|
|
523
|
-
if (!_context) {
|
|
681
|
+
if (!_embedWorkerReady) {
|
|
524
682
|
sendResponse(entry.socket, {
|
|
525
683
|
id: entry.request.id,
|
|
526
684
|
error: "Model not loaded yet \u2014 embeddings unavailable"
|
|
527
685
|
});
|
|
528
686
|
continue;
|
|
529
687
|
}
|
|
530
|
-
|
|
531
|
-
const embedding = await _context.getEmbeddingFor(text);
|
|
532
|
-
const vector = Array.from(embedding.vector);
|
|
533
|
-
embedding.vector = null;
|
|
534
|
-
if (vector.length !== EMBEDDING_DIM) {
|
|
535
|
-
throw new Error(`Dimension mismatch: got ${vector.length}, expected ${EMBEDDING_DIM}`);
|
|
536
|
-
}
|
|
537
|
-
vectors.push(vector);
|
|
538
|
-
}
|
|
688
|
+
const vectors = await embedTexts(entry.request.texts);
|
|
539
689
|
_requestsServed++;
|
|
540
690
|
sendResponse(entry.socket, { id: entry.request.id, vectors });
|
|
541
691
|
} catch (err) {
|
|
@@ -544,7 +694,6 @@ async function processQueue() {
|
|
|
544
694
|
error: err instanceof Error ? err.message : String(err)
|
|
545
695
|
});
|
|
546
696
|
}
|
|
547
|
-
global.gc?.();
|
|
548
697
|
await new Promise((r) => setTimeout(r, 0));
|
|
549
698
|
}
|
|
550
699
|
} finally {
|
|
@@ -553,11 +702,13 @@ async function processQueue() {
|
|
|
553
702
|
}
|
|
554
703
|
}
|
|
555
704
|
function sendResponse(socket, data) {
|
|
556
|
-
if (
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
705
|
+
if (socket.destroyed) return;
|
|
706
|
+
try {
|
|
707
|
+
const ok = socket.write(JSON.stringify(data) + "\n");
|
|
708
|
+
if (!ok) {
|
|
709
|
+
socket.destroy(new Error("Write backpressure \u2014 client too slow"));
|
|
560
710
|
}
|
|
711
|
+
} catch {
|
|
561
712
|
}
|
|
562
713
|
}
|
|
563
714
|
function resetIdleTimer() {
|
|
@@ -587,7 +738,7 @@ async function shutdown() {
|
|
|
587
738
|
logRestartEvent("daemon_shutdown_start", { uptime_s: Math.floor((Date.now() - _startedAt2) / 1e3), active_writes: _activeWrites, active_connections: _activeConnections });
|
|
588
739
|
resetIdleTimer();
|
|
589
740
|
flushToDisk();
|
|
590
|
-
if (process.platform === "darwin" &&
|
|
741
|
+
if (process.platform === "darwin" && _embedWorker && isRssOverThreshold(RSS_BACKPRESSURE_HIGH)) {
|
|
591
742
|
process.stderr.write(
|
|
592
743
|
`[exed] RSS CRITICAL during shutdown \u2014 fast Metal exit (skipping WAL/backup/shards).
|
|
593
744
|
`
|
|
@@ -629,8 +780,8 @@ async function shutdown() {
|
|
|
629
780
|
`);
|
|
630
781
|
}
|
|
631
782
|
try {
|
|
632
|
-
const { getClient
|
|
633
|
-
const client =
|
|
783
|
+
const { getClient } = await import("./database.js");
|
|
784
|
+
const client = getClient();
|
|
634
785
|
await client.execute("PRAGMA wal_checkpoint(TRUNCATE)");
|
|
635
786
|
process.stderr.write("[exed] WAL checkpoint complete \u2014 all writes flushed to DB.\n");
|
|
636
787
|
} catch (e) {
|
|
@@ -638,7 +789,7 @@ async function shutdown() {
|
|
|
638
789
|
`);
|
|
639
790
|
}
|
|
640
791
|
try {
|
|
641
|
-
const { stopProjectionWorker } = await import("../projection-worker-
|
|
792
|
+
const { stopProjectionWorker } = await import("../projection-worker-EBUYNMU2.js");
|
|
642
793
|
stopProjectionWorker();
|
|
643
794
|
} catch {
|
|
644
795
|
}
|
|
@@ -647,49 +798,13 @@ async function shutdown() {
|
|
|
647
798
|
disposeShards();
|
|
648
799
|
} catch {
|
|
649
800
|
}
|
|
650
|
-
if (
|
|
651
|
-
try {
|
|
652
|
-
unlinkSync(SOCKET_PATH);
|
|
653
|
-
} catch (e) {
|
|
654
|
-
process.stderr.write(`[exed] socket cleanup: ${e.message}
|
|
655
|
-
`);
|
|
656
|
-
}
|
|
657
|
-
try {
|
|
658
|
-
unlinkSync(PID_PATH);
|
|
659
|
-
} catch (e) {
|
|
660
|
-
process.stderr.write(`[exed] pid cleanup: ${e.message}
|
|
661
|
-
`);
|
|
662
|
-
}
|
|
663
|
-
process.stderr.write("[exed] Shutdown complete (darwin fast native exit).\n");
|
|
664
|
-
process.kill(process.pid, "SIGKILL");
|
|
665
|
-
process.exit(0);
|
|
666
|
-
}
|
|
667
|
-
if (_context) {
|
|
668
|
-
try {
|
|
669
|
-
await _context.dispose();
|
|
670
|
-
} catch (e) {
|
|
671
|
-
process.stderr.write(`[exed] context dispose: ${e.message}
|
|
672
|
-
`);
|
|
673
|
-
}
|
|
674
|
-
_context = null;
|
|
675
|
-
}
|
|
676
|
-
if (_model) {
|
|
677
|
-
try {
|
|
678
|
-
await _model.dispose();
|
|
679
|
-
} catch (e) {
|
|
680
|
-
process.stderr.write(`[exed] model dispose: ${e.message}
|
|
681
|
-
`);
|
|
682
|
-
}
|
|
683
|
-
_model = null;
|
|
684
|
-
}
|
|
685
|
-
if (_llama) {
|
|
801
|
+
if (_embedWorker) {
|
|
686
802
|
try {
|
|
687
|
-
|
|
688
|
-
} catch
|
|
689
|
-
process.stderr.write(`[exed] llama dispose: ${e.message}
|
|
690
|
-
`);
|
|
803
|
+
_embedWorker.kill("SIGTERM");
|
|
804
|
+
} catch {
|
|
691
805
|
}
|
|
692
|
-
|
|
806
|
+
_embedWorker = null;
|
|
807
|
+
_embedWorkerReady = false;
|
|
693
808
|
}
|
|
694
809
|
try {
|
|
695
810
|
unlinkSync(SOCKET_PATH);
|
|
@@ -707,16 +822,15 @@ async function shutdown() {
|
|
|
707
822
|
process.exit(0);
|
|
708
823
|
}
|
|
709
824
|
async function handleHealthCheck(socket, requestId) {
|
|
710
|
-
const healthy =
|
|
825
|
+
const healthy = _embedWorkerReady;
|
|
711
826
|
let testOk = false;
|
|
712
|
-
if (healthy) {
|
|
827
|
+
if (healthy && _embedWorker) {
|
|
713
828
|
try {
|
|
714
|
-
const
|
|
715
|
-
testOk =
|
|
829
|
+
const vectors = await embedTexts(["health check"]);
|
|
830
|
+
testOk = vectors.length > 0 && vectors[0].length === EMBEDDING_DIM;
|
|
716
831
|
} catch (e) {
|
|
717
832
|
process.stderr.write(`[exed] health check embed failed: ${e instanceof Error ? e.message : String(e)}
|
|
718
833
|
`);
|
|
719
|
-
testOk = false;
|
|
720
834
|
}
|
|
721
835
|
}
|
|
722
836
|
const dbConnected = _storeInitialized;
|
|
@@ -761,8 +875,8 @@ async function handleDbExecute(socket, requestId, sql, args) {
|
|
|
761
875
|
sendResponse(socket, { id: requestId, error: "DB not initialized" });
|
|
762
876
|
return;
|
|
763
877
|
}
|
|
764
|
-
const { getClient
|
|
765
|
-
const client =
|
|
878
|
+
const { getClient } = await import("./database.js");
|
|
879
|
+
const client = getClient();
|
|
766
880
|
const deserializedArgs = deserializeArgs(args);
|
|
767
881
|
const result = await client.execute({ sql, args: deserializedArgs });
|
|
768
882
|
_dbRequestsServed++;
|
|
@@ -787,8 +901,8 @@ async function handleDbBatch(socket, requestId, statements, mode) {
|
|
|
787
901
|
sendResponse(socket, { id: requestId, error: "DB not initialized" });
|
|
788
902
|
return;
|
|
789
903
|
}
|
|
790
|
-
const { getClient
|
|
791
|
-
const client =
|
|
904
|
+
const { getClient } = await import("./database.js");
|
|
905
|
+
const client = getClient();
|
|
792
906
|
const stmts = statements.map((s) => ({
|
|
793
907
|
sql: s.sql,
|
|
794
908
|
args: deserializeArgs(s.args)
|
|
@@ -911,22 +1025,20 @@ async function _writeMemoryRecordInner(entry) {
|
|
|
911
1025
|
});
|
|
912
1026
|
if (duplicate) return duplicate;
|
|
913
1027
|
let vectorBlob = null;
|
|
914
|
-
if (
|
|
1028
|
+
if (_embedWorkerReady && !governed.skipEmbedding) {
|
|
915
1029
|
try {
|
|
916
|
-
const
|
|
917
|
-
|
|
918
|
-
embedding.vector = null;
|
|
919
|
-
if (vector.length === EMBEDDING_DIM) {
|
|
1030
|
+
const vectors = await embedTexts([record.raw_text]);
|
|
1031
|
+
if (vectors.length > 0 && vectors[0].length === EMBEDDING_DIM) {
|
|
920
1032
|
const { vectorToBlob } = await import("./store.js");
|
|
921
|
-
vectorBlob = vectorToBlob(
|
|
1033
|
+
vectorBlob = vectorToBlob(vectors[0]);
|
|
922
1034
|
}
|
|
923
1035
|
} catch (e) {
|
|
924
1036
|
process.stderr.write(`[exed] embed failed for ingest (NULL vector, backfill later): ${e instanceof Error ? e.message : String(e)}
|
|
925
1037
|
`);
|
|
926
1038
|
}
|
|
927
1039
|
}
|
|
928
|
-
const { getClient
|
|
929
|
-
const client =
|
|
1040
|
+
const { getClient } = await import("./database.js");
|
|
1041
|
+
const client = getClient();
|
|
930
1042
|
const hasVector = vectorBlob !== null;
|
|
931
1043
|
await client.execute({
|
|
932
1044
|
sql: hasVector ? `INSERT OR IGNORE INTO memories
|
|
@@ -984,7 +1096,7 @@ async function _writeMemoryRecordInner(entry) {
|
|
|
984
1096
|
const vRow = await client.execute({ sql: "SELECT version FROM memories WHERE id = ?", args: [id] });
|
|
985
1097
|
const version = Number(vRow.rows[0]?.version) || 0;
|
|
986
1098
|
try {
|
|
987
|
-
const { insertOntologyForMemory } = await import("../agentic-ontology-
|
|
1099
|
+
const { insertOntologyForMemory } = await import("../agentic-ontology-S54AFODT.js");
|
|
988
1100
|
await insertOntologyForMemory({
|
|
989
1101
|
id,
|
|
990
1102
|
agent_id: record.agent_id,
|
|
@@ -1058,7 +1170,7 @@ function startMessageQueueDrain() {
|
|
|
1058
1170
|
fired("message_queue_drain");
|
|
1059
1171
|
if (!await ensureStoreForPolling()) return;
|
|
1060
1172
|
try {
|
|
1061
|
-
const { claimMessageQueue, ackMessageQueue } = await import("../message-queue-client-
|
|
1173
|
+
const { claimMessageQueue, ackMessageQueue } = await import("../message-queue-client-PTQ2S7D7.js");
|
|
1062
1174
|
const lease = claimMessageQueue();
|
|
1063
1175
|
if (!lease || lease.entries.length === 0) return;
|
|
1064
1176
|
acted("message_queue_drain");
|
|
@@ -1339,8 +1451,10 @@ async function startMcpHttpServer() {
|
|
|
1339
1451
|
if (sid !== keepSid && existingOwner === ownerKey) {
|
|
1340
1452
|
const age = sessionLastSeen.has(sid) ? Math.round((Date.now() - sessionLastSeen.get(sid)) / 1e3) : -1;
|
|
1341
1453
|
closeMcpSession2(sid, "duplicate_session_eviction");
|
|
1342
|
-
|
|
1454
|
+
if (age > 5) {
|
|
1455
|
+
process.stderr.write(`[exed] MCP duplicate session eviction: closed ${sid.slice(0, 8)} for ${ownerKey} (age=${age}s, replaced by ${keepSid?.slice(0, 8) ?? "none"})
|
|
1343
1456
|
`);
|
|
1457
|
+
}
|
|
1344
1458
|
}
|
|
1345
1459
|
}
|
|
1346
1460
|
}, sweepStaleMcpSessions2 = function() {
|
|
@@ -1410,14 +1524,15 @@ async function startMcpHttpServer() {
|
|
|
1410
1524
|
);
|
|
1411
1525
|
}
|
|
1412
1526
|
Promise.all([
|
|
1413
|
-
import("../tool-capability-index-
|
|
1527
|
+
import("../tool-capability-index-42VVN5BS.js"),
|
|
1414
1528
|
import("../tool-gates-3IC7DK4R.js")
|
|
1415
1529
|
]).then(async ([{ getToolCapabilityIndex }, { TOOL_CATEGORIES }]) => {
|
|
1416
1530
|
const gateState = getCachedLicenseGate();
|
|
1417
1531
|
const index = getToolCapabilityIndex();
|
|
1418
1532
|
await index.buildIndex(
|
|
1419
1533
|
(fakeServer) => registerAllTools(fakeServer, gateState.license, gateState.hasKey),
|
|
1420
|
-
TOOL_CATEGORIES
|
|
1534
|
+
TOOL_CATEGORIES,
|
|
1535
|
+
_embedWorkerReady ? embedTexts : void 0
|
|
1421
1536
|
);
|
|
1422
1537
|
}).catch((err) => {
|
|
1423
1538
|
process.stderr.write(`[exed] Tool capability index failed: ${err.message}
|
|
@@ -1507,17 +1622,21 @@ async function startMcpHttpServer() {
|
|
|
1507
1622
|
}
|
|
1508
1623
|
try {
|
|
1509
1624
|
const { getClient: getDbClient } = await import("./database.js");
|
|
1625
|
+
const { extractKeywords, keywordsToString } = await import("../keyword-extractor-UJHFWVZE.js");
|
|
1510
1626
|
const client = getDbClient();
|
|
1627
|
+
const webhookText = `[${result.source}] ${result.eventType}: ${result.text}`;
|
|
1628
|
+
const webhookKeywords = keywordsToString(extractKeywords(webhookText));
|
|
1511
1629
|
await client.execute({
|
|
1512
|
-
sql: `INSERT INTO memories (id, agent_id, agent_role, session_id, timestamp, tool_name, project_name, has_error, raw_text, version, session_scope)
|
|
1513
|
-
VALUES (?, 'webhook', 'system', ?, ?, ?, ?, 0, ?, 0, NULL)`,
|
|
1630
|
+
sql: `INSERT INTO memories (id, agent_id, agent_role, session_id, timestamp, tool_name, project_name, has_error, raw_text, version, session_scope, keywords)
|
|
1631
|
+
VALUES (?, 'webhook', 'system', ?, ?, ?, ?, 0, ?, 0, NULL, ?)`,
|
|
1514
1632
|
args: [
|
|
1515
1633
|
randomUUID(),
|
|
1516
1634
|
`webhook-${serviceName}`,
|
|
1517
1635
|
(/* @__PURE__ */ new Date()).toISOString(),
|
|
1518
1636
|
`webhook:${result.source}`,
|
|
1519
1637
|
result.source,
|
|
1520
|
-
|
|
1638
|
+
webhookText,
|
|
1639
|
+
webhookKeywords
|
|
1521
1640
|
]
|
|
1522
1641
|
});
|
|
1523
1642
|
} catch (e) {
|
|
@@ -1676,8 +1795,8 @@ async function startMcpHttpServer() {
|
|
|
1676
1795
|
sessionLastSeen.set(sessionId, Date.now());
|
|
1677
1796
|
} else if (!sessionId && req.method === "POST" && isInitializeRequest(parsedBody)) {
|
|
1678
1797
|
try {
|
|
1679
|
-
const { initDatabase, isInitialized
|
|
1680
|
-
if (!
|
|
1798
|
+
const { initDatabase, isInitialized } = await import("./database.js");
|
|
1799
|
+
if (!isInitialized()) {
|
|
1681
1800
|
const { loadConfig } = await import("./config.js");
|
|
1682
1801
|
const { getMasterKey } = await import("./keychain.js");
|
|
1683
1802
|
const config = await loadConfig();
|
|
@@ -1749,7 +1868,7 @@ async function startMcpHttpServer() {
|
|
|
1749
1868
|
};
|
|
1750
1869
|
const sessionMcp = new McpServer({ name: "exe-os", version: "1.3.0" });
|
|
1751
1870
|
const gateState = getCachedLicenseGate();
|
|
1752
|
-
const { wrapServerWithTelemetry } = await import("../tool-telemetry-
|
|
1871
|
+
const { wrapServerWithTelemetry } = await import("../tool-telemetry-GZ5E2AUL.js");
|
|
1753
1872
|
const wrappedMcp = wrapServerWithTelemetry(sessionMcp);
|
|
1754
1873
|
registerAllTools(wrappedMcp, gateState.license, gateState.hasKey);
|
|
1755
1874
|
await sessionMcp.connect(transport);
|
|
@@ -1768,8 +1887,8 @@ async function startMcpHttpServer() {
|
|
|
1768
1887
|
activeSessions: transports.size
|
|
1769
1888
|
});
|
|
1770
1889
|
try {
|
|
1771
|
-
const { initDatabase, isInitialized
|
|
1772
|
-
if (!
|
|
1890
|
+
const { initDatabase, isInitialized } = await import("./database.js");
|
|
1891
|
+
if (!isInitialized()) {
|
|
1773
1892
|
const { loadConfig } = await import("./config.js");
|
|
1774
1893
|
const { getMasterKey } = await import("./keychain.js");
|
|
1775
1894
|
const config = await loadConfig();
|
|
@@ -1825,7 +1944,7 @@ async function startMcpHttpServer() {
|
|
|
1825
1944
|
const sessionMcp = new McpServer({ name: "exe-os", version: "1.3.0" });
|
|
1826
1945
|
sessionServers.set(recoveredSessionId, sessionMcp);
|
|
1827
1946
|
const gateState = getCachedLicenseGate();
|
|
1828
|
-
const { wrapServerWithTelemetry } = await import("../tool-telemetry-
|
|
1947
|
+
const { wrapServerWithTelemetry } = await import("../tool-telemetry-GZ5E2AUL.js");
|
|
1829
1948
|
const wrappedMcp = wrapServerWithTelemetry(sessionMcp);
|
|
1830
1949
|
registerAllTools(wrappedMcp, gateState.license, gateState.hasKey);
|
|
1831
1950
|
await sessionMcp.connect(transport);
|
|
@@ -2001,8 +2120,8 @@ var _storeInitialized = false;
|
|
|
2001
2120
|
async function ensureStoreForPolling() {
|
|
2002
2121
|
if (_storeInitialized) return true;
|
|
2003
2122
|
try {
|
|
2004
|
-
const { initDatabase, isInitialized
|
|
2005
|
-
if (!
|
|
2123
|
+
const { initDatabase, isInitialized } = await import("./database.js");
|
|
2124
|
+
if (!isInitialized()) {
|
|
2006
2125
|
const { loadConfig } = await import("./config.js");
|
|
2007
2126
|
const { getMasterKey } = await import("./keychain.js");
|
|
2008
2127
|
const config = await loadConfig();
|
|
@@ -2021,7 +2140,7 @@ async function ensureStoreForPolling() {
|
|
|
2021
2140
|
}
|
|
2022
2141
|
}
|
|
2023
2142
|
async function startReviewPolling() {
|
|
2024
|
-
const polling = await import("../review-polling-
|
|
2143
|
+
const polling = await import("../review-polling-2N7KQFZZ.js");
|
|
2025
2144
|
const state = {
|
|
2026
2145
|
lastIntercomSent: /* @__PURE__ */ new Map(),
|
|
2027
2146
|
lastNudgeSent: /* @__PURE__ */ new Map(),
|
|
@@ -2031,8 +2150,8 @@ async function startReviewPolling() {
|
|
|
2031
2150
|
fired("review_polling");
|
|
2032
2151
|
if (!await ensureStoreForPolling()) return;
|
|
2033
2152
|
try {
|
|
2034
|
-
const { getClient
|
|
2035
|
-
const deps = polling.createRealDeps(
|
|
2153
|
+
const { getClient } = await import("./database.js");
|
|
2154
|
+
const deps = polling.createRealDeps(getClient);
|
|
2036
2155
|
const sent = await polling.pollPendingReviews(deps, state);
|
|
2037
2156
|
if (sent.length > 0) acted("review_polling");
|
|
2038
2157
|
for (const s of sent) {
|
|
@@ -2055,9 +2174,9 @@ function startSessionTTL() {
|
|
|
2055
2174
|
fired("session_ttl");
|
|
2056
2175
|
if (!await ensureStoreForPolling()) return;
|
|
2057
2176
|
try {
|
|
2058
|
-
const { getClient
|
|
2059
|
-
const { checkSessionTTL, createSessionTTLRealDeps } = await import("../daemon-orchestration-
|
|
2060
|
-
const deps = createSessionTTLRealDeps(
|
|
2177
|
+
const { getClient } = await import("./database.js");
|
|
2178
|
+
const { checkSessionTTL, createSessionTTLRealDeps } = await import("../daemon-orchestration-CHV6MB42.js");
|
|
2179
|
+
const deps = createSessionTTLRealDeps(getClient);
|
|
2061
2180
|
const killed = await checkSessionTTL(deps);
|
|
2062
2181
|
if (killed.length > 0) acted("session_ttl");
|
|
2063
2182
|
} catch (err) {
|
|
@@ -2070,105 +2189,7 @@ function startSessionTTL() {
|
|
|
2070
2189
|
process.stderr.write(`[exed] Session TTL started (every ${SESSION_TTL_INTERVAL_MS / 6e4}m)
|
|
2071
2190
|
`);
|
|
2072
2191
|
}
|
|
2073
|
-
var COO_RESTART_INTERVAL_MS = 6e4;
|
|
2074
|
-
var _cooLastSeen = 0;
|
|
2075
2192
|
var COO_RESTART_COOLDOWN_MS = 5 * 6e4;
|
|
2076
|
-
function startCooAutoRestart() {
|
|
2077
|
-
const tick = async () => {
|
|
2078
|
-
fired("coo_restart");
|
|
2079
|
-
try {
|
|
2080
|
-
const { listTmuxSessions } = await import("./tmux-status.js");
|
|
2081
|
-
const { isExeSession } = await import("./tmux-routing.js");
|
|
2082
|
-
const sessions = listTmuxSessions();
|
|
2083
|
-
const cooSessions = sessions.filter((s) => isExeSession(s));
|
|
2084
|
-
if (cooSessions.length > 0) {
|
|
2085
|
-
_cooLastSeen = Date.now();
|
|
2086
|
-
try {
|
|
2087
|
-
const { getClient: getClient2 } = await import("./database.js");
|
|
2088
|
-
const { pollCoordinatorContextRestart, createCoordinatorRestartRealDeps } = await import("../daemon-orchestration-VO5XQIJL.js");
|
|
2089
|
-
const deps = createCoordinatorRestartRealDeps(getClient2);
|
|
2090
|
-
const restarted = await pollCoordinatorContextRestart(deps, process.cwd());
|
|
2091
|
-
if (restarted.length > 0) {
|
|
2092
|
-
acted("coordinator_context_restart");
|
|
2093
|
-
process.stderr.write(`[exed] Coordinator context restart: ${restarted.join(", ")}
|
|
2094
|
-
`);
|
|
2095
|
-
}
|
|
2096
|
-
} catch (err) {
|
|
2097
|
-
process.stderr.write(`[exed] Coordinator context restart error: ${err instanceof Error ? err.message : String(err)}
|
|
2098
|
-
`);
|
|
2099
|
-
}
|
|
2100
|
-
return;
|
|
2101
|
-
}
|
|
2102
|
-
if (_cooLastSeen === 0) return;
|
|
2103
|
-
if (Date.now() - _cooLastSeen < COO_RESTART_COOLDOWN_MS) return;
|
|
2104
|
-
process.stderr.write("[exed] COO session died \u2014 auto-restarting\n");
|
|
2105
|
-
acted("coo_restart");
|
|
2106
|
-
const { pushNotifyAsync: pushNotifyAsync2 } = await import("../push-notifications-VJZFCUFZ.js");
|
|
2107
|
-
const { listSessions } = await import("./session-registry.js");
|
|
2108
|
-
const allSessions = listSessions();
|
|
2109
|
-
const lastCoo = allSessions.filter((s) => isExeSession(s.windowName)).sort((a, b) => (b.registeredAt ?? "").localeCompare(a.registeredAt ?? ""))[0];
|
|
2110
|
-
const projectDir = lastCoo?.projectDir ?? process.cwd();
|
|
2111
|
-
const { execSync: execSync2 } = await import("child_process");
|
|
2112
|
-
const { getCoordinatorName } = await import("./employees.js");
|
|
2113
|
-
const cooSession = lastCoo?.windowName ?? "exe1";
|
|
2114
|
-
const coordinatorName = getCoordinatorName();
|
|
2115
|
-
try {
|
|
2116
|
-
const npmRoot = execSync2("npm root -g", { encoding: "utf-8", timeout: 5e3 }).trim();
|
|
2117
|
-
const launchScript = `${npmRoot}/@askexenow/exe-os/dist/bin/exe-launch-agent.js`;
|
|
2118
|
-
execSync2(
|
|
2119
|
-
`tmux new-session -d -s "${cooSession}" -c "${projectDir}" "node ${launchScript} ${coordinatorName}" 2>/dev/null`,
|
|
2120
|
-
{ timeout: 1e4 }
|
|
2121
|
-
);
|
|
2122
|
-
setTimeout(() => {
|
|
2123
|
-
try {
|
|
2124
|
-
execSync2(
|
|
2125
|
-
`tmux send-keys -t "${cooSession}" '/exe' Enter 2>/dev/null`,
|
|
2126
|
-
{ timeout: 5e3 }
|
|
2127
|
-
);
|
|
2128
|
-
process.stderr.write(`[exed] Sent /exe boot to COO session "${cooSession}"
|
|
2129
|
-
`);
|
|
2130
|
-
} catch {
|
|
2131
|
-
}
|
|
2132
|
-
}, 3e3);
|
|
2133
|
-
_cooLastSeen = Date.now();
|
|
2134
|
-
process.stderr.write(`[exed] COO session "${cooSession}" relaunched in ${projectDir} with identity
|
|
2135
|
-
`);
|
|
2136
|
-
pushNotifyAsync2({
|
|
2137
|
-
event: "context_full",
|
|
2138
|
-
title: "COO session recovered",
|
|
2139
|
-
body: `Coordinator relaunched as ${coordinatorName} in ${projectDir}`,
|
|
2140
|
-
priority: "normal"
|
|
2141
|
-
});
|
|
2142
|
-
logDaemonHealth({
|
|
2143
|
-
event: "boot",
|
|
2144
|
-
pid: process.pid,
|
|
2145
|
-
message: `COO auto-restart: relaunched ${cooSession} with identity`,
|
|
2146
|
-
data: { session: cooSession, projectDir, coordinator: coordinatorName }
|
|
2147
|
-
});
|
|
2148
|
-
} catch (err) {
|
|
2149
|
-
process.stderr.write(`[exed] COO restart failed: ${err instanceof Error ? err.message : String(err)}
|
|
2150
|
-
`);
|
|
2151
|
-
}
|
|
2152
|
-
} catch (err) {
|
|
2153
|
-
process.stderr.write(`[exed] COO restart check error: ${err instanceof Error ? err.message : String(err)}
|
|
2154
|
-
`);
|
|
2155
|
-
}
|
|
2156
|
-
};
|
|
2157
|
-
const timer = setInterval(() => void tick(), COO_RESTART_INTERVAL_MS);
|
|
2158
|
-
timer.unref();
|
|
2159
|
-
void (async () => {
|
|
2160
|
-
try {
|
|
2161
|
-
const { listTmuxSessions } = await import("./tmux-status.js");
|
|
2162
|
-
const { isExeSession } = await import("./tmux-routing.js");
|
|
2163
|
-
if (listTmuxSessions().some((s) => isExeSession(s))) {
|
|
2164
|
-
_cooLastSeen = Date.now();
|
|
2165
|
-
}
|
|
2166
|
-
} catch {
|
|
2167
|
-
}
|
|
2168
|
-
})();
|
|
2169
|
-
process.stderr.write(`[exed] COO auto-restart started (every ${COO_RESTART_INTERVAL_MS / 1e3}s)
|
|
2170
|
-
`);
|
|
2171
|
-
}
|
|
2172
2193
|
var IDLE_KILL_INTERVAL_MS = 30 * 1e3;
|
|
2173
2194
|
var _idleTickCounts = /* @__PURE__ */ new Map();
|
|
2174
2195
|
function startIdleKill() {
|
|
@@ -2183,7 +2204,7 @@ function startIdleKill() {
|
|
|
2183
2204
|
const agentId = file.replace(".terminate", "");
|
|
2184
2205
|
const signalPath = path3.join(signalDir, file);
|
|
2185
2206
|
try {
|
|
2186
|
-
const sessions =
|
|
2207
|
+
const sessions = getTmuxSessions();
|
|
2187
2208
|
const match = sessions.find((s) => s.startsWith(`${agentId}-`) || s.startsWith(`${agentId}2-`) || s.startsWith(`${agentId}3-`));
|
|
2188
2209
|
if (match) {
|
|
2189
2210
|
execSyncNode(`tmux kill-session -t ${match} 2>/dev/null`, { timeout: 2e3 });
|
|
@@ -2200,12 +2221,12 @@ function startIdleKill() {
|
|
|
2200
2221
|
} catch {
|
|
2201
2222
|
}
|
|
2202
2223
|
try {
|
|
2203
|
-
const
|
|
2204
|
-
|
|
2205
|
-
const { getClient
|
|
2206
|
-
const { pollIdleKill, createIdleKillRealDeps } = await import("../daemon-orchestration-
|
|
2224
|
+
const cfg = await getCachedConfig();
|
|
2225
|
+
if (!cfg) return;
|
|
2226
|
+
const { getClient } = await import("./database.js");
|
|
2227
|
+
const { pollIdleKill, createIdleKillRealDeps } = await import("../daemon-orchestration-CHV6MB42.js");
|
|
2207
2228
|
const deps = createIdleKillRealDeps(
|
|
2208
|
-
|
|
2229
|
+
getClient,
|
|
2209
2230
|
cfg.sessionLifecycle.idleKillIntercomAckWindowMs
|
|
2210
2231
|
);
|
|
2211
2232
|
const killed = await pollIdleKill(deps, _idleTickCounts, {
|
|
@@ -2239,8 +2260,8 @@ function startConsolidation() {
|
|
|
2239
2260
|
if (!config.consolidationEnabled) return;
|
|
2240
2261
|
if (!process.env.ANTHROPIC_API_KEY) return;
|
|
2241
2262
|
if (!await ensureStoreForPolling()) return;
|
|
2242
|
-
const { getClient
|
|
2243
|
-
const client =
|
|
2263
|
+
const { getClient } = await import("./database.js");
|
|
2264
|
+
const client = getClient();
|
|
2244
2265
|
const { countUnconsolidated, isUserIdle, runConsolidation } = await import("./consolidation.js");
|
|
2245
2266
|
const count = await countUnconsolidated(client);
|
|
2246
2267
|
if (count < 20) {
|
|
@@ -2307,6 +2328,7 @@ var CLOUD_SYNC_AUTO_RECOVERY_MS = 30 * 6e4;
|
|
|
2307
2328
|
function startCloudSyncTimer() {
|
|
2308
2329
|
const tick = async () => {
|
|
2309
2330
|
fired("cloud_sync");
|
|
2331
|
+
if (_shuttingDown) return;
|
|
2310
2332
|
if (_cloudSyncPaused) {
|
|
2311
2333
|
if (Date.now() - _cloudSyncPausedAt > CLOUD_SYNC_AUTO_RECOVERY_MS) {
|
|
2312
2334
|
process.stderr.write(`[exed] Cloud sync auto-recovery: retrying after ${Math.round(CLOUD_SYNC_AUTO_RECOVERY_MS / 6e4)}m pause
|
|
@@ -2418,7 +2440,7 @@ function startSkillRefinement() {
|
|
|
2418
2440
|
fired("skill_refinement");
|
|
2419
2441
|
try {
|
|
2420
2442
|
if (!await ensureStoreForPolling()) return;
|
|
2421
|
-
const { runSkillRefinement } = await import("../skill-refinement-
|
|
2443
|
+
const { runSkillRefinement } = await import("../skill-refinement-WXBTANDQ.js");
|
|
2422
2444
|
const result = await runSkillRefinement();
|
|
2423
2445
|
if (result.reviewed > 0) {
|
|
2424
2446
|
acted("skill_refinement");
|
|
@@ -2446,8 +2468,8 @@ function startGraphExtraction() {
|
|
|
2446
2468
|
if (config.graphRagEnabled === false) return;
|
|
2447
2469
|
if (!process.env.ANTHROPIC_API_KEY) return;
|
|
2448
2470
|
if (!await ensureStoreForPolling()) return;
|
|
2449
|
-
const { getClient
|
|
2450
|
-
const client =
|
|
2471
|
+
const { getClient } = await import("./database.js");
|
|
2472
|
+
const client = getClient();
|
|
2451
2473
|
const { extractBatch } = await import("../graph-rag-G3EG5Q6L.js");
|
|
2452
2474
|
const result = await extractBatch(client, 50, config.selfQueryModel);
|
|
2453
2475
|
if (result.processed > 0) {
|
|
@@ -2474,8 +2496,8 @@ async function writeAgentStats() {
|
|
|
2474
2496
|
if (!await ensureStoreForPolling()) return;
|
|
2475
2497
|
try {
|
|
2476
2498
|
acted("agent_stats");
|
|
2477
|
-
const { getClient
|
|
2478
|
-
const client =
|
|
2499
|
+
const { getClient } = await import("./database.js");
|
|
2500
|
+
const client = getClient();
|
|
2479
2501
|
const result = await client.execute({
|
|
2480
2502
|
sql: `SELECT agent_id,
|
|
2481
2503
|
COUNT(*) as total,
|
|
@@ -2547,8 +2569,8 @@ function startConfidenceDecay() {
|
|
|
2547
2569
|
fired("confidence_decay");
|
|
2548
2570
|
if (!await ensureStoreForPolling()) return;
|
|
2549
2571
|
try {
|
|
2550
|
-
const { getClient
|
|
2551
|
-
const client =
|
|
2572
|
+
const { getClient } = await import("./database.js");
|
|
2573
|
+
const client = getClient();
|
|
2552
2574
|
const result = await client.execute({
|
|
2553
2575
|
sql: `UPDATE memories
|
|
2554
2576
|
SET confidence = MAX(0.3, COALESCE(confidence, 0.7) - 0.01)
|
|
@@ -2579,8 +2601,8 @@ function startReflectionSweep() {
|
|
|
2579
2601
|
fired("reflection_sweep");
|
|
2580
2602
|
if (!await ensureStoreForPolling()) return;
|
|
2581
2603
|
try {
|
|
2582
|
-
const { getClient
|
|
2583
|
-
const client =
|
|
2604
|
+
const { getClient } = await import("./database.js");
|
|
2605
|
+
const client = getClient();
|
|
2584
2606
|
const agents = await client.execute(
|
|
2585
2607
|
"SELECT DISTINCT agent_id FROM memory_cards WHERE active = 1 LIMIT 50"
|
|
2586
2608
|
);
|
|
@@ -2588,7 +2610,7 @@ function startReflectionSweep() {
|
|
|
2588
2610
|
for (const row of agents.rows) {
|
|
2589
2611
|
const agentId = row.agent_id;
|
|
2590
2612
|
try {
|
|
2591
|
-
const { runReflection } = await import("../memory-reflection-
|
|
2613
|
+
const { runReflection } = await import("../memory-reflection-MTPRQNI6.js");
|
|
2592
2614
|
const result = await runReflection(agentId);
|
|
2593
2615
|
totalInsights += result.contradictions + result.patterns + result.summaries;
|
|
2594
2616
|
} catch (e) {
|
|
@@ -2617,7 +2639,7 @@ function startReflectionSweep() {
|
|
|
2617
2639
|
if (memories > 100 && cards < memories * 0.1) {
|
|
2618
2640
|
process.stderr.write(`[exed] Memory cards sparse (${cards} cards / ${memories} memories) \u2014 running backfill...
|
|
2619
2641
|
`);
|
|
2620
|
-
const { insertMemoryCardsForBatch } = await import("../memory-cards-
|
|
2642
|
+
const { insertMemoryCardsForBatch } = await import("../memory-cards-SFDKDIAW.js");
|
|
2621
2643
|
const batchSize = 500;
|
|
2622
2644
|
let offset = 0;
|
|
2623
2645
|
let totalCards = 0;
|
|
@@ -2639,6 +2661,7 @@ function startReflectionSweep() {
|
|
|
2639
2661
|
}
|
|
2640
2662
|
offset += batch.rows.length;
|
|
2641
2663
|
if (offset > 5e4) break;
|
|
2664
|
+
if (offset % (batchSize * 5) === 0) await new Promise((r) => setTimeout(r, 100));
|
|
2642
2665
|
}
|
|
2643
2666
|
if (totalCards > 0) {
|
|
2644
2667
|
process.stderr.write(`[exed] Card backfill: ${totalCards} cards from ${offset} memories
|
|
@@ -2688,9 +2711,9 @@ function startSoftDeletePurge() {
|
|
|
2688
2711
|
fired("soft_delete_purge");
|
|
2689
2712
|
if (!await ensureStoreForPolling()) return;
|
|
2690
2713
|
try {
|
|
2691
|
-
const { getClient
|
|
2714
|
+
const { getClient } = await import("./database.js");
|
|
2692
2715
|
const { SOFT_DELETE_RETENTION_DAYS } = await import("./database.js");
|
|
2693
|
-
const client =
|
|
2716
|
+
const client = getClient();
|
|
2694
2717
|
const cutoffDate = new Date(Date.now() - SOFT_DELETE_RETENTION_DAYS * 24 * 60 * 60 * 1e3).toISOString();
|
|
2695
2718
|
const result = await client.execute({
|
|
2696
2719
|
sql: `SELECT COUNT(*) as cnt FROM memories WHERE status = 'deleted' AND deleted_at IS NOT NULL AND deleted_at < ?`,
|
|
@@ -2722,7 +2745,7 @@ function startIntercomQueueDrain() {
|
|
|
2722
2745
|
const tick = async () => {
|
|
2723
2746
|
fired("intercom_queue_drain");
|
|
2724
2747
|
try {
|
|
2725
|
-
const { drainQueue } = await import("../intercom-queue-
|
|
2748
|
+
const { drainQueue } = await import("../intercom-queue-RNM6EPGA.js");
|
|
2726
2749
|
const { isSessionBusy } = await import("./tmux-routing.js");
|
|
2727
2750
|
const { sendIntercom: centralSendIntercom } = await import("./tmux-routing.js");
|
|
2728
2751
|
const result = drainQueue(
|
|
@@ -2758,7 +2781,7 @@ function startOrphanReaper() {
|
|
|
2758
2781
|
const tick = async () => {
|
|
2759
2782
|
fired("orphan_reaper");
|
|
2760
2783
|
try {
|
|
2761
|
-
const { reapOrphanedMcpProcesses, createOrphanReaperRealDeps } = await import("../daemon-orchestration-
|
|
2784
|
+
const { reapOrphanedMcpProcesses, createOrphanReaperRealDeps } = await import("../daemon-orchestration-CHV6MB42.js");
|
|
2762
2785
|
const deps = createOrphanReaperRealDeps();
|
|
2763
2786
|
const reaped = await reapOrphanedMcpProcesses(deps);
|
|
2764
2787
|
if (reaped.length > 0) acted("orphan_reaper");
|
|
@@ -2782,7 +2805,7 @@ function startZombieAgentReaper() {
|
|
|
2782
2805
|
const tick = async () => {
|
|
2783
2806
|
fired("zombie_agent_reaper");
|
|
2784
2807
|
try {
|
|
2785
|
-
const { reapZombieAgentProcesses, createZombieAgentReaperRealDeps } = await import("../daemon-orchestration-
|
|
2808
|
+
const { reapZombieAgentProcesses, createZombieAgentReaperRealDeps } = await import("../daemon-orchestration-CHV6MB42.js");
|
|
2786
2809
|
const deps = createZombieAgentReaperRealDeps();
|
|
2787
2810
|
const reaped = reapZombieAgentProcesses(deps);
|
|
2788
2811
|
if (reaped.length > 0) acted("zombie_agent_reaper");
|
|
@@ -2805,7 +2828,7 @@ function startWorktreeReaper() {
|
|
|
2805
2828
|
const tick = async () => {
|
|
2806
2829
|
fired("worktree_reaper");
|
|
2807
2830
|
try {
|
|
2808
|
-
const { reapOrphanedWorktrees, createWorktreeReaperRealDeps } = await import("../daemon-orchestration-
|
|
2831
|
+
const { reapOrphanedWorktrees, createWorktreeReaperRealDeps } = await import("../daemon-orchestration-CHV6MB42.js");
|
|
2809
2832
|
const deps = await createWorktreeReaperRealDeps();
|
|
2810
2833
|
const result = await reapOrphanedWorktrees(deps);
|
|
2811
2834
|
if (result.pruned.length > 0) {
|
|
@@ -2829,105 +2852,14 @@ function startWorktreeReaper() {
|
|
|
2829
2852
|
`);
|
|
2830
2853
|
}
|
|
2831
2854
|
var AUTO_WAKE_INTERVAL_MS = 60 * 1e3;
|
|
2832
|
-
function startAutoWake() {
|
|
2833
|
-
const tick = async () => {
|
|
2834
|
-
fired("auto_wake");
|
|
2835
|
-
if (process.env.EXE_AUTO_WAKE === "0") return;
|
|
2836
|
-
if (!await ensureStoreForPolling()) return;
|
|
2837
|
-
try {
|
|
2838
|
-
const { getClient: getClient2 } = await import("./database.js");
|
|
2839
|
-
const { pollOrphanedTasks, createAutoWakeRealDeps } = await import("../daemon-orchestration-VO5XQIJL.js");
|
|
2840
|
-
const deps = createAutoWakeRealDeps(getClient2, process.cwd());
|
|
2841
|
-
const woken = await pollOrphanedTasks(deps);
|
|
2842
|
-
if (woken.length > 0) acted("auto_wake");
|
|
2843
|
-
for (const agent of woken) {
|
|
2844
|
-
process.stderr.write(`[exed] Auto-wake: spawned ${agent}
|
|
2845
|
-
`);
|
|
2846
|
-
}
|
|
2847
|
-
try {
|
|
2848
|
-
const signalBase = path3.join(os2.homedir(), ".exe-os", "task-signals");
|
|
2849
|
-
if (existsSync3(signalBase)) {
|
|
2850
|
-
const { readdirSync, statSync: statSyncFs } = await import("fs");
|
|
2851
|
-
const { getSessionState, sendIntercom } = await import("./tmux-routing.js");
|
|
2852
|
-
const sessionCache = path3.join(os2.homedir(), ".exe-os", "session-cache");
|
|
2853
|
-
const sessions = execFileSyncNode("tmux", ["list-sessions", "-F", "#{session_name}"], { encoding: "utf8", timeout: 2e3 }).trim().split("\n").filter(Boolean);
|
|
2854
|
-
const signalDirs = [];
|
|
2855
|
-
for (const entry of readdirSync(signalBase, { withFileTypes: true })) {
|
|
2856
|
-
if (entry.isDirectory()) signalDirs.push({ dir: path3.join(signalBase, entry.name), scope: entry.name });
|
|
2857
|
-
}
|
|
2858
|
-
signalDirs.push({ dir: signalBase, scope: null });
|
|
2859
|
-
const runtimeFor = (sessionName) => {
|
|
2860
|
-
try {
|
|
2861
|
-
const dispatchInfoPath = path3.join(sessionCache, `dispatch-info-${sessionName}.json`);
|
|
2862
|
-
if (!existsSync3(dispatchInfoPath)) return "claude";
|
|
2863
|
-
const raw = JSON.parse(readFileSync2(dispatchInfoPath, "utf8"));
|
|
2864
|
-
return raw.runtime ?? "claude";
|
|
2865
|
-
} catch {
|
|
2866
|
-
return "claude";
|
|
2867
|
-
}
|
|
2868
|
-
};
|
|
2869
|
-
for (const { dir, scope } of signalDirs) {
|
|
2870
|
-
let files = [];
|
|
2871
|
-
try {
|
|
2872
|
-
files = readdirSync(dir).filter((f) => f.endsWith(".pending") && !f.includes("review-"));
|
|
2873
|
-
} catch {
|
|
2874
|
-
continue;
|
|
2875
|
-
}
|
|
2876
|
-
for (const file of files) {
|
|
2877
|
-
const signalPath = path3.join(dir, file);
|
|
2878
|
-
const age = Date.now() - statSyncFs(signalPath).mtimeMs;
|
|
2879
|
-
if (age > 5 * 60 * 1e3) continue;
|
|
2880
|
-
const agentId = file.includes(".") ? file.split(".")[0] : file.replace(".pending", "");
|
|
2881
|
-
const match = sessions.find((s) => {
|
|
2882
|
-
const agentPart = s.split("-")[0] ?? s;
|
|
2883
|
-
const base = agentPart.replace(/\d+$/, "");
|
|
2884
|
-
if (base !== agentId && s !== agentId) return false;
|
|
2885
|
-
if (!scope) return true;
|
|
2886
|
-
return s.endsWith(`-${scope}`);
|
|
2887
|
-
});
|
|
2888
|
-
if (!match) continue;
|
|
2889
|
-
const state = getSessionState(match);
|
|
2890
|
-
if (state !== "idle") continue;
|
|
2891
|
-
const runtime = runtimeFor(match);
|
|
2892
|
-
if (runtime === "codex" || runtime === "opencode" || runtime === "exe-agent") {
|
|
2893
|
-
execFileSyncNode("tmux", ["kill-session", "-t", match], { timeout: 3e3 });
|
|
2894
|
-
acted("auto_wake");
|
|
2895
|
-
process.stderr.write(
|
|
2896
|
-
`[exed] Signal-wake: killed idle ${runtime} session ${match} for fresh task signal ${file}; auto-wake will respawn (signal age ${Math.round(age / 1e3)}s)
|
|
2897
|
-
`
|
|
2898
|
-
);
|
|
2899
|
-
} else {
|
|
2900
|
-
sendIntercom(match, { force: true });
|
|
2901
|
-
process.stderr.write(`[exed] Signal-nudge: sent intercom to ${match} (signal age ${Math.round(age / 1e3)}s)
|
|
2902
|
-
`);
|
|
2903
|
-
}
|
|
2904
|
-
}
|
|
2905
|
-
}
|
|
2906
|
-
}
|
|
2907
|
-
} catch (err) {
|
|
2908
|
-
process.stderr.write(`[exed] Signal-wake error: ${err instanceof Error ? err.message : String(err)}
|
|
2909
|
-
`);
|
|
2910
|
-
}
|
|
2911
|
-
} catch (err) {
|
|
2912
|
-
process.stderr.write(`[exed] Auto-wake error: ${err instanceof Error ? err.message : String(err)}
|
|
2913
|
-
`);
|
|
2914
|
-
}
|
|
2915
|
-
};
|
|
2916
|
-
setTimeout(() => {
|
|
2917
|
-
const timer = setInterval(() => void tick(), AUTO_WAKE_INTERVAL_MS);
|
|
2918
|
-
timer.unref();
|
|
2919
|
-
}, 15e3);
|
|
2920
|
-
process.stderr.write(`[exed] Auto-wake started (every ${AUTO_WAKE_INTERVAL_MS / 1e3}s, offset 15s)
|
|
2921
|
-
`);
|
|
2922
|
-
}
|
|
2923
2855
|
var WAL_CHECKPOINT_INTERVAL_MS = 5 * 60 * 1e3;
|
|
2924
2856
|
function startWalCheckpoint() {
|
|
2925
2857
|
const tick = async () => {
|
|
2926
2858
|
fired("wal_checkpoint");
|
|
2927
2859
|
if (!await ensureStoreForPolling()) return;
|
|
2928
2860
|
try {
|
|
2929
|
-
const { getClient
|
|
2930
|
-
const client =
|
|
2861
|
+
const { getClient } = await import("./database.js");
|
|
2862
|
+
const client = getClient();
|
|
2931
2863
|
await client.execute("PRAGMA wal_checkpoint(TRUNCATE)");
|
|
2932
2864
|
acted("wal_checkpoint");
|
|
2933
2865
|
} catch {
|
|
@@ -2944,9 +2876,9 @@ function startStuckTaskRelease() {
|
|
|
2944
2876
|
fired("stuck_task_release");
|
|
2945
2877
|
if (!await ensureStoreForPolling()) return;
|
|
2946
2878
|
try {
|
|
2947
|
-
const { getClient
|
|
2948
|
-
const { releaseStuckTasks, createStuckTaskRealDeps } = await import("../daemon-orchestration-
|
|
2949
|
-
const deps = createStuckTaskRealDeps(
|
|
2879
|
+
const { getClient } = await import("./database.js");
|
|
2880
|
+
const { releaseStuckTasks, createStuckTaskRealDeps } = await import("../daemon-orchestration-CHV6MB42.js");
|
|
2881
|
+
const deps = createStuckTaskRealDeps(getClient);
|
|
2950
2882
|
const released = await releaseStuckTasks(deps);
|
|
2951
2883
|
if (released.length > 0) {
|
|
2952
2884
|
acted("stuck_task_release");
|
|
@@ -2986,18 +2918,23 @@ function startTaskEnforcementScanner() {
|
|
|
2986
2918
|
const tick = async () => {
|
|
2987
2919
|
fired("task_enforcement");
|
|
2988
2920
|
try {
|
|
2989
|
-
const { runTaskEnforcementTick } = await import("../task-enforcement-
|
|
2921
|
+
const { runTaskEnforcementTick } = await import("../task-enforcement-2JIJSXPU.js");
|
|
2990
2922
|
const { getTransport } = await import("./transport.js");
|
|
2991
2923
|
const { loadAgentConfig } = await import("./agent-config.js");
|
|
2992
|
-
const { getClient
|
|
2924
|
+
const { getClient } = await import("./database.js");
|
|
2993
2925
|
const { loadEmployeesSync } = await import("./employees.js");
|
|
2994
|
-
const { sessionScopeFilter } = await import("../task-scope-
|
|
2926
|
+
const { sessionScopeFilter } = await import("../task-scope-W73Z3XWE.js");
|
|
2927
|
+
const transport = getTransport();
|
|
2928
|
+
const allSessions = getTmuxSessions().filter((s) => s.includes("-"));
|
|
2929
|
+
const paneCache = await batchCapturePanes(allSessions, 20);
|
|
2995
2930
|
await runTaskEnforcementTick({
|
|
2996
|
-
transport
|
|
2931
|
+
transport,
|
|
2997
2932
|
agentConfig: loadAgentConfig(),
|
|
2998
2933
|
employees: loadEmployeesSync(),
|
|
2999
|
-
client: await
|
|
3000
|
-
scopeFilter: sessionScopeFilter()
|
|
2934
|
+
client: await getClient(),
|
|
2935
|
+
scopeFilter: sessionScopeFilter(),
|
|
2936
|
+
capturePaneAsync,
|
|
2937
|
+
paneCache
|
|
3001
2938
|
});
|
|
3002
2939
|
acted("task_enforcement");
|
|
3003
2940
|
} catch (err) {
|
|
@@ -3095,11 +3032,9 @@ function startApiWatchdog() {
|
|
|
3095
3032
|
const tick = async () => {
|
|
3096
3033
|
fired("api_watchdog");
|
|
3097
3034
|
try {
|
|
3098
|
-
const
|
|
3099
|
-
|
|
3100
|
-
if (!cfg.apiWatchdog?.enabled) return;
|
|
3035
|
+
const cfg = await getCachedConfig();
|
|
3036
|
+
if (!cfg?.apiWatchdog?.enabled) return;
|
|
3101
3037
|
const cooldownMs = (cfg.apiWatchdog.cooldownMinutes ?? 10) * 6e4;
|
|
3102
|
-
const { execFileSync } = await import("child_process");
|
|
3103
3038
|
const sessions = getTmuxSessions();
|
|
3104
3039
|
if (sessions.length === 0) return;
|
|
3105
3040
|
for (const session of sessions) {
|
|
@@ -3108,7 +3043,8 @@ function startApiWatchdog() {
|
|
|
3108
3043
|
if (Date.now() - lastNudge < cooldownMs) continue;
|
|
3109
3044
|
let pane;
|
|
3110
3045
|
try {
|
|
3111
|
-
pane =
|
|
3046
|
+
pane = await capturePaneAsync(session, 30);
|
|
3047
|
+
if (!pane) continue;
|
|
3112
3048
|
} catch {
|
|
3113
3049
|
continue;
|
|
3114
3050
|
}
|
|
@@ -3127,7 +3063,7 @@ function startApiWatchdog() {
|
|
|
3127
3063
|
process.stderr.write(`[exed] API watchdog KILLING ${session} after ${nudgeCount} failed nudges (matched: ${matchedPattern.source})
|
|
3128
3064
|
`);
|
|
3129
3065
|
try {
|
|
3130
|
-
|
|
3066
|
+
execFileSyncNode("tmux", ["kill-session", "-t", session], { timeout: 3e3 });
|
|
3131
3067
|
_apiWatchdogNudgeCounts.delete(session);
|
|
3132
3068
|
_apiWatchdogLastError.delete(session);
|
|
3133
3069
|
} catch {
|
|
@@ -3135,7 +3071,7 @@ function startApiWatchdog() {
|
|
|
3135
3071
|
continue;
|
|
3136
3072
|
}
|
|
3137
3073
|
try {
|
|
3138
|
-
|
|
3074
|
+
execFileSyncNode("tmux", ["send-keys", "-t", session, "", "Enter"], { timeout: 2e3 });
|
|
3139
3075
|
_apiWatchdogLastNudge.set(session, Date.now());
|
|
3140
3076
|
acted("api_watchdog");
|
|
3141
3077
|
process.stderr.write(`[exed] API watchdog nudged ${session} (attempt ${nudgeCount}/3, matched: ${matchedPattern.source})
|
|
@@ -3197,35 +3133,6 @@ function startBackgroundJobGuardrails() {
|
|
|
3197
3133
|
function handleSignalShutdown() {
|
|
3198
3134
|
if (_shuttingDown) return;
|
|
3199
3135
|
_shuttingDown = true;
|
|
3200
|
-
if (process.platform === "darwin" && _llama) {
|
|
3201
|
-
resetIdleTimer();
|
|
3202
|
-
logRestartEvent("daemon_signal_received", { platform: "darwin", fast_exit: true, uptime_s: Math.floor((Date.now() - _startedAt2) / 1e3) });
|
|
3203
|
-
logShutdown("darwin signal fast native exit", "SIGTERM/SIGINT");
|
|
3204
|
-
try {
|
|
3205
|
-
flushToDisk();
|
|
3206
|
-
} catch {
|
|
3207
|
-
}
|
|
3208
|
-
try {
|
|
3209
|
-
if (isInitialized()) {
|
|
3210
|
-
const cl = getClient();
|
|
3211
|
-
if (typeof cl.executeSync === "function") {
|
|
3212
|
-
cl.executeSync("PRAGMA wal_checkpoint(PASSIVE)");
|
|
3213
|
-
}
|
|
3214
|
-
}
|
|
3215
|
-
} catch {
|
|
3216
|
-
}
|
|
3217
|
-
try {
|
|
3218
|
-
unlinkSync(SOCKET_PATH);
|
|
3219
|
-
} catch {
|
|
3220
|
-
}
|
|
3221
|
-
try {
|
|
3222
|
-
unlinkSync(PID_PATH);
|
|
3223
|
-
} catch {
|
|
3224
|
-
}
|
|
3225
|
-
process.stderr.write("[exed] Shutdown complete (darwin signal fast native exit).\n");
|
|
3226
|
-
process.kill(process.pid, "SIGKILL");
|
|
3227
|
-
process.exit(0);
|
|
3228
|
-
}
|
|
3229
3136
|
logRestartEvent("daemon_signal_received", { platform: process.platform, fast_exit: false, uptime_s: Math.floor((Date.now() - _startedAt2) / 1e3) });
|
|
3230
3137
|
logShutdown("graceful shutdown", "SIGTERM/SIGINT");
|
|
3231
3138
|
void shutdown();
|
|
@@ -3244,8 +3151,8 @@ function startBugAutoFix() {
|
|
|
3244
3151
|
const tick = async () => {
|
|
3245
3152
|
fired("bug_autofix");
|
|
3246
3153
|
try {
|
|
3247
|
-
const { getClient
|
|
3248
|
-
const client = await
|
|
3154
|
+
const { getClient } = await import("./database.js");
|
|
3155
|
+
const client = await getClient();
|
|
3249
3156
|
const result = await client.execute({
|
|
3250
3157
|
sql: `SELECT id, title, priority, assigned_to, status FROM tasks
|
|
3251
3158
|
WHERE priority IN ('p0', 'p1')
|
|
@@ -3257,7 +3164,10 @@ function startBugAutoFix() {
|
|
|
3257
3164
|
args: []
|
|
3258
3165
|
});
|
|
3259
3166
|
if (result.rows.length === 0) return;
|
|
3260
|
-
const
|
|
3167
|
+
const { loadEmployeesSync: loadEmpSync } = await import("./employees.js");
|
|
3168
|
+
const empRoster = loadEmpSync();
|
|
3169
|
+
const ctoEmp = empRoster.find((e) => e.role === "CTO");
|
|
3170
|
+
const ctoName = ctoEmp?.name ?? "yoshi";
|
|
3261
3171
|
for (const row of result.rows) {
|
|
3262
3172
|
const taskId = String(row.id);
|
|
3263
3173
|
if (_autoFixDispatched.has(taskId)) continue;
|
|
@@ -3355,7 +3265,6 @@ function checkExistingDaemon() {
|
|
|
3355
3265
|
}
|
|
3356
3266
|
return false;
|
|
3357
3267
|
}
|
|
3358
|
-
process.kill(pid, 0);
|
|
3359
3268
|
try {
|
|
3360
3269
|
const state = execSyncNode(`ps -p ${pid} -o state=`, { encoding: "utf8", timeout: 2e3 }).trim();
|
|
3361
3270
|
if (state.startsWith("Z")) {
|
|
@@ -3373,6 +3282,18 @@ function checkExistingDaemon() {
|
|
|
3373
3282
|
return false;
|
|
3374
3283
|
}
|
|
3375
3284
|
} catch {
|
|
3285
|
+
process.stderr.write(`[exed] PID ${pid} no longer exists \u2014 cleaning stale PID file.
|
|
3286
|
+
`);
|
|
3287
|
+
try {
|
|
3288
|
+
unlinkSync(PID_PATH);
|
|
3289
|
+
} catch {
|
|
3290
|
+
}
|
|
3291
|
+
try {
|
|
3292
|
+
unlinkSync(SOCKET_PATH);
|
|
3293
|
+
} catch {
|
|
3294
|
+
}
|
|
3295
|
+
killOrphanDaemons();
|
|
3296
|
+
return false;
|
|
3376
3297
|
}
|
|
3377
3298
|
process.stderr.write(`[exed] Another daemon is already running (PID ${pid}). Exiting.
|
|
3378
3299
|
`);
|
|
@@ -3415,16 +3336,25 @@ function startAutoUpdateCheck() {
|
|
|
3415
3336
|
`
|
|
3416
3337
|
);
|
|
3417
3338
|
if (autoInstall) {
|
|
3418
|
-
process.stderr.write("[exed] Auto-installing update...\n");
|
|
3419
|
-
const {
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3339
|
+
process.stderr.write("[exed] Auto-installing update (async \u2014 MCP stays responsive)...\n");
|
|
3340
|
+
const { spawn: spawnChild } = await import("child_process");
|
|
3341
|
+
const child = spawnChild("npm", ["install", "-g", "@askexenow/exe-os@latest"], {
|
|
3342
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
3343
|
+
timeout: 12e4
|
|
3344
|
+
});
|
|
3345
|
+
child.on("close", (code) => {
|
|
3346
|
+
if (code === 0) {
|
|
3347
|
+
process.stderr.write(`[exed] Updated to v${result.remoteVersion}. Restart daemon to apply.
|
|
3348
|
+
`);
|
|
3349
|
+
} else {
|
|
3350
|
+
process.stderr.write(`[exed] Auto-update failed (exit ${code}).
|
|
3351
|
+
`);
|
|
3352
|
+
}
|
|
3353
|
+
});
|
|
3354
|
+
child.on("error", (err) => {
|
|
3355
|
+
process.stderr.write(`[exed] Auto-update error: ${err.message}
|
|
3356
|
+
`);
|
|
3423
3357
|
});
|
|
3424
|
-
process.stderr.write(
|
|
3425
|
-
`[exed] Updated to v${result.remoteVersion}. Restart daemon to apply.
|
|
3426
|
-
`
|
|
3427
|
-
);
|
|
3428
3358
|
}
|
|
3429
3359
|
void checkIntervalMs;
|
|
3430
3360
|
} catch (err) {
|
|
@@ -3460,7 +3390,7 @@ try {
|
|
|
3460
3390
|
chmodSync(DAEMON_BOOT_PATH, 420);
|
|
3461
3391
|
} catch {
|
|
3462
3392
|
}
|
|
3463
|
-
await new Promise((r) => setTimeout(r,
|
|
3393
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
3464
3394
|
try {
|
|
3465
3395
|
const claimedPid = parseInt(readFileSync2(PID_PATH, "utf8").trim(), 10);
|
|
3466
3396
|
if (claimedPid !== process.pid) {
|
|
@@ -3478,6 +3408,20 @@ try {
|
|
|
3478
3408
|
process.exit(0);
|
|
3479
3409
|
}
|
|
3480
3410
|
process.env.EXE_IS_DAEMON = "1";
|
|
3411
|
+
if (process.platform === "darwin") {
|
|
3412
|
+
try {
|
|
3413
|
+
const oldPlist = path3.join(os2.homedir(), "Library", "LaunchAgents", "com.askexe.exed-keepalive.plist");
|
|
3414
|
+
if (existsSync3(oldPlist)) {
|
|
3415
|
+
try {
|
|
3416
|
+
execSyncNode(`launchctl unload "${oldPlist}" 2>/dev/null`, { timeout: 5e3 });
|
|
3417
|
+
} catch {
|
|
3418
|
+
}
|
|
3419
|
+
unlinkSync(oldPlist);
|
|
3420
|
+
process.stderr.write("[exed] Removed old keepalive plist (replaced by launchd KeepAlive).\n");
|
|
3421
|
+
}
|
|
3422
|
+
} catch {
|
|
3423
|
+
}
|
|
3424
|
+
}
|
|
3481
3425
|
logBoot({ socketPath: SOCKET_PATH, pidPath: PID_PATH });
|
|
3482
3426
|
try {
|
|
3483
3427
|
const mcpRegPath = new URL("../mcp/register-tools.js", import.meta.url).href;
|
|
@@ -3495,17 +3439,89 @@ try {
|
|
|
3495
3439
|
`
|
|
3496
3440
|
);
|
|
3497
3441
|
});
|
|
3442
|
+
{
|
|
3443
|
+
let lastTick = Date.now();
|
|
3444
|
+
const LAG_WARN_MS = 3e3;
|
|
3445
|
+
const LAG_CRITICAL_MS = 1e4;
|
|
3446
|
+
const lagTimer = setInterval(() => {
|
|
3447
|
+
const now = Date.now();
|
|
3448
|
+
const lag = now - lastTick - 2e3;
|
|
3449
|
+
lastTick = now;
|
|
3450
|
+
if (lag > LAG_CRITICAL_MS) {
|
|
3451
|
+
const msg = `[exed] EVENT LOOP BLOCKED ${(lag / 1e3).toFixed(1)}s \u2014 MCP UNRESPONSIVE. Sessions will die.`;
|
|
3452
|
+
process.stderr.write(msg + "\n");
|
|
3453
|
+
logDaemonHealth({ event: "event_loop_blocked", pid: process.pid, message: msg, data: { lagMs: lag } });
|
|
3454
|
+
try {
|
|
3455
|
+
writeFileSync2(path3.join(os2.homedir(), ".exe-os", "session-cache", "event-loop-blocked.marker"), `lag=${lag}ms at ${(/* @__PURE__ */ new Date()).toISOString()}
|
|
3456
|
+
`);
|
|
3457
|
+
} catch {
|
|
3458
|
+
}
|
|
3459
|
+
} else if (lag > LAG_WARN_MS) {
|
|
3460
|
+
process.stderr.write(`[exed] Event loop lag: ${(lag / 1e3).toFixed(1)}s (warn threshold: ${LAG_WARN_MS / 1e3}s)
|
|
3461
|
+
`);
|
|
3462
|
+
}
|
|
3463
|
+
}, 2e3);
|
|
3464
|
+
lagTimer.unref();
|
|
3465
|
+
}
|
|
3498
3466
|
startMemoryQueueDrain();
|
|
3499
3467
|
startMessageQueueDrain();
|
|
3500
3468
|
startIntercomQueueDrain();
|
|
3501
3469
|
startReviewPolling();
|
|
3502
3470
|
startSessionTTL();
|
|
3503
3471
|
startTaskEnforcementScanner();
|
|
3504
|
-
startAutoWake();
|
|
3505
|
-
startCooAutoRestart();
|
|
3506
3472
|
startApiWatchdog();
|
|
3507
3473
|
startRssWatchdog();
|
|
3508
3474
|
setTimeout(startWalCheckpoint, 5e3);
|
|
3475
|
+
setTimeout(async () => {
|
|
3476
|
+
try {
|
|
3477
|
+
if (!await ensureStoreForPolling()) return;
|
|
3478
|
+
const { readdirSync, readFileSync: rfs } = await import("fs");
|
|
3479
|
+
const { getClient } = await import("./database.js");
|
|
3480
|
+
const client = getClient();
|
|
3481
|
+
const tasksRoot = path3.join(os2.homedir(), ".exe-os", "tasks");
|
|
3482
|
+
let synced = 0;
|
|
3483
|
+
const walk = (dir) => {
|
|
3484
|
+
try {
|
|
3485
|
+
for (const e of readdirSync(dir, { withFileTypes: true })) {
|
|
3486
|
+
const full = path3.join(dir, e.name);
|
|
3487
|
+
if (e.isDirectory()) walk(full);
|
|
3488
|
+
else if (e.name.endsWith(".md")) {
|
|
3489
|
+
try {
|
|
3490
|
+
const content = rfs(full, "utf8");
|
|
3491
|
+
const id = content.match(/\*\*ID:\*\*\s*([a-f0-9-]+)/)?.[1];
|
|
3492
|
+
const title = content.split("\n")[0]?.replace(/^# /, "") || "";
|
|
3493
|
+
const assignedTo = content.match(/\*\*Assigned to:\*\*\s*(\w+)/)?.[1];
|
|
3494
|
+
const assignedBy = content.match(/\*\*Assigned by:\*\*\s*(\w+)/)?.[1] || "exe";
|
|
3495
|
+
const project = content.match(/\*\*Project:\*\*\s*([\w-]+)/)?.[1] || "exe-os";
|
|
3496
|
+
const status = content.match(/\*\*Status:\*\*\s*(\w+)/)?.[1] || "open";
|
|
3497
|
+
const priority = content.match(/\*\*Priority:\*\*\s*(\w+)/)?.[1] || "p1";
|
|
3498
|
+
const scope = full.match(/tasks\/([^/]+)\//)?.[1] || null;
|
|
3499
|
+
if (id && assignedTo) {
|
|
3500
|
+
client.execute({
|
|
3501
|
+
sql: `INSERT OR IGNORE INTO tasks (id, title, assigned_to, assigned_by, project_name, status, priority, task_file, session_scope, created_at, updated_at)
|
|
3502
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
3503
|
+
args: [id, title, assignedTo, assignedBy, project, status, priority, full, scope, (/* @__PURE__ */ new Date()).toISOString(), (/* @__PURE__ */ new Date()).toISOString()]
|
|
3504
|
+
}).then(() => {
|
|
3505
|
+
synced++;
|
|
3506
|
+
}).catch(() => {
|
|
3507
|
+
});
|
|
3508
|
+
}
|
|
3509
|
+
} catch {
|
|
3510
|
+
}
|
|
3511
|
+
}
|
|
3512
|
+
}
|
|
3513
|
+
} catch {
|
|
3514
|
+
}
|
|
3515
|
+
};
|
|
3516
|
+
walk(tasksRoot);
|
|
3517
|
+
await new Promise((r) => setTimeout(r, 2e3));
|
|
3518
|
+
if (synced > 0) process.stderr.write(`[exed] Task re-sync: ${synced} missing DB rows restored from disk.
|
|
3519
|
+
`);
|
|
3520
|
+
} catch (e) {
|
|
3521
|
+
process.stderr.write(`[exed] Task re-sync error: ${e.message}
|
|
3522
|
+
`);
|
|
3523
|
+
}
|
|
3524
|
+
}, 8e3);
|
|
3509
3525
|
setTimeout(startIdleKill, 1e4);
|
|
3510
3526
|
setTimeout(startStuckTaskRelease, 15e3);
|
|
3511
3527
|
setTimeout(startOrphanReaper, 2e4);
|
|
@@ -3513,8 +3529,8 @@ try {
|
|
|
3513
3529
|
setTimeout(startAgentStats, 3e4);
|
|
3514
3530
|
setTimeout(startConsolidation, 6e4);
|
|
3515
3531
|
setTimeout(startSkillSweep, 9e4);
|
|
3516
|
-
setTimeout(startSkillRefinement,
|
|
3517
|
-
setTimeout(startGraphExtraction,
|
|
3532
|
+
setTimeout(startSkillRefinement, 18e4);
|
|
3533
|
+
setTimeout(startGraphExtraction, 27e4);
|
|
3518
3534
|
setTimeout(startReflectionSweep, 15e4);
|
|
3519
3535
|
setTimeout(startConfidenceDecay, 18e4);
|
|
3520
3536
|
setTimeout(startSoftDeletePurge, 21e4);
|
|
@@ -3528,9 +3544,9 @@ try {
|
|
|
3528
3544
|
fired("intercom_dedup_cleanup");
|
|
3529
3545
|
try {
|
|
3530
3546
|
if (!await ensureStoreForPolling()) return;
|
|
3531
|
-
const { getClient
|
|
3547
|
+
const { getClient } = await import("./database.js");
|
|
3532
3548
|
const { deduplicateIntercomMemories } = await import("./consolidation.js");
|
|
3533
|
-
const count = await deduplicateIntercomMemories(
|
|
3549
|
+
const count = await deduplicateIntercomMemories(getClient());
|
|
3534
3550
|
if (count > 0) {
|
|
3535
3551
|
acted("intercom_dedup_cleanup");
|
|
3536
3552
|
process.stderr.write(`[exed] Intercom dedup: archived ${count} duplicate intercom memories
|
|
@@ -3545,9 +3561,9 @@ try {
|
|
|
3545
3561
|
fired("trace_ttl_sweep");
|
|
3546
3562
|
try {
|
|
3547
3563
|
if (!await ensureStoreForPolling()) return;
|
|
3548
|
-
const { getClient
|
|
3564
|
+
const { getClient } = await import("./database.js");
|
|
3549
3565
|
const { sweepExpiredTraces } = await import("./consolidation.js");
|
|
3550
|
-
const count = await sweepExpiredTraces(
|
|
3566
|
+
const count = await sweepExpiredTraces(getClient());
|
|
3551
3567
|
if (count > 0) {
|
|
3552
3568
|
acted("trace_ttl_sweep");
|
|
3553
3569
|
process.stderr.write(`[exed] Trace TTL: deleted ${count} expired raw tool traces (>7d, importance<7)
|
|
@@ -3562,7 +3578,7 @@ try {
|
|
|
3562
3578
|
fired("orphan_task_cleanup");
|
|
3563
3579
|
try {
|
|
3564
3580
|
if (!await ensureStoreForPolling()) return;
|
|
3565
|
-
const { cleanOrphanedTaskFiles } = await import("../tasks-crud-
|
|
3581
|
+
const { cleanOrphanedTaskFiles } = await import("../tasks-crud-HPJKI3QQ.js");
|
|
3566
3582
|
const count = await cleanOrphanedTaskFiles();
|
|
3567
3583
|
if (count > 0) {
|
|
3568
3584
|
acted("orphan_task_cleanup");
|
|
@@ -3591,100 +3607,107 @@ try {
|
|
|
3591
3607
|
} catch {
|
|
3592
3608
|
}
|
|
3593
3609
|
}, 15 * 60 * 1e3);
|
|
3594
|
-
const { startToolTelemetryFlush } = await import("../tool-telemetry-
|
|
3610
|
+
const { startToolTelemetryFlush } = await import("../tool-telemetry-GZ5E2AUL.js");
|
|
3595
3611
|
startToolTelemetryFlush();
|
|
3596
3612
|
process.stderr.write("[exed] Tool telemetry started (flush every 5m)\n");
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
const {
|
|
3610
|
-
const
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
|
|
3613
|
+
setTimeout(async () => {
|
|
3614
|
+
try {
|
|
3615
|
+
const { startProjectionWorker } = await import("../projection-worker-EBUYNMU2.js");
|
|
3616
|
+
startProjectionWorker();
|
|
3617
|
+
} catch {
|
|
3618
|
+
}
|
|
3619
|
+
}, 2e4);
|
|
3620
|
+
setTimeout(async () => {
|
|
3621
|
+
try {
|
|
3622
|
+
const { loadConfig } = await import("./config.js");
|
|
3623
|
+
const config = await loadConfig();
|
|
3624
|
+
if (config.cloud?.endpoint) {
|
|
3625
|
+
const { getMasterKey } = await import("./keychain.js");
|
|
3626
|
+
const masterKey = await getMasterKey();
|
|
3627
|
+
if (masterKey) {
|
|
3628
|
+
const { deriveWsAuthToken, deriveOrgId } = await import("./ws-auth.js");
|
|
3629
|
+
const { getDeviceInfo } = await import("./device-registry.js");
|
|
3630
|
+
const { createWsClient } = await import("./ws-client.js");
|
|
3631
|
+
const { initStore } = await import("./store.js");
|
|
3632
|
+
const { sendMessage, deliverLocalMessage, setWsClientSend } = await import("./messaging.js");
|
|
3633
|
+
const { getClient } = await import("./database.js");
|
|
3634
|
+
await initStore();
|
|
3635
|
+
const wsClient = createWsClient({
|
|
3636
|
+
endpoint: config.cloud.endpoint.replace(/^https?/, "wss") + "/ws",
|
|
3637
|
+
orgId: deriveOrgId(masterKey),
|
|
3638
|
+
authToken: deriveWsAuthToken(masterKey),
|
|
3639
|
+
deviceInfo: getDeviceInfo()
|
|
3640
|
+
}, {
|
|
3641
|
+
onMessage: async (payload) => {
|
|
3642
|
+
try {
|
|
3643
|
+
const data = JSON.parse(payload);
|
|
3644
|
+
const msg = await sendMessage({
|
|
3645
|
+
fromAgent: data.fromAgent,
|
|
3646
|
+
targetAgent: data.targetAgent,
|
|
3647
|
+
targetProject: data.targetProject,
|
|
3648
|
+
content: data.content,
|
|
3649
|
+
priority: data.priority
|
|
3650
|
+
});
|
|
3651
|
+
await deliverLocalMessage(msg.id);
|
|
3652
|
+
} catch (err) {
|
|
3653
|
+
process.stderr.write(`[exed] WS message delivery failed: ${err instanceof Error ? err.message : String(err)}
|
|
3632
3654
|
`);
|
|
3633
|
-
|
|
3634
|
-
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
|
|
3655
|
+
}
|
|
3656
|
+
},
|
|
3657
|
+
onRegistry: async (devices) => {
|
|
3658
|
+
try {
|
|
3659
|
+
const client = getClient();
|
|
3660
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3661
|
+
for (const d of devices) {
|
|
3662
|
+
await client.execute({
|
|
3663
|
+
sql: `INSERT INTO device_registry (device_id, friendly_name, hostname, projects, agents, connected, last_seen)
|
|
3642
3664
|
VALUES (?, ?, ?, ?, ?, 1, ?)
|
|
3643
3665
|
ON CONFLICT(device_id) DO UPDATE SET
|
|
3644
3666
|
friendly_name = ?, hostname = ?, projects = ?, agents = ?, connected = 1, last_seen = ?`,
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
|
|
3657
|
-
|
|
3658
|
-
|
|
3659
|
-
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
|
|
3667
|
+
args: [
|
|
3668
|
+
d.deviceId,
|
|
3669
|
+
d.friendlyName,
|
|
3670
|
+
d.hostname,
|
|
3671
|
+
JSON.stringify(d.projects),
|
|
3672
|
+
JSON.stringify(d.agents),
|
|
3673
|
+
now,
|
|
3674
|
+
d.friendlyName,
|
|
3675
|
+
d.hostname,
|
|
3676
|
+
JSON.stringify(d.projects),
|
|
3677
|
+
JSON.stringify(d.agents),
|
|
3678
|
+
now
|
|
3679
|
+
]
|
|
3680
|
+
});
|
|
3681
|
+
}
|
|
3682
|
+
const connectedIds = devices.map((d) => d.deviceId);
|
|
3683
|
+
if (connectedIds.length > 0) {
|
|
3684
|
+
const placeholders = connectedIds.map(() => "?").join(",");
|
|
3685
|
+
await client.execute({
|
|
3686
|
+
sql: `UPDATE device_registry SET connected = 0 WHERE device_id NOT IN (${placeholders})`,
|
|
3687
|
+
args: connectedIds
|
|
3688
|
+
});
|
|
3689
|
+
}
|
|
3690
|
+
} catch (err) {
|
|
3691
|
+
process.stderr.write(`[exed] Registry update failed: ${err instanceof Error ? err.message : String(err)}
|
|
3670
3692
|
`);
|
|
3693
|
+
}
|
|
3694
|
+
},
|
|
3695
|
+
onConnect: () => {
|
|
3696
|
+
process.stderr.write("[exed] WS connected to Exe Cloud\n");
|
|
3697
|
+
},
|
|
3698
|
+
onDisconnect: () => {
|
|
3699
|
+
process.stderr.write("[exed] WS disconnected from Exe Cloud\n");
|
|
3671
3700
|
}
|
|
3672
|
-
}
|
|
3673
|
-
|
|
3674
|
-
|
|
3675
|
-
|
|
3676
|
-
onDisconnect: () => {
|
|
3677
|
-
process.stderr.write("[exed] WS disconnected from Exe Cloud\n");
|
|
3678
|
-
}
|
|
3679
|
-
});
|
|
3680
|
-
setWsClientSend((targetDevice, payload) => wsClient.send(targetDevice, payload));
|
|
3681
|
-
process.stderr.write("[exed] WS client initialized\n");
|
|
3701
|
+
});
|
|
3702
|
+
setWsClientSend((targetDevice, payload) => wsClient.send(targetDevice, payload));
|
|
3703
|
+
process.stderr.write("[exed] WS client initialized\n");
|
|
3704
|
+
}
|
|
3682
3705
|
}
|
|
3683
|
-
}
|
|
3684
|
-
|
|
3685
|
-
process.stderr.write(`[exed] WS client init failed (non-fatal): ${err instanceof Error ? err.message : String(err)}
|
|
3706
|
+
} catch (err) {
|
|
3707
|
+
process.stderr.write(`[exed] WS client init failed (non-fatal): ${err instanceof Error ? err.message : String(err)}
|
|
3686
3708
|
`);
|
|
3687
|
-
|
|
3709
|
+
}
|
|
3710
|
+
}, 3e4);
|
|
3688
3711
|
} catch (err) {
|
|
3689
3712
|
process.stderr.write(`[exed] FATAL: ${err instanceof Error ? err.message : String(err)}
|
|
3690
3713
|
`);
|