@askexenow/exe-os 0.9.16 → 0.9.18
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/dist/bin/backfill-conversations.js +1242 -909
- package/dist/bin/backfill-responses.js +1245 -912
- package/dist/bin/backfill-vectors.js +1244 -906
- package/dist/bin/cleanup-stale-review-tasks.js +1870 -417
- package/dist/bin/cli.js +217 -107
- package/dist/bin/exe-agent-config.js +2 -2
- package/dist/bin/exe-agent.js +62 -0
- package/dist/bin/exe-assign.js +346 -10
- package/dist/bin/exe-boot.js +387 -32
- package/dist/bin/exe-call.js +72 -2
- package/dist/bin/exe-cloud.js +8 -0
- package/dist/bin/exe-dispatch.js +1821 -225
- package/dist/bin/exe-doctor.js +720 -52
- package/dist/bin/exe-export-behaviors.js +1429 -148
- package/dist/bin/exe-forget.js +1408 -34
- package/dist/bin/exe-gateway.js +1629 -1295
- package/dist/bin/exe-heartbeat.js +1899 -448
- package/dist/bin/exe-kill.js +1624 -346
- package/dist/bin/exe-launch-agent.js +726 -90
- package/dist/bin/exe-link.js +27 -8
- package/dist/bin/exe-new-employee.js +75 -9
- package/dist/bin/exe-pending-messages.js +2769 -1316
- package/dist/bin/exe-pending-notifications.js +2829 -1376
- package/dist/bin/exe-pending-reviews.js +2847 -1392
- package/dist/bin/exe-rename.js +89 -8
- package/dist/bin/exe-review.js +1494 -312
- package/dist/bin/exe-search.js +1608 -1300
- package/dist/bin/exe-session-cleanup.js +194 -91
- package/dist/bin/exe-settings.js +10 -2
- package/dist/bin/exe-start-codex.js +769 -120
- package/dist/bin/exe-start-opencode.js +763 -108
- package/dist/bin/exe-status.js +1887 -434
- package/dist/bin/exe-team.js +1782 -324
- package/dist/bin/git-sweep.js +408 -33
- package/dist/bin/graph-backfill.js +681 -27
- package/dist/bin/graph-export.js +1419 -141
- package/dist/bin/install.js +4 -8
- package/dist/bin/intercom-check.js +8641 -0
- package/dist/bin/scan-tasks.js +553 -38
- package/dist/bin/setup.js +82 -10
- package/dist/bin/shard-migrate.js +682 -28
- package/dist/gateway/index.js +1629 -1295
- package/dist/hooks/bug-report-worker.js +1136 -183
- package/dist/hooks/codex-stop-task-finalizer.js +1060 -107
- package/dist/hooks/commit-complete.js +408 -33
- package/dist/hooks/error-recall.js +1608 -1300
- package/dist/hooks/ingest-worker.js +250 -7966
- package/dist/hooks/ingest.js +707 -119
- package/dist/hooks/instructions-loaded.js +383 -20
- package/dist/hooks/notification.js +383 -20
- package/dist/hooks/post-compact.js +384 -21
- package/dist/hooks/{response-ingest-worker.js → post-tool-combined.js} +4157 -1716
- package/dist/hooks/pre-compact.js +486 -45
- package/dist/hooks/pre-tool-use.js +385 -22
- package/dist/hooks/prompt-submit.js +291 -96
- package/dist/hooks/session-end.js +490 -48
- package/dist/hooks/session-start.js +236 -22
- package/dist/hooks/stop.js +192 -50
- package/dist/hooks/subagent-stop.js +95 -18
- package/dist/hooks/summary-worker.js +205 -361
- package/dist/index.js +221 -105
- package/dist/lib/agent-config.js +2 -2
- package/dist/lib/cloud-sync.js +27 -8
- package/dist/lib/consolidation.js +437 -41
- package/dist/lib/database.js +20 -8
- package/dist/lib/db-daemon-client.js +2 -2
- package/dist/lib/db.js +20 -8
- package/dist/lib/device-registry.js +27 -8
- package/dist/lib/employee-templates.js +62 -0
- package/dist/lib/employees.js +2 -2
- package/dist/lib/exe-daemon.js +6703 -6259
- package/dist/lib/hybrid-search.js +1608 -1300
- package/dist/lib/identity.js +1 -1
- package/dist/lib/messaging.js +11 -3
- package/dist/lib/reminders.js +1 -1
- package/dist/lib/schedules.js +718 -26
- package/dist/lib/session-registry.js +11 -0
- package/dist/lib/skill-learning.js +639 -9
- package/dist/lib/store.js +676 -27
- package/dist/lib/tasks.js +665 -27
- package/dist/lib/tmux-routing.js +732 -94
- package/dist/lib/token-spend.js +1 -1
- package/dist/mcp/server.js +856 -383
- package/dist/mcp/tools/complete-reminder.js +1 -1
- package/dist/mcp/tools/create-reminder.js +1 -1
- package/dist/mcp/tools/create-task.js +616 -46
- package/dist/mcp/tools/deactivate-behavior.js +9 -1
- package/dist/mcp/tools/list-reminders.js +1 -1
- package/dist/mcp/tools/list-tasks.js +10 -2
- package/dist/mcp/tools/send-message.js +11 -3
- package/dist/mcp/tools/update-task.js +677 -39
- package/dist/runtime/index.js +425 -37
- package/dist/tui/App.js +365 -34
- package/package.json +5 -2
- package/src/commands/exe/intercom.md +6 -17
- package/dist/bin/wiki-sync.js +0 -2991
- package/dist/hooks/prompt-ingest-worker.js +0 -3979
|
@@ -35,12 +35,25 @@ async function ensurePrivateDir(dirPath) {
|
|
|
35
35
|
} catch {
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
|
+
function ensurePrivateDirSync(dirPath) {
|
|
39
|
+
mkdirSync(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
40
|
+
try {
|
|
41
|
+
chmodSync(dirPath, PRIVATE_DIR_MODE);
|
|
42
|
+
} catch {
|
|
43
|
+
}
|
|
44
|
+
}
|
|
38
45
|
async function enforcePrivateFile(filePath) {
|
|
39
46
|
try {
|
|
40
47
|
await chmod(filePath, PRIVATE_FILE_MODE);
|
|
41
48
|
} catch {
|
|
42
49
|
}
|
|
43
50
|
}
|
|
51
|
+
function enforcePrivateFileSync(filePath) {
|
|
52
|
+
try {
|
|
53
|
+
if (existsSync(filePath)) chmodSync(filePath, PRIVATE_FILE_MODE);
|
|
54
|
+
} catch {
|
|
55
|
+
}
|
|
56
|
+
}
|
|
44
57
|
var PRIVATE_DIR_MODE, PRIVATE_FILE_MODE;
|
|
45
58
|
var init_secure_files = __esm({
|
|
46
59
|
"src/lib/secure-files.ts"() {
|
|
@@ -51,6 +64,21 @@ var init_secure_files = __esm({
|
|
|
51
64
|
});
|
|
52
65
|
|
|
53
66
|
// src/lib/config.ts
|
|
67
|
+
var config_exports = {};
|
|
68
|
+
__export(config_exports, {
|
|
69
|
+
CONFIG_MIGRATIONS: () => CONFIG_MIGRATIONS,
|
|
70
|
+
CONFIG_PATH: () => CONFIG_PATH,
|
|
71
|
+
CURRENT_CONFIG_VERSION: () => CURRENT_CONFIG_VERSION,
|
|
72
|
+
DB_PATH: () => DB_PATH,
|
|
73
|
+
EXE_AI_DIR: () => EXE_AI_DIR,
|
|
74
|
+
LEGACY_LANCE_PATH: () => LEGACY_LANCE_PATH,
|
|
75
|
+
MODELS_DIR: () => MODELS_DIR,
|
|
76
|
+
loadConfig: () => loadConfig,
|
|
77
|
+
loadConfigFrom: () => loadConfigFrom,
|
|
78
|
+
loadConfigSync: () => loadConfigSync,
|
|
79
|
+
migrateConfig: () => migrateConfig,
|
|
80
|
+
saveConfig: () => saveConfig
|
|
81
|
+
});
|
|
54
82
|
import { readFile, writeFile } from "fs/promises";
|
|
55
83
|
import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
|
|
56
84
|
import path from "path";
|
|
@@ -154,6 +182,46 @@ async function loadConfig() {
|
|
|
154
182
|
return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
|
|
155
183
|
}
|
|
156
184
|
}
|
|
185
|
+
function loadConfigSync() {
|
|
186
|
+
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
187
|
+
const configPath = path.join(dir, "config.json");
|
|
188
|
+
if (!existsSync2(configPath)) {
|
|
189
|
+
return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
|
|
190
|
+
}
|
|
191
|
+
try {
|
|
192
|
+
const raw = readFileSync(configPath, "utf-8");
|
|
193
|
+
let parsed = JSON.parse(raw);
|
|
194
|
+
parsed = migrateLegacyConfig(parsed);
|
|
195
|
+
const { config: migratedCfg } = migrateConfig(parsed);
|
|
196
|
+
normalizeScalingRoadmap(migratedCfg);
|
|
197
|
+
normalizeSessionLifecycle(migratedCfg);
|
|
198
|
+
normalizeAutoUpdate(migratedCfg);
|
|
199
|
+
return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db"), ...migratedCfg };
|
|
200
|
+
} catch {
|
|
201
|
+
return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
async function saveConfig(config) {
|
|
205
|
+
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
206
|
+
await ensurePrivateDir(dir);
|
|
207
|
+
const configPath = path.join(dir, "config.json");
|
|
208
|
+
await writeFile(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
209
|
+
await enforcePrivateFile(configPath);
|
|
210
|
+
}
|
|
211
|
+
async function loadConfigFrom(configPath) {
|
|
212
|
+
const raw = await readFile(configPath, "utf-8");
|
|
213
|
+
try {
|
|
214
|
+
let parsed = JSON.parse(raw);
|
|
215
|
+
parsed = migrateLegacyConfig(parsed);
|
|
216
|
+
const { config: migratedCfg } = migrateConfig(parsed);
|
|
217
|
+
normalizeScalingRoadmap(migratedCfg);
|
|
218
|
+
normalizeSessionLifecycle(migratedCfg);
|
|
219
|
+
normalizeAutoUpdate(migratedCfg);
|
|
220
|
+
return { ...DEFAULT_CONFIG, ...migratedCfg };
|
|
221
|
+
} catch {
|
|
222
|
+
return { ...DEFAULT_CONFIG };
|
|
223
|
+
}
|
|
224
|
+
}
|
|
157
225
|
var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
|
|
158
226
|
var init_config = __esm({
|
|
159
227
|
"src/lib/config.ts"() {
|
|
@@ -340,9 +408,9 @@ var MAX_RETRIES, BASE_DELAY_MS, MAX_JITTER_MS;
|
|
|
340
408
|
var init_db_retry = __esm({
|
|
341
409
|
"src/lib/db-retry.ts"() {
|
|
342
410
|
"use strict";
|
|
343
|
-
MAX_RETRIES =
|
|
344
|
-
BASE_DELAY_MS =
|
|
345
|
-
MAX_JITTER_MS =
|
|
411
|
+
MAX_RETRIES = 5;
|
|
412
|
+
BASE_DELAY_MS = 250;
|
|
413
|
+
MAX_JITTER_MS = 400;
|
|
346
414
|
}
|
|
347
415
|
});
|
|
348
416
|
|
|
@@ -930,7 +998,696 @@ var init_database_adapter = __esm({
|
|
|
930
998
|
}
|
|
931
999
|
});
|
|
932
1000
|
|
|
1001
|
+
// src/types/memory.ts
|
|
1002
|
+
var EMBEDDING_DIM;
|
|
1003
|
+
var init_memory = __esm({
|
|
1004
|
+
"src/types/memory.ts"() {
|
|
1005
|
+
"use strict";
|
|
1006
|
+
EMBEDDING_DIM = 1024;
|
|
1007
|
+
}
|
|
1008
|
+
});
|
|
1009
|
+
|
|
1010
|
+
// src/lib/daemon-auth.ts
|
|
1011
|
+
import crypto from "crypto";
|
|
1012
|
+
import path4 from "path";
|
|
1013
|
+
import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
1014
|
+
function normalizeToken(token) {
|
|
1015
|
+
if (!token) return null;
|
|
1016
|
+
const trimmed = token.trim();
|
|
1017
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
1018
|
+
}
|
|
1019
|
+
function readDaemonToken() {
|
|
1020
|
+
try {
|
|
1021
|
+
if (!existsSync4(DAEMON_TOKEN_PATH)) return null;
|
|
1022
|
+
return normalizeToken(readFileSync3(DAEMON_TOKEN_PATH, "utf8"));
|
|
1023
|
+
} catch {
|
|
1024
|
+
return null;
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
function ensureDaemonToken(seed) {
|
|
1028
|
+
const existing = readDaemonToken();
|
|
1029
|
+
if (existing) return existing;
|
|
1030
|
+
const token = normalizeToken(seed) ?? crypto.randomBytes(32).toString("hex");
|
|
1031
|
+
ensurePrivateDirSync(EXE_AI_DIR);
|
|
1032
|
+
writeFileSync2(DAEMON_TOKEN_PATH, `${token}
|
|
1033
|
+
`, "utf8");
|
|
1034
|
+
enforcePrivateFileSync(DAEMON_TOKEN_PATH);
|
|
1035
|
+
return token;
|
|
1036
|
+
}
|
|
1037
|
+
var DAEMON_TOKEN_PATH;
|
|
1038
|
+
var init_daemon_auth = __esm({
|
|
1039
|
+
"src/lib/daemon-auth.ts"() {
|
|
1040
|
+
"use strict";
|
|
1041
|
+
init_config();
|
|
1042
|
+
init_secure_files();
|
|
1043
|
+
DAEMON_TOKEN_PATH = path4.join(EXE_AI_DIR, "exed.token");
|
|
1044
|
+
}
|
|
1045
|
+
});
|
|
1046
|
+
|
|
1047
|
+
// src/lib/exe-daemon-client.ts
|
|
1048
|
+
import net from "net";
|
|
1049
|
+
import os4 from "os";
|
|
1050
|
+
import { spawn } from "child_process";
|
|
1051
|
+
import { randomUUID } from "crypto";
|
|
1052
|
+
import { existsSync as existsSync5, unlinkSync as unlinkSync2, readFileSync as readFileSync4, openSync, closeSync, statSync } from "fs";
|
|
1053
|
+
import path5 from "path";
|
|
1054
|
+
import { fileURLToPath } from "url";
|
|
1055
|
+
function handleData(chunk) {
|
|
1056
|
+
_buffer += chunk.toString();
|
|
1057
|
+
if (_buffer.length > MAX_BUFFER) {
|
|
1058
|
+
_buffer = "";
|
|
1059
|
+
return;
|
|
1060
|
+
}
|
|
1061
|
+
let newlineIdx;
|
|
1062
|
+
while ((newlineIdx = _buffer.indexOf("\n")) !== -1) {
|
|
1063
|
+
const line = _buffer.slice(0, newlineIdx).trim();
|
|
1064
|
+
_buffer = _buffer.slice(newlineIdx + 1);
|
|
1065
|
+
if (!line) continue;
|
|
1066
|
+
try {
|
|
1067
|
+
const response = JSON.parse(line);
|
|
1068
|
+
const id = response.id;
|
|
1069
|
+
if (!id) continue;
|
|
1070
|
+
const entry = _pending.get(id);
|
|
1071
|
+
if (entry) {
|
|
1072
|
+
clearTimeout(entry.timer);
|
|
1073
|
+
_pending.delete(id);
|
|
1074
|
+
entry.resolve(response);
|
|
1075
|
+
}
|
|
1076
|
+
} catch {
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
function cleanupStaleFiles() {
|
|
1081
|
+
if (existsSync5(PID_PATH)) {
|
|
1082
|
+
try {
|
|
1083
|
+
const pid = parseInt(readFileSync4(PID_PATH, "utf8").trim(), 10);
|
|
1084
|
+
if (pid > 0) {
|
|
1085
|
+
try {
|
|
1086
|
+
process.kill(pid, 0);
|
|
1087
|
+
return;
|
|
1088
|
+
} catch {
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
} catch {
|
|
1092
|
+
}
|
|
1093
|
+
try {
|
|
1094
|
+
unlinkSync2(PID_PATH);
|
|
1095
|
+
} catch {
|
|
1096
|
+
}
|
|
1097
|
+
try {
|
|
1098
|
+
unlinkSync2(SOCKET_PATH);
|
|
1099
|
+
} catch {
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
function findPackageRoot() {
|
|
1104
|
+
let dir = path5.dirname(fileURLToPath(import.meta.url));
|
|
1105
|
+
const { root } = path5.parse(dir);
|
|
1106
|
+
while (dir !== root) {
|
|
1107
|
+
if (existsSync5(path5.join(dir, "package.json"))) return dir;
|
|
1108
|
+
dir = path5.dirname(dir);
|
|
1109
|
+
}
|
|
1110
|
+
return null;
|
|
1111
|
+
}
|
|
1112
|
+
function getAvailableMemoryGB() {
|
|
1113
|
+
if (process.platform === "darwin") {
|
|
1114
|
+
try {
|
|
1115
|
+
const { execSync: execSync5 } = __require("child_process");
|
|
1116
|
+
const vmstat = execSync5("vm_stat", { encoding: "utf8" });
|
|
1117
|
+
const pageSize = 16384;
|
|
1118
|
+
const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
|
|
1119
|
+
const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
|
|
1120
|
+
const free = vmstat.match(/Pages free:\s+(\d+)/);
|
|
1121
|
+
const inactive = vmstat.match(/Pages inactive:\s+(\d+)/);
|
|
1122
|
+
const speculative = vmstat.match(/Pages speculative:\s+(\d+)/);
|
|
1123
|
+
const freePages = free ? parseInt(free[1], 10) : 0;
|
|
1124
|
+
const inactivePages = inactive ? parseInt(inactive[1], 10) : 0;
|
|
1125
|
+
const speculativePages = speculative ? parseInt(speculative[1], 10) : 0;
|
|
1126
|
+
return (freePages + inactivePages + speculativePages) * actualPageSize / (1024 * 1024 * 1024);
|
|
1127
|
+
} catch {
|
|
1128
|
+
return os4.freemem() / (1024 * 1024 * 1024);
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
return os4.freemem() / (1024 * 1024 * 1024);
|
|
1132
|
+
}
|
|
1133
|
+
function spawnDaemon() {
|
|
1134
|
+
const freeGB = getAvailableMemoryGB();
|
|
1135
|
+
const totalGB = os4.totalmem() / (1024 * 1024 * 1024);
|
|
1136
|
+
if (totalGB <= 8) {
|
|
1137
|
+
process.stderr.write(
|
|
1138
|
+
`[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
|
|
1139
|
+
`
|
|
1140
|
+
);
|
|
1141
|
+
return;
|
|
1142
|
+
}
|
|
1143
|
+
if (totalGB <= 16 && freeGB < 2) {
|
|
1144
|
+
process.stderr.write(
|
|
1145
|
+
`[exed-client] SKIP: low memory (${freeGB.toFixed(1)}GB available / ${totalGB.toFixed(0)}GB total). Embedding daemon not started \u2014 using keyword search only.
|
|
1146
|
+
`
|
|
1147
|
+
);
|
|
1148
|
+
return;
|
|
1149
|
+
}
|
|
1150
|
+
const pkgRoot = findPackageRoot();
|
|
1151
|
+
if (!pkgRoot) {
|
|
1152
|
+
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
1153
|
+
return;
|
|
1154
|
+
}
|
|
1155
|
+
const daemonPath = path5.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
1156
|
+
if (!existsSync5(daemonPath)) {
|
|
1157
|
+
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
1158
|
+
`);
|
|
1159
|
+
return;
|
|
1160
|
+
}
|
|
1161
|
+
const resolvedPath = daemonPath;
|
|
1162
|
+
const daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV] ?? null);
|
|
1163
|
+
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
1164
|
+
`);
|
|
1165
|
+
const logPath = path5.join(path5.dirname(SOCKET_PATH), "exed.log");
|
|
1166
|
+
let stderrFd = "ignore";
|
|
1167
|
+
try {
|
|
1168
|
+
stderrFd = openSync(logPath, "a");
|
|
1169
|
+
} catch {
|
|
1170
|
+
}
|
|
1171
|
+
const heapCapMB = totalGB <= 8 ? 256 : 512;
|
|
1172
|
+
const nodeArgs = [`--max-old-space-size=${heapCapMB}`, resolvedPath];
|
|
1173
|
+
const child = spawn(process.execPath, nodeArgs, {
|
|
1174
|
+
detached: true,
|
|
1175
|
+
stdio: ["ignore", "ignore", stderrFd],
|
|
1176
|
+
env: {
|
|
1177
|
+
...process.env,
|
|
1178
|
+
TMUX: void 0,
|
|
1179
|
+
// Daemon is global — must not inherit session scope
|
|
1180
|
+
TMUX_PANE: void 0,
|
|
1181
|
+
// Prevents resolveExeSession() from scoping to one session
|
|
1182
|
+
EXE_DAEMON_SOCK: SOCKET_PATH,
|
|
1183
|
+
EXE_DAEMON_PID: PID_PATH,
|
|
1184
|
+
[DAEMON_TOKEN_ENV]: daemonToken
|
|
1185
|
+
}
|
|
1186
|
+
});
|
|
1187
|
+
child.unref();
|
|
1188
|
+
if (typeof stderrFd === "number") {
|
|
1189
|
+
try {
|
|
1190
|
+
closeSync(stderrFd);
|
|
1191
|
+
} catch {
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
function acquireSpawnLock() {
|
|
1196
|
+
try {
|
|
1197
|
+
const fd = openSync(SPAWN_LOCK_PATH, "wx");
|
|
1198
|
+
closeSync(fd);
|
|
1199
|
+
return true;
|
|
1200
|
+
} catch {
|
|
1201
|
+
try {
|
|
1202
|
+
const stat = statSync(SPAWN_LOCK_PATH);
|
|
1203
|
+
if (Date.now() - stat.mtimeMs > SPAWN_LOCK_STALE_MS) {
|
|
1204
|
+
try {
|
|
1205
|
+
unlinkSync2(SPAWN_LOCK_PATH);
|
|
1206
|
+
} catch {
|
|
1207
|
+
}
|
|
1208
|
+
try {
|
|
1209
|
+
const fd = openSync(SPAWN_LOCK_PATH, "wx");
|
|
1210
|
+
closeSync(fd);
|
|
1211
|
+
return true;
|
|
1212
|
+
} catch {
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
} catch {
|
|
1216
|
+
}
|
|
1217
|
+
return false;
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
function releaseSpawnLock() {
|
|
1221
|
+
try {
|
|
1222
|
+
unlinkSync2(SPAWN_LOCK_PATH);
|
|
1223
|
+
} catch {
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
function connectToSocket() {
|
|
1227
|
+
return new Promise((resolve) => {
|
|
1228
|
+
if (_socket && _connected) {
|
|
1229
|
+
resolve(true);
|
|
1230
|
+
return;
|
|
1231
|
+
}
|
|
1232
|
+
const socket = net.createConnection({ path: SOCKET_PATH });
|
|
1233
|
+
const connectTimeout = setTimeout(() => {
|
|
1234
|
+
socket.destroy();
|
|
1235
|
+
resolve(false);
|
|
1236
|
+
}, 2e3);
|
|
1237
|
+
socket.on("connect", () => {
|
|
1238
|
+
clearTimeout(connectTimeout);
|
|
1239
|
+
_socket = socket;
|
|
1240
|
+
_connected = true;
|
|
1241
|
+
_buffer = "";
|
|
1242
|
+
socket.on("data", handleData);
|
|
1243
|
+
socket.on("close", () => {
|
|
1244
|
+
_connected = false;
|
|
1245
|
+
_socket = null;
|
|
1246
|
+
for (const [id, entry] of _pending) {
|
|
1247
|
+
clearTimeout(entry.timer);
|
|
1248
|
+
_pending.delete(id);
|
|
1249
|
+
entry.resolve({ error: "Connection closed" });
|
|
1250
|
+
}
|
|
1251
|
+
});
|
|
1252
|
+
socket.on("error", () => {
|
|
1253
|
+
_connected = false;
|
|
1254
|
+
_socket = null;
|
|
1255
|
+
});
|
|
1256
|
+
resolve(true);
|
|
1257
|
+
});
|
|
1258
|
+
socket.on("error", () => {
|
|
1259
|
+
clearTimeout(connectTimeout);
|
|
1260
|
+
resolve(false);
|
|
1261
|
+
});
|
|
1262
|
+
});
|
|
1263
|
+
}
|
|
1264
|
+
async function connectEmbedDaemon() {
|
|
1265
|
+
if (_socket && _connected) return true;
|
|
1266
|
+
if (await connectToSocket()) return true;
|
|
1267
|
+
if (acquireSpawnLock()) {
|
|
1268
|
+
try {
|
|
1269
|
+
cleanupStaleFiles();
|
|
1270
|
+
spawnDaemon();
|
|
1271
|
+
} finally {
|
|
1272
|
+
releaseSpawnLock();
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
const start = Date.now();
|
|
1276
|
+
let delay2 = 100;
|
|
1277
|
+
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
1278
|
+
await new Promise((r) => setTimeout(r, delay2));
|
|
1279
|
+
if (await connectToSocket()) return true;
|
|
1280
|
+
delay2 = Math.min(delay2 * 2, 3e3);
|
|
1281
|
+
}
|
|
1282
|
+
return false;
|
|
1283
|
+
}
|
|
1284
|
+
function sendRequest(texts, priority) {
|
|
1285
|
+
return sendDaemonRequest({ texts, priority });
|
|
1286
|
+
}
|
|
1287
|
+
function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
1288
|
+
return new Promise((resolve) => {
|
|
1289
|
+
if (!_socket || !_connected) {
|
|
1290
|
+
resolve({ error: "Not connected" });
|
|
1291
|
+
return;
|
|
1292
|
+
}
|
|
1293
|
+
const id = randomUUID();
|
|
1294
|
+
const token = process.env[DAEMON_TOKEN_ENV] ?? readDaemonToken();
|
|
1295
|
+
const timer = setTimeout(() => {
|
|
1296
|
+
_pending.delete(id);
|
|
1297
|
+
resolve({ error: "Request timeout" });
|
|
1298
|
+
}, timeoutMs);
|
|
1299
|
+
_pending.set(id, { resolve, timer });
|
|
1300
|
+
try {
|
|
1301
|
+
_socket.write(JSON.stringify({ id, token, ...payload }) + "\n");
|
|
1302
|
+
} catch {
|
|
1303
|
+
clearTimeout(timer);
|
|
1304
|
+
_pending.delete(id);
|
|
1305
|
+
resolve({ error: "Write failed" });
|
|
1306
|
+
}
|
|
1307
|
+
});
|
|
1308
|
+
}
|
|
1309
|
+
async function pingDaemon() {
|
|
1310
|
+
if (!_socket || !_connected) return null;
|
|
1311
|
+
const response = await sendDaemonRequest({ type: "health" }, 5e3);
|
|
1312
|
+
if (response.health) {
|
|
1313
|
+
return response.health;
|
|
1314
|
+
}
|
|
1315
|
+
return null;
|
|
1316
|
+
}
|
|
1317
|
+
function killAndRespawnDaemon() {
|
|
1318
|
+
if (!acquireSpawnLock()) {
|
|
1319
|
+
process.stderr.write("[exed-client] Another process is already restarting daemon \u2014 skipping\n");
|
|
1320
|
+
if (_socket) {
|
|
1321
|
+
_socket.destroy();
|
|
1322
|
+
_socket = null;
|
|
1323
|
+
}
|
|
1324
|
+
_connected = false;
|
|
1325
|
+
_buffer = "";
|
|
1326
|
+
return;
|
|
1327
|
+
}
|
|
1328
|
+
try {
|
|
1329
|
+
process.stderr.write("[exed-client] Killing daemon for restart...\n");
|
|
1330
|
+
if (existsSync5(PID_PATH)) {
|
|
1331
|
+
try {
|
|
1332
|
+
const pid = parseInt(readFileSync4(PID_PATH, "utf8").trim(), 10);
|
|
1333
|
+
if (pid > 0) {
|
|
1334
|
+
try {
|
|
1335
|
+
process.kill(pid, "SIGKILL");
|
|
1336
|
+
} catch {
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
} catch {
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
if (_socket) {
|
|
1343
|
+
_socket.destroy();
|
|
1344
|
+
_socket = null;
|
|
1345
|
+
}
|
|
1346
|
+
_connected = false;
|
|
1347
|
+
_buffer = "";
|
|
1348
|
+
try {
|
|
1349
|
+
unlinkSync2(PID_PATH);
|
|
1350
|
+
} catch {
|
|
1351
|
+
}
|
|
1352
|
+
try {
|
|
1353
|
+
unlinkSync2(SOCKET_PATH);
|
|
1354
|
+
} catch {
|
|
1355
|
+
}
|
|
1356
|
+
spawnDaemon();
|
|
1357
|
+
} finally {
|
|
1358
|
+
releaseSpawnLock();
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1361
|
+
function isDaemonTooYoung() {
|
|
1362
|
+
try {
|
|
1363
|
+
const stat = statSync(PID_PATH);
|
|
1364
|
+
return Date.now() - stat.mtimeMs < MIN_DAEMON_AGE_MS;
|
|
1365
|
+
} catch {
|
|
1366
|
+
return false;
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
async function retryThenRestart(doRequest, label) {
|
|
1370
|
+
const result = await doRequest();
|
|
1371
|
+
if (!result.error) {
|
|
1372
|
+
_consecutiveFailures = 0;
|
|
1373
|
+
return result;
|
|
1374
|
+
}
|
|
1375
|
+
_consecutiveFailures++;
|
|
1376
|
+
for (let i = 0; i < MAX_RETRIES_BEFORE_RESTART; i++) {
|
|
1377
|
+
const delayMs = RETRY_DELAYS_MS[i] ?? 5e3;
|
|
1378
|
+
process.stderr.write(`[exed-client] ${label} failed (${result.error}), retry ${i + 1}/${MAX_RETRIES_BEFORE_RESTART} in ${delayMs}ms
|
|
1379
|
+
`);
|
|
1380
|
+
await new Promise((r) => setTimeout(r, delayMs));
|
|
1381
|
+
if (!_connected) {
|
|
1382
|
+
if (!await connectToSocket()) continue;
|
|
1383
|
+
}
|
|
1384
|
+
const retry = await doRequest();
|
|
1385
|
+
if (!retry.error) {
|
|
1386
|
+
_consecutiveFailures = 0;
|
|
1387
|
+
return retry;
|
|
1388
|
+
}
|
|
1389
|
+
_consecutiveFailures++;
|
|
1390
|
+
}
|
|
1391
|
+
if (isDaemonTooYoung()) {
|
|
1392
|
+
process.stderr.write(`[exed-client] ${label}: daemon too young (< ${MIN_DAEMON_AGE_MS / 1e3}s) \u2014 skipping restart
|
|
1393
|
+
`);
|
|
1394
|
+
return { error: result.error };
|
|
1395
|
+
}
|
|
1396
|
+
process.stderr.write(`[exed-client] ${label}: ${_consecutiveFailures} consecutive failures \u2014 restarting daemon
|
|
1397
|
+
`);
|
|
1398
|
+
killAndRespawnDaemon();
|
|
1399
|
+
const start = Date.now();
|
|
1400
|
+
let delay2 = 200;
|
|
1401
|
+
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
1402
|
+
await new Promise((r) => setTimeout(r, delay2));
|
|
1403
|
+
if (await connectToSocket()) break;
|
|
1404
|
+
delay2 = Math.min(delay2 * 2, 3e3);
|
|
1405
|
+
}
|
|
1406
|
+
if (!_connected) return { error: "Daemon restart failed" };
|
|
1407
|
+
const final = await doRequest();
|
|
1408
|
+
if (!final.error) _consecutiveFailures = 0;
|
|
1409
|
+
return final;
|
|
1410
|
+
}
|
|
1411
|
+
async function embedViaClient(text, priority = "high") {
|
|
1412
|
+
if (!_connected && !await connectEmbedDaemon()) return null;
|
|
1413
|
+
_requestCount++;
|
|
1414
|
+
if (_requestCount % HEALTH_CHECK_INTERVAL === 0) {
|
|
1415
|
+
const health = await pingDaemon();
|
|
1416
|
+
if (!health && !isDaemonTooYoung()) {
|
|
1417
|
+
process.stderr.write(`[exed-client] Periodic health check failed at request ${_requestCount} \u2014 restarting daemon
|
|
1418
|
+
`);
|
|
1419
|
+
killAndRespawnDaemon();
|
|
1420
|
+
const start = Date.now();
|
|
1421
|
+
let d = 200;
|
|
1422
|
+
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
1423
|
+
await new Promise((r) => setTimeout(r, d));
|
|
1424
|
+
if (await connectToSocket()) break;
|
|
1425
|
+
d = Math.min(d * 2, 3e3);
|
|
1426
|
+
}
|
|
1427
|
+
if (!_connected) return null;
|
|
1428
|
+
}
|
|
1429
|
+
}
|
|
1430
|
+
const result = await retryThenRestart(
|
|
1431
|
+
() => sendRequest([text], priority),
|
|
1432
|
+
"Embed"
|
|
1433
|
+
);
|
|
1434
|
+
return !result.error && result.vectors?.[0] ? result.vectors[0] : null;
|
|
1435
|
+
}
|
|
1436
|
+
function disconnectClient() {
|
|
1437
|
+
if (_socket) {
|
|
1438
|
+
_socket.destroy();
|
|
1439
|
+
_socket = null;
|
|
1440
|
+
}
|
|
1441
|
+
_connected = false;
|
|
1442
|
+
_buffer = "";
|
|
1443
|
+
for (const [id, entry] of _pending) {
|
|
1444
|
+
clearTimeout(entry.timer);
|
|
1445
|
+
_pending.delete(id);
|
|
1446
|
+
entry.resolve({ error: "Client disconnected" });
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
function isClientConnected() {
|
|
1450
|
+
return _connected;
|
|
1451
|
+
}
|
|
1452
|
+
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;
|
|
1453
|
+
var init_exe_daemon_client = __esm({
|
|
1454
|
+
"src/lib/exe-daemon-client.ts"() {
|
|
1455
|
+
"use strict";
|
|
1456
|
+
init_config();
|
|
1457
|
+
init_daemon_auth();
|
|
1458
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path5.join(EXE_AI_DIR, "exed.sock");
|
|
1459
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path5.join(EXE_AI_DIR, "exed.pid");
|
|
1460
|
+
SPAWN_LOCK_PATH = path5.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
1461
|
+
SPAWN_LOCK_STALE_MS = 3e4;
|
|
1462
|
+
CONNECT_TIMEOUT_MS = 15e3;
|
|
1463
|
+
REQUEST_TIMEOUT_MS = 3e4;
|
|
1464
|
+
DAEMON_TOKEN_ENV = "EXE_DAEMON_TOKEN";
|
|
1465
|
+
_socket = null;
|
|
1466
|
+
_connected = false;
|
|
1467
|
+
_buffer = "";
|
|
1468
|
+
_requestCount = 0;
|
|
1469
|
+
_consecutiveFailures = 0;
|
|
1470
|
+
HEALTH_CHECK_INTERVAL = 100;
|
|
1471
|
+
MAX_RETRIES_BEFORE_RESTART = 3;
|
|
1472
|
+
RETRY_DELAYS_MS = [1e3, 3e3, 5e3];
|
|
1473
|
+
MIN_DAEMON_AGE_MS = 3e4;
|
|
1474
|
+
_pending = /* @__PURE__ */ new Map();
|
|
1475
|
+
MAX_BUFFER = 1e7;
|
|
1476
|
+
}
|
|
1477
|
+
});
|
|
1478
|
+
|
|
1479
|
+
// src/lib/daemon-protocol.ts
|
|
1480
|
+
function serializeValue(v) {
|
|
1481
|
+
if (v === null || v === void 0) return null;
|
|
1482
|
+
if (typeof v === "bigint") return Number(v);
|
|
1483
|
+
if (typeof v === "boolean") return v ? 1 : 0;
|
|
1484
|
+
if (v instanceof Uint8Array) {
|
|
1485
|
+
return { __blob: Buffer.from(v).toString("base64") };
|
|
1486
|
+
}
|
|
1487
|
+
if (ArrayBuffer.isView(v)) {
|
|
1488
|
+
return { __blob: Buffer.from(v.buffer, v.byteOffset, v.byteLength).toString("base64") };
|
|
1489
|
+
}
|
|
1490
|
+
if (v instanceof ArrayBuffer) {
|
|
1491
|
+
return { __blob: Buffer.from(v).toString("base64") };
|
|
1492
|
+
}
|
|
1493
|
+
if (typeof v === "string" || typeof v === "number") return v;
|
|
1494
|
+
return String(v);
|
|
1495
|
+
}
|
|
1496
|
+
function deserializeValue(v) {
|
|
1497
|
+
if (v === null) return null;
|
|
1498
|
+
if (typeof v === "object" && v !== null && "__blob" in v) {
|
|
1499
|
+
const buf = Buffer.from(v.__blob, "base64");
|
|
1500
|
+
return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
|
|
1501
|
+
}
|
|
1502
|
+
return v;
|
|
1503
|
+
}
|
|
1504
|
+
function deserializeResultSet(srs) {
|
|
1505
|
+
const rows = srs.rows.map((obj) => {
|
|
1506
|
+
const values = srs.columns.map(
|
|
1507
|
+
(col) => deserializeValue(obj[col] ?? null)
|
|
1508
|
+
);
|
|
1509
|
+
const row = values;
|
|
1510
|
+
for (let i = 0; i < srs.columns.length; i++) {
|
|
1511
|
+
const col = srs.columns[i];
|
|
1512
|
+
if (col !== void 0) {
|
|
1513
|
+
row[col] = values[i] ?? null;
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1516
|
+
Object.defineProperty(row, "length", {
|
|
1517
|
+
value: values.length,
|
|
1518
|
+
enumerable: false
|
|
1519
|
+
});
|
|
1520
|
+
return row;
|
|
1521
|
+
});
|
|
1522
|
+
return {
|
|
1523
|
+
columns: srs.columns,
|
|
1524
|
+
columnTypes: srs.columnTypes ?? [],
|
|
1525
|
+
rows,
|
|
1526
|
+
rowsAffected: srs.rowsAffected,
|
|
1527
|
+
lastInsertRowid: srs.lastInsertRowid != null ? BigInt(srs.lastInsertRowid) : void 0,
|
|
1528
|
+
toJSON: () => ({
|
|
1529
|
+
columns: srs.columns,
|
|
1530
|
+
columnTypes: srs.columnTypes ?? [],
|
|
1531
|
+
rows: srs.rows,
|
|
1532
|
+
rowsAffected: srs.rowsAffected,
|
|
1533
|
+
lastInsertRowid: srs.lastInsertRowid
|
|
1534
|
+
})
|
|
1535
|
+
};
|
|
1536
|
+
}
|
|
1537
|
+
var init_daemon_protocol = __esm({
|
|
1538
|
+
"src/lib/daemon-protocol.ts"() {
|
|
1539
|
+
"use strict";
|
|
1540
|
+
}
|
|
1541
|
+
});
|
|
1542
|
+
|
|
1543
|
+
// src/lib/db-daemon-client.ts
|
|
1544
|
+
var db_daemon_client_exports = {};
|
|
1545
|
+
__export(db_daemon_client_exports, {
|
|
1546
|
+
createDaemonDbClient: () => createDaemonDbClient,
|
|
1547
|
+
initDaemonDbClient: () => initDaemonDbClient
|
|
1548
|
+
});
|
|
1549
|
+
function normalizeStatement2(stmt) {
|
|
1550
|
+
if (typeof stmt === "string") {
|
|
1551
|
+
return { sql: stmt, args: [] };
|
|
1552
|
+
}
|
|
1553
|
+
const sql = stmt.sql;
|
|
1554
|
+
let args = [];
|
|
1555
|
+
if (Array.isArray(stmt.args)) {
|
|
1556
|
+
args = stmt.args.map((v) => serializeValue(v));
|
|
1557
|
+
} else if (stmt.args && typeof stmt.args === "object") {
|
|
1558
|
+
const named = {};
|
|
1559
|
+
for (const [key, val] of Object.entries(stmt.args)) {
|
|
1560
|
+
named[key] = serializeValue(val);
|
|
1561
|
+
}
|
|
1562
|
+
return { sql, args: named };
|
|
1563
|
+
}
|
|
1564
|
+
return { sql, args };
|
|
1565
|
+
}
|
|
1566
|
+
function createDaemonDbClient(fallbackClient) {
|
|
1567
|
+
let _useDaemon = false;
|
|
1568
|
+
const client = {
|
|
1569
|
+
async execute(stmt) {
|
|
1570
|
+
if (!_useDaemon || !isClientConnected()) {
|
|
1571
|
+
return fallbackClient.execute(stmt);
|
|
1572
|
+
}
|
|
1573
|
+
const { sql, args } = normalizeStatement2(stmt);
|
|
1574
|
+
const response = await sendDaemonRequest({
|
|
1575
|
+
type: "db-execute",
|
|
1576
|
+
sql,
|
|
1577
|
+
args
|
|
1578
|
+
});
|
|
1579
|
+
if (response.error) {
|
|
1580
|
+
const errMsg = String(response.error);
|
|
1581
|
+
if (errMsg === "Not connected" || errMsg === "Request timeout" || errMsg === "Write failed" || errMsg === "DB not initialized") {
|
|
1582
|
+
process.stderr.write(`[db-daemon] Transport error (${errMsg}), falling back to direct
|
|
1583
|
+
`);
|
|
1584
|
+
return fallbackClient.execute(stmt);
|
|
1585
|
+
}
|
|
1586
|
+
throw new Error(errMsg);
|
|
1587
|
+
}
|
|
1588
|
+
if (response.db) {
|
|
1589
|
+
return deserializeResultSet(response.db);
|
|
1590
|
+
}
|
|
1591
|
+
process.stderr.write("[db-daemon] Unexpected response shape, falling back to direct\n");
|
|
1592
|
+
return fallbackClient.execute(stmt);
|
|
1593
|
+
},
|
|
1594
|
+
async batch(stmts, mode) {
|
|
1595
|
+
if (!_useDaemon || !isClientConnected()) {
|
|
1596
|
+
return fallbackClient.batch(stmts, mode);
|
|
1597
|
+
}
|
|
1598
|
+
const statements = stmts.map(normalizeStatement2);
|
|
1599
|
+
const response = await sendDaemonRequest({
|
|
1600
|
+
type: "db-batch",
|
|
1601
|
+
statements,
|
|
1602
|
+
mode: mode ?? "deferred"
|
|
1603
|
+
});
|
|
1604
|
+
if (response.error) {
|
|
1605
|
+
const errMsg = String(response.error);
|
|
1606
|
+
if (errMsg === "Not connected" || errMsg === "Request timeout" || errMsg === "Write failed" || errMsg === "DB not initialized") {
|
|
1607
|
+
process.stderr.write(`[db-daemon] Batch transport error (${errMsg}), falling back to direct
|
|
1608
|
+
`);
|
|
1609
|
+
return fallbackClient.batch(stmts, mode);
|
|
1610
|
+
}
|
|
1611
|
+
throw new Error(errMsg);
|
|
1612
|
+
}
|
|
1613
|
+
const batchResults = response["db-batch"];
|
|
1614
|
+
if (batchResults) {
|
|
1615
|
+
return batchResults.map(deserializeResultSet);
|
|
1616
|
+
}
|
|
1617
|
+
process.stderr.write("[db-daemon] Unexpected batch response shape, falling back to direct\n");
|
|
1618
|
+
return fallbackClient.batch(stmts, mode);
|
|
1619
|
+
},
|
|
1620
|
+
// Transaction support — delegate to fallback (transactions need direct connection)
|
|
1621
|
+
async transaction(mode) {
|
|
1622
|
+
return fallbackClient.transaction(mode);
|
|
1623
|
+
},
|
|
1624
|
+
// executeMultiple — delegate to fallback (used only for schema migrations)
|
|
1625
|
+
async executeMultiple(sql) {
|
|
1626
|
+
return fallbackClient.executeMultiple(sql);
|
|
1627
|
+
},
|
|
1628
|
+
// migrate — delegate to fallback
|
|
1629
|
+
async migrate(stmts) {
|
|
1630
|
+
return fallbackClient.migrate(stmts);
|
|
1631
|
+
},
|
|
1632
|
+
// Sync mode — delegate to fallback
|
|
1633
|
+
sync() {
|
|
1634
|
+
return fallbackClient.sync();
|
|
1635
|
+
},
|
|
1636
|
+
close() {
|
|
1637
|
+
_useDaemon = false;
|
|
1638
|
+
},
|
|
1639
|
+
get closed() {
|
|
1640
|
+
return fallbackClient.closed;
|
|
1641
|
+
},
|
|
1642
|
+
get protocol() {
|
|
1643
|
+
return fallbackClient.protocol;
|
|
1644
|
+
}
|
|
1645
|
+
};
|
|
1646
|
+
return {
|
|
1647
|
+
...client,
|
|
1648
|
+
/** Enable daemon routing (call after confirming daemon is connected) */
|
|
1649
|
+
_enableDaemon() {
|
|
1650
|
+
_useDaemon = true;
|
|
1651
|
+
},
|
|
1652
|
+
/** Check if daemon routing is active */
|
|
1653
|
+
_isDaemonActive() {
|
|
1654
|
+
return _useDaemon && isClientConnected();
|
|
1655
|
+
}
|
|
1656
|
+
};
|
|
1657
|
+
}
|
|
1658
|
+
async function initDaemonDbClient(fallbackClient) {
|
|
1659
|
+
if (process.env.EXE_IS_DAEMON === "1") return null;
|
|
1660
|
+
const connected = await connectEmbedDaemon();
|
|
1661
|
+
if (!connected) {
|
|
1662
|
+
process.stderr.write("[db-daemon] Daemon unavailable \u2014 using direct SQLite\n");
|
|
1663
|
+
return null;
|
|
1664
|
+
}
|
|
1665
|
+
const client = createDaemonDbClient(fallbackClient);
|
|
1666
|
+
client._enableDaemon();
|
|
1667
|
+
process.stderr.write("[db-daemon] DB routing through daemon (single-writer)\n");
|
|
1668
|
+
return client;
|
|
1669
|
+
}
|
|
1670
|
+
var init_db_daemon_client = __esm({
|
|
1671
|
+
"src/lib/db-daemon-client.ts"() {
|
|
1672
|
+
"use strict";
|
|
1673
|
+
init_exe_daemon_client();
|
|
1674
|
+
init_daemon_protocol();
|
|
1675
|
+
}
|
|
1676
|
+
});
|
|
1677
|
+
|
|
933
1678
|
// src/lib/database.ts
|
|
1679
|
+
var database_exports = {};
|
|
1680
|
+
__export(database_exports, {
|
|
1681
|
+
disposeDatabase: () => disposeDatabase,
|
|
1682
|
+
disposeTurso: () => disposeTurso,
|
|
1683
|
+
ensureSchema: () => ensureSchema,
|
|
1684
|
+
getClient: () => getClient,
|
|
1685
|
+
getRawClient: () => getRawClient,
|
|
1686
|
+
initDaemonClient: () => initDaemonClient,
|
|
1687
|
+
initDatabase: () => initDatabase,
|
|
1688
|
+
initTurso: () => initTurso,
|
|
1689
|
+
isInitialized: () => isInitialized
|
|
1690
|
+
});
|
|
934
1691
|
import { createClient } from "@libsql/client";
|
|
935
1692
|
async function initDatabase(config) {
|
|
936
1693
|
if (_walCheckpointTimer) {
|
|
@@ -969,15 +1726,18 @@ async function initDatabase(config) {
|
|
|
969
1726
|
});
|
|
970
1727
|
}, 3e4);
|
|
971
1728
|
_walCheckpointTimer.unref();
|
|
972
|
-
if (process.env.DATABASE_URL) {
|
|
1729
|
+
if (process.env.DATABASE_URL && process.env.EXE_USE_POSTGRES === "1") {
|
|
973
1730
|
_adapterClient = await createPrismaDbAdapter(_resilientClient);
|
|
974
1731
|
}
|
|
975
1732
|
}
|
|
1733
|
+
function isInitialized() {
|
|
1734
|
+
return _adapterClient !== null || _client !== null;
|
|
1735
|
+
}
|
|
976
1736
|
function getClient() {
|
|
977
1737
|
if (!_adapterClient) {
|
|
978
1738
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
979
1739
|
}
|
|
980
|
-
if (process.env.DATABASE_URL) {
|
|
1740
|
+
if (process.env.DATABASE_URL && process.env.EXE_USE_POSTGRES === "1") {
|
|
981
1741
|
return _adapterClient;
|
|
982
1742
|
}
|
|
983
1743
|
if (process.env.EXE_IS_DAEMON === "1") {
|
|
@@ -988,6 +1748,22 @@ function getClient() {
|
|
|
988
1748
|
}
|
|
989
1749
|
return _resilientClient;
|
|
990
1750
|
}
|
|
1751
|
+
async function initDaemonClient() {
|
|
1752
|
+
if (process.env.DATABASE_URL && process.env.EXE_USE_POSTGRES === "1") return;
|
|
1753
|
+
if (process.env.EXE_IS_DAEMON === "1") return;
|
|
1754
|
+
if (process.env.VITEST) return;
|
|
1755
|
+
if (!_resilientClient) return;
|
|
1756
|
+
if (_daemonClient) return;
|
|
1757
|
+
try {
|
|
1758
|
+
const { initDaemonDbClient: initDaemonDbClient2 } = await Promise.resolve().then(() => (init_db_daemon_client(), db_daemon_client_exports));
|
|
1759
|
+
_daemonClient = await initDaemonDbClient2(_resilientClient);
|
|
1760
|
+
} catch (err) {
|
|
1761
|
+
process.stderr.write(
|
|
1762
|
+
`[database] Daemon client init failed (non-fatal): ${err instanceof Error ? err.message : String(err)}
|
|
1763
|
+
`
|
|
1764
|
+
);
|
|
1765
|
+
}
|
|
1766
|
+
}
|
|
991
1767
|
function getRawClient() {
|
|
992
1768
|
if (!_client) {
|
|
993
1769
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
@@ -1126,6 +1902,13 @@ async function ensureSchema() {
|
|
|
1126
1902
|
});
|
|
1127
1903
|
} catch {
|
|
1128
1904
|
}
|
|
1905
|
+
try {
|
|
1906
|
+
await client.execute({
|
|
1907
|
+
sql: `ALTER TABLE behaviors ADD COLUMN vector F32_BLOB(${EMBEDDING_DIM})`,
|
|
1908
|
+
args: []
|
|
1909
|
+
});
|
|
1910
|
+
} catch {
|
|
1911
|
+
}
|
|
1129
1912
|
try {
|
|
1130
1913
|
await client.execute({
|
|
1131
1914
|
sql: `ALTER TABLE tasks ADD COLUMN blocked_by TEXT`,
|
|
@@ -1970,30 +2753,51 @@ async function ensureSchema() {
|
|
|
1970
2753
|
} catch {
|
|
1971
2754
|
}
|
|
1972
2755
|
}
|
|
1973
|
-
|
|
2756
|
+
async function disposeDatabase() {
|
|
2757
|
+
if (_walCheckpointTimer) {
|
|
2758
|
+
clearInterval(_walCheckpointTimer);
|
|
2759
|
+
_walCheckpointTimer = null;
|
|
2760
|
+
}
|
|
2761
|
+
if (_daemonClient) {
|
|
2762
|
+
_daemonClient.close();
|
|
2763
|
+
_daemonClient = null;
|
|
2764
|
+
}
|
|
2765
|
+
if (_adapterClient && _adapterClient !== _resilientClient) {
|
|
2766
|
+
_adapterClient.close();
|
|
2767
|
+
}
|
|
2768
|
+
_adapterClient = null;
|
|
2769
|
+
if (_client) {
|
|
2770
|
+
_client.close();
|
|
2771
|
+
_client = null;
|
|
2772
|
+
_resilientClient = null;
|
|
2773
|
+
}
|
|
2774
|
+
}
|
|
2775
|
+
var _client, _resilientClient, _walCheckpointTimer, _daemonClient, _adapterClient, initTurso, disposeTurso;
|
|
1974
2776
|
var init_database = __esm({
|
|
1975
2777
|
"src/lib/database.ts"() {
|
|
1976
2778
|
"use strict";
|
|
1977
2779
|
init_db_retry();
|
|
1978
2780
|
init_employees();
|
|
1979
2781
|
init_database_adapter();
|
|
2782
|
+
init_memory();
|
|
1980
2783
|
_client = null;
|
|
1981
2784
|
_resilientClient = null;
|
|
1982
2785
|
_walCheckpointTimer = null;
|
|
1983
2786
|
_daemonClient = null;
|
|
1984
2787
|
_adapterClient = null;
|
|
1985
2788
|
initTurso = initDatabase;
|
|
2789
|
+
disposeTurso = disposeDatabase;
|
|
1986
2790
|
}
|
|
1987
2791
|
});
|
|
1988
2792
|
|
|
1989
2793
|
// src/lib/session-registry.ts
|
|
1990
|
-
import
|
|
1991
|
-
import
|
|
2794
|
+
import path6 from "path";
|
|
2795
|
+
import os5 from "os";
|
|
1992
2796
|
var REGISTRY_PATH;
|
|
1993
2797
|
var init_session_registry = __esm({
|
|
1994
2798
|
"src/lib/session-registry.ts"() {
|
|
1995
2799
|
"use strict";
|
|
1996
|
-
REGISTRY_PATH =
|
|
2800
|
+
REGISTRY_PATH = path6.join(os5.homedir(), ".exe-os", "session-registry.json");
|
|
1997
2801
|
}
|
|
1998
2802
|
});
|
|
1999
2803
|
|
|
@@ -2239,12 +3043,12 @@ var init_runtime_table = __esm({
|
|
|
2239
3043
|
});
|
|
2240
3044
|
|
|
2241
3045
|
// src/lib/agent-config.ts
|
|
2242
|
-
import { readFileSync as
|
|
2243
|
-
import
|
|
3046
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync6 } from "fs";
|
|
3047
|
+
import path7 from "path";
|
|
2244
3048
|
function loadAgentConfig() {
|
|
2245
|
-
if (!
|
|
3049
|
+
if (!existsSync6(AGENT_CONFIG_PATH)) return {};
|
|
2246
3050
|
try {
|
|
2247
|
-
return JSON.parse(
|
|
3051
|
+
return JSON.parse(readFileSync5(AGENT_CONFIG_PATH, "utf-8"));
|
|
2248
3052
|
} catch {
|
|
2249
3053
|
return {};
|
|
2250
3054
|
}
|
|
@@ -2264,9 +3068,9 @@ var init_agent_config = __esm({
|
|
|
2264
3068
|
init_config();
|
|
2265
3069
|
init_runtime_table();
|
|
2266
3070
|
init_secure_files();
|
|
2267
|
-
AGENT_CONFIG_PATH =
|
|
3071
|
+
AGENT_CONFIG_PATH = path7.join(EXE_AI_DIR, "agent-config.json");
|
|
2268
3072
|
DEFAULT_MODELS = {
|
|
2269
|
-
claude: "claude-opus-4",
|
|
3073
|
+
claude: "claude-opus-4.6",
|
|
2270
3074
|
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
2271
3075
|
opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
|
|
2272
3076
|
};
|
|
@@ -2282,17 +3086,17 @@ __export(intercom_queue_exports, {
|
|
|
2282
3086
|
queueIntercom: () => queueIntercom,
|
|
2283
3087
|
readQueue: () => readQueue
|
|
2284
3088
|
});
|
|
2285
|
-
import { readFileSync as
|
|
2286
|
-
import
|
|
2287
|
-
import
|
|
3089
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, renameSync as renameSync3, existsSync as existsSync7, mkdirSync as mkdirSync2 } from "fs";
|
|
3090
|
+
import path8 from "path";
|
|
3091
|
+
import os6 from "os";
|
|
2288
3092
|
function ensureDir() {
|
|
2289
|
-
const dir =
|
|
2290
|
-
if (!
|
|
3093
|
+
const dir = path8.dirname(QUEUE_PATH);
|
|
3094
|
+
if (!existsSync7(dir)) mkdirSync2(dir, { recursive: true });
|
|
2291
3095
|
}
|
|
2292
3096
|
function readQueue() {
|
|
2293
3097
|
try {
|
|
2294
|
-
if (!
|
|
2295
|
-
return JSON.parse(
|
|
3098
|
+
if (!existsSync7(QUEUE_PATH)) return [];
|
|
3099
|
+
return JSON.parse(readFileSync6(QUEUE_PATH, "utf8"));
|
|
2296
3100
|
} catch {
|
|
2297
3101
|
return [];
|
|
2298
3102
|
}
|
|
@@ -2300,7 +3104,7 @@ function readQueue() {
|
|
|
2300
3104
|
function writeQueue(queue) {
|
|
2301
3105
|
ensureDir();
|
|
2302
3106
|
const tmp = `${QUEUE_PATH}.tmp`;
|
|
2303
|
-
|
|
3107
|
+
writeFileSync4(tmp, JSON.stringify(queue, null, 2));
|
|
2304
3108
|
renameSync3(tmp, QUEUE_PATH);
|
|
2305
3109
|
}
|
|
2306
3110
|
function queueIntercom(targetSession, reason) {
|
|
@@ -2392,35 +3196,35 @@ var QUEUE_PATH, MAX_RETRIES2, TTL_MS, INTERCOM_LOG;
|
|
|
2392
3196
|
var init_intercom_queue = __esm({
|
|
2393
3197
|
"src/lib/intercom-queue.ts"() {
|
|
2394
3198
|
"use strict";
|
|
2395
|
-
QUEUE_PATH =
|
|
3199
|
+
QUEUE_PATH = path8.join(os6.homedir(), ".exe-os", "intercom-queue.json");
|
|
2396
3200
|
MAX_RETRIES2 = 5;
|
|
2397
3201
|
TTL_MS = 60 * 60 * 1e3;
|
|
2398
|
-
INTERCOM_LOG =
|
|
3202
|
+
INTERCOM_LOG = path8.join(os6.homedir(), ".exe-os", "intercom.log");
|
|
2399
3203
|
}
|
|
2400
3204
|
});
|
|
2401
3205
|
|
|
2402
3206
|
// src/lib/license.ts
|
|
2403
|
-
import { readFileSync as
|
|
2404
|
-
import { randomUUID } from "crypto";
|
|
3207
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, existsSync as existsSync8, mkdirSync as mkdirSync3 } from "fs";
|
|
3208
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
2405
3209
|
import { createRequire as createRequire2 } from "module";
|
|
2406
3210
|
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
2407
|
-
import
|
|
2408
|
-
import
|
|
3211
|
+
import os7 from "os";
|
|
3212
|
+
import path9 from "path";
|
|
2409
3213
|
import { jwtVerify, importSPKI } from "jose";
|
|
2410
3214
|
var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH;
|
|
2411
3215
|
var init_license = __esm({
|
|
2412
3216
|
"src/lib/license.ts"() {
|
|
2413
3217
|
"use strict";
|
|
2414
3218
|
init_config();
|
|
2415
|
-
LICENSE_PATH =
|
|
2416
|
-
CACHE_PATH =
|
|
2417
|
-
DEVICE_ID_PATH =
|
|
3219
|
+
LICENSE_PATH = path9.join(EXE_AI_DIR, "license.key");
|
|
3220
|
+
CACHE_PATH = path9.join(EXE_AI_DIR, "license-cache.json");
|
|
3221
|
+
DEVICE_ID_PATH = path9.join(EXE_AI_DIR, "device-id");
|
|
2418
3222
|
}
|
|
2419
3223
|
});
|
|
2420
3224
|
|
|
2421
3225
|
// src/lib/plan-limits.ts
|
|
2422
|
-
import { readFileSync as
|
|
2423
|
-
import
|
|
3226
|
+
import { readFileSync as readFileSync8, existsSync as existsSync9 } from "fs";
|
|
3227
|
+
import path10 from "path";
|
|
2424
3228
|
var CACHE_PATH2;
|
|
2425
3229
|
var init_plan_limits = __esm({
|
|
2426
3230
|
"src/lib/plan-limits.ts"() {
|
|
@@ -2429,15 +3233,15 @@ var init_plan_limits = __esm({
|
|
|
2429
3233
|
init_employees();
|
|
2430
3234
|
init_license();
|
|
2431
3235
|
init_config();
|
|
2432
|
-
CACHE_PATH2 =
|
|
3236
|
+
CACHE_PATH2 = path10.join(EXE_AI_DIR, "license-cache.json");
|
|
2433
3237
|
}
|
|
2434
3238
|
});
|
|
2435
3239
|
|
|
2436
3240
|
// src/lib/tmux-routing.ts
|
|
2437
|
-
import { readFileSync as
|
|
2438
|
-
import
|
|
2439
|
-
import
|
|
2440
|
-
import { fileURLToPath } from "url";
|
|
3241
|
+
import { readFileSync as readFileSync9, writeFileSync as writeFileSync6, mkdirSync as mkdirSync4, existsSync as existsSync10, appendFileSync, readdirSync } from "fs";
|
|
3242
|
+
import path11 from "path";
|
|
3243
|
+
import os8 from "os";
|
|
3244
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
2441
3245
|
function getMySession() {
|
|
2442
3246
|
return getTransport().getMySession();
|
|
2443
3247
|
}
|
|
@@ -2449,7 +3253,7 @@ function extractRootExe(name) {
|
|
|
2449
3253
|
}
|
|
2450
3254
|
function getParentExe(sessionKey) {
|
|
2451
3255
|
try {
|
|
2452
|
-
const data = JSON.parse(
|
|
3256
|
+
const data = JSON.parse(readFileSync9(path11.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
2453
3257
|
return data.parentExe || null;
|
|
2454
3258
|
} catch {
|
|
2455
3259
|
return null;
|
|
@@ -2457,8 +3261,8 @@ function getParentExe(sessionKey) {
|
|
|
2457
3261
|
}
|
|
2458
3262
|
function getDispatchedBy(sessionKey) {
|
|
2459
3263
|
try {
|
|
2460
|
-
const data = JSON.parse(
|
|
2461
|
-
|
|
3264
|
+
const data = JSON.parse(readFileSync9(
|
|
3265
|
+
path11.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
|
|
2462
3266
|
"utf8"
|
|
2463
3267
|
));
|
|
2464
3268
|
return data.dispatchedBy ?? data.parentExe ?? null;
|
|
@@ -2490,8 +3294,8 @@ function resolveExeSession() {
|
|
|
2490
3294
|
}
|
|
2491
3295
|
function readDebounceState() {
|
|
2492
3296
|
try {
|
|
2493
|
-
if (!
|
|
2494
|
-
const raw = JSON.parse(
|
|
3297
|
+
if (!existsSync10(DEBOUNCE_FILE)) return {};
|
|
3298
|
+
const raw = JSON.parse(readFileSync9(DEBOUNCE_FILE, "utf8"));
|
|
2495
3299
|
const state = {};
|
|
2496
3300
|
for (const [key, val] of Object.entries(raw)) {
|
|
2497
3301
|
if (typeof val === "number") {
|
|
@@ -2507,8 +3311,8 @@ function readDebounceState() {
|
|
|
2507
3311
|
}
|
|
2508
3312
|
function writeDebounceState(state) {
|
|
2509
3313
|
try {
|
|
2510
|
-
if (!
|
|
2511
|
-
|
|
3314
|
+
if (!existsSync10(SESSION_CACHE)) mkdirSync4(SESSION_CACHE, { recursive: true });
|
|
3315
|
+
writeFileSync6(DEBOUNCE_FILE, JSON.stringify(state));
|
|
2512
3316
|
} catch {
|
|
2513
3317
|
}
|
|
2514
3318
|
}
|
|
@@ -2568,7 +3372,7 @@ function getSessionState(sessionName) {
|
|
|
2568
3372
|
function isExeSession(sessionName) {
|
|
2569
3373
|
const matchesBaseWithInstance = (baseName) => sessionName === baseName || sessionName.startsWith(baseName) && /^\d+$/.test(sessionName.slice(baseName.length));
|
|
2570
3374
|
const coordinatorName = getCoordinatorName();
|
|
2571
|
-
return matchesBaseWithInstance(coordinatorName)
|
|
3375
|
+
return matchesBaseWithInstance(coordinatorName);
|
|
2572
3376
|
}
|
|
2573
3377
|
function sendIntercom(targetSession) {
|
|
2574
3378
|
const transport = getTransport();
|
|
@@ -2603,8 +3407,8 @@ function sendIntercom(targetSession) {
|
|
|
2603
3407
|
try {
|
|
2604
3408
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
2605
3409
|
const agent = baseAgentName(rawAgent);
|
|
2606
|
-
const markerPath =
|
|
2607
|
-
if (
|
|
3410
|
+
const markerPath = path11.join(SESSION_CACHE, `current-task-${agent}.json`);
|
|
3411
|
+
if (existsSync10(markerPath)) {
|
|
2608
3412
|
logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker + not idle \u2014 will auto-chain)`);
|
|
2609
3413
|
return "debounced";
|
|
2610
3414
|
}
|
|
@@ -2614,8 +3418,8 @@ function sendIntercom(targetSession) {
|
|
|
2614
3418
|
try {
|
|
2615
3419
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
2616
3420
|
const agent = baseAgentName(rawAgent);
|
|
2617
|
-
const taskDir =
|
|
2618
|
-
if (
|
|
3421
|
+
const taskDir = path11.join(process.cwd(), "exe", agent);
|
|
3422
|
+
if (existsSync10(taskDir)) {
|
|
2619
3423
|
const files = readdirSync(taskDir).filter(
|
|
2620
3424
|
(f) => f.endsWith(".md") && f !== "DONE.txt"
|
|
2621
3425
|
);
|
|
@@ -2691,12 +3495,12 @@ var init_tmux_routing = __esm({
|
|
|
2691
3495
|
init_intercom_queue();
|
|
2692
3496
|
init_plan_limits();
|
|
2693
3497
|
init_employees();
|
|
2694
|
-
SPAWN_LOCK_DIR =
|
|
2695
|
-
SESSION_CACHE =
|
|
3498
|
+
SPAWN_LOCK_DIR = path11.join(os8.homedir(), ".exe-os", "spawn-locks");
|
|
3499
|
+
SESSION_CACHE = path11.join(os8.homedir(), ".exe-os", "session-cache");
|
|
2696
3500
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
2697
3501
|
CODEX_DEBOUNCE_MS = 12e4;
|
|
2698
|
-
INTERCOM_LOG2 =
|
|
2699
|
-
DEBOUNCE_FILE =
|
|
3502
|
+
INTERCOM_LOG2 = path11.join(os8.homedir(), ".exe-os", "intercom.log");
|
|
3503
|
+
DEBOUNCE_FILE = path11.join(SESSION_CACHE, "intercom-debounce.json");
|
|
2700
3504
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
2701
3505
|
BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
|
|
2702
3506
|
}
|
|
@@ -2736,20 +3540,20 @@ var init_task_scope = __esm({
|
|
|
2736
3540
|
});
|
|
2737
3541
|
|
|
2738
3542
|
// src/lib/notifications.ts
|
|
2739
|
-
import
|
|
2740
|
-
import
|
|
2741
|
-
import
|
|
3543
|
+
import crypto2 from "crypto";
|
|
3544
|
+
import path12 from "path";
|
|
3545
|
+
import os9 from "os";
|
|
2742
3546
|
import {
|
|
2743
|
-
readFileSync as
|
|
3547
|
+
readFileSync as readFileSync10,
|
|
2744
3548
|
readdirSync as readdirSync2,
|
|
2745
|
-
unlinkSync as
|
|
2746
|
-
existsSync as
|
|
3549
|
+
unlinkSync as unlinkSync3,
|
|
3550
|
+
existsSync as existsSync11,
|
|
2747
3551
|
rmdirSync
|
|
2748
3552
|
} from "fs";
|
|
2749
3553
|
async function writeNotification(notification) {
|
|
2750
3554
|
try {
|
|
2751
3555
|
const client = getClient();
|
|
2752
|
-
const id =
|
|
3556
|
+
const id = crypto2.randomUUID();
|
|
2753
3557
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2754
3558
|
const sessionScope = notification.sessionScope === void 0 ? getCurrentSessionScope() : notification.sessionScope;
|
|
2755
3559
|
await client.execute({
|
|
@@ -2848,12 +3652,12 @@ var init_state_bus = __esm({
|
|
|
2848
3652
|
});
|
|
2849
3653
|
|
|
2850
3654
|
// src/lib/tasks-crud.ts
|
|
2851
|
-
import
|
|
2852
|
-
import
|
|
2853
|
-
import
|
|
3655
|
+
import crypto3 from "crypto";
|
|
3656
|
+
import path13 from "path";
|
|
3657
|
+
import os10 from "os";
|
|
2854
3658
|
import { execSync as execSync4 } from "child_process";
|
|
2855
3659
|
import { mkdir as mkdir3, writeFile as writeFile3, appendFile } from "fs/promises";
|
|
2856
|
-
import { existsSync as
|
|
3660
|
+
import { existsSync as existsSync12, readFileSync as readFileSync11 } from "fs";
|
|
2857
3661
|
async function writeCheckpoint(input) {
|
|
2858
3662
|
const client = getClient();
|
|
2859
3663
|
const row = await resolveTask(client, input.taskId);
|
|
@@ -3199,8 +4003,8 @@ var init_tasks_crud = __esm({
|
|
|
3199
4003
|
});
|
|
3200
4004
|
|
|
3201
4005
|
// src/lib/tasks-review.ts
|
|
3202
|
-
import
|
|
3203
|
-
import { existsSync as
|
|
4006
|
+
import path14 from "path";
|
|
4007
|
+
import { existsSync as existsSync13, readdirSync as readdirSync3, unlinkSync as unlinkSync4 } from "fs";
|
|
3204
4008
|
async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
3205
4009
|
if (String(row.assigned_by) !== "system" || !taskFile.includes("review-")) return;
|
|
3206
4010
|
try {
|
|
@@ -3245,11 +4049,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
3245
4049
|
);
|
|
3246
4050
|
}
|
|
3247
4051
|
try {
|
|
3248
|
-
const cacheDir =
|
|
3249
|
-
if (
|
|
4052
|
+
const cacheDir = path14.join(EXE_AI_DIR, "session-cache");
|
|
4053
|
+
if (existsSync13(cacheDir)) {
|
|
3250
4054
|
for (const f of readdirSync3(cacheDir)) {
|
|
3251
4055
|
if (f.startsWith("review-notified-")) {
|
|
3252
|
-
|
|
4056
|
+
unlinkSync4(path14.join(cacheDir, f));
|
|
3253
4057
|
}
|
|
3254
4058
|
}
|
|
3255
4059
|
}
|
|
@@ -3271,7 +4075,7 @@ var init_tasks_review = __esm({
|
|
|
3271
4075
|
});
|
|
3272
4076
|
|
|
3273
4077
|
// src/lib/tasks-chain.ts
|
|
3274
|
-
import
|
|
4078
|
+
import path15 from "path";
|
|
3275
4079
|
import { readFile as readFile3, writeFile as writeFile4 } from "fs/promises";
|
|
3276
4080
|
async function cascadeUnblock(taskId, baseDir, now) {
|
|
3277
4081
|
const client = getClient();
|
|
@@ -3288,7 +4092,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
3288
4092
|
});
|
|
3289
4093
|
for (const ur of unblockedRows.rows) {
|
|
3290
4094
|
try {
|
|
3291
|
-
const ubFile =
|
|
4095
|
+
const ubFile = path15.join(baseDir, String(ur.task_file));
|
|
3292
4096
|
let ubContent = await readFile3(ubFile, "utf-8");
|
|
3293
4097
|
ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
|
|
3294
4098
|
ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
|
|
@@ -3359,7 +4163,13 @@ var init_tasks_chain = __esm({
|
|
|
3359
4163
|
function notifyTaskDone() {
|
|
3360
4164
|
try {
|
|
3361
4165
|
const key = getSessionKey();
|
|
3362
|
-
if (key && !process.env.VITEST)
|
|
4166
|
+
if (key && !process.env.VITEST) {
|
|
4167
|
+
notifyParentExe(key);
|
|
4168
|
+
process.stderr.write(
|
|
4169
|
+
`[dispatch-audit] notifyTaskDone \u2192 parent coordinator (session=${key})
|
|
4170
|
+
`
|
|
4171
|
+
);
|
|
4172
|
+
}
|
|
3363
4173
|
} catch {
|
|
3364
4174
|
}
|
|
3365
4175
|
}
|
|
@@ -3380,16 +4190,91 @@ var init_tasks_notify = __esm({
|
|
|
3380
4190
|
}
|
|
3381
4191
|
});
|
|
3382
4192
|
|
|
4193
|
+
// src/lib/embedder.ts
|
|
4194
|
+
var embedder_exports = {};
|
|
4195
|
+
__export(embedder_exports, {
|
|
4196
|
+
disposeEmbedder: () => disposeEmbedder,
|
|
4197
|
+
embed: () => embed,
|
|
4198
|
+
embedDirect: () => embedDirect,
|
|
4199
|
+
getEmbedder: () => getEmbedder
|
|
4200
|
+
});
|
|
4201
|
+
async function getEmbedder() {
|
|
4202
|
+
const ok = await connectEmbedDaemon();
|
|
4203
|
+
if (!ok) {
|
|
4204
|
+
throw new Error(
|
|
4205
|
+
"Could not connect to embedding daemon. Ensure the model is installed (run /exe-setup)."
|
|
4206
|
+
);
|
|
4207
|
+
}
|
|
4208
|
+
}
|
|
4209
|
+
async function embed(text) {
|
|
4210
|
+
const priority = process.env.EXE_EMBED_PRIORITY === "low" ? "low" : "high";
|
|
4211
|
+
const vector = await embedViaClient(text, priority);
|
|
4212
|
+
if (!vector) {
|
|
4213
|
+
throw new Error(
|
|
4214
|
+
"Embedding failed: daemon unavailable. Run /exe-setup to verify model installation."
|
|
4215
|
+
);
|
|
4216
|
+
}
|
|
4217
|
+
if (vector.length !== EMBEDDING_DIM) {
|
|
4218
|
+
throw new Error(
|
|
4219
|
+
`Embedding dimension mismatch: expected ${EMBEDDING_DIM}, got ${vector.length}. Ensure the correct Jina v5-small Q4_K_M GGUF model is installed.`
|
|
4220
|
+
);
|
|
4221
|
+
}
|
|
4222
|
+
return vector;
|
|
4223
|
+
}
|
|
4224
|
+
async function disposeEmbedder() {
|
|
4225
|
+
disconnectClient();
|
|
4226
|
+
}
|
|
4227
|
+
async function embedDirect(text) {
|
|
4228
|
+
const llamaCpp = await import("node-llama-cpp");
|
|
4229
|
+
const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
4230
|
+
const { existsSync: existsSync16 } = await import("fs");
|
|
4231
|
+
const path20 = await import("path");
|
|
4232
|
+
const modelPath = path20.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
|
|
4233
|
+
if (!existsSync16(modelPath)) {
|
|
4234
|
+
throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
|
|
4235
|
+
}
|
|
4236
|
+
const llama = await llamaCpp.getLlama();
|
|
4237
|
+
const model = await llama.loadModel({ modelPath });
|
|
4238
|
+
const context = await model.createEmbeddingContext();
|
|
4239
|
+
try {
|
|
4240
|
+
const embedding = await context.getEmbeddingFor(text);
|
|
4241
|
+
const vector = Array.from(embedding.vector);
|
|
4242
|
+
if (vector.length !== EMBEDDING_DIM) {
|
|
4243
|
+
throw new Error(
|
|
4244
|
+
`Embedding dimension mismatch: expected ${EMBEDDING_DIM}, got ${vector.length}.`
|
|
4245
|
+
);
|
|
4246
|
+
}
|
|
4247
|
+
return vector;
|
|
4248
|
+
} finally {
|
|
4249
|
+
await context.dispose();
|
|
4250
|
+
await model.dispose();
|
|
4251
|
+
}
|
|
4252
|
+
}
|
|
4253
|
+
var init_embedder = __esm({
|
|
4254
|
+
"src/lib/embedder.ts"() {
|
|
4255
|
+
"use strict";
|
|
4256
|
+
init_memory();
|
|
4257
|
+
init_exe_daemon_client();
|
|
4258
|
+
}
|
|
4259
|
+
});
|
|
4260
|
+
|
|
3383
4261
|
// src/lib/behaviors.ts
|
|
3384
|
-
import
|
|
4262
|
+
import crypto4 from "crypto";
|
|
3385
4263
|
async function storeBehavior(opts) {
|
|
3386
4264
|
const client = getClient();
|
|
3387
|
-
const id =
|
|
4265
|
+
const id = crypto4.randomUUID();
|
|
3388
4266
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4267
|
+
let vector = null;
|
|
4268
|
+
try {
|
|
4269
|
+
const { embed: embed2 } = await Promise.resolve().then(() => (init_embedder(), embedder_exports));
|
|
4270
|
+
const vec = await embed2(opts.content);
|
|
4271
|
+
vector = new Float32Array(vec);
|
|
4272
|
+
} catch {
|
|
4273
|
+
}
|
|
3389
4274
|
await client.execute({
|
|
3390
|
-
sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, priority, content, active, created_at, updated_at)
|
|
3391
|
-
VALUES (?, ?, ?, ?, ?, ?, 1, ?, ?)`,
|
|
3392
|
-
args: [id, opts.agentId, opts.projectName ?? null, opts.domain ?? null, opts.priority ?? "p1", opts.content, now, now]
|
|
4275
|
+
sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, priority, content, active, created_at, updated_at, vector)
|
|
4276
|
+
VALUES (?, ?, ?, ?, ?, ?, 1, ?, ?, ?)`,
|
|
4277
|
+
args: [id, opts.agentId, opts.projectName ?? null, opts.domain ?? null, opts.priority ?? "p1", opts.content, now, now, vector ? vector.buffer : null]
|
|
3393
4278
|
});
|
|
3394
4279
|
return id;
|
|
3395
4280
|
}
|
|
@@ -3413,7 +4298,7 @@ __export(skill_learning_exports, {
|
|
|
3413
4298
|
storeTrajectory: () => storeTrajectory,
|
|
3414
4299
|
sweepTrajectories: () => sweepTrajectories
|
|
3415
4300
|
});
|
|
3416
|
-
import
|
|
4301
|
+
import crypto5 from "crypto";
|
|
3417
4302
|
async function extractTrajectory(taskId, agentId) {
|
|
3418
4303
|
const client = getClient();
|
|
3419
4304
|
const result = await client.execute({
|
|
@@ -3442,11 +4327,11 @@ async function extractTrajectory(taskId, agentId) {
|
|
|
3442
4327
|
return signature;
|
|
3443
4328
|
}
|
|
3444
4329
|
function hashSignature(signature) {
|
|
3445
|
-
return
|
|
4330
|
+
return crypto5.createHash("sha256").update(signature.join("|")).digest("hex").slice(0, 16);
|
|
3446
4331
|
}
|
|
3447
4332
|
async function storeTrajectory(opts) {
|
|
3448
4333
|
const client = getClient();
|
|
3449
|
-
const id =
|
|
4334
|
+
const id = crypto5.randomUUID();
|
|
3450
4335
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3451
4336
|
const signatureHash = hashSignature(opts.signature);
|
|
3452
4337
|
await client.execute({
|
|
@@ -3691,20 +4576,20 @@ var init_skill_learning = __esm({
|
|
|
3691
4576
|
});
|
|
3692
4577
|
|
|
3693
4578
|
// src/lib/tasks.ts
|
|
3694
|
-
import
|
|
3695
|
-
import { writeFileSync as
|
|
4579
|
+
import path16 from "path";
|
|
4580
|
+
import { writeFileSync as writeFileSync7, mkdirSync as mkdirSync5, unlinkSync as unlinkSync5 } from "fs";
|
|
3696
4581
|
async function updateTask(input) {
|
|
3697
4582
|
const { row, taskFile, now, taskId } = await updateTaskStatus(input);
|
|
3698
4583
|
try {
|
|
3699
4584
|
const agent = String(row.assigned_to);
|
|
3700
|
-
const cacheDir =
|
|
3701
|
-
const cachePath =
|
|
4585
|
+
const cacheDir = path16.join(EXE_AI_DIR, "session-cache");
|
|
4586
|
+
const cachePath = path16.join(cacheDir, `current-task-${agent}.json`);
|
|
3702
4587
|
if (input.status === "in_progress") {
|
|
3703
4588
|
mkdirSync5(cacheDir, { recursive: true });
|
|
3704
|
-
|
|
4589
|
+
writeFileSync7(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
|
|
3705
4590
|
} else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled" || input.status === "closed") {
|
|
3706
4591
|
try {
|
|
3707
|
-
|
|
4592
|
+
unlinkSync5(cachePath);
|
|
3708
4593
|
} catch {
|
|
3709
4594
|
}
|
|
3710
4595
|
}
|
|
@@ -3857,12 +4742,12 @@ __export(shard_manager_exports, {
|
|
|
3857
4742
|
listShards: () => listShards,
|
|
3858
4743
|
shardExists: () => shardExists
|
|
3859
4744
|
});
|
|
3860
|
-
import
|
|
3861
|
-
import { existsSync as
|
|
4745
|
+
import path18 from "path";
|
|
4746
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync6, readdirSync as readdirSync4 } from "fs";
|
|
3862
4747
|
import { createClient as createClient2 } from "@libsql/client";
|
|
3863
4748
|
function initShardManager(encryptionKey) {
|
|
3864
4749
|
_encryptionKey = encryptionKey;
|
|
3865
|
-
if (!
|
|
4750
|
+
if (!existsSync15(SHARDS_DIR)) {
|
|
3866
4751
|
mkdirSync6(SHARDS_DIR, { recursive: true });
|
|
3867
4752
|
}
|
|
3868
4753
|
_shardingEnabled = true;
|
|
@@ -3892,7 +4777,7 @@ function getShardClient(projectName) {
|
|
|
3892
4777
|
while (_shards.size >= MAX_OPEN_SHARDS) {
|
|
3893
4778
|
evictLRU();
|
|
3894
4779
|
}
|
|
3895
|
-
const dbPath =
|
|
4780
|
+
const dbPath = path18.join(SHARDS_DIR, `${safeName}.db`);
|
|
3896
4781
|
const client = createClient2({
|
|
3897
4782
|
url: `file:${dbPath}`,
|
|
3898
4783
|
encryptionKey: _encryptionKey
|
|
@@ -3903,10 +4788,10 @@ function getShardClient(projectName) {
|
|
|
3903
4788
|
}
|
|
3904
4789
|
function shardExists(projectName) {
|
|
3905
4790
|
const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
3906
|
-
return
|
|
4791
|
+
return existsSync15(path18.join(SHARDS_DIR, `${safeName}.db`));
|
|
3907
4792
|
}
|
|
3908
4793
|
function listShards() {
|
|
3909
|
-
if (!
|
|
4794
|
+
if (!existsSync15(SHARDS_DIR)) return [];
|
|
3910
4795
|
return readdirSync4(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
3911
4796
|
}
|
|
3912
4797
|
async function ensureShardSchema(client) {
|
|
@@ -4153,7 +5038,7 @@ var init_shard_manager = __esm({
|
|
|
4153
5038
|
"src/lib/shard-manager.ts"() {
|
|
4154
5039
|
"use strict";
|
|
4155
5040
|
init_config();
|
|
4156
|
-
SHARDS_DIR =
|
|
5041
|
+
SHARDS_DIR = path18.join(EXE_AI_DIR, "shards");
|
|
4157
5042
|
SHARD_IDLE_MS = 5 * 60 * 1e3;
|
|
4158
5043
|
MAX_OPEN_SHARDS = 10;
|
|
4159
5044
|
EVICTION_INTERVAL_MS = 60 * 1e3;
|
|
@@ -4273,6 +5158,68 @@ var init_platform_procedures = __esm({
|
|
|
4273
5158
|
domain: "architecture",
|
|
4274
5159
|
priority: "p0",
|
|
4275
5160
|
content: "Tasks live in the DB. Intercom (tmux send-keys) is fire-and-forget \u2014 it may fail, get garbled, or arrive mid-work. Never rely on intercom for task delivery. The UserPromptSubmit hook checks the DB for new tasks on every prompt. Your operating procedures step 7 says check for next work. The daemon nudges idle agents as a speedup. If you have no tasks, you found them all."
|
|
5161
|
+
},
|
|
5162
|
+
// --- MCP is the ONLY data interface ---
|
|
5163
|
+
{
|
|
5164
|
+
title: "MCP disconnect \u2014 ask the user, never work around it",
|
|
5165
|
+
domain: "workflow",
|
|
5166
|
+
priority: "p0",
|
|
5167
|
+
content: "If MCP tools are unavailable, disconnected, or returning connection errors: STOP. Tell the user clearly: 'MCP server is disconnected. Please run /mcp to reconnect.' Do NOT attempt workarounds \u2014 no raw Node imports, no direct DB access, no CLI hacks, no daemon socket calls. MCP is the ONLY data interface. Working around it wastes time, hits bundling issues, and bypasses the contract boundary. Ask once, wait, proceed when reconnected."
|
|
5168
|
+
},
|
|
5169
|
+
// --- MCP Tool Catalog (Layer 0 — every agent knows what tools exist) ---
|
|
5170
|
+
{
|
|
5171
|
+
title: "MCP tools \u2014 memory and search",
|
|
5172
|
+
domain: "tool-use",
|
|
5173
|
+
priority: "p1",
|
|
5174
|
+
content: "recall_my_memory: search your own memories (semantic + FTS). ask_team_memory: search a colleague's memories by agent name. store_memory: persist a memory (decisions, summaries, context). commit_memory: high-importance memory that survives consolidation. search_everything: unified search across memories, tasks, entities, conversations. get_session_context: temporal window of memories around a timestamp. consolidate_memories: merge duplicate/related memories into insights. get_memory_cardinality: count memories per agent (health check)."
|
|
5175
|
+
},
|
|
5176
|
+
{
|
|
5177
|
+
title: "MCP tools \u2014 task orchestration",
|
|
5178
|
+
domain: "tool-use",
|
|
5179
|
+
priority: "p1",
|
|
5180
|
+
content: "create_task: dispatch work to an employee (auto-spawns session). The ONLY dispatch path. list_tasks: query tasks by status, assignee, project. get_task: fetch full task details by ID. update_task: change status (in_progress, done, blocked, cancelled) + add result summary. close_task: finalize a reviewed task (COO only). checkpoint_task: save progress state for crash recovery. resume_employee: re-spawn an employee session for an existing task."
|
|
5181
|
+
},
|
|
5182
|
+
{
|
|
5183
|
+
title: "MCP tools \u2014 knowledge graph (GraphRAG)",
|
|
5184
|
+
domain: "tool-use",
|
|
5185
|
+
priority: "p1",
|
|
5186
|
+
content: "query_relationships: find connections between entities in the knowledge graph. get_entity_neighbors: explore an entity's direct connections. get_hot_entities: find most-referenced entities (trending topics). get_graph_stats: graph health \u2014 entity/relationship counts, density. export_graph: export graph data for visualization. merge_entities: deduplicate entities (alias resolution). find_similar_trajectories: match tool-call patterns to past task solutions."
|
|
5187
|
+
},
|
|
5188
|
+
{
|
|
5189
|
+
title: "MCP tools \u2014 identity, behavior, and decisions",
|
|
5190
|
+
domain: "tool-use",
|
|
5191
|
+
priority: "p1",
|
|
5192
|
+
content: "get_identity: read an agent's exe.md (Layer 1 identity). update_identity: write an agent's exe.md. Identity > behavior \u2014 use for permanent rules. store_behavior: record a correction or pattern for an agent (Layer 2 expertise). list_behaviors: view an agent's active behaviors. deactivate_behavior: soft-delete a stale or conflicting behavior. store_decision: record an ADR (architectural decision record). get_decision: retrieve a past decision by query."
|
|
5193
|
+
},
|
|
5194
|
+
{
|
|
5195
|
+
title: "MCP tools \u2014 communication and messaging",
|
|
5196
|
+
domain: "tool-use",
|
|
5197
|
+
priority: "p1",
|
|
5198
|
+
content: "send_message: send supplementary context to another agent (NOT for actionable work \u2014 use create_task). acknowledge_messages: mark messages as read. send_whatsapp: send WhatsApp message via gateway (customer-facing alerts). query_conversations: search ingested conversations across all channels (WhatsApp, email, etc.)."
|
|
5199
|
+
},
|
|
5200
|
+
{
|
|
5201
|
+
title: "MCP tools \u2014 wiki, documents, and content",
|
|
5202
|
+
domain: "tool-use",
|
|
5203
|
+
priority: "p1",
|
|
5204
|
+
content: "create_wiki_page: create a wiki page in exe-wiki. list_wiki_pages: browse wiki pages. get_wiki_page: read a wiki page. update_wiki_page: edit a wiki page. ingest_document: import a file (PDF, MD, etc.) into memory as chunks. list_documents: browse ingested documents by workspace. purge_document: remove a document and its memory chunks. set_document_importance: adjust chunk importance scores. rerank_documents: re-score document relevance for a query."
|
|
5205
|
+
},
|
|
5206
|
+
{
|
|
5207
|
+
title: "MCP tools \u2014 system, operations, and admin",
|
|
5208
|
+
domain: "tool-use",
|
|
5209
|
+
priority: "p1",
|
|
5210
|
+
content: "get_agent_spend: token usage per agent/session (cost tracking). list_agent_sessions: view agent session history. get_session_kills: audit log of killed sessions. get_daemon_health: check exed daemon status. get_auto_wake_status: daemon auto-wake configuration. get_worker_gate: check worker deployment gates. run_memory_audit: health check \u2014 duplicates, null vectors, orphaned rows. run_consolidation: trigger sleep-time memory consolidation. cloud_sync: force a cloud sync cycle. backup_vps: trigger VPS backup."
|
|
5211
|
+
},
|
|
5212
|
+
{
|
|
5213
|
+
title: "MCP tools \u2014 config, licensing, and team",
|
|
5214
|
+
domain: "tool-use",
|
|
5215
|
+
priority: "p1",
|
|
5216
|
+
content: "set_agent_config: view/change per-agent runtime and model settings. list_employees: view the employee roster. add_person: add a person to the CRM contacts roster. list_people: browse CRM contacts. get_person: fetch contact details. get_license_status: check license validity. create_license: generate a new license key (admin). list_licenses: view all issued licenses. activate_license: activate a license on a device."
|
|
5217
|
+
},
|
|
5218
|
+
{
|
|
5219
|
+
title: "MCP tools \u2014 advanced (triggers, skills, orchestration)",
|
|
5220
|
+
domain: "tool-use",
|
|
5221
|
+
priority: "p1",
|
|
5222
|
+
content: "create_trigger: set up a scheduled recurring agent job (cron). list_triggers: view active triggers. load_skill: load a slash-command skill dynamically. apply_starter_pack: import a pre-built behavior + identity pack for a role. export_orchestration: export full org state (tasks, behaviors, identities) as portable JSON. import_orchestration: import org state into a new instance. deploy_client: deploy a customer client instance. query_company_brain: unified RAG query across all company knowledge. create_reminder: set a text reminder (shown in boot brief). list_reminders: view pending reminders. complete_reminder: mark a reminder done. global_procedure: manage Layer 0 procedures (actions: store, list, deactivate). Legacy aliases: store_global_procedure, list_global_procedures, deactivate_global_procedure."
|
|
4276
5223
|
}
|
|
4277
5224
|
];
|
|
4278
5225
|
PLATFORM_PROCEDURE_TITLES = new Set(
|
|
@@ -4289,7 +5236,7 @@ __export(global_procedures_exports, {
|
|
|
4289
5236
|
loadGlobalProcedures: () => loadGlobalProcedures,
|
|
4290
5237
|
storeGlobalProcedure: () => storeGlobalProcedure
|
|
4291
5238
|
});
|
|
4292
|
-
import { randomUUID as
|
|
5239
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
4293
5240
|
async function loadGlobalProcedures() {
|
|
4294
5241
|
const client = getClient();
|
|
4295
5242
|
const result = await client.execute({
|
|
@@ -4318,7 +5265,7 @@ ${sections.join("\n\n")}
|
|
|
4318
5265
|
`;
|
|
4319
5266
|
}
|
|
4320
5267
|
async function storeGlobalProcedure(input) {
|
|
4321
|
-
const id =
|
|
5268
|
+
const id = randomUUID3();
|
|
4322
5269
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4323
5270
|
const client = getClient();
|
|
4324
5271
|
await client.execute({
|
|
@@ -4358,21 +5305,22 @@ init_tasks();
|
|
|
4358
5305
|
import { spawnSync } from "child_process";
|
|
4359
5306
|
|
|
4360
5307
|
// src/lib/store.ts
|
|
4361
|
-
|
|
5308
|
+
init_memory();
|
|
4362
5309
|
init_database();
|
|
5310
|
+
import { createHash } from "crypto";
|
|
4363
5311
|
|
|
4364
5312
|
// src/lib/keychain.ts
|
|
4365
5313
|
import { readFile as readFile4, writeFile as writeFile5, unlink, mkdir as mkdir4, chmod as chmod2 } from "fs/promises";
|
|
4366
|
-
import { existsSync as
|
|
4367
|
-
import
|
|
4368
|
-
import
|
|
5314
|
+
import { existsSync as existsSync14 } from "fs";
|
|
5315
|
+
import path17 from "path";
|
|
5316
|
+
import os11 from "os";
|
|
4369
5317
|
var SERVICE = "exe-mem";
|
|
4370
5318
|
var ACCOUNT = "master-key";
|
|
4371
5319
|
function getKeyDir() {
|
|
4372
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
5320
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path17.join(os11.homedir(), ".exe-os");
|
|
4373
5321
|
}
|
|
4374
5322
|
function getKeyPath() {
|
|
4375
|
-
return
|
|
5323
|
+
return path17.join(getKeyDir(), "master.key");
|
|
4376
5324
|
}
|
|
4377
5325
|
async function tryKeytar() {
|
|
4378
5326
|
try {
|
|
@@ -4393,9 +5341,9 @@ async function getMasterKey() {
|
|
|
4393
5341
|
}
|
|
4394
5342
|
}
|
|
4395
5343
|
const keyPath = getKeyPath();
|
|
4396
|
-
if (!
|
|
5344
|
+
if (!existsSync14(keyPath)) {
|
|
4397
5345
|
process.stderr.write(
|
|
4398
|
-
`[keychain] Key not found at ${keyPath} (HOME=${
|
|
5346
|
+
`[keychain] Key not found at ${keyPath} (HOME=${os11.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
4399
5347
|
`
|
|
4400
5348
|
);
|
|
4401
5349
|
return null;
|
|
@@ -4474,6 +5422,11 @@ async function initStore(options) {
|
|
|
4474
5422
|
encryptionKey: hexKey
|
|
4475
5423
|
});
|
|
4476
5424
|
await retryOnBusy2(() => ensureSchema(), "ensureSchema");
|
|
5425
|
+
try {
|
|
5426
|
+
const { initDaemonClient: initDaemonClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
5427
|
+
await initDaemonClient2();
|
|
5428
|
+
} catch {
|
|
5429
|
+
}
|
|
4477
5430
|
if (!options?.lightweight) {
|
|
4478
5431
|
try {
|
|
4479
5432
|
const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
@@ -4495,7 +5448,7 @@ async function initStore(options) {
|
|
|
4495
5448
|
}
|
|
4496
5449
|
|
|
4497
5450
|
// src/lib/codex-stop-finalizer.ts
|
|
4498
|
-
import
|
|
5451
|
+
import path19 from "path";
|
|
4499
5452
|
var SECRET_PATH_PATTERNS = [
|
|
4500
5453
|
/(^|\/)\.env($|\.)/i,
|
|
4501
5454
|
/(^|\/)[^/]*credentials[^/]*(\/|$)/i,
|
|
@@ -4538,7 +5491,7 @@ function parseGitStatusPorcelainZ(output) {
|
|
|
4538
5491
|
}
|
|
4539
5492
|
function isSecretLikePath(filePath) {
|
|
4540
5493
|
const normalized = normalizePath(filePath);
|
|
4541
|
-
const basename =
|
|
5494
|
+
const basename = path19.basename(normalized);
|
|
4542
5495
|
if (basename === ".env" || basename.startsWith(".env.")) {
|
|
4543
5496
|
return true;
|
|
4544
5497
|
}
|