@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
|
@@ -277,12 +277,68 @@ 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
|
import { createClient } from "@libsql/client";
|
|
282
337
|
async function initDatabase(config) {
|
|
283
338
|
if (_client) {
|
|
284
339
|
_client.close();
|
|
285
340
|
_client = null;
|
|
341
|
+
_resilientClient = null;
|
|
286
342
|
}
|
|
287
343
|
const opts = {
|
|
288
344
|
url: `file:${config.dbPath}`
|
|
@@ -291,17 +347,24 @@ async function initDatabase(config) {
|
|
|
291
347
|
opts.encryptionKey = config.encryptionKey;
|
|
292
348
|
}
|
|
293
349
|
_client = createClient(opts);
|
|
350
|
+
_resilientClient = wrapWithRetry(_client);
|
|
294
351
|
}
|
|
295
352
|
function getClient() {
|
|
353
|
+
if (!_resilientClient) {
|
|
354
|
+
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
355
|
+
}
|
|
356
|
+
return _resilientClient;
|
|
357
|
+
}
|
|
358
|
+
function getRawClient() {
|
|
296
359
|
if (!_client) {
|
|
297
360
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
298
361
|
}
|
|
299
362
|
return _client;
|
|
300
363
|
}
|
|
301
364
|
async function ensureSchema() {
|
|
302
|
-
const client =
|
|
365
|
+
const client = getRawClient();
|
|
303
366
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
304
|
-
await client.execute("PRAGMA busy_timeout =
|
|
367
|
+
await client.execute("PRAGMA busy_timeout = 30000");
|
|
305
368
|
try {
|
|
306
369
|
await client.execute("PRAGMA libsql_vector_search_ef = 128");
|
|
307
370
|
} catch {
|
|
@@ -1094,13 +1157,16 @@ async function disposeDatabase() {
|
|
|
1094
1157
|
if (_client) {
|
|
1095
1158
|
_client.close();
|
|
1096
1159
|
_client = null;
|
|
1160
|
+
_resilientClient = null;
|
|
1097
1161
|
}
|
|
1098
1162
|
}
|
|
1099
|
-
var _client, initTurso, disposeTurso;
|
|
1163
|
+
var _client, _resilientClient, initTurso, disposeTurso;
|
|
1100
1164
|
var init_database = __esm({
|
|
1101
1165
|
"src/lib/database.ts"() {
|
|
1102
1166
|
"use strict";
|
|
1167
|
+
init_db_retry();
|
|
1103
1168
|
_client = null;
|
|
1169
|
+
_resilientClient = null;
|
|
1104
1170
|
initTurso = initDatabase;
|
|
1105
1171
|
disposeTurso = disposeDatabase;
|
|
1106
1172
|
}
|
|
@@ -1213,7 +1279,7 @@ function listShards() {
|
|
|
1213
1279
|
}
|
|
1214
1280
|
async function ensureShardSchema(client) {
|
|
1215
1281
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
1216
|
-
await client.execute("PRAGMA busy_timeout =
|
|
1282
|
+
await client.execute("PRAGMA busy_timeout = 30000");
|
|
1217
1283
|
try {
|
|
1218
1284
|
await client.execute("PRAGMA libsql_vector_search_ef = 128");
|
|
1219
1285
|
} catch {
|
|
@@ -1474,7 +1540,8 @@ async function writeMemory(record) {
|
|
|
1474
1540
|
has_error: record.has_error ? 1 : 0,
|
|
1475
1541
|
raw_text: record.raw_text,
|
|
1476
1542
|
vector: record.vector,
|
|
1477
|
-
version:
|
|
1543
|
+
version: 0,
|
|
1544
|
+
// Placeholder — assigned atomically at flush time
|
|
1478
1545
|
task_id: record.task_id ?? null,
|
|
1479
1546
|
importance: record.importance ?? 5,
|
|
1480
1547
|
status: record.status ?? "active",
|
|
@@ -1508,6 +1575,13 @@ async function flushBatch() {
|
|
|
1508
1575
|
_flushing = true;
|
|
1509
1576
|
try {
|
|
1510
1577
|
const batch = _pendingRecords.slice(0);
|
|
1578
|
+
const client = getClient();
|
|
1579
|
+
const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
|
|
1580
|
+
let baseVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
|
|
1581
|
+
for (const row of batch) {
|
|
1582
|
+
row.version = baseVersion++;
|
|
1583
|
+
}
|
|
1584
|
+
_nextVersion = baseVersion;
|
|
1511
1585
|
const buildStmt = (row) => {
|
|
1512
1586
|
const hasVector = row.vector !== null;
|
|
1513
1587
|
const taskId = row.task_id ?? null;
|
|
@@ -2098,11 +2172,11 @@ async function connectEmbedDaemon() {
|
|
|
2098
2172
|
}
|
|
2099
2173
|
}
|
|
2100
2174
|
const start = Date.now();
|
|
2101
|
-
let
|
|
2175
|
+
let delay2 = 100;
|
|
2102
2176
|
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
2103
|
-
await new Promise((r) => setTimeout(r,
|
|
2177
|
+
await new Promise((r) => setTimeout(r, delay2));
|
|
2104
2178
|
if (await connectToSocket()) return true;
|
|
2105
|
-
|
|
2179
|
+
delay2 = Math.min(delay2 * 2, 3e3);
|
|
2106
2180
|
}
|
|
2107
2181
|
return false;
|
|
2108
2182
|
}
|
|
@@ -2194,11 +2268,11 @@ async function embedViaClient(text, priority = "high") {
|
|
|
2194
2268
|
`);
|
|
2195
2269
|
killAndRespawnDaemon();
|
|
2196
2270
|
const start = Date.now();
|
|
2197
|
-
let
|
|
2271
|
+
let delay2 = 200;
|
|
2198
2272
|
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
2199
|
-
await new Promise((r) => setTimeout(r,
|
|
2273
|
+
await new Promise((r) => setTimeout(r, delay2));
|
|
2200
2274
|
if (await connectToSocket()) break;
|
|
2201
|
-
|
|
2275
|
+
delay2 = Math.min(delay2 * 2, 3e3);
|
|
2202
2276
|
}
|
|
2203
2277
|
if (!_connected) return null;
|
|
2204
2278
|
}
|
|
@@ -2210,11 +2284,11 @@ async function embedViaClient(text, priority = "high") {
|
|
|
2210
2284
|
`);
|
|
2211
2285
|
killAndRespawnDaemon();
|
|
2212
2286
|
const start = Date.now();
|
|
2213
|
-
let
|
|
2287
|
+
let delay2 = 200;
|
|
2214
2288
|
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
2215
|
-
await new Promise((r) => setTimeout(r,
|
|
2289
|
+
await new Promise((r) => setTimeout(r, delay2));
|
|
2216
2290
|
if (await connectToSocket()) break;
|
|
2217
|
-
|
|
2291
|
+
delay2 = Math.min(delay2 * 2, 3e3);
|
|
2218
2292
|
}
|
|
2219
2293
|
if (!_connected) return null;
|
|
2220
2294
|
const retry = await sendRequest([text], priority);
|
|
@@ -3035,8 +3109,8 @@ function drainQueue(isSessionBusy, sendKeys) {
|
|
|
3035
3109
|
} catch {
|
|
3036
3110
|
}
|
|
3037
3111
|
item.attempts++;
|
|
3038
|
-
if (item.attempts >=
|
|
3039
|
-
logQueue(`FAILED \u2192 ${item.targetSession} (${
|
|
3112
|
+
if (item.attempts >= MAX_RETRIES2) {
|
|
3113
|
+
logQueue(`FAILED \u2192 ${item.targetSession} (${MAX_RETRIES2} retries exhausted, reason: ${item.reason})`);
|
|
3040
3114
|
failed++;
|
|
3041
3115
|
continue;
|
|
3042
3116
|
}
|
|
@@ -3069,12 +3143,12 @@ function logQueue(msg) {
|
|
|
3069
3143
|
} catch {
|
|
3070
3144
|
}
|
|
3071
3145
|
}
|
|
3072
|
-
var QUEUE_PATH,
|
|
3146
|
+
var QUEUE_PATH, MAX_RETRIES2, TTL_MS, INTERCOM_LOG;
|
|
3073
3147
|
var init_intercom_queue = __esm({
|
|
3074
3148
|
"src/lib/intercom-queue.ts"() {
|
|
3075
3149
|
"use strict";
|
|
3076
3150
|
QUEUE_PATH = path10.join(os3.homedir(), ".exe-os", "intercom-queue.json");
|
|
3077
|
-
|
|
3151
|
+
MAX_RETRIES2 = 5;
|
|
3078
3152
|
TTL_MS = 60 * 60 * 1e3;
|
|
3079
3153
|
INTERCOM_LOG = path10.join(os3.homedir(), ".exe-os", "intercom.log");
|
|
3080
3154
|
}
|
|
@@ -3082,7 +3156,7 @@ var init_intercom_queue = __esm({
|
|
|
3082
3156
|
|
|
3083
3157
|
// src/lib/employees.ts
|
|
3084
3158
|
import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
|
|
3085
|
-
import { existsSync as existsSync9, symlinkSync, readlinkSync } from "fs";
|
|
3159
|
+
import { existsSync as existsSync9, symlinkSync, readlinkSync, readFileSync as readFileSync7 } from "fs";
|
|
3086
3160
|
import { execSync as execSync6 } from "child_process";
|
|
3087
3161
|
import path11 from "path";
|
|
3088
3162
|
async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
@@ -3109,7 +3183,7 @@ var init_employees = __esm({
|
|
|
3109
3183
|
});
|
|
3110
3184
|
|
|
3111
3185
|
// src/lib/license.ts
|
|
3112
|
-
import { readFileSync as
|
|
3186
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync4, existsSync as existsSync10, mkdirSync as mkdirSync5 } from "fs";
|
|
3113
3187
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
3114
3188
|
import path12 from "path";
|
|
3115
3189
|
import { jwtVerify, importSPKI } from "jose";
|
|
@@ -3132,12 +3206,12 @@ var init_license = __esm({
|
|
|
3132
3206
|
});
|
|
3133
3207
|
|
|
3134
3208
|
// src/lib/plan-limits.ts
|
|
3135
|
-
import { readFileSync as
|
|
3209
|
+
import { readFileSync as readFileSync9, existsSync as existsSync11 } from "fs";
|
|
3136
3210
|
import path13 from "path";
|
|
3137
3211
|
function getLicenseSync() {
|
|
3138
3212
|
try {
|
|
3139
3213
|
if (!existsSync11(CACHE_PATH2)) return freeLicense();
|
|
3140
|
-
const raw = JSON.parse(
|
|
3214
|
+
const raw = JSON.parse(readFileSync9(CACHE_PATH2, "utf8"));
|
|
3141
3215
|
if (!raw.token || typeof raw.token !== "string") return freeLicense();
|
|
3142
3216
|
const parts = raw.token.split(".");
|
|
3143
3217
|
if (parts.length !== 3) return freeLicense();
|
|
@@ -3176,7 +3250,7 @@ function assertEmployeeLimitSync(rosterPath) {
|
|
|
3176
3250
|
let count = 0;
|
|
3177
3251
|
try {
|
|
3178
3252
|
if (existsSync11(filePath)) {
|
|
3179
|
-
const raw =
|
|
3253
|
+
const raw = readFileSync9(filePath, "utf8");
|
|
3180
3254
|
const employees = JSON.parse(raw);
|
|
3181
3255
|
count = Array.isArray(employees) ? employees.length : 0;
|
|
3182
3256
|
}
|
|
@@ -3211,10 +3285,46 @@ var init_plan_limits = __esm({
|
|
|
3211
3285
|
|
|
3212
3286
|
// src/lib/tmux-routing.ts
|
|
3213
3287
|
import { execFileSync as execFileSync2, execSync as execSync7 } from "child_process";
|
|
3214
|
-
import { readFileSync as
|
|
3288
|
+
import { readFileSync as readFileSync10, writeFileSync as writeFileSync5, mkdirSync as mkdirSync6, existsSync as existsSync12, appendFileSync } from "fs";
|
|
3215
3289
|
import path14 from "path";
|
|
3216
3290
|
import os4 from "os";
|
|
3217
3291
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
3292
|
+
import { unlinkSync as unlinkSync3 } from "fs";
|
|
3293
|
+
function spawnLockPath(sessionName) {
|
|
3294
|
+
return path14.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
|
|
3295
|
+
}
|
|
3296
|
+
function isProcessAlive(pid) {
|
|
3297
|
+
try {
|
|
3298
|
+
process.kill(pid, 0);
|
|
3299
|
+
return true;
|
|
3300
|
+
} catch {
|
|
3301
|
+
return false;
|
|
3302
|
+
}
|
|
3303
|
+
}
|
|
3304
|
+
function acquireSpawnLock2(sessionName) {
|
|
3305
|
+
if (!existsSync12(SPAWN_LOCK_DIR)) {
|
|
3306
|
+
mkdirSync6(SPAWN_LOCK_DIR, { recursive: true });
|
|
3307
|
+
}
|
|
3308
|
+
const lockFile = spawnLockPath(sessionName);
|
|
3309
|
+
if (existsSync12(lockFile)) {
|
|
3310
|
+
try {
|
|
3311
|
+
const lock = JSON.parse(readFileSync10(lockFile, "utf8"));
|
|
3312
|
+
const age = Date.now() - lock.timestamp;
|
|
3313
|
+
if (isProcessAlive(lock.pid) && age < 6e4) {
|
|
3314
|
+
return false;
|
|
3315
|
+
}
|
|
3316
|
+
} catch {
|
|
3317
|
+
}
|
|
3318
|
+
}
|
|
3319
|
+
writeFileSync5(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
|
|
3320
|
+
return true;
|
|
3321
|
+
}
|
|
3322
|
+
function releaseSpawnLock2(sessionName) {
|
|
3323
|
+
try {
|
|
3324
|
+
unlinkSync3(spawnLockPath(sessionName));
|
|
3325
|
+
} catch {
|
|
3326
|
+
}
|
|
3327
|
+
}
|
|
3218
3328
|
function resolveBehaviorsExporterScript() {
|
|
3219
3329
|
try {
|
|
3220
3330
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
@@ -3260,7 +3370,7 @@ function extractRootExe(name) {
|
|
|
3260
3370
|
}
|
|
3261
3371
|
function getParentExe(sessionKey) {
|
|
3262
3372
|
try {
|
|
3263
|
-
const data = JSON.parse(
|
|
3373
|
+
const data = JSON.parse(readFileSync10(path14.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
3264
3374
|
return data.parentExe || null;
|
|
3265
3375
|
} catch {
|
|
3266
3376
|
return null;
|
|
@@ -3284,17 +3394,17 @@ function isEmployeeAlive(sessionName) {
|
|
|
3284
3394
|
}
|
|
3285
3395
|
function findFreeInstance(employeeName, exeSession, maxInstances = 10, isAlive = isEmployeeAlive) {
|
|
3286
3396
|
const base = employeeSessionName(employeeName, exeSession);
|
|
3287
|
-
if (!isAlive(base)) return 0;
|
|
3397
|
+
if (!isAlive(base) && acquireSpawnLock2(base)) return 0;
|
|
3288
3398
|
for (let i = 2; i <= maxInstances; i++) {
|
|
3289
3399
|
const candidate = employeeSessionName(employeeName, exeSession, i);
|
|
3290
|
-
if (!isAlive(candidate)) return i;
|
|
3400
|
+
if (!isAlive(candidate) && acquireSpawnLock2(candidate)) return i;
|
|
3291
3401
|
}
|
|
3292
3402
|
return null;
|
|
3293
3403
|
}
|
|
3294
3404
|
function readDebounceState() {
|
|
3295
3405
|
try {
|
|
3296
3406
|
if (!existsSync12(DEBOUNCE_FILE)) return {};
|
|
3297
|
-
return JSON.parse(
|
|
3407
|
+
return JSON.parse(readFileSync10(DEBOUNCE_FILE, "utf8"));
|
|
3298
3408
|
} catch {
|
|
3299
3409
|
return {};
|
|
3300
3410
|
}
|
|
@@ -3468,7 +3578,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
3468
3578
|
const claudeJsonPath = path14.join(os4.homedir(), ".claude.json");
|
|
3469
3579
|
let claudeJson = {};
|
|
3470
3580
|
try {
|
|
3471
|
-
claudeJson = JSON.parse(
|
|
3581
|
+
claudeJson = JSON.parse(readFileSync10(claudeJsonPath, "utf8"));
|
|
3472
3582
|
} catch {
|
|
3473
3583
|
}
|
|
3474
3584
|
if (!claudeJson.projects) claudeJson.projects = {};
|
|
@@ -3486,7 +3596,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
3486
3596
|
const settingsPath = path14.join(projSettingsDir, "settings.json");
|
|
3487
3597
|
let settings = {};
|
|
3488
3598
|
try {
|
|
3489
|
-
settings = JSON.parse(
|
|
3599
|
+
settings = JSON.parse(readFileSync10(settingsPath, "utf8"));
|
|
3490
3600
|
} catch {
|
|
3491
3601
|
}
|
|
3492
3602
|
const perms = settings.permissions ?? {};
|
|
@@ -3599,6 +3709,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
3599
3709
|
command: spawnCommand
|
|
3600
3710
|
});
|
|
3601
3711
|
if (spawnResult.error) {
|
|
3712
|
+
releaseSpawnLock2(sessionName);
|
|
3602
3713
|
return { sessionName, error: `tmux new-session failed: ${spawnResult.error}` };
|
|
3603
3714
|
}
|
|
3604
3715
|
transport.pipeLog(sessionName, logFile);
|
|
@@ -3636,6 +3747,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
3636
3747
|
}
|
|
3637
3748
|
}
|
|
3638
3749
|
if (!booted) {
|
|
3750
|
+
releaseSpawnLock2(sessionName);
|
|
3639
3751
|
return { sessionName, error: `${useExeAgent ? "exe-agent" : "claude"} did not boot within 15s` };
|
|
3640
3752
|
}
|
|
3641
3753
|
if (!useExeAgent) {
|
|
@@ -3652,9 +3764,10 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
3652
3764
|
pid: 0,
|
|
3653
3765
|
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3654
3766
|
});
|
|
3767
|
+
releaseSpawnLock2(sessionName);
|
|
3655
3768
|
return { sessionName };
|
|
3656
3769
|
}
|
|
3657
|
-
var SESSION_CACHE, BEHAVIORS_EXPORT_TIMEOUT_MS, INTERCOM_DEBOUNCE_MS, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS, BUSY_PATTERN;
|
|
3770
|
+
var SPAWN_LOCK_DIR, SESSION_CACHE, BEHAVIORS_EXPORT_TIMEOUT_MS, INTERCOM_DEBOUNCE_MS, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS, BUSY_PATTERN;
|
|
3658
3771
|
var init_tmux_routing = __esm({
|
|
3659
3772
|
"src/lib/tmux-routing.ts"() {
|
|
3660
3773
|
"use strict";
|
|
@@ -3666,6 +3779,7 @@ var init_tmux_routing = __esm({
|
|
|
3666
3779
|
init_provider_table();
|
|
3667
3780
|
init_intercom_queue();
|
|
3668
3781
|
init_plan_limits();
|
|
3782
|
+
SPAWN_LOCK_DIR = path14.join(os4.homedir(), ".exe-os", "spawn-locks");
|
|
3669
3783
|
SESSION_CACHE = path14.join(os4.homedir(), ".exe-os", "session-cache");
|
|
3670
3784
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
3671
3785
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
@@ -3813,7 +3927,7 @@ async function deliverLocalMessage(messageId) {
|
|
|
3813
3927
|
return true;
|
|
3814
3928
|
} catch {
|
|
3815
3929
|
const newRetryCount = msg.retryCount + 1;
|
|
3816
|
-
if (newRetryCount >=
|
|
3930
|
+
if (newRetryCount >= MAX_RETRIES3) {
|
|
3817
3931
|
await markFailed(messageId, "session unavailable after 10 retries");
|
|
3818
3932
|
} else {
|
|
3819
3933
|
await client.execute({
|
|
@@ -3902,7 +4016,7 @@ async function retryPendingMessages() {
|
|
|
3902
4016
|
sql: `SELECT * FROM messages
|
|
3903
4017
|
WHERE status = 'pending' AND retry_count < ?
|
|
3904
4018
|
ORDER BY id`,
|
|
3905
|
-
args: [
|
|
4019
|
+
args: [MAX_RETRIES3]
|
|
3906
4020
|
});
|
|
3907
4021
|
let delivered = 0;
|
|
3908
4022
|
for (const row of result.rows) {
|
|
@@ -3915,13 +4029,13 @@ async function retryPendingMessages() {
|
|
|
3915
4029
|
}
|
|
3916
4030
|
return delivered;
|
|
3917
4031
|
}
|
|
3918
|
-
var
|
|
4032
|
+
var MAX_RETRIES3, _wsClientSend;
|
|
3919
4033
|
var init_messaging = __esm({
|
|
3920
4034
|
"src/lib/messaging.ts"() {
|
|
3921
4035
|
"use strict";
|
|
3922
4036
|
init_database();
|
|
3923
4037
|
init_tmux_routing();
|
|
3924
|
-
|
|
4038
|
+
MAX_RETRIES3 = 10;
|
|
3925
4039
|
_wsClientSend = null;
|
|
3926
4040
|
}
|
|
3927
4041
|
});
|
|
@@ -3931,9 +4045,9 @@ import crypto4 from "crypto";
|
|
|
3931
4045
|
import path15 from "path";
|
|
3932
4046
|
import os5 from "os";
|
|
3933
4047
|
import {
|
|
3934
|
-
readFileSync as
|
|
4048
|
+
readFileSync as readFileSync11,
|
|
3935
4049
|
readdirSync as readdirSync3,
|
|
3936
|
-
unlinkSync as
|
|
4050
|
+
unlinkSync as unlinkSync4,
|
|
3937
4051
|
existsSync as existsSync13,
|
|
3938
4052
|
rmdirSync
|
|
3939
4053
|
} from "fs";
|
|
@@ -3973,7 +4087,7 @@ import crypto5 from "crypto";
|
|
|
3973
4087
|
import path16 from "path";
|
|
3974
4088
|
import { execSync as execSync8 } from "child_process";
|
|
3975
4089
|
import { mkdir as mkdir4, writeFile as writeFile4, appendFile } from "fs/promises";
|
|
3976
|
-
import { existsSync as existsSync14, readFileSync as
|
|
4090
|
+
import { existsSync as existsSync14, readFileSync as readFileSync12 } from "fs";
|
|
3977
4091
|
function extractParentFromContext(contextBody) {
|
|
3978
4092
|
if (!contextBody) return null;
|
|
3979
4093
|
const match = contextBody.match(
|
|
@@ -4163,7 +4277,7 @@ async function ensureGitignoreExe(baseDir) {
|
|
|
4163
4277
|
const gitignorePath = path16.join(baseDir, ".gitignore");
|
|
4164
4278
|
try {
|
|
4165
4279
|
if (existsSync14(gitignorePath)) {
|
|
4166
|
-
const content =
|
|
4280
|
+
const content = readFileSync12(gitignorePath, "utf-8");
|
|
4167
4281
|
if (/^\/?exe\/?$/m.test(content)) return;
|
|
4168
4282
|
await appendFile(gitignorePath, "\n# Employee task assignments (private)\n/exe/\n");
|
|
4169
4283
|
} else {
|
|
@@ -4190,7 +4304,7 @@ __export(tasks_review_exports, {
|
|
|
4190
4304
|
listPendingReviews: () => listPendingReviews
|
|
4191
4305
|
});
|
|
4192
4306
|
import path17 from "path";
|
|
4193
|
-
import { existsSync as existsSync15, readdirSync as readdirSync4, unlinkSync as
|
|
4307
|
+
import { existsSync as existsSync15, readdirSync as readdirSync4, unlinkSync as unlinkSync5 } from "fs";
|
|
4194
4308
|
async function countPendingReviews() {
|
|
4195
4309
|
const client = getClient();
|
|
4196
4310
|
const result = await client.execute({
|
|
@@ -4404,7 +4518,7 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
4404
4518
|
if (existsSync15(cacheDir)) {
|
|
4405
4519
|
for (const f of readdirSync4(cacheDir)) {
|
|
4406
4520
|
if (f.startsWith("review-notified-")) {
|
|
4407
|
-
|
|
4521
|
+
unlinkSync5(path17.join(cacheDir, f));
|
|
4408
4522
|
}
|
|
4409
4523
|
}
|
|
4410
4524
|
}
|
|
@@ -4429,7 +4543,7 @@ init_config();
|
|
|
4429
4543
|
init_config();
|
|
4430
4544
|
init_store();
|
|
4431
4545
|
import { spawn as spawn2 } from "child_process";
|
|
4432
|
-
import { readFileSync as
|
|
4546
|
+
import { readFileSync as readFileSync13, writeFileSync as writeFileSync6, mkdirSync as mkdirSync7, existsSync as existsSync16, openSync as openSync2, closeSync as closeSync2 } from "fs";
|
|
4433
4547
|
import path18 from "path";
|
|
4434
4548
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
4435
4549
|
|
|
@@ -4838,7 +4952,7 @@ function getActiveAgent() {
|
|
|
4838
4952
|
"tmux display-message -p '#{session_name}' 2>/dev/null",
|
|
4839
4953
|
{ encoding: "utf8", timeout: 2e3 }
|
|
4840
4954
|
).trim();
|
|
4841
|
-
const empMatch = sessionName.match(/^(
|
|
4955
|
+
const empMatch = sessionName.match(/^([a-zA-Z]+)\d*-exe\d+$/);
|
|
4842
4956
|
if (empMatch && empMatch[1] !== "exe") {
|
|
4843
4957
|
return { agentId: empMatch[1], agentRole: "employee" };
|
|
4844
4958
|
}
|
|
@@ -4872,7 +4986,7 @@ function openWorkerLog() {
|
|
|
4872
4986
|
var CACHE_DIR2 = path18.join(EXE_AI_DIR, "session-cache");
|
|
4873
4987
|
function loadInjectedIds(sessionId) {
|
|
4874
4988
|
try {
|
|
4875
|
-
const raw =
|
|
4989
|
+
const raw = readFileSync13(path18.join(CACHE_DIR2, `${sessionId}.json`), "utf8");
|
|
4876
4990
|
return new Set(JSON.parse(raw));
|
|
4877
4991
|
} catch {
|
|
4878
4992
|
return /* @__PURE__ */ new Set();
|
|
@@ -4967,7 +5081,7 @@ ${fresh.map(
|
|
|
4967
5081
|
const lastCheckPath = path18.join(CACHE_DIR2, `review-lastcheck-${sessionKey}.json`);
|
|
4968
5082
|
let lastCheckedAt = "";
|
|
4969
5083
|
try {
|
|
4970
|
-
lastCheckedAt =
|
|
5084
|
+
lastCheckedAt = readFileSync13(lastCheckPath, "utf8").trim();
|
|
4971
5085
|
} catch {
|
|
4972
5086
|
}
|
|
4973
5087
|
const totalCount = await countPendingReviews2();
|