@askexenow/exe-os 0.8.32 → 0.8.36
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 +332 -348
- package/dist/bin/backfill-responses.js +72 -12
- package/dist/bin/backfill-vectors.js +72 -12
- package/dist/bin/cleanup-stale-review-tasks.js +63 -3
- package/dist/bin/cli.js +1518 -1122
- package/dist/bin/exe-agent.js +4 -4
- package/dist/bin/exe-assign.js +80 -18
- package/dist/bin/exe-boot.js +408 -89
- package/dist/bin/exe-call.js +83 -24
- package/dist/bin/exe-dispatch.js +18 -10
- package/dist/bin/exe-doctor.js +63 -3
- package/dist/bin/exe-export-behaviors.js +64 -3
- package/dist/bin/exe-forget.js +69 -4
- package/dist/bin/exe-gateway.js +121 -36
- package/dist/bin/exe-heartbeat.js +77 -13
- package/dist/bin/exe-kill.js +64 -3
- package/dist/bin/exe-launch-agent.js +162 -35
- package/dist/bin/exe-link.js +946 -0
- package/dist/bin/exe-new-employee.js +121 -36
- package/dist/bin/exe-pending-messages.js +72 -7
- package/dist/bin/exe-pending-notifications.js +63 -3
- package/dist/bin/exe-pending-reviews.js +75 -10
- package/dist/bin/exe-rename.js +1287 -0
- package/dist/bin/exe-review.js +64 -4
- package/dist/bin/exe-search.js +79 -13
- package/dist/bin/exe-session-cleanup.js +91 -26
- package/dist/bin/exe-status.js +64 -4
- package/dist/bin/exe-team.js +64 -4
- package/dist/bin/git-sweep.js +71 -4
- package/dist/bin/graph-backfill.js +64 -3
- package/dist/bin/graph-export.js +64 -3
- package/dist/bin/install.js +3 -3
- package/dist/bin/scan-tasks.js +71 -4
- package/dist/bin/setup.js +156 -38
- package/dist/bin/shard-migrate.js +64 -3
- package/dist/bin/wiki-sync.js +64 -3
- package/dist/gateway/index.js +122 -37
- package/dist/hooks/bug-report-worker.js +209 -23
- package/dist/hooks/commit-complete.js +71 -4
- package/dist/hooks/error-recall.js +79 -13
- package/dist/hooks/ingest-worker.js +129 -43
- package/dist/hooks/instructions-loaded.js +71 -4
- package/dist/hooks/notification.js +71 -4
- package/dist/hooks/post-compact.js +71 -4
- package/dist/hooks/pre-compact.js +71 -4
- package/dist/hooks/pre-tool-use.js +413 -194
- package/dist/hooks/prompt-ingest-worker.js +82 -22
- package/dist/hooks/prompt-submit.js +103 -37
- package/dist/hooks/response-ingest-worker.js +87 -22
- package/dist/hooks/session-end.js +71 -4
- package/dist/hooks/session-start.js +79 -13
- package/dist/hooks/stop.js +71 -4
- package/dist/hooks/subagent-stop.js +71 -4
- package/dist/hooks/summary-worker.js +303 -50
- package/dist/index.js +134 -46
- package/dist/lib/cloud-sync.js +209 -15
- package/dist/lib/consolidation.js +4 -4
- package/dist/lib/database.js +64 -2
- package/dist/lib/device-registry.js +70 -3
- package/dist/lib/employee-templates.js +48 -22
- package/dist/lib/employees.js +34 -1
- package/dist/lib/exe-daemon.js +136 -53
- package/dist/lib/hybrid-search.js +79 -13
- package/dist/lib/identity-templates.js +57 -6
- package/dist/lib/identity.js +3 -3
- package/dist/lib/messaging.js +22 -14
- package/dist/lib/reminders.js +3 -3
- package/dist/lib/schedules.js +63 -3
- package/dist/lib/skill-learning.js +3 -3
- package/dist/lib/status-brief.js +63 -5
- package/dist/lib/store.js +64 -3
- package/dist/lib/task-router.js +4 -2
- package/dist/lib/tasks.js +48 -21
- package/dist/lib/tmux-routing.js +47 -20
- package/dist/mcp/server.js +727 -58
- 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 +151 -24
- package/dist/mcp/tools/deactivate-behavior.js +3 -3
- package/dist/mcp/tools/list-reminders.js +3 -3
- package/dist/mcp/tools/list-tasks.js +17 -8
- package/dist/mcp/tools/send-message.js +24 -16
- package/dist/mcp/tools/update-task.js +25 -16
- package/dist/runtime/index.js +112 -24
- package/dist/tui/App.js +139 -36
- package/package.json +6 -2
- package/src/commands/exe/rename.md +12 -0
package/dist/lib/exe-daemon.js
CHANGED
|
@@ -277,6 +277,61 @@ var init_memory = __esm({
|
|
|
277
277
|
}
|
|
278
278
|
});
|
|
279
279
|
|
|
280
|
+
// src/lib/db-retry.ts
|
|
281
|
+
function isBusyError(err) {
|
|
282
|
+
if (err instanceof Error) {
|
|
283
|
+
const msg = err.message.toLowerCase();
|
|
284
|
+
return msg.includes("sqlite_busy") || msg.includes("database is locked");
|
|
285
|
+
}
|
|
286
|
+
return false;
|
|
287
|
+
}
|
|
288
|
+
function delay(ms) {
|
|
289
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
290
|
+
}
|
|
291
|
+
async function retryOnBusy(fn, label) {
|
|
292
|
+
let lastError;
|
|
293
|
+
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
294
|
+
try {
|
|
295
|
+
return await fn();
|
|
296
|
+
} catch (err) {
|
|
297
|
+
lastError = err;
|
|
298
|
+
if (!isBusyError(err) || attempt === MAX_RETRIES) {
|
|
299
|
+
throw err;
|
|
300
|
+
}
|
|
301
|
+
const backoff = BASE_DELAY_MS * Math.pow(2, attempt);
|
|
302
|
+
const jitter = Math.floor(Math.random() * MAX_JITTER_MS);
|
|
303
|
+
process.stderr.write(
|
|
304
|
+
`[exe-os] SQLITE_BUSY ${label} retry ${attempt + 1}/${MAX_RETRIES} \u2014 waiting ${backoff + jitter}ms
|
|
305
|
+
`
|
|
306
|
+
);
|
|
307
|
+
await delay(backoff + jitter);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
throw lastError;
|
|
311
|
+
}
|
|
312
|
+
function wrapWithRetry(client) {
|
|
313
|
+
return new Proxy(client, {
|
|
314
|
+
get(target, prop, receiver) {
|
|
315
|
+
if (prop === "execute") {
|
|
316
|
+
return (sql) => retryOnBusy(() => target.execute(sql), "execute");
|
|
317
|
+
}
|
|
318
|
+
if (prop === "batch") {
|
|
319
|
+
return (stmts) => retryOnBusy(() => target.batch(stmts), "batch");
|
|
320
|
+
}
|
|
321
|
+
return Reflect.get(target, prop, receiver);
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
var MAX_RETRIES, BASE_DELAY_MS, MAX_JITTER_MS;
|
|
326
|
+
var init_db_retry = __esm({
|
|
327
|
+
"src/lib/db-retry.ts"() {
|
|
328
|
+
"use strict";
|
|
329
|
+
MAX_RETRIES = 3;
|
|
330
|
+
BASE_DELAY_MS = 200;
|
|
331
|
+
MAX_JITTER_MS = 300;
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
|
|
280
335
|
// src/lib/database.ts
|
|
281
336
|
var database_exports = {};
|
|
282
337
|
__export(database_exports, {
|
|
@@ -284,6 +339,7 @@ __export(database_exports, {
|
|
|
284
339
|
disposeTurso: () => disposeTurso,
|
|
285
340
|
ensureSchema: () => ensureSchema,
|
|
286
341
|
getClient: () => getClient,
|
|
342
|
+
getRawClient: () => getRawClient,
|
|
287
343
|
initDatabase: () => initDatabase,
|
|
288
344
|
initTurso: () => initTurso,
|
|
289
345
|
isInitialized: () => isInitialized
|
|
@@ -293,6 +349,7 @@ async function initDatabase(config) {
|
|
|
293
349
|
if (_client) {
|
|
294
350
|
_client.close();
|
|
295
351
|
_client = null;
|
|
352
|
+
_resilientClient = null;
|
|
296
353
|
}
|
|
297
354
|
const opts = {
|
|
298
355
|
url: `file:${config.dbPath}`
|
|
@@ -301,20 +358,27 @@ async function initDatabase(config) {
|
|
|
301
358
|
opts.encryptionKey = config.encryptionKey;
|
|
302
359
|
}
|
|
303
360
|
_client = createClient(opts);
|
|
361
|
+
_resilientClient = wrapWithRetry(_client);
|
|
304
362
|
}
|
|
305
363
|
function isInitialized() {
|
|
306
364
|
return _client !== null;
|
|
307
365
|
}
|
|
308
366
|
function getClient() {
|
|
367
|
+
if (!_resilientClient) {
|
|
368
|
+
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
369
|
+
}
|
|
370
|
+
return _resilientClient;
|
|
371
|
+
}
|
|
372
|
+
function getRawClient() {
|
|
309
373
|
if (!_client) {
|
|
310
374
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
311
375
|
}
|
|
312
376
|
return _client;
|
|
313
377
|
}
|
|
314
378
|
async function ensureSchema() {
|
|
315
|
-
const client =
|
|
379
|
+
const client = getRawClient();
|
|
316
380
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
317
|
-
await client.execute("PRAGMA busy_timeout =
|
|
381
|
+
await client.execute("PRAGMA busy_timeout = 30000");
|
|
318
382
|
try {
|
|
319
383
|
await client.execute("PRAGMA libsql_vector_search_ef = 128");
|
|
320
384
|
} catch {
|
|
@@ -1107,13 +1171,16 @@ async function disposeDatabase() {
|
|
|
1107
1171
|
if (_client) {
|
|
1108
1172
|
_client.close();
|
|
1109
1173
|
_client = null;
|
|
1174
|
+
_resilientClient = null;
|
|
1110
1175
|
}
|
|
1111
1176
|
}
|
|
1112
|
-
var _client, initTurso, disposeTurso;
|
|
1177
|
+
var _client, _resilientClient, initTurso, disposeTurso;
|
|
1113
1178
|
var init_database = __esm({
|
|
1114
1179
|
"src/lib/database.ts"() {
|
|
1115
1180
|
"use strict";
|
|
1181
|
+
init_db_retry();
|
|
1116
1182
|
_client = null;
|
|
1183
|
+
_resilientClient = null;
|
|
1117
1184
|
initTurso = initDatabase;
|
|
1118
1185
|
disposeTurso = disposeDatabase;
|
|
1119
1186
|
}
|
|
@@ -1323,7 +1390,7 @@ function listShards() {
|
|
|
1323
1390
|
}
|
|
1324
1391
|
async function ensureShardSchema(client) {
|
|
1325
1392
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
1326
|
-
await client.execute("PRAGMA busy_timeout =
|
|
1393
|
+
await client.execute("PRAGMA busy_timeout = 30000");
|
|
1327
1394
|
try {
|
|
1328
1395
|
await client.execute("PRAGMA libsql_vector_search_ef = 128");
|
|
1329
1396
|
} catch {
|
|
@@ -2296,8 +2363,8 @@ function drainQueue(isSessionBusy2, sendKeys) {
|
|
|
2296
2363
|
} catch {
|
|
2297
2364
|
}
|
|
2298
2365
|
item.attempts++;
|
|
2299
|
-
if (item.attempts >=
|
|
2300
|
-
logQueue(`FAILED \u2192 ${item.targetSession} (${
|
|
2366
|
+
if (item.attempts >= MAX_RETRIES2) {
|
|
2367
|
+
logQueue(`FAILED \u2192 ${item.targetSession} (${MAX_RETRIES2} retries exhausted, reason: ${item.reason})`);
|
|
2301
2368
|
failed++;
|
|
2302
2369
|
continue;
|
|
2303
2370
|
}
|
|
@@ -2330,12 +2397,12 @@ function logQueue(msg) {
|
|
|
2330
2397
|
} catch {
|
|
2331
2398
|
}
|
|
2332
2399
|
}
|
|
2333
|
-
var QUEUE_PATH,
|
|
2400
|
+
var QUEUE_PATH, MAX_RETRIES2, TTL_MS, INTERCOM_LOG;
|
|
2334
2401
|
var init_intercom_queue = __esm({
|
|
2335
2402
|
"src/lib/intercom-queue.ts"() {
|
|
2336
2403
|
"use strict";
|
|
2337
2404
|
QUEUE_PATH = path5.join(os3.homedir(), ".exe-os", "intercom-queue.json");
|
|
2338
|
-
|
|
2405
|
+
MAX_RETRIES2 = 5;
|
|
2339
2406
|
TTL_MS = 60 * 60 * 1e3;
|
|
2340
2407
|
INTERCOM_LOG = path5.join(os3.homedir(), ".exe-os", "intercom.log");
|
|
2341
2408
|
}
|
|
@@ -2343,7 +2410,7 @@ var init_intercom_queue = __esm({
|
|
|
2343
2410
|
|
|
2344
2411
|
// src/lib/employees.ts
|
|
2345
2412
|
import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
|
|
2346
|
-
import { existsSync as existsSync6, symlinkSync, readlinkSync } from "fs";
|
|
2413
|
+
import { existsSync as existsSync6, symlinkSync, readlinkSync, readFileSync as readFileSync4 } from "fs";
|
|
2347
2414
|
import { execSync as execSync4 } from "child_process";
|
|
2348
2415
|
import path6 from "path";
|
|
2349
2416
|
async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
@@ -2357,20 +2424,35 @@ async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
|
2357
2424
|
return [];
|
|
2358
2425
|
}
|
|
2359
2426
|
}
|
|
2427
|
+
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
2428
|
+
if (!existsSync6(employeesPath)) return [];
|
|
2429
|
+
try {
|
|
2430
|
+
return JSON.parse(readFileSync4(employeesPath, "utf-8"));
|
|
2431
|
+
} catch {
|
|
2432
|
+
return [];
|
|
2433
|
+
}
|
|
2434
|
+
}
|
|
2360
2435
|
function getEmployee(employees, name) {
|
|
2361
2436
|
return employees.find((e) => e.name.toLowerCase() === name.toLowerCase());
|
|
2362
2437
|
}
|
|
2363
|
-
|
|
2438
|
+
function isMultiInstance(agentName, employees) {
|
|
2439
|
+
const roster = employees ?? loadEmployeesSync();
|
|
2440
|
+
const emp = getEmployee(roster, agentName);
|
|
2441
|
+
if (!emp) return false;
|
|
2442
|
+
return MULTI_INSTANCE_ROLES.has(emp.role.toLowerCase());
|
|
2443
|
+
}
|
|
2444
|
+
var EMPLOYEES_PATH, MULTI_INSTANCE_ROLES;
|
|
2364
2445
|
var init_employees = __esm({
|
|
2365
2446
|
"src/lib/employees.ts"() {
|
|
2366
2447
|
"use strict";
|
|
2367
2448
|
init_config();
|
|
2368
2449
|
EMPLOYEES_PATH = path6.join(EXE_AI_DIR, "exe-employees.json");
|
|
2450
|
+
MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
|
|
2369
2451
|
}
|
|
2370
2452
|
});
|
|
2371
2453
|
|
|
2372
2454
|
// src/lib/license.ts
|
|
2373
|
-
import { readFileSync as
|
|
2455
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync7, mkdirSync as mkdirSync4 } from "fs";
|
|
2374
2456
|
import { randomUUID } from "crypto";
|
|
2375
2457
|
import path7 from "path";
|
|
2376
2458
|
import { jwtVerify, importSPKI } from "jose";
|
|
@@ -2393,12 +2475,12 @@ var init_license = __esm({
|
|
|
2393
2475
|
});
|
|
2394
2476
|
|
|
2395
2477
|
// src/lib/plan-limits.ts
|
|
2396
|
-
import { readFileSync as
|
|
2478
|
+
import { readFileSync as readFileSync6, existsSync as existsSync8 } from "fs";
|
|
2397
2479
|
import path8 from "path";
|
|
2398
2480
|
function getLicenseSync() {
|
|
2399
2481
|
try {
|
|
2400
2482
|
if (!existsSync8(CACHE_PATH2)) return freeLicense();
|
|
2401
|
-
const raw = JSON.parse(
|
|
2483
|
+
const raw = JSON.parse(readFileSync6(CACHE_PATH2, "utf8"));
|
|
2402
2484
|
if (!raw.token || typeof raw.token !== "string") return freeLicense();
|
|
2403
2485
|
const parts = raw.token.split(".");
|
|
2404
2486
|
if (parts.length !== 3) return freeLicense();
|
|
@@ -2437,7 +2519,7 @@ function assertEmployeeLimitSync(rosterPath) {
|
|
|
2437
2519
|
let count = 0;
|
|
2438
2520
|
try {
|
|
2439
2521
|
if (existsSync8(filePath)) {
|
|
2440
|
-
const raw =
|
|
2522
|
+
const raw = readFileSync6(filePath, "utf8");
|
|
2441
2523
|
const employees = JSON.parse(raw);
|
|
2442
2524
|
count = Array.isArray(employees) ? employees.length : 0;
|
|
2443
2525
|
}
|
|
@@ -2475,7 +2557,7 @@ import crypto2 from "crypto";
|
|
|
2475
2557
|
import path9 from "path";
|
|
2476
2558
|
import os4 from "os";
|
|
2477
2559
|
import {
|
|
2478
|
-
readFileSync as
|
|
2560
|
+
readFileSync as readFileSync7,
|
|
2479
2561
|
readdirSync,
|
|
2480
2562
|
unlinkSync,
|
|
2481
2563
|
existsSync as existsSync9,
|
|
@@ -2631,7 +2713,7 @@ import crypto4 from "crypto";
|
|
|
2631
2713
|
import path10 from "path";
|
|
2632
2714
|
import { execSync as execSync5 } from "child_process";
|
|
2633
2715
|
import { mkdir as mkdir4, writeFile as writeFile4, appendFile } from "fs/promises";
|
|
2634
|
-
import { existsSync as existsSync10, readFileSync as
|
|
2716
|
+
import { existsSync as existsSync10, readFileSync as readFileSync8 } from "fs";
|
|
2635
2717
|
async function writeCheckpoint(input) {
|
|
2636
2718
|
const client = getClient();
|
|
2637
2719
|
const row = await resolveTask(client, input.taskId);
|
|
@@ -3008,7 +3090,7 @@ async function ensureGitignoreExe(baseDir) {
|
|
|
3008
3090
|
const gitignorePath = path10.join(baseDir, ".gitignore");
|
|
3009
3091
|
try {
|
|
3010
3092
|
if (existsSync10(gitignorePath)) {
|
|
3011
|
-
const content =
|
|
3093
|
+
const content = readFileSync8(gitignorePath, "utf-8");
|
|
3012
3094
|
if (/^\/?exe\/?$/m.test(content)) return;
|
|
3013
3095
|
await appendFile(gitignorePath, "\n# Employee task assignments (private)\n/exe/\n");
|
|
3014
3096
|
} else {
|
|
@@ -3483,7 +3565,7 @@ async function dispatchTaskToEmployee(input) {
|
|
|
3483
3565
|
} else {
|
|
3484
3566
|
const projectDir = input.projectDir ?? process.cwd();
|
|
3485
3567
|
const result = ensureEmployee(input.assignedTo, exeSession, projectDir, {
|
|
3486
|
-
autoInstance: input.assignedTo
|
|
3568
|
+
autoInstance: isMultiInstance(input.assignedTo)
|
|
3487
3569
|
});
|
|
3488
3570
|
if (result.status === "failed") {
|
|
3489
3571
|
process.stderr.write(
|
|
@@ -3518,6 +3600,7 @@ var init_tasks_notify = __esm({
|
|
|
3518
3600
|
init_session_key();
|
|
3519
3601
|
init_notifications();
|
|
3520
3602
|
init_transport();
|
|
3603
|
+
init_employees();
|
|
3521
3604
|
}
|
|
3522
3605
|
});
|
|
3523
3606
|
|
|
@@ -4291,7 +4374,7 @@ __export(tmux_routing_exports, {
|
|
|
4291
4374
|
verifyPaneAtCapacity: () => verifyPaneAtCapacity
|
|
4292
4375
|
});
|
|
4293
4376
|
import { execFileSync as execFileSync2, execSync as execSync7 } from "child_process";
|
|
4294
|
-
import { readFileSync as
|
|
4377
|
+
import { readFileSync as readFileSync9, writeFileSync as writeFileSync5, mkdirSync as mkdirSync6, existsSync as existsSync12, appendFileSync } from "fs";
|
|
4295
4378
|
import path15 from "path";
|
|
4296
4379
|
import os5 from "os";
|
|
4297
4380
|
import { fileURLToPath } from "url";
|
|
@@ -4358,7 +4441,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
4358
4441
|
}
|
|
4359
4442
|
function getParentExe(sessionKey) {
|
|
4360
4443
|
try {
|
|
4361
|
-
const data = JSON.parse(
|
|
4444
|
+
const data = JSON.parse(readFileSync9(path15.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
4362
4445
|
return data.parentExe || null;
|
|
4363
4446
|
} catch {
|
|
4364
4447
|
return null;
|
|
@@ -4366,7 +4449,7 @@ function getParentExe(sessionKey) {
|
|
|
4366
4449
|
}
|
|
4367
4450
|
function getDispatchedBy(sessionKey) {
|
|
4368
4451
|
try {
|
|
4369
|
-
const data = JSON.parse(
|
|
4452
|
+
const data = JSON.parse(readFileSync9(
|
|
4370
4453
|
path15.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
|
|
4371
4454
|
"utf8"
|
|
4372
4455
|
));
|
|
@@ -4429,7 +4512,7 @@ async function verifyPaneAtCapacity(sessionName) {
|
|
|
4429
4512
|
function readDebounceState() {
|
|
4430
4513
|
try {
|
|
4431
4514
|
if (!existsSync12(DEBOUNCE_FILE)) return {};
|
|
4432
|
-
return JSON.parse(
|
|
4515
|
+
return JSON.parse(readFileSync9(DEBOUNCE_FILE, "utf8"));
|
|
4433
4516
|
} catch {
|
|
4434
4517
|
return {};
|
|
4435
4518
|
}
|
|
@@ -4629,7 +4712,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4629
4712
|
const claudeJsonPath = path15.join(os5.homedir(), ".claude.json");
|
|
4630
4713
|
let claudeJson = {};
|
|
4631
4714
|
try {
|
|
4632
|
-
claudeJson = JSON.parse(
|
|
4715
|
+
claudeJson = JSON.parse(readFileSync9(claudeJsonPath, "utf8"));
|
|
4633
4716
|
} catch {
|
|
4634
4717
|
}
|
|
4635
4718
|
if (!claudeJson.projects) claudeJson.projects = {};
|
|
@@ -4647,7 +4730,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4647
4730
|
const settingsPath = path15.join(projSettingsDir, "settings.json");
|
|
4648
4731
|
let settings = {};
|
|
4649
4732
|
try {
|
|
4650
|
-
settings = JSON.parse(
|
|
4733
|
+
settings = JSON.parse(readFileSync9(settingsPath, "utf8"));
|
|
4651
4734
|
} catch {
|
|
4652
4735
|
}
|
|
4653
4736
|
const perms = settings.permissions ?? {};
|
|
@@ -5319,7 +5402,7 @@ __export(agent_signals_exports, {
|
|
|
5319
5402
|
hasOpenTasks: () => hasOpenTasks,
|
|
5320
5403
|
hasUnreadInbox: () => hasUnreadInbox
|
|
5321
5404
|
});
|
|
5322
|
-
import { readFileSync as
|
|
5405
|
+
import { readFileSync as readFileSync10, existsSync as existsSync13 } from "fs";
|
|
5323
5406
|
import os6 from "os";
|
|
5324
5407
|
import path16 from "path";
|
|
5325
5408
|
async function hasOpenTasks(client, agentId) {
|
|
@@ -5364,7 +5447,7 @@ async function hasUnreadInbox(client, agentId) {
|
|
|
5364
5447
|
function hadRecentIntercomAck(sessionName, windowMs, nowMs = Date.now(), intercomLog = path16.join(os6.homedir(), ".exe-os", "intercom.log")) {
|
|
5365
5448
|
if (!existsSync13(intercomLog)) return false;
|
|
5366
5449
|
try {
|
|
5367
|
-
const raw =
|
|
5450
|
+
const raw = readFileSync10(intercomLog, "utf8");
|
|
5368
5451
|
const lines = raw.split("\n");
|
|
5369
5452
|
for (let i = lines.length - 1; i >= 0; i--) {
|
|
5370
5453
|
const line = lines[i];
|
|
@@ -5796,11 +5879,11 @@ ${synthesisText}`;
|
|
|
5796
5879
|
}
|
|
5797
5880
|
const insertSql = vector ? `INSERT INTO memories
|
|
5798
5881
|
(id, agent_id, agent_role, session_id, timestamp,
|
|
5799
|
-
tool_name, project_name, has_error, raw_text, vector, version, consolidated)
|
|
5800
|
-
VALUES (?, ?, 'consolidation', 'daemon-consolidation', ?, 'consolidation', ?, 0, ?, vector32(?), 0, 1)` : `INSERT INTO memories
|
|
5882
|
+
tool_name, project_name, has_error, raw_text, vector, version, consolidated, importance)
|
|
5883
|
+
VALUES (?, ?, 'consolidation', 'daemon-consolidation', ?, 'consolidation', ?, 0, ?, vector32(?), 0, 1, 9)` : `INSERT INTO memories
|
|
5801
5884
|
(id, agent_id, agent_role, session_id, timestamp,
|
|
5802
|
-
tool_name, project_name, has_error, raw_text, vector, version, consolidated)
|
|
5803
|
-
VALUES (?, ?, 'consolidation', 'daemon-consolidation', ?, 'consolidation', ?, 0, ?, NULL, 0, 1)`;
|
|
5885
|
+
tool_name, project_name, has_error, raw_text, vector, version, consolidated, importance)
|
|
5886
|
+
VALUES (?, ?, 'consolidation', 'daemon-consolidation', ?, 'consolidation', ?, 0, ?, NULL, 0, 1, 9)`;
|
|
5804
5887
|
const insertArgs = vector ? [consolidatedId, cluster.agentId, now, cluster.projectName, rawText, vectorToBlob(vector)] : [consolidatedId, cluster.agentId, now, cluster.projectName, rawText];
|
|
5805
5888
|
await client.execute({ sql: insertSql, args: insertArgs });
|
|
5806
5889
|
const sourceIds = cluster.memories.map((m) => m.id);
|
|
@@ -6047,7 +6130,7 @@ var init_consolidation = __esm({
|
|
|
6047
6130
|
import net from "net";
|
|
6048
6131
|
import { spawn } from "child_process";
|
|
6049
6132
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
6050
|
-
import { existsSync as existsSync14, unlinkSync as unlinkSync4, readFileSync as
|
|
6133
|
+
import { existsSync as existsSync14, unlinkSync as unlinkSync4, readFileSync as readFileSync11, openSync, closeSync, statSync } from "fs";
|
|
6051
6134
|
import path17 from "path";
|
|
6052
6135
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
6053
6136
|
function handleData(chunk) {
|
|
@@ -6072,7 +6155,7 @@ function handleData(chunk) {
|
|
|
6072
6155
|
function cleanupStaleFiles() {
|
|
6073
6156
|
if (existsSync14(PID_PATH)) {
|
|
6074
6157
|
try {
|
|
6075
|
-
const pid = parseInt(
|
|
6158
|
+
const pid = parseInt(readFileSync11(PID_PATH, "utf8").trim(), 10);
|
|
6076
6159
|
if (pid > 0) {
|
|
6077
6160
|
try {
|
|
6078
6161
|
process.kill(pid, 0);
|
|
@@ -6220,11 +6303,11 @@ async function connectEmbedDaemon() {
|
|
|
6220
6303
|
}
|
|
6221
6304
|
}
|
|
6222
6305
|
const start = Date.now();
|
|
6223
|
-
let
|
|
6306
|
+
let delay2 = 100;
|
|
6224
6307
|
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
6225
|
-
await new Promise((r) => setTimeout(r,
|
|
6308
|
+
await new Promise((r) => setTimeout(r, delay2));
|
|
6226
6309
|
if (await connectToSocket()) return true;
|
|
6227
|
-
|
|
6310
|
+
delay2 = Math.min(delay2 * 2, 3e3);
|
|
6228
6311
|
}
|
|
6229
6312
|
return false;
|
|
6230
6313
|
}
|
|
@@ -6280,7 +6363,7 @@ function killAndRespawnDaemon() {
|
|
|
6280
6363
|
process.stderr.write("[exed-client] Killing daemon for restart...\n");
|
|
6281
6364
|
if (existsSync14(PID_PATH)) {
|
|
6282
6365
|
try {
|
|
6283
|
-
const pid = parseInt(
|
|
6366
|
+
const pid = parseInt(readFileSync11(PID_PATH, "utf8").trim(), 10);
|
|
6284
6367
|
if (pid > 0) {
|
|
6285
6368
|
try {
|
|
6286
6369
|
process.kill(pid, "SIGKILL");
|
|
@@ -6316,11 +6399,11 @@ async function embedViaClient(text, priority = "high") {
|
|
|
6316
6399
|
`);
|
|
6317
6400
|
killAndRespawnDaemon();
|
|
6318
6401
|
const start = Date.now();
|
|
6319
|
-
let
|
|
6402
|
+
let delay2 = 200;
|
|
6320
6403
|
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
6321
|
-
await new Promise((r) => setTimeout(r,
|
|
6404
|
+
await new Promise((r) => setTimeout(r, delay2));
|
|
6322
6405
|
if (await connectToSocket()) break;
|
|
6323
|
-
|
|
6406
|
+
delay2 = Math.min(delay2 * 2, 3e3);
|
|
6324
6407
|
}
|
|
6325
6408
|
if (!_connected) return null;
|
|
6326
6409
|
}
|
|
@@ -6332,11 +6415,11 @@ async function embedViaClient(text, priority = "high") {
|
|
|
6332
6415
|
`);
|
|
6333
6416
|
killAndRespawnDaemon();
|
|
6334
6417
|
const start = Date.now();
|
|
6335
|
-
let
|
|
6418
|
+
let delay2 = 200;
|
|
6336
6419
|
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
6337
|
-
await new Promise((r) => setTimeout(r,
|
|
6420
|
+
await new Promise((r) => setTimeout(r, delay2));
|
|
6338
6421
|
if (await connectToSocket()) break;
|
|
6339
|
-
|
|
6422
|
+
delay2 = Math.min(delay2 * 2, 3e3);
|
|
6340
6423
|
}
|
|
6341
6424
|
if (!_connected) return null;
|
|
6342
6425
|
const retry = await sendRequest([text], priority);
|
|
@@ -7232,11 +7315,11 @@ __export(update_check_exports, {
|
|
|
7232
7315
|
getRemoteVersion: () => getRemoteVersion
|
|
7233
7316
|
});
|
|
7234
7317
|
import { execSync as execSync11 } from "child_process";
|
|
7235
|
-
import { readFileSync as
|
|
7318
|
+
import { readFileSync as readFileSync12 } from "fs";
|
|
7236
7319
|
import path18 from "path";
|
|
7237
7320
|
function getLocalVersion(packageRoot) {
|
|
7238
7321
|
const pkgPath = path18.join(packageRoot, "package.json");
|
|
7239
|
-
const pkg = JSON.parse(
|
|
7322
|
+
const pkg = JSON.parse(readFileSync12(pkgPath, "utf-8"));
|
|
7240
7323
|
return pkg.version;
|
|
7241
7324
|
}
|
|
7242
7325
|
function getRemoteVersion() {
|
|
@@ -7308,12 +7391,12 @@ __export(device_registry_exports, {
|
|
|
7308
7391
|
});
|
|
7309
7392
|
import crypto9 from "crypto";
|
|
7310
7393
|
import os7 from "os";
|
|
7311
|
-
import { readFileSync as
|
|
7394
|
+
import { readFileSync as readFileSync13, writeFileSync as writeFileSync6, mkdirSync as mkdirSync7, existsSync as existsSync15 } from "fs";
|
|
7312
7395
|
import path19 from "path";
|
|
7313
7396
|
function getDeviceInfo() {
|
|
7314
7397
|
if (existsSync15(DEVICE_JSON_PATH)) {
|
|
7315
7398
|
try {
|
|
7316
|
-
const raw =
|
|
7399
|
+
const raw = readFileSync13(DEVICE_JSON_PATH, "utf8");
|
|
7317
7400
|
const data = JSON.parse(raw);
|
|
7318
7401
|
if (data.deviceId && data.friendlyName && data.hostname) {
|
|
7319
7402
|
return data;
|
|
@@ -7691,7 +7774,7 @@ async function deliverLocalMessage(messageId) {
|
|
|
7691
7774
|
return true;
|
|
7692
7775
|
} catch {
|
|
7693
7776
|
const newRetryCount = msg.retryCount + 1;
|
|
7694
|
-
if (newRetryCount >=
|
|
7777
|
+
if (newRetryCount >= MAX_RETRIES3) {
|
|
7695
7778
|
await markFailed(messageId, "session unavailable after 10 retries");
|
|
7696
7779
|
} else {
|
|
7697
7780
|
await client.execute({
|
|
@@ -7780,7 +7863,7 @@ async function retryPendingMessages() {
|
|
|
7780
7863
|
sql: `SELECT * FROM messages
|
|
7781
7864
|
WHERE status = 'pending' AND retry_count < ?
|
|
7782
7865
|
ORDER BY id`,
|
|
7783
|
-
args: [
|
|
7866
|
+
args: [MAX_RETRIES3]
|
|
7784
7867
|
});
|
|
7785
7868
|
let delivered = 0;
|
|
7786
7869
|
for (const row of result.rows) {
|
|
@@ -7793,13 +7876,13 @@ async function retryPendingMessages() {
|
|
|
7793
7876
|
}
|
|
7794
7877
|
return delivered;
|
|
7795
7878
|
}
|
|
7796
|
-
var
|
|
7879
|
+
var MAX_RETRIES3, _wsClientSend;
|
|
7797
7880
|
var init_messaging = __esm({
|
|
7798
7881
|
"src/lib/messaging.ts"() {
|
|
7799
7882
|
"use strict";
|
|
7800
7883
|
init_database();
|
|
7801
7884
|
init_tmux_routing();
|
|
7802
|
-
|
|
7885
|
+
MAX_RETRIES3 = 10;
|
|
7803
7886
|
_wsClientSend = null;
|
|
7804
7887
|
}
|
|
7805
7888
|
});
|
|
@@ -7808,7 +7891,7 @@ var init_messaging = __esm({
|
|
|
7808
7891
|
init_config();
|
|
7809
7892
|
init_memory();
|
|
7810
7893
|
import net2 from "net";
|
|
7811
|
-
import { writeFileSync as writeFileSync7, unlinkSync as unlinkSync5, mkdirSync as mkdirSync8, existsSync as existsSync16, readFileSync as
|
|
7894
|
+
import { writeFileSync as writeFileSync7, unlinkSync as unlinkSync5, mkdirSync as mkdirSync8, existsSync as existsSync16, readFileSync as readFileSync14 } from "fs";
|
|
7812
7895
|
import path20 from "path";
|
|
7813
7896
|
import { getLlama } from "node-llama-cpp";
|
|
7814
7897
|
var SOCKET_PATH2 = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path20.join(EXE_AI_DIR, "exed.sock");
|
|
@@ -7955,7 +8038,7 @@ function startServer() {
|
|
|
7955
8038
|
const oldPath = path20.join(path20.dirname(SOCKET_PATH2), oldFile);
|
|
7956
8039
|
try {
|
|
7957
8040
|
if (oldFile.endsWith(".pid")) {
|
|
7958
|
-
const pid = parseInt(
|
|
8041
|
+
const pid = parseInt(readFileSync14(oldPath, "utf8").trim(), 10);
|
|
7959
8042
|
if (pid > 0) try {
|
|
7960
8043
|
process.kill(pid, "SIGKILL");
|
|
7961
8044
|
} catch {
|
|
@@ -8379,7 +8462,7 @@ process.on("SIGTERM", () => void shutdown());
|
|
|
8379
8462
|
function checkExistingDaemon() {
|
|
8380
8463
|
try {
|
|
8381
8464
|
if (!existsSync16(PID_PATH2)) return false;
|
|
8382
|
-
const pid = parseInt(
|
|
8465
|
+
const pid = parseInt(readFileSync14(PID_PATH2, "utf8").trim(), 10);
|
|
8383
8466
|
if (!pid || isNaN(pid)) return false;
|
|
8384
8467
|
process.kill(pid, 0);
|
|
8385
8468
|
process.stderr.write(`[exed] Another daemon is already running (PID ${pid}). Exiting.
|
|
@@ -23,12 +23,68 @@ var init_memory = __esm({
|
|
|
23
23
|
}
|
|
24
24
|
});
|
|
25
25
|
|
|
26
|
+
// src/lib/db-retry.ts
|
|
27
|
+
function isBusyError(err) {
|
|
28
|
+
if (err instanceof Error) {
|
|
29
|
+
const msg = err.message.toLowerCase();
|
|
30
|
+
return msg.includes("sqlite_busy") || msg.includes("database is locked");
|
|
31
|
+
}
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
function delay(ms) {
|
|
35
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
36
|
+
}
|
|
37
|
+
async function retryOnBusy(fn, label) {
|
|
38
|
+
let lastError;
|
|
39
|
+
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
40
|
+
try {
|
|
41
|
+
return await fn();
|
|
42
|
+
} catch (err) {
|
|
43
|
+
lastError = err;
|
|
44
|
+
if (!isBusyError(err) || attempt === MAX_RETRIES) {
|
|
45
|
+
throw err;
|
|
46
|
+
}
|
|
47
|
+
const backoff = BASE_DELAY_MS * Math.pow(2, attempt);
|
|
48
|
+
const jitter = Math.floor(Math.random() * MAX_JITTER_MS);
|
|
49
|
+
process.stderr.write(
|
|
50
|
+
`[exe-os] SQLITE_BUSY ${label} retry ${attempt + 1}/${MAX_RETRIES} \u2014 waiting ${backoff + jitter}ms
|
|
51
|
+
`
|
|
52
|
+
);
|
|
53
|
+
await delay(backoff + jitter);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
throw lastError;
|
|
57
|
+
}
|
|
58
|
+
function wrapWithRetry(client) {
|
|
59
|
+
return new Proxy(client, {
|
|
60
|
+
get(target, prop, receiver) {
|
|
61
|
+
if (prop === "execute") {
|
|
62
|
+
return (sql) => retryOnBusy(() => target.execute(sql), "execute");
|
|
63
|
+
}
|
|
64
|
+
if (prop === "batch") {
|
|
65
|
+
return (stmts) => retryOnBusy(() => target.batch(stmts), "batch");
|
|
66
|
+
}
|
|
67
|
+
return Reflect.get(target, prop, receiver);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
var MAX_RETRIES, BASE_DELAY_MS, MAX_JITTER_MS;
|
|
72
|
+
var init_db_retry = __esm({
|
|
73
|
+
"src/lib/db-retry.ts"() {
|
|
74
|
+
"use strict";
|
|
75
|
+
MAX_RETRIES = 3;
|
|
76
|
+
BASE_DELAY_MS = 200;
|
|
77
|
+
MAX_JITTER_MS = 300;
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
26
81
|
// src/lib/database.ts
|
|
27
82
|
import { createClient } from "@libsql/client";
|
|
28
83
|
async function initDatabase(config) {
|
|
29
84
|
if (_client) {
|
|
30
85
|
_client.close();
|
|
31
86
|
_client = null;
|
|
87
|
+
_resilientClient = null;
|
|
32
88
|
}
|
|
33
89
|
const opts = {
|
|
34
90
|
url: `file:${config.dbPath}`
|
|
@@ -37,17 +93,24 @@ async function initDatabase(config) {
|
|
|
37
93
|
opts.encryptionKey = config.encryptionKey;
|
|
38
94
|
}
|
|
39
95
|
_client = createClient(opts);
|
|
96
|
+
_resilientClient = wrapWithRetry(_client);
|
|
40
97
|
}
|
|
41
98
|
function getClient() {
|
|
99
|
+
if (!_resilientClient) {
|
|
100
|
+
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
101
|
+
}
|
|
102
|
+
return _resilientClient;
|
|
103
|
+
}
|
|
104
|
+
function getRawClient() {
|
|
42
105
|
if (!_client) {
|
|
43
106
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
44
107
|
}
|
|
45
108
|
return _client;
|
|
46
109
|
}
|
|
47
110
|
async function ensureSchema() {
|
|
48
|
-
const client =
|
|
111
|
+
const client = getRawClient();
|
|
49
112
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
50
|
-
await client.execute("PRAGMA busy_timeout =
|
|
113
|
+
await client.execute("PRAGMA busy_timeout = 30000");
|
|
51
114
|
try {
|
|
52
115
|
await client.execute("PRAGMA libsql_vector_search_ef = 128");
|
|
53
116
|
} catch {
|
|
@@ -840,13 +903,16 @@ async function disposeDatabase() {
|
|
|
840
903
|
if (_client) {
|
|
841
904
|
_client.close();
|
|
842
905
|
_client = null;
|
|
906
|
+
_resilientClient = null;
|
|
843
907
|
}
|
|
844
908
|
}
|
|
845
|
-
var _client, initTurso, disposeTurso;
|
|
909
|
+
var _client, _resilientClient, initTurso, disposeTurso;
|
|
846
910
|
var init_database = __esm({
|
|
847
911
|
"src/lib/database.ts"() {
|
|
848
912
|
"use strict";
|
|
913
|
+
init_db_retry();
|
|
849
914
|
_client = null;
|
|
915
|
+
_resilientClient = null;
|
|
850
916
|
initTurso = initDatabase;
|
|
851
917
|
disposeTurso = disposeDatabase;
|
|
852
918
|
}
|
|
@@ -1202,7 +1268,7 @@ function listShards() {
|
|
|
1202
1268
|
}
|
|
1203
1269
|
async function ensureShardSchema(client) {
|
|
1204
1270
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
1205
|
-
await client.execute("PRAGMA busy_timeout =
|
|
1271
|
+
await client.execute("PRAGMA busy_timeout = 30000");
|
|
1206
1272
|
try {
|
|
1207
1273
|
await client.execute("PRAGMA libsql_vector_search_ef = 128");
|
|
1208
1274
|
} catch {
|
|
@@ -2087,11 +2153,11 @@ async function connectEmbedDaemon() {
|
|
|
2087
2153
|
}
|
|
2088
2154
|
}
|
|
2089
2155
|
const start = Date.now();
|
|
2090
|
-
let
|
|
2156
|
+
let delay2 = 100;
|
|
2091
2157
|
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
2092
|
-
await new Promise((r) => setTimeout(r,
|
|
2158
|
+
await new Promise((r) => setTimeout(r, delay2));
|
|
2093
2159
|
if (await connectToSocket()) return true;
|
|
2094
|
-
|
|
2160
|
+
delay2 = Math.min(delay2 * 2, 3e3);
|
|
2095
2161
|
}
|
|
2096
2162
|
return false;
|
|
2097
2163
|
}
|
|
@@ -2183,11 +2249,11 @@ async function embedViaClient(text, priority = "high") {
|
|
|
2183
2249
|
`);
|
|
2184
2250
|
killAndRespawnDaemon();
|
|
2185
2251
|
const start = Date.now();
|
|
2186
|
-
let
|
|
2252
|
+
let delay2 = 200;
|
|
2187
2253
|
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
2188
|
-
await new Promise((r) => setTimeout(r,
|
|
2254
|
+
await new Promise((r) => setTimeout(r, delay2));
|
|
2189
2255
|
if (await connectToSocket()) break;
|
|
2190
|
-
|
|
2256
|
+
delay2 = Math.min(delay2 * 2, 3e3);
|
|
2191
2257
|
}
|
|
2192
2258
|
if (!_connected) return null;
|
|
2193
2259
|
}
|
|
@@ -2199,11 +2265,11 @@ async function embedViaClient(text, priority = "high") {
|
|
|
2199
2265
|
`);
|
|
2200
2266
|
killAndRespawnDaemon();
|
|
2201
2267
|
const start = Date.now();
|
|
2202
|
-
let
|
|
2268
|
+
let delay2 = 200;
|
|
2203
2269
|
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
2204
|
-
await new Promise((r) => setTimeout(r,
|
|
2270
|
+
await new Promise((r) => setTimeout(r, delay2));
|
|
2205
2271
|
if (await connectToSocket()) break;
|
|
2206
|
-
|
|
2272
|
+
delay2 = Math.min(delay2 * 2, 3e3);
|
|
2207
2273
|
}
|
|
2208
2274
|
if (!_connected) return null;
|
|
2209
2275
|
const retry = await sendRequest([text], priority);
|