@askexenow/exe-os 0.8.33 → 0.8.37
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 +341 -349
- package/dist/bin/backfill-responses.js +81 -13
- package/dist/bin/backfill-vectors.js +72 -12
- package/dist/bin/cleanup-stale-review-tasks.js +63 -3
- package/dist/bin/cli.js +1737 -1117
- package/dist/bin/exe-assign.js +89 -19
- package/dist/bin/exe-boot.js +951 -101
- package/dist/bin/exe-call.js +61 -2
- package/dist/bin/exe-dispatch.js +61 -13
- package/dist/bin/exe-doctor.js +63 -3
- package/dist/bin/exe-export-behaviors.js +71 -3
- package/dist/bin/exe-forget.js +69 -4
- package/dist/bin/exe-gateway.js +178 -45
- package/dist/bin/exe-heartbeat.js +79 -14
- package/dist/bin/exe-kill.js +71 -3
- package/dist/bin/exe-launch-agent.js +148 -14
- package/dist/bin/exe-link.js +1437 -0
- package/dist/bin/exe-new-employee.js +98 -13
- package/dist/bin/exe-pending-messages.js +74 -8
- package/dist/bin/exe-pending-notifications.js +63 -3
- package/dist/bin/exe-pending-reviews.js +77 -11
- package/dist/bin/exe-rename.js +1287 -0
- package/dist/bin/exe-review.js +73 -5
- package/dist/bin/exe-search.js +88 -14
- package/dist/bin/exe-session-cleanup.js +102 -28
- package/dist/bin/exe-status.js +64 -4
- package/dist/bin/exe-team.js +64 -4
- package/dist/bin/git-sweep.js +80 -5
- package/dist/bin/graph-backfill.js +71 -3
- package/dist/bin/graph-export.js +71 -3
- package/dist/bin/install.js +38 -8
- package/dist/bin/scan-tasks.js +80 -5
- package/dist/bin/setup.js +128 -10
- package/dist/bin/shard-migrate.js +71 -3
- package/dist/bin/wiki-sync.js +71 -3
- package/dist/gateway/index.js +179 -46
- package/dist/hooks/bug-report-worker.js +254 -28
- package/dist/hooks/commit-complete.js +80 -5
- package/dist/hooks/error-recall.js +89 -15
- package/dist/hooks/exe-heartbeat-hook.js +1 -1
- package/dist/hooks/ingest-worker.js +185 -51
- package/dist/hooks/ingest.js +1 -1
- package/dist/hooks/instructions-loaded.js +81 -6
- package/dist/hooks/notification.js +81 -6
- package/dist/hooks/post-compact.js +81 -6
- package/dist/hooks/pre-compact.js +81 -6
- package/dist/hooks/pre-tool-use.js +423 -196
- package/dist/hooks/prompt-ingest-worker.js +91 -23
- package/dist/hooks/prompt-submit.js +159 -45
- package/dist/hooks/response-ingest-worker.js +96 -23
- package/dist/hooks/session-end.js +81 -6
- package/dist/hooks/session-start.js +89 -15
- package/dist/hooks/stop.js +81 -6
- package/dist/hooks/subagent-stop.js +81 -6
- package/dist/hooks/summary-worker.js +807 -55
- package/dist/index.js +198 -60
- package/dist/lib/cloud-sync.js +703 -18
- 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 +26 -0
- package/dist/lib/employees.js +34 -1
- package/dist/lib/exe-daemon.js +207 -74
- package/dist/lib/hybrid-search.js +88 -14
- package/dist/lib/identity-templates.js +51 -0
- package/dist/lib/identity.js +3 -3
- package/dist/lib/messaging.js +65 -17
- 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 +73 -4
- package/dist/lib/task-router.js +4 -2
- package/dist/lib/tasks.js +95 -28
- package/dist/lib/tmux-routing.js +92 -23
- package/dist/mcp/server.js +800 -74
- 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 +198 -31
- package/dist/mcp/tools/deactivate-behavior.js +4 -4
- package/dist/mcp/tools/list-reminders.js +3 -3
- package/dist/mcp/tools/list-tasks.js +19 -9
- package/dist/mcp/tools/send-message.js +69 -21
- package/dist/mcp/tools/update-task.js +28 -18
- package/dist/runtime/index.js +166 -28
- package/dist/tui/App.js +193 -40
- package/package.json +7 -3
- package/src/commands/exe/afk.md +116 -0
- package/src/commands/exe/rename.md +12 -0
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 {
|
|
@@ -2134,7 +2200,8 @@ async function writeMemory(record) {
|
|
|
2134
2200
|
has_error: record.has_error ? 1 : 0,
|
|
2135
2201
|
raw_text: record.raw_text,
|
|
2136
2202
|
vector: record.vector,
|
|
2137
|
-
version:
|
|
2203
|
+
version: 0,
|
|
2204
|
+
// Placeholder — assigned atomically at flush time
|
|
2138
2205
|
task_id: record.task_id ?? null,
|
|
2139
2206
|
importance: record.importance ?? 5,
|
|
2140
2207
|
status: record.status ?? "active",
|
|
@@ -2168,6 +2235,13 @@ async function flushBatch() {
|
|
|
2168
2235
|
_flushing = true;
|
|
2169
2236
|
try {
|
|
2170
2237
|
const batch = _pendingRecords.slice(0);
|
|
2238
|
+
const client = getClient();
|
|
2239
|
+
const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
|
|
2240
|
+
let baseVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
|
|
2241
|
+
for (const row of batch) {
|
|
2242
|
+
row.version = baseVersion++;
|
|
2243
|
+
}
|
|
2244
|
+
_nextVersion = baseVersion;
|
|
2171
2245
|
const buildStmt = (row) => {
|
|
2172
2246
|
const hasVector = row.vector !== null;
|
|
2173
2247
|
const taskId = row.task_id ?? null;
|
|
@@ -4510,25 +4584,43 @@ var init_intercom_queue = __esm({
|
|
|
4510
4584
|
|
|
4511
4585
|
// src/lib/employees.ts
|
|
4512
4586
|
import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
|
|
4513
|
-
import { existsSync as existsSync8, symlinkSync, readlinkSync } from "fs";
|
|
4587
|
+
import { existsSync as existsSync8, symlinkSync, readlinkSync, readFileSync as readFileSync7 } from "fs";
|
|
4514
4588
|
import { execSync as execSync3 } from "child_process";
|
|
4515
4589
|
import path9 from "path";
|
|
4516
|
-
|
|
4590
|
+
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
4591
|
+
if (!existsSync8(employeesPath)) return [];
|
|
4592
|
+
try {
|
|
4593
|
+
return JSON.parse(readFileSync7(employeesPath, "utf-8"));
|
|
4594
|
+
} catch {
|
|
4595
|
+
return [];
|
|
4596
|
+
}
|
|
4597
|
+
}
|
|
4598
|
+
function getEmployee(employees, name) {
|
|
4599
|
+
return employees.find((e) => e.name.toLowerCase() === name.toLowerCase());
|
|
4600
|
+
}
|
|
4601
|
+
function isMultiInstance(agentName, employees) {
|
|
4602
|
+
const roster = employees ?? loadEmployeesSync();
|
|
4603
|
+
const emp = getEmployee(roster, agentName);
|
|
4604
|
+
if (!emp) return false;
|
|
4605
|
+
return MULTI_INSTANCE_ROLES.has(emp.role.toLowerCase());
|
|
4606
|
+
}
|
|
4607
|
+
var EMPLOYEES_PATH, MULTI_INSTANCE_ROLES;
|
|
4517
4608
|
var init_employees = __esm({
|
|
4518
4609
|
"src/lib/employees.ts"() {
|
|
4519
4610
|
"use strict";
|
|
4520
4611
|
init_config();
|
|
4521
4612
|
EMPLOYEES_PATH = path9.join(EXE_AI_DIR, "exe-employees.json");
|
|
4613
|
+
MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
|
|
4522
4614
|
}
|
|
4523
4615
|
});
|
|
4524
4616
|
|
|
4525
4617
|
// src/lib/plan-limits.ts
|
|
4526
|
-
import { readFileSync as
|
|
4618
|
+
import { readFileSync as readFileSync8, existsSync as existsSync9 } from "fs";
|
|
4527
4619
|
import path10 from "path";
|
|
4528
4620
|
function getLicenseSync() {
|
|
4529
4621
|
try {
|
|
4530
4622
|
if (!existsSync9(CACHE_PATH2)) return freeLicense();
|
|
4531
|
-
const raw = JSON.parse(
|
|
4623
|
+
const raw = JSON.parse(readFileSync8(CACHE_PATH2, "utf8"));
|
|
4532
4624
|
if (!raw.token || typeof raw.token !== "string") return freeLicense();
|
|
4533
4625
|
const parts = raw.token.split(".");
|
|
4534
4626
|
if (parts.length !== 3) return freeLicense();
|
|
@@ -4567,7 +4659,7 @@ function assertEmployeeLimitSync(rosterPath) {
|
|
|
4567
4659
|
let count = 0;
|
|
4568
4660
|
try {
|
|
4569
4661
|
if (existsSync9(filePath)) {
|
|
4570
|
-
const raw =
|
|
4662
|
+
const raw = readFileSync8(filePath, "utf8");
|
|
4571
4663
|
const employees = JSON.parse(raw);
|
|
4572
4664
|
count = Array.isArray(employees) ? employees.length : 0;
|
|
4573
4665
|
}
|
|
@@ -4602,10 +4694,46 @@ var init_plan_limits = __esm({
|
|
|
4602
4694
|
|
|
4603
4695
|
// src/lib/tmux-routing.ts
|
|
4604
4696
|
import { execFileSync as execFileSync2, execSync as execSync4 } from "child_process";
|
|
4605
|
-
import { readFileSync as
|
|
4697
|
+
import { readFileSync as readFileSync9, writeFileSync as writeFileSync4, mkdirSync as mkdirSync6, existsSync as existsSync10, appendFileSync } from "fs";
|
|
4606
4698
|
import path11 from "path";
|
|
4607
4699
|
import os4 from "os";
|
|
4608
4700
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
4701
|
+
import { unlinkSync as unlinkSync2 } from "fs";
|
|
4702
|
+
function spawnLockPath(sessionName) {
|
|
4703
|
+
return path11.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
|
|
4704
|
+
}
|
|
4705
|
+
function isProcessAlive(pid) {
|
|
4706
|
+
try {
|
|
4707
|
+
process.kill(pid, 0);
|
|
4708
|
+
return true;
|
|
4709
|
+
} catch {
|
|
4710
|
+
return false;
|
|
4711
|
+
}
|
|
4712
|
+
}
|
|
4713
|
+
function acquireSpawnLock2(sessionName) {
|
|
4714
|
+
if (!existsSync10(SPAWN_LOCK_DIR)) {
|
|
4715
|
+
mkdirSync6(SPAWN_LOCK_DIR, { recursive: true });
|
|
4716
|
+
}
|
|
4717
|
+
const lockFile = spawnLockPath(sessionName);
|
|
4718
|
+
if (existsSync10(lockFile)) {
|
|
4719
|
+
try {
|
|
4720
|
+
const lock = JSON.parse(readFileSync9(lockFile, "utf8"));
|
|
4721
|
+
const age = Date.now() - lock.timestamp;
|
|
4722
|
+
if (isProcessAlive(lock.pid) && age < 6e4) {
|
|
4723
|
+
return false;
|
|
4724
|
+
}
|
|
4725
|
+
} catch {
|
|
4726
|
+
}
|
|
4727
|
+
}
|
|
4728
|
+
writeFileSync4(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
|
|
4729
|
+
return true;
|
|
4730
|
+
}
|
|
4731
|
+
function releaseSpawnLock2(sessionName) {
|
|
4732
|
+
try {
|
|
4733
|
+
unlinkSync2(spawnLockPath(sessionName));
|
|
4734
|
+
} catch {
|
|
4735
|
+
}
|
|
4736
|
+
}
|
|
4609
4737
|
function resolveBehaviorsExporterScript() {
|
|
4610
4738
|
try {
|
|
4611
4739
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
@@ -4651,7 +4779,7 @@ function extractRootExe(name) {
|
|
|
4651
4779
|
}
|
|
4652
4780
|
function getParentExe(sessionKey) {
|
|
4653
4781
|
try {
|
|
4654
|
-
const data = JSON.parse(
|
|
4782
|
+
const data = JSON.parse(readFileSync9(path11.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
4655
4783
|
return data.parentExe || null;
|
|
4656
4784
|
} catch {
|
|
4657
4785
|
return null;
|
|
@@ -4659,7 +4787,7 @@ function getParentExe(sessionKey) {
|
|
|
4659
4787
|
}
|
|
4660
4788
|
function getDispatchedBy(sessionKey) {
|
|
4661
4789
|
try {
|
|
4662
|
-
const data = JSON.parse(
|
|
4790
|
+
const data = JSON.parse(readFileSync9(
|
|
4663
4791
|
path11.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
|
|
4664
4792
|
"utf8"
|
|
4665
4793
|
));
|
|
@@ -4686,17 +4814,17 @@ function isEmployeeAlive(sessionName) {
|
|
|
4686
4814
|
}
|
|
4687
4815
|
function findFreeInstance(employeeName, exeSession, maxInstances = 10, isAlive = isEmployeeAlive) {
|
|
4688
4816
|
const base = employeeSessionName(employeeName, exeSession);
|
|
4689
|
-
if (!isAlive(base)) return 0;
|
|
4817
|
+
if (!isAlive(base) && acquireSpawnLock2(base)) return 0;
|
|
4690
4818
|
for (let i = 2; i <= maxInstances; i++) {
|
|
4691
4819
|
const candidate = employeeSessionName(employeeName, exeSession, i);
|
|
4692
|
-
if (!isAlive(candidate)) return i;
|
|
4820
|
+
if (!isAlive(candidate) && acquireSpawnLock2(candidate)) return i;
|
|
4693
4821
|
}
|
|
4694
4822
|
return null;
|
|
4695
4823
|
}
|
|
4696
4824
|
function readDebounceState() {
|
|
4697
4825
|
try {
|
|
4698
4826
|
if (!existsSync10(DEBOUNCE_FILE)) return {};
|
|
4699
|
-
return JSON.parse(
|
|
4827
|
+
return JSON.parse(readFileSync9(DEBOUNCE_FILE, "utf8"));
|
|
4700
4828
|
} catch {
|
|
4701
4829
|
return {};
|
|
4702
4830
|
}
|
|
@@ -4892,7 +5020,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4892
5020
|
const claudeJsonPath = path11.join(os4.homedir(), ".claude.json");
|
|
4893
5021
|
let claudeJson = {};
|
|
4894
5022
|
try {
|
|
4895
|
-
claudeJson = JSON.parse(
|
|
5023
|
+
claudeJson = JSON.parse(readFileSync9(claudeJsonPath, "utf8"));
|
|
4896
5024
|
} catch {
|
|
4897
5025
|
}
|
|
4898
5026
|
if (!claudeJson.projects) claudeJson.projects = {};
|
|
@@ -4910,7 +5038,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4910
5038
|
const settingsPath = path11.join(projSettingsDir, "settings.json");
|
|
4911
5039
|
let settings = {};
|
|
4912
5040
|
try {
|
|
4913
|
-
settings = JSON.parse(
|
|
5041
|
+
settings = JSON.parse(readFileSync9(settingsPath, "utf8"));
|
|
4914
5042
|
} catch {
|
|
4915
5043
|
}
|
|
4916
5044
|
const perms = settings.permissions ?? {};
|
|
@@ -5023,6 +5151,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5023
5151
|
command: spawnCommand
|
|
5024
5152
|
});
|
|
5025
5153
|
if (spawnResult.error) {
|
|
5154
|
+
releaseSpawnLock2(sessionName);
|
|
5026
5155
|
return { sessionName, error: `tmux new-session failed: ${spawnResult.error}` };
|
|
5027
5156
|
}
|
|
5028
5157
|
transport.pipeLog(sessionName, logFile);
|
|
@@ -5060,6 +5189,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5060
5189
|
}
|
|
5061
5190
|
}
|
|
5062
5191
|
if (!booted) {
|
|
5192
|
+
releaseSpawnLock2(sessionName);
|
|
5063
5193
|
return { sessionName, error: `${useExeAgent ? "exe-agent" : "claude"} did not boot within 15s` };
|
|
5064
5194
|
}
|
|
5065
5195
|
if (!useExeAgent) {
|
|
@@ -5076,9 +5206,10 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5076
5206
|
pid: 0,
|
|
5077
5207
|
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5078
5208
|
});
|
|
5209
|
+
releaseSpawnLock2(sessionName);
|
|
5079
5210
|
return { sessionName };
|
|
5080
5211
|
}
|
|
5081
|
-
var SESSION_CACHE, BEHAVIORS_EXPORT_TIMEOUT_MS, INTERCOM_DEBOUNCE_MS, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS, BUSY_PATTERN;
|
|
5212
|
+
var SPAWN_LOCK_DIR, SESSION_CACHE, BEHAVIORS_EXPORT_TIMEOUT_MS, INTERCOM_DEBOUNCE_MS, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS, BUSY_PATTERN;
|
|
5082
5213
|
var init_tmux_routing = __esm({
|
|
5083
5214
|
"src/lib/tmux-routing.ts"() {
|
|
5084
5215
|
"use strict";
|
|
@@ -5090,6 +5221,7 @@ var init_tmux_routing = __esm({
|
|
|
5090
5221
|
init_provider_table();
|
|
5091
5222
|
init_intercom_queue();
|
|
5092
5223
|
init_plan_limits();
|
|
5224
|
+
SPAWN_LOCK_DIR = path11.join(os4.homedir(), ".exe-os", "spawn-locks");
|
|
5093
5225
|
SESSION_CACHE = path11.join(os4.homedir(), ".exe-os", "session-cache");
|
|
5094
5226
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
5095
5227
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
@@ -5237,7 +5369,7 @@ async function deliverLocalMessage(messageId) {
|
|
|
5237
5369
|
return true;
|
|
5238
5370
|
} catch {
|
|
5239
5371
|
const newRetryCount = msg.retryCount + 1;
|
|
5240
|
-
if (newRetryCount >=
|
|
5372
|
+
if (newRetryCount >= MAX_RETRIES2) {
|
|
5241
5373
|
await markFailed(messageId, "session unavailable after 10 retries");
|
|
5242
5374
|
} else {
|
|
5243
5375
|
await client.execute({
|
|
@@ -5326,7 +5458,7 @@ async function retryPendingMessages() {
|
|
|
5326
5458
|
sql: `SELECT * FROM messages
|
|
5327
5459
|
WHERE status = 'pending' AND retry_count < ?
|
|
5328
5460
|
ORDER BY id`,
|
|
5329
|
-
args: [
|
|
5461
|
+
args: [MAX_RETRIES2]
|
|
5330
5462
|
});
|
|
5331
5463
|
let delivered = 0;
|
|
5332
5464
|
for (const row of result.rows) {
|
|
@@ -5339,13 +5471,13 @@ async function retryPendingMessages() {
|
|
|
5339
5471
|
}
|
|
5340
5472
|
return delivered;
|
|
5341
5473
|
}
|
|
5342
|
-
var
|
|
5474
|
+
var MAX_RETRIES2, _wsClientSend;
|
|
5343
5475
|
var init_messaging = __esm({
|
|
5344
5476
|
"src/lib/messaging.ts"() {
|
|
5345
5477
|
"use strict";
|
|
5346
5478
|
init_database();
|
|
5347
5479
|
init_tmux_routing();
|
|
5348
|
-
|
|
5480
|
+
MAX_RETRIES2 = 10;
|
|
5349
5481
|
_wsClientSend = null;
|
|
5350
5482
|
}
|
|
5351
5483
|
});
|
|
@@ -5355,9 +5487,9 @@ import crypto4 from "crypto";
|
|
|
5355
5487
|
import path12 from "path";
|
|
5356
5488
|
import os5 from "os";
|
|
5357
5489
|
import {
|
|
5358
|
-
readFileSync as
|
|
5490
|
+
readFileSync as readFileSync10,
|
|
5359
5491
|
readdirSync,
|
|
5360
|
-
unlinkSync as
|
|
5492
|
+
unlinkSync as unlinkSync3,
|
|
5361
5493
|
existsSync as existsSync11,
|
|
5362
5494
|
rmdirSync
|
|
5363
5495
|
} from "fs";
|
|
@@ -5407,7 +5539,7 @@ import crypto5 from "crypto";
|
|
|
5407
5539
|
import path13 from "path";
|
|
5408
5540
|
import { execSync as execSync5 } from "child_process";
|
|
5409
5541
|
import { mkdir as mkdir4, writeFile as writeFile4, appendFile } from "fs/promises";
|
|
5410
|
-
import { existsSync as existsSync12, readFileSync as
|
|
5542
|
+
import { existsSync as existsSync12, readFileSync as readFileSync11 } from "fs";
|
|
5411
5543
|
async function writeCheckpoint(input) {
|
|
5412
5544
|
const client = getClient();
|
|
5413
5545
|
const row = await resolveTask(client, input.taskId);
|
|
@@ -5784,7 +5916,7 @@ async function ensureGitignoreExe(baseDir) {
|
|
|
5784
5916
|
const gitignorePath = path13.join(baseDir, ".gitignore");
|
|
5785
5917
|
try {
|
|
5786
5918
|
if (existsSync12(gitignorePath)) {
|
|
5787
|
-
const content =
|
|
5919
|
+
const content = readFileSync11(gitignorePath, "utf-8");
|
|
5788
5920
|
if (/^\/?exe\/?$/m.test(content)) return;
|
|
5789
5921
|
await appendFile(gitignorePath, "\n# Employee task assignments (private)\n/exe/\n");
|
|
5790
5922
|
} else {
|
|
@@ -5805,7 +5937,7 @@ var init_tasks_crud = __esm({
|
|
|
5805
5937
|
|
|
5806
5938
|
// src/lib/tasks-review.ts
|
|
5807
5939
|
import path14 from "path";
|
|
5808
|
-
import { existsSync as existsSync13, readdirSync as readdirSync2, unlinkSync as
|
|
5940
|
+
import { existsSync as existsSync13, readdirSync as readdirSync2, unlinkSync as unlinkSync4 } from "fs";
|
|
5809
5941
|
async function countPendingReviews() {
|
|
5810
5942
|
const client = getClient();
|
|
5811
5943
|
const result = await client.execute({
|
|
@@ -5929,7 +6061,7 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
5929
6061
|
if (existsSync13(cacheDir)) {
|
|
5930
6062
|
for (const f of readdirSync2(cacheDir)) {
|
|
5931
6063
|
if (f.startsWith("review-notified-")) {
|
|
5932
|
-
|
|
6064
|
+
unlinkSync4(path14.join(cacheDir, f));
|
|
5933
6065
|
}
|
|
5934
6066
|
}
|
|
5935
6067
|
}
|
|
@@ -6160,7 +6292,7 @@ async function dispatchTaskToEmployee(input) {
|
|
|
6160
6292
|
} else {
|
|
6161
6293
|
const projectDir = input.projectDir ?? process.cwd();
|
|
6162
6294
|
const result = ensureEmployee(input.assignedTo, exeSession, projectDir, {
|
|
6163
|
-
autoInstance: input.assignedTo
|
|
6295
|
+
autoInstance: isMultiInstance(input.assignedTo)
|
|
6164
6296
|
});
|
|
6165
6297
|
if (result.status === "failed") {
|
|
6166
6298
|
process.stderr.write(
|
|
@@ -6195,6 +6327,7 @@ var init_tasks_notify = __esm({
|
|
|
6195
6327
|
init_session_key();
|
|
6196
6328
|
init_notifications();
|
|
6197
6329
|
init_transport();
|
|
6330
|
+
init_employees();
|
|
6198
6331
|
}
|
|
6199
6332
|
});
|
|
6200
6333
|
|
|
@@ -6529,7 +6662,7 @@ __export(tasks_exports, {
|
|
|
6529
6662
|
writeCheckpoint: () => writeCheckpoint
|
|
6530
6663
|
});
|
|
6531
6664
|
import path17 from "path";
|
|
6532
|
-
import { writeFileSync as writeFileSync5, mkdirSync as mkdirSync7, unlinkSync as
|
|
6665
|
+
import { writeFileSync as writeFileSync5, mkdirSync as mkdirSync7, unlinkSync as unlinkSync5 } from "fs";
|
|
6533
6666
|
async function createTask(input) {
|
|
6534
6667
|
const result = await createTaskCore(input);
|
|
6535
6668
|
if (!input.skipDispatch && result.status !== "blocked" && !process.env.VITEST) {
|
|
@@ -6555,7 +6688,7 @@ async function updateTask(input) {
|
|
|
6555
6688
|
writeFileSync5(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
|
|
6556
6689
|
} else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled") {
|
|
6557
6690
|
try {
|
|
6558
|
-
|
|
6691
|
+
unlinkSync5(cachePath);
|
|
6559
6692
|
} catch {
|
|
6560
6693
|
}
|
|
6561
6694
|
}
|
|
@@ -6666,7 +6799,7 @@ var init_tasks = __esm({
|
|
|
6666
6799
|
});
|
|
6667
6800
|
|
|
6668
6801
|
// src/automation/trigger-engine.ts
|
|
6669
|
-
import { readFileSync as
|
|
6802
|
+
import { readFileSync as readFileSync12, writeFileSync as writeFileSync6, existsSync as existsSync14, mkdirSync as mkdirSync8 } from "fs";
|
|
6670
6803
|
import { randomUUID as randomUUID7 } from "crypto";
|
|
6671
6804
|
import path18 from "path";
|
|
6672
6805
|
import os6 from "os";
|
|
@@ -6724,7 +6857,7 @@ function evaluateConditions(conditions, record) {
|
|
|
6724
6857
|
function loadTriggers(project) {
|
|
6725
6858
|
if (!existsSync14(TRIGGERS_PATH)) return [];
|
|
6726
6859
|
try {
|
|
6727
|
-
const raw =
|
|
6860
|
+
const raw = readFileSync12(TRIGGERS_PATH, "utf-8");
|
|
6728
6861
|
const all = JSON.parse(raw);
|
|
6729
6862
|
if (!Array.isArray(all)) return [];
|
|
6730
6863
|
if (project) {
|
|
@@ -7020,7 +7153,7 @@ var init_crm_webhook = __esm({
|
|
|
7020
7153
|
});
|
|
7021
7154
|
|
|
7022
7155
|
// src/bin/exe-gateway.ts
|
|
7023
|
-
import { existsSync as existsSync15, readFileSync as
|
|
7156
|
+
import { existsSync as existsSync15, readFileSync as readFileSync13 } from "fs";
|
|
7024
7157
|
import path19 from "path";
|
|
7025
7158
|
import os7 from "os";
|
|
7026
7159
|
|
|
@@ -7857,7 +7990,7 @@ function loadConfig2() {
|
|
|
7857
7990
|
return {};
|
|
7858
7991
|
}
|
|
7859
7992
|
try {
|
|
7860
|
-
const raw =
|
|
7993
|
+
const raw = readFileSync13(CONFIG_PATH3, "utf-8");
|
|
7861
7994
|
return JSON.parse(raw);
|
|
7862
7995
|
} catch (err) {
|
|
7863
7996
|
console.error(
|