@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/bin/exe-forget.js
CHANGED
|
@@ -22,12 +22,68 @@ var init_memory = __esm({
|
|
|
22
22
|
}
|
|
23
23
|
});
|
|
24
24
|
|
|
25
|
+
// src/lib/db-retry.ts
|
|
26
|
+
function isBusyError(err) {
|
|
27
|
+
if (err instanceof Error) {
|
|
28
|
+
const msg = err.message.toLowerCase();
|
|
29
|
+
return msg.includes("sqlite_busy") || msg.includes("database is locked");
|
|
30
|
+
}
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
function delay(ms) {
|
|
34
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
35
|
+
}
|
|
36
|
+
async function retryOnBusy(fn, label) {
|
|
37
|
+
let lastError;
|
|
38
|
+
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
39
|
+
try {
|
|
40
|
+
return await fn();
|
|
41
|
+
} catch (err) {
|
|
42
|
+
lastError = err;
|
|
43
|
+
if (!isBusyError(err) || attempt === MAX_RETRIES) {
|
|
44
|
+
throw err;
|
|
45
|
+
}
|
|
46
|
+
const backoff = BASE_DELAY_MS * Math.pow(2, attempt);
|
|
47
|
+
const jitter = Math.floor(Math.random() * MAX_JITTER_MS);
|
|
48
|
+
process.stderr.write(
|
|
49
|
+
`[exe-os] SQLITE_BUSY ${label} retry ${attempt + 1}/${MAX_RETRIES} \u2014 waiting ${backoff + jitter}ms
|
|
50
|
+
`
|
|
51
|
+
);
|
|
52
|
+
await delay(backoff + jitter);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
throw lastError;
|
|
56
|
+
}
|
|
57
|
+
function wrapWithRetry(client) {
|
|
58
|
+
return new Proxy(client, {
|
|
59
|
+
get(target, prop, receiver) {
|
|
60
|
+
if (prop === "execute") {
|
|
61
|
+
return (sql) => retryOnBusy(() => target.execute(sql), "execute");
|
|
62
|
+
}
|
|
63
|
+
if (prop === "batch") {
|
|
64
|
+
return (stmts) => retryOnBusy(() => target.batch(stmts), "batch");
|
|
65
|
+
}
|
|
66
|
+
return Reflect.get(target, prop, receiver);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
var MAX_RETRIES, BASE_DELAY_MS, MAX_JITTER_MS;
|
|
71
|
+
var init_db_retry = __esm({
|
|
72
|
+
"src/lib/db-retry.ts"() {
|
|
73
|
+
"use strict";
|
|
74
|
+
MAX_RETRIES = 3;
|
|
75
|
+
BASE_DELAY_MS = 200;
|
|
76
|
+
MAX_JITTER_MS = 300;
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
|
|
25
80
|
// src/lib/database.ts
|
|
26
81
|
import { createClient } from "@libsql/client";
|
|
27
82
|
async function initDatabase(config) {
|
|
28
83
|
if (_client) {
|
|
29
84
|
_client.close();
|
|
30
85
|
_client = null;
|
|
86
|
+
_resilientClient = null;
|
|
31
87
|
}
|
|
32
88
|
const opts = {
|
|
33
89
|
url: `file:${config.dbPath}`
|
|
@@ -36,17 +92,24 @@ async function initDatabase(config) {
|
|
|
36
92
|
opts.encryptionKey = config.encryptionKey;
|
|
37
93
|
}
|
|
38
94
|
_client = createClient(opts);
|
|
95
|
+
_resilientClient = wrapWithRetry(_client);
|
|
39
96
|
}
|
|
40
97
|
function getClient() {
|
|
98
|
+
if (!_resilientClient) {
|
|
99
|
+
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
100
|
+
}
|
|
101
|
+
return _resilientClient;
|
|
102
|
+
}
|
|
103
|
+
function getRawClient() {
|
|
41
104
|
if (!_client) {
|
|
42
105
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
43
106
|
}
|
|
44
107
|
return _client;
|
|
45
108
|
}
|
|
46
109
|
async function ensureSchema() {
|
|
47
|
-
const client =
|
|
110
|
+
const client = getRawClient();
|
|
48
111
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
49
|
-
await client.execute("PRAGMA busy_timeout =
|
|
112
|
+
await client.execute("PRAGMA busy_timeout = 30000");
|
|
50
113
|
try {
|
|
51
114
|
await client.execute("PRAGMA libsql_vector_search_ef = 128");
|
|
52
115
|
} catch {
|
|
@@ -835,11 +898,13 @@ async function ensureSchema() {
|
|
|
835
898
|
}
|
|
836
899
|
}
|
|
837
900
|
}
|
|
838
|
-
var _client, initTurso;
|
|
901
|
+
var _client, _resilientClient, initTurso;
|
|
839
902
|
var init_database = __esm({
|
|
840
903
|
"src/lib/database.ts"() {
|
|
841
904
|
"use strict";
|
|
905
|
+
init_db_retry();
|
|
842
906
|
_client = null;
|
|
907
|
+
_resilientClient = null;
|
|
843
908
|
initTurso = initDatabase;
|
|
844
909
|
}
|
|
845
910
|
});
|
|
@@ -1140,7 +1205,7 @@ function listShards() {
|
|
|
1140
1205
|
}
|
|
1141
1206
|
async function ensureShardSchema(client) {
|
|
1142
1207
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
1143
|
-
await client.execute("PRAGMA busy_timeout =
|
|
1208
|
+
await client.execute("PRAGMA busy_timeout = 30000");
|
|
1144
1209
|
try {
|
|
1145
1210
|
await client.execute("PRAGMA libsql_vector_search_ef = 128");
|
|
1146
1211
|
} catch {
|
package/dist/bin/exe-gateway.js
CHANGED
|
@@ -281,12 +281,68 @@ var init_crm_bridge = __esm({
|
|
|
281
281
|
}
|
|
282
282
|
});
|
|
283
283
|
|
|
284
|
+
// src/lib/db-retry.ts
|
|
285
|
+
function isBusyError(err) {
|
|
286
|
+
if (err instanceof Error) {
|
|
287
|
+
const msg = err.message.toLowerCase();
|
|
288
|
+
return msg.includes("sqlite_busy") || msg.includes("database is locked");
|
|
289
|
+
}
|
|
290
|
+
return false;
|
|
291
|
+
}
|
|
292
|
+
function delay(ms) {
|
|
293
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
294
|
+
}
|
|
295
|
+
async function retryOnBusy(fn, label) {
|
|
296
|
+
let lastError;
|
|
297
|
+
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
298
|
+
try {
|
|
299
|
+
return await fn();
|
|
300
|
+
} catch (err) {
|
|
301
|
+
lastError = err;
|
|
302
|
+
if (!isBusyError(err) || attempt === MAX_RETRIES) {
|
|
303
|
+
throw err;
|
|
304
|
+
}
|
|
305
|
+
const backoff = BASE_DELAY_MS * Math.pow(2, attempt);
|
|
306
|
+
const jitter = Math.floor(Math.random() * MAX_JITTER_MS);
|
|
307
|
+
process.stderr.write(
|
|
308
|
+
`[exe-os] SQLITE_BUSY ${label} retry ${attempt + 1}/${MAX_RETRIES} \u2014 waiting ${backoff + jitter}ms
|
|
309
|
+
`
|
|
310
|
+
);
|
|
311
|
+
await delay(backoff + jitter);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
throw lastError;
|
|
315
|
+
}
|
|
316
|
+
function wrapWithRetry(client) {
|
|
317
|
+
return new Proxy(client, {
|
|
318
|
+
get(target, prop, receiver) {
|
|
319
|
+
if (prop === "execute") {
|
|
320
|
+
return (sql) => retryOnBusy(() => target.execute(sql), "execute");
|
|
321
|
+
}
|
|
322
|
+
if (prop === "batch") {
|
|
323
|
+
return (stmts) => retryOnBusy(() => target.batch(stmts), "batch");
|
|
324
|
+
}
|
|
325
|
+
return Reflect.get(target, prop, receiver);
|
|
326
|
+
}
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
var MAX_RETRIES, BASE_DELAY_MS, MAX_JITTER_MS;
|
|
330
|
+
var init_db_retry = __esm({
|
|
331
|
+
"src/lib/db-retry.ts"() {
|
|
332
|
+
"use strict";
|
|
333
|
+
MAX_RETRIES = 3;
|
|
334
|
+
BASE_DELAY_MS = 200;
|
|
335
|
+
MAX_JITTER_MS = 300;
|
|
336
|
+
}
|
|
337
|
+
});
|
|
338
|
+
|
|
284
339
|
// src/lib/database.ts
|
|
285
340
|
import { createClient } from "@libsql/client";
|
|
286
341
|
async function initDatabase(config2) {
|
|
287
342
|
if (_client) {
|
|
288
343
|
_client.close();
|
|
289
344
|
_client = null;
|
|
345
|
+
_resilientClient = null;
|
|
290
346
|
}
|
|
291
347
|
const opts = {
|
|
292
348
|
url: `file:${config2.dbPath}`
|
|
@@ -295,17 +351,24 @@ async function initDatabase(config2) {
|
|
|
295
351
|
opts.encryptionKey = config2.encryptionKey;
|
|
296
352
|
}
|
|
297
353
|
_client = createClient(opts);
|
|
354
|
+
_resilientClient = wrapWithRetry(_client);
|
|
298
355
|
}
|
|
299
356
|
function getClient() {
|
|
357
|
+
if (!_resilientClient) {
|
|
358
|
+
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
359
|
+
}
|
|
360
|
+
return _resilientClient;
|
|
361
|
+
}
|
|
362
|
+
function getRawClient() {
|
|
300
363
|
if (!_client) {
|
|
301
364
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
302
365
|
}
|
|
303
366
|
return _client;
|
|
304
367
|
}
|
|
305
368
|
async function ensureSchema() {
|
|
306
|
-
const client =
|
|
369
|
+
const client = getRawClient();
|
|
307
370
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
308
|
-
await client.execute("PRAGMA busy_timeout =
|
|
371
|
+
await client.execute("PRAGMA busy_timeout = 30000");
|
|
309
372
|
try {
|
|
310
373
|
await client.execute("PRAGMA libsql_vector_search_ef = 128");
|
|
311
374
|
} catch {
|
|
@@ -1098,13 +1161,16 @@ async function disposeDatabase() {
|
|
|
1098
1161
|
if (_client) {
|
|
1099
1162
|
_client.close();
|
|
1100
1163
|
_client = null;
|
|
1164
|
+
_resilientClient = null;
|
|
1101
1165
|
}
|
|
1102
1166
|
}
|
|
1103
|
-
var _client, initTurso, disposeTurso;
|
|
1167
|
+
var _client, _resilientClient, initTurso, disposeTurso;
|
|
1104
1168
|
var init_database = __esm({
|
|
1105
1169
|
"src/lib/database.ts"() {
|
|
1106
1170
|
"use strict";
|
|
1171
|
+
init_db_retry();
|
|
1107
1172
|
_client = null;
|
|
1173
|
+
_resilientClient = null;
|
|
1108
1174
|
initTurso = initDatabase;
|
|
1109
1175
|
disposeTurso = disposeDatabase;
|
|
1110
1176
|
}
|
|
@@ -1539,11 +1605,11 @@ async function connectEmbedDaemon() {
|
|
|
1539
1605
|
}
|
|
1540
1606
|
}
|
|
1541
1607
|
const start = Date.now();
|
|
1542
|
-
let
|
|
1608
|
+
let delay2 = 100;
|
|
1543
1609
|
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
1544
|
-
await new Promise((r) => setTimeout(r,
|
|
1610
|
+
await new Promise((r) => setTimeout(r, delay2));
|
|
1545
1611
|
if (await connectToSocket()) return true;
|
|
1546
|
-
|
|
1612
|
+
delay2 = Math.min(delay2 * 2, 3e3);
|
|
1547
1613
|
}
|
|
1548
1614
|
return false;
|
|
1549
1615
|
}
|
|
@@ -1635,11 +1701,11 @@ async function embedViaClient(text, priority = "high") {
|
|
|
1635
1701
|
`);
|
|
1636
1702
|
killAndRespawnDaemon();
|
|
1637
1703
|
const start = Date.now();
|
|
1638
|
-
let
|
|
1704
|
+
let delay2 = 200;
|
|
1639
1705
|
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
1640
|
-
await new Promise((r) => setTimeout(r,
|
|
1706
|
+
await new Promise((r) => setTimeout(r, delay2));
|
|
1641
1707
|
if (await connectToSocket()) break;
|
|
1642
|
-
|
|
1708
|
+
delay2 = Math.min(delay2 * 2, 3e3);
|
|
1643
1709
|
}
|
|
1644
1710
|
if (!_connected) return null;
|
|
1645
1711
|
}
|
|
@@ -1651,11 +1717,11 @@ async function embedViaClient(text, priority = "high") {
|
|
|
1651
1717
|
`);
|
|
1652
1718
|
killAndRespawnDaemon();
|
|
1653
1719
|
const start = Date.now();
|
|
1654
|
-
let
|
|
1720
|
+
let delay2 = 200;
|
|
1655
1721
|
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
1656
|
-
await new Promise((r) => setTimeout(r,
|
|
1722
|
+
await new Promise((r) => setTimeout(r, delay2));
|
|
1657
1723
|
if (await connectToSocket()) break;
|
|
1658
|
-
|
|
1724
|
+
delay2 = Math.min(delay2 * 2, 3e3);
|
|
1659
1725
|
}
|
|
1660
1726
|
if (!_connected) return null;
|
|
1661
1727
|
const retry = await sendRequest([text], priority);
|
|
@@ -1873,7 +1939,7 @@ function listShards() {
|
|
|
1873
1939
|
}
|
|
1874
1940
|
async function ensureShardSchema(client) {
|
|
1875
1941
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
1876
|
-
await client.execute("PRAGMA busy_timeout =
|
|
1942
|
+
await client.execute("PRAGMA busy_timeout = 30000");
|
|
1877
1943
|
try {
|
|
1878
1944
|
await client.execute("PRAGMA libsql_vector_search_ef = 128");
|
|
1879
1945
|
} catch {
|
|
@@ -4510,25 +4576,43 @@ var init_intercom_queue = __esm({
|
|
|
4510
4576
|
|
|
4511
4577
|
// src/lib/employees.ts
|
|
4512
4578
|
import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
|
|
4513
|
-
import { existsSync as existsSync8, symlinkSync, readlinkSync } from "fs";
|
|
4579
|
+
import { existsSync as existsSync8, symlinkSync, readlinkSync, readFileSync as readFileSync7 } from "fs";
|
|
4514
4580
|
import { execSync as execSync3 } from "child_process";
|
|
4515
4581
|
import path9 from "path";
|
|
4516
|
-
|
|
4582
|
+
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
4583
|
+
if (!existsSync8(employeesPath)) return [];
|
|
4584
|
+
try {
|
|
4585
|
+
return JSON.parse(readFileSync7(employeesPath, "utf-8"));
|
|
4586
|
+
} catch {
|
|
4587
|
+
return [];
|
|
4588
|
+
}
|
|
4589
|
+
}
|
|
4590
|
+
function getEmployee(employees, name) {
|
|
4591
|
+
return employees.find((e) => e.name.toLowerCase() === name.toLowerCase());
|
|
4592
|
+
}
|
|
4593
|
+
function isMultiInstance(agentName, employees) {
|
|
4594
|
+
const roster = employees ?? loadEmployeesSync();
|
|
4595
|
+
const emp = getEmployee(roster, agentName);
|
|
4596
|
+
if (!emp) return false;
|
|
4597
|
+
return MULTI_INSTANCE_ROLES.has(emp.role.toLowerCase());
|
|
4598
|
+
}
|
|
4599
|
+
var EMPLOYEES_PATH, MULTI_INSTANCE_ROLES;
|
|
4517
4600
|
var init_employees = __esm({
|
|
4518
4601
|
"src/lib/employees.ts"() {
|
|
4519
4602
|
"use strict";
|
|
4520
4603
|
init_config();
|
|
4521
4604
|
EMPLOYEES_PATH = path9.join(EXE_AI_DIR, "exe-employees.json");
|
|
4605
|
+
MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
|
|
4522
4606
|
}
|
|
4523
4607
|
});
|
|
4524
4608
|
|
|
4525
4609
|
// src/lib/plan-limits.ts
|
|
4526
|
-
import { readFileSync as
|
|
4610
|
+
import { readFileSync as readFileSync8, existsSync as existsSync9 } from "fs";
|
|
4527
4611
|
import path10 from "path";
|
|
4528
4612
|
function getLicenseSync() {
|
|
4529
4613
|
try {
|
|
4530
4614
|
if (!existsSync9(CACHE_PATH2)) return freeLicense();
|
|
4531
|
-
const raw = JSON.parse(
|
|
4615
|
+
const raw = JSON.parse(readFileSync8(CACHE_PATH2, "utf8"));
|
|
4532
4616
|
if (!raw.token || typeof raw.token !== "string") return freeLicense();
|
|
4533
4617
|
const parts = raw.token.split(".");
|
|
4534
4618
|
if (parts.length !== 3) return freeLicense();
|
|
@@ -4567,7 +4651,7 @@ function assertEmployeeLimitSync(rosterPath) {
|
|
|
4567
4651
|
let count = 0;
|
|
4568
4652
|
try {
|
|
4569
4653
|
if (existsSync9(filePath)) {
|
|
4570
|
-
const raw =
|
|
4654
|
+
const raw = readFileSync8(filePath, "utf8");
|
|
4571
4655
|
const employees = JSON.parse(raw);
|
|
4572
4656
|
count = Array.isArray(employees) ? employees.length : 0;
|
|
4573
4657
|
}
|
|
@@ -4602,7 +4686,7 @@ var init_plan_limits = __esm({
|
|
|
4602
4686
|
|
|
4603
4687
|
// src/lib/tmux-routing.ts
|
|
4604
4688
|
import { execFileSync as execFileSync2, execSync as execSync4 } from "child_process";
|
|
4605
|
-
import { readFileSync as
|
|
4689
|
+
import { readFileSync as readFileSync9, writeFileSync as writeFileSync4, mkdirSync as mkdirSync6, existsSync as existsSync10, appendFileSync } from "fs";
|
|
4606
4690
|
import path11 from "path";
|
|
4607
4691
|
import os4 from "os";
|
|
4608
4692
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
@@ -4651,7 +4735,7 @@ function extractRootExe(name) {
|
|
|
4651
4735
|
}
|
|
4652
4736
|
function getParentExe(sessionKey) {
|
|
4653
4737
|
try {
|
|
4654
|
-
const data = JSON.parse(
|
|
4738
|
+
const data = JSON.parse(readFileSync9(path11.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
4655
4739
|
return data.parentExe || null;
|
|
4656
4740
|
} catch {
|
|
4657
4741
|
return null;
|
|
@@ -4659,7 +4743,7 @@ function getParentExe(sessionKey) {
|
|
|
4659
4743
|
}
|
|
4660
4744
|
function getDispatchedBy(sessionKey) {
|
|
4661
4745
|
try {
|
|
4662
|
-
const data = JSON.parse(
|
|
4746
|
+
const data = JSON.parse(readFileSync9(
|
|
4663
4747
|
path11.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
|
|
4664
4748
|
"utf8"
|
|
4665
4749
|
));
|
|
@@ -4696,7 +4780,7 @@ function findFreeInstance(employeeName, exeSession, maxInstances = 10, isAlive =
|
|
|
4696
4780
|
function readDebounceState() {
|
|
4697
4781
|
try {
|
|
4698
4782
|
if (!existsSync10(DEBOUNCE_FILE)) return {};
|
|
4699
|
-
return JSON.parse(
|
|
4783
|
+
return JSON.parse(readFileSync9(DEBOUNCE_FILE, "utf8"));
|
|
4700
4784
|
} catch {
|
|
4701
4785
|
return {};
|
|
4702
4786
|
}
|
|
@@ -4892,7 +4976,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4892
4976
|
const claudeJsonPath = path11.join(os4.homedir(), ".claude.json");
|
|
4893
4977
|
let claudeJson = {};
|
|
4894
4978
|
try {
|
|
4895
|
-
claudeJson = JSON.parse(
|
|
4979
|
+
claudeJson = JSON.parse(readFileSync9(claudeJsonPath, "utf8"));
|
|
4896
4980
|
} catch {
|
|
4897
4981
|
}
|
|
4898
4982
|
if (!claudeJson.projects) claudeJson.projects = {};
|
|
@@ -4910,7 +4994,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4910
4994
|
const settingsPath = path11.join(projSettingsDir, "settings.json");
|
|
4911
4995
|
let settings = {};
|
|
4912
4996
|
try {
|
|
4913
|
-
settings = JSON.parse(
|
|
4997
|
+
settings = JSON.parse(readFileSync9(settingsPath, "utf8"));
|
|
4914
4998
|
} catch {
|
|
4915
4999
|
}
|
|
4916
5000
|
const perms = settings.permissions ?? {};
|
|
@@ -5237,7 +5321,7 @@ async function deliverLocalMessage(messageId) {
|
|
|
5237
5321
|
return true;
|
|
5238
5322
|
} catch {
|
|
5239
5323
|
const newRetryCount = msg.retryCount + 1;
|
|
5240
|
-
if (newRetryCount >=
|
|
5324
|
+
if (newRetryCount >= MAX_RETRIES2) {
|
|
5241
5325
|
await markFailed(messageId, "session unavailable after 10 retries");
|
|
5242
5326
|
} else {
|
|
5243
5327
|
await client.execute({
|
|
@@ -5326,7 +5410,7 @@ async function retryPendingMessages() {
|
|
|
5326
5410
|
sql: `SELECT * FROM messages
|
|
5327
5411
|
WHERE status = 'pending' AND retry_count < ?
|
|
5328
5412
|
ORDER BY id`,
|
|
5329
|
-
args: [
|
|
5413
|
+
args: [MAX_RETRIES2]
|
|
5330
5414
|
});
|
|
5331
5415
|
let delivered = 0;
|
|
5332
5416
|
for (const row of result.rows) {
|
|
@@ -5339,13 +5423,13 @@ async function retryPendingMessages() {
|
|
|
5339
5423
|
}
|
|
5340
5424
|
return delivered;
|
|
5341
5425
|
}
|
|
5342
|
-
var
|
|
5426
|
+
var MAX_RETRIES2, _wsClientSend;
|
|
5343
5427
|
var init_messaging = __esm({
|
|
5344
5428
|
"src/lib/messaging.ts"() {
|
|
5345
5429
|
"use strict";
|
|
5346
5430
|
init_database();
|
|
5347
5431
|
init_tmux_routing();
|
|
5348
|
-
|
|
5432
|
+
MAX_RETRIES2 = 10;
|
|
5349
5433
|
_wsClientSend = null;
|
|
5350
5434
|
}
|
|
5351
5435
|
});
|
|
@@ -5355,7 +5439,7 @@ import crypto4 from "crypto";
|
|
|
5355
5439
|
import path12 from "path";
|
|
5356
5440
|
import os5 from "os";
|
|
5357
5441
|
import {
|
|
5358
|
-
readFileSync as
|
|
5442
|
+
readFileSync as readFileSync10,
|
|
5359
5443
|
readdirSync,
|
|
5360
5444
|
unlinkSync as unlinkSync2,
|
|
5361
5445
|
existsSync as existsSync11,
|
|
@@ -5407,7 +5491,7 @@ import crypto5 from "crypto";
|
|
|
5407
5491
|
import path13 from "path";
|
|
5408
5492
|
import { execSync as execSync5 } from "child_process";
|
|
5409
5493
|
import { mkdir as mkdir4, writeFile as writeFile4, appendFile } from "fs/promises";
|
|
5410
|
-
import { existsSync as existsSync12, readFileSync as
|
|
5494
|
+
import { existsSync as existsSync12, readFileSync as readFileSync11 } from "fs";
|
|
5411
5495
|
async function writeCheckpoint(input) {
|
|
5412
5496
|
const client = getClient();
|
|
5413
5497
|
const row = await resolveTask(client, input.taskId);
|
|
@@ -5784,7 +5868,7 @@ async function ensureGitignoreExe(baseDir) {
|
|
|
5784
5868
|
const gitignorePath = path13.join(baseDir, ".gitignore");
|
|
5785
5869
|
try {
|
|
5786
5870
|
if (existsSync12(gitignorePath)) {
|
|
5787
|
-
const content =
|
|
5871
|
+
const content = readFileSync11(gitignorePath, "utf-8");
|
|
5788
5872
|
if (/^\/?exe\/?$/m.test(content)) return;
|
|
5789
5873
|
await appendFile(gitignorePath, "\n# Employee task assignments (private)\n/exe/\n");
|
|
5790
5874
|
} else {
|
|
@@ -6160,7 +6244,7 @@ async function dispatchTaskToEmployee(input) {
|
|
|
6160
6244
|
} else {
|
|
6161
6245
|
const projectDir = input.projectDir ?? process.cwd();
|
|
6162
6246
|
const result = ensureEmployee(input.assignedTo, exeSession, projectDir, {
|
|
6163
|
-
autoInstance: input.assignedTo
|
|
6247
|
+
autoInstance: isMultiInstance(input.assignedTo)
|
|
6164
6248
|
});
|
|
6165
6249
|
if (result.status === "failed") {
|
|
6166
6250
|
process.stderr.write(
|
|
@@ -6195,6 +6279,7 @@ var init_tasks_notify = __esm({
|
|
|
6195
6279
|
init_session_key();
|
|
6196
6280
|
init_notifications();
|
|
6197
6281
|
init_transport();
|
|
6282
|
+
init_employees();
|
|
6198
6283
|
}
|
|
6199
6284
|
});
|
|
6200
6285
|
|
|
@@ -6666,7 +6751,7 @@ var init_tasks = __esm({
|
|
|
6666
6751
|
});
|
|
6667
6752
|
|
|
6668
6753
|
// src/automation/trigger-engine.ts
|
|
6669
|
-
import { readFileSync as
|
|
6754
|
+
import { readFileSync as readFileSync12, writeFileSync as writeFileSync6, existsSync as existsSync14, mkdirSync as mkdirSync8 } from "fs";
|
|
6670
6755
|
import { randomUUID as randomUUID7 } from "crypto";
|
|
6671
6756
|
import path18 from "path";
|
|
6672
6757
|
import os6 from "os";
|
|
@@ -6724,7 +6809,7 @@ function evaluateConditions(conditions, record) {
|
|
|
6724
6809
|
function loadTriggers(project) {
|
|
6725
6810
|
if (!existsSync14(TRIGGERS_PATH)) return [];
|
|
6726
6811
|
try {
|
|
6727
|
-
const raw =
|
|
6812
|
+
const raw = readFileSync12(TRIGGERS_PATH, "utf-8");
|
|
6728
6813
|
const all = JSON.parse(raw);
|
|
6729
6814
|
if (!Array.isArray(all)) return [];
|
|
6730
6815
|
if (project) {
|
|
@@ -7020,7 +7105,7 @@ var init_crm_webhook = __esm({
|
|
|
7020
7105
|
});
|
|
7021
7106
|
|
|
7022
7107
|
// src/bin/exe-gateway.ts
|
|
7023
|
-
import { existsSync as existsSync15, readFileSync as
|
|
7108
|
+
import { existsSync as existsSync15, readFileSync as readFileSync13 } from "fs";
|
|
7024
7109
|
import path19 from "path";
|
|
7025
7110
|
import os7 from "os";
|
|
7026
7111
|
|
|
@@ -7857,7 +7942,7 @@ function loadConfig2() {
|
|
|
7857
7942
|
return {};
|
|
7858
7943
|
}
|
|
7859
7944
|
try {
|
|
7860
|
-
const raw =
|
|
7945
|
+
const raw = readFileSync13(CONFIG_PATH3, "utf-8");
|
|
7861
7946
|
return JSON.parse(raw);
|
|
7862
7947
|
} catch (err) {
|
|
7863
7948
|
console.error(
|
|
@@ -15,12 +15,68 @@ var __export = (target, all) => {
|
|
|
15
15
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
16
16
|
};
|
|
17
17
|
|
|
18
|
+
// src/lib/db-retry.ts
|
|
19
|
+
function isBusyError(err) {
|
|
20
|
+
if (err instanceof Error) {
|
|
21
|
+
const msg = err.message.toLowerCase();
|
|
22
|
+
return msg.includes("sqlite_busy") || msg.includes("database is locked");
|
|
23
|
+
}
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
function delay(ms) {
|
|
27
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
28
|
+
}
|
|
29
|
+
async function retryOnBusy(fn, label) {
|
|
30
|
+
let lastError;
|
|
31
|
+
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
32
|
+
try {
|
|
33
|
+
return await fn();
|
|
34
|
+
} catch (err) {
|
|
35
|
+
lastError = err;
|
|
36
|
+
if (!isBusyError(err) || attempt === MAX_RETRIES) {
|
|
37
|
+
throw err;
|
|
38
|
+
}
|
|
39
|
+
const backoff = BASE_DELAY_MS * Math.pow(2, attempt);
|
|
40
|
+
const jitter = Math.floor(Math.random() * MAX_JITTER_MS);
|
|
41
|
+
process.stderr.write(
|
|
42
|
+
`[exe-os] SQLITE_BUSY ${label} retry ${attempt + 1}/${MAX_RETRIES} \u2014 waiting ${backoff + jitter}ms
|
|
43
|
+
`
|
|
44
|
+
);
|
|
45
|
+
await delay(backoff + jitter);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
throw lastError;
|
|
49
|
+
}
|
|
50
|
+
function wrapWithRetry(client) {
|
|
51
|
+
return new Proxy(client, {
|
|
52
|
+
get(target, prop, receiver) {
|
|
53
|
+
if (prop === "execute") {
|
|
54
|
+
return (sql) => retryOnBusy(() => target.execute(sql), "execute");
|
|
55
|
+
}
|
|
56
|
+
if (prop === "batch") {
|
|
57
|
+
return (stmts) => retryOnBusy(() => target.batch(stmts), "batch");
|
|
58
|
+
}
|
|
59
|
+
return Reflect.get(target, prop, receiver);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
var MAX_RETRIES, BASE_DELAY_MS, MAX_JITTER_MS;
|
|
64
|
+
var init_db_retry = __esm({
|
|
65
|
+
"src/lib/db-retry.ts"() {
|
|
66
|
+
"use strict";
|
|
67
|
+
MAX_RETRIES = 3;
|
|
68
|
+
BASE_DELAY_MS = 200;
|
|
69
|
+
MAX_JITTER_MS = 300;
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
|
|
18
73
|
// src/lib/database.ts
|
|
19
74
|
import { createClient } from "@libsql/client";
|
|
20
75
|
async function initDatabase(config) {
|
|
21
76
|
if (_client) {
|
|
22
77
|
_client.close();
|
|
23
78
|
_client = null;
|
|
79
|
+
_resilientClient = null;
|
|
24
80
|
}
|
|
25
81
|
const opts = {
|
|
26
82
|
url: `file:${config.dbPath}`
|
|
@@ -29,17 +85,24 @@ async function initDatabase(config) {
|
|
|
29
85
|
opts.encryptionKey = config.encryptionKey;
|
|
30
86
|
}
|
|
31
87
|
_client = createClient(opts);
|
|
88
|
+
_resilientClient = wrapWithRetry(_client);
|
|
32
89
|
}
|
|
33
90
|
function getClient() {
|
|
91
|
+
if (!_resilientClient) {
|
|
92
|
+
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
93
|
+
}
|
|
94
|
+
return _resilientClient;
|
|
95
|
+
}
|
|
96
|
+
function getRawClient() {
|
|
34
97
|
if (!_client) {
|
|
35
98
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
36
99
|
}
|
|
37
100
|
return _client;
|
|
38
101
|
}
|
|
39
102
|
async function ensureSchema() {
|
|
40
|
-
const client =
|
|
103
|
+
const client = getRawClient();
|
|
41
104
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
42
|
-
await client.execute("PRAGMA busy_timeout =
|
|
105
|
+
await client.execute("PRAGMA busy_timeout = 30000");
|
|
43
106
|
try {
|
|
44
107
|
await client.execute("PRAGMA libsql_vector_search_ef = 128");
|
|
45
108
|
} catch {
|
|
@@ -828,11 +891,13 @@ async function ensureSchema() {
|
|
|
828
891
|
}
|
|
829
892
|
}
|
|
830
893
|
}
|
|
831
|
-
var _client, initTurso;
|
|
894
|
+
var _client, _resilientClient, initTurso;
|
|
832
895
|
var init_database = __esm({
|
|
833
896
|
"src/lib/database.ts"() {
|
|
834
897
|
"use strict";
|
|
898
|
+
init_db_retry();
|
|
835
899
|
_client = null;
|
|
900
|
+
_resilientClient = null;
|
|
836
901
|
initTurso = initDatabase;
|
|
837
902
|
}
|
|
838
903
|
});
|
|
@@ -1103,7 +1168,7 @@ function listShards() {
|
|
|
1103
1168
|
}
|
|
1104
1169
|
async function ensureShardSchema(client) {
|
|
1105
1170
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
1106
|
-
await client.execute("PRAGMA busy_timeout =
|
|
1171
|
+
await client.execute("PRAGMA busy_timeout = 30000");
|
|
1107
1172
|
try {
|
|
1108
1173
|
await client.execute("PRAGMA libsql_vector_search_ef = 128");
|
|
1109
1174
|
} catch {
|
|
@@ -1289,7 +1354,7 @@ var init_shard_manager = __esm({
|
|
|
1289
1354
|
|
|
1290
1355
|
// src/lib/employees.ts
|
|
1291
1356
|
import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
|
|
1292
|
-
import { existsSync as existsSync4, symlinkSync, readlinkSync } from "fs";
|
|
1357
|
+
import { existsSync as existsSync4, symlinkSync, readlinkSync, readFileSync as readFileSync2 } from "fs";
|
|
1293
1358
|
import { execSync } from "child_process";
|
|
1294
1359
|
import path4 from "path";
|
|
1295
1360
|
var EMPLOYEES_PATH;
|
|
@@ -1306,7 +1371,7 @@ import crypto2 from "crypto";
|
|
|
1306
1371
|
import path5 from "path";
|
|
1307
1372
|
import os2 from "os";
|
|
1308
1373
|
import {
|
|
1309
|
-
readFileSync as
|
|
1374
|
+
readFileSync as readFileSync3,
|
|
1310
1375
|
readdirSync,
|
|
1311
1376
|
unlinkSync,
|
|
1312
1377
|
existsSync as existsSync5,
|
|
@@ -1324,7 +1389,7 @@ import crypto3 from "crypto";
|
|
|
1324
1389
|
import path6 from "path";
|
|
1325
1390
|
import { execSync as execSync2 } from "child_process";
|
|
1326
1391
|
import { mkdir as mkdir4, writeFile as writeFile4, appendFile } from "fs/promises";
|
|
1327
|
-
import { existsSync as existsSync6, readFileSync as
|
|
1392
|
+
import { existsSync as existsSync6, readFileSync as readFileSync4 } from "fs";
|
|
1328
1393
|
var init_tasks_crud = __esm({
|
|
1329
1394
|
"src/lib/tasks-crud.ts"() {
|
|
1330
1395
|
"use strict";
|
|
@@ -1388,7 +1453,7 @@ var init_provider_table = __esm({
|
|
|
1388
1453
|
});
|
|
1389
1454
|
|
|
1390
1455
|
// src/lib/intercom-queue.ts
|
|
1391
|
-
import { readFileSync as
|
|
1456
|
+
import { readFileSync as readFileSync5, writeFileSync, renameSync as renameSync2, existsSync as existsSync7, mkdirSync as mkdirSync2 } from "fs";
|
|
1392
1457
|
import path8 from "path";
|
|
1393
1458
|
import os4 from "os";
|
|
1394
1459
|
var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
|
|
@@ -1402,7 +1467,7 @@ var init_intercom_queue = __esm({
|
|
|
1402
1467
|
});
|
|
1403
1468
|
|
|
1404
1469
|
// src/lib/license.ts
|
|
1405
|
-
import { readFileSync as
|
|
1470
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync2, existsSync as existsSync8, mkdirSync as mkdirSync3 } from "fs";
|
|
1406
1471
|
import { randomUUID } from "crypto";
|
|
1407
1472
|
import path9 from "path";
|
|
1408
1473
|
import { jwtVerify, importSPKI } from "jose";
|
|
@@ -1418,7 +1483,7 @@ var init_license = __esm({
|
|
|
1418
1483
|
});
|
|
1419
1484
|
|
|
1420
1485
|
// src/lib/plan-limits.ts
|
|
1421
|
-
import { readFileSync as
|
|
1486
|
+
import { readFileSync as readFileSync7, existsSync as existsSync9 } from "fs";
|
|
1422
1487
|
import path10 from "path";
|
|
1423
1488
|
var CACHE_PATH2;
|
|
1424
1489
|
var init_plan_limits = __esm({
|
|
@@ -1483,7 +1548,7 @@ var init_tasks_review = __esm({
|
|
|
1483
1548
|
|
|
1484
1549
|
// src/bin/exe-heartbeat.ts
|
|
1485
1550
|
import { createHash } from "crypto";
|
|
1486
|
-
import { readFileSync as
|
|
1551
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4 } from "fs";
|
|
1487
1552
|
import os6 from "os";
|
|
1488
1553
|
import path13 from "path";
|
|
1489
1554
|
|
|
@@ -1622,7 +1687,7 @@ function getMarkerPath() {
|
|
|
1622
1687
|
var UNREAD_MESSAGE_STATUSES = ["pending", "delivered"];
|
|
1623
1688
|
function readMarker() {
|
|
1624
1689
|
try {
|
|
1625
|
-
const raw =
|
|
1690
|
+
const raw = readFileSync8(getMarkerPath(), "utf8");
|
|
1626
1691
|
const parsed = JSON.parse(raw);
|
|
1627
1692
|
if (typeof parsed.lastFiredAt !== "string" || typeof parsed.lastSurfaceHash !== "string") {
|
|
1628
1693
|
return null;
|
|
@@ -1740,7 +1805,6 @@ async function runHeartbeat() {
|
|
|
1740
1805
|
writeMarker({ lastFiredAt: new Date(now).toISOString(), lastSurfaceHash: hash });
|
|
1741
1806
|
try {
|
|
1742
1807
|
const client = getClient();
|
|
1743
|
-
await client.execute({ sql: "PRAGMA busy_timeout = 5000", args: [] });
|
|
1744
1808
|
await client.execute({
|
|
1745
1809
|
sql: `UPDATE messages SET status = 'acknowledged', processed_at = datetime('now')
|
|
1746
1810
|
WHERE target_agent = 'exe' AND status IN ('pending', 'delivered')`,
|