@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/lib/tmux-routing.js
CHANGED
|
@@ -317,19 +317,27 @@ var init_intercom_queue = __esm({
|
|
|
317
317
|
}
|
|
318
318
|
});
|
|
319
319
|
|
|
320
|
+
// src/lib/db-retry.ts
|
|
321
|
+
var init_db_retry = __esm({
|
|
322
|
+
"src/lib/db-retry.ts"() {
|
|
323
|
+
"use strict";
|
|
324
|
+
}
|
|
325
|
+
});
|
|
326
|
+
|
|
320
327
|
// src/lib/database.ts
|
|
321
328
|
import { createClient } from "@libsql/client";
|
|
322
329
|
function getClient() {
|
|
323
|
-
if (!
|
|
330
|
+
if (!_resilientClient) {
|
|
324
331
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
325
332
|
}
|
|
326
|
-
return
|
|
333
|
+
return _resilientClient;
|
|
327
334
|
}
|
|
328
|
-
var
|
|
335
|
+
var _resilientClient;
|
|
329
336
|
var init_database = __esm({
|
|
330
337
|
"src/lib/database.ts"() {
|
|
331
338
|
"use strict";
|
|
332
|
-
|
|
339
|
+
init_db_retry();
|
|
340
|
+
_resilientClient = null;
|
|
333
341
|
}
|
|
334
342
|
});
|
|
335
343
|
|
|
@@ -524,20 +532,38 @@ var init_config = __esm({
|
|
|
524
532
|
|
|
525
533
|
// src/lib/employees.ts
|
|
526
534
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
527
|
-
import { existsSync as existsSync4, symlinkSync, readlinkSync } from "fs";
|
|
535
|
+
import { existsSync as existsSync4, symlinkSync, readlinkSync, readFileSync as readFileSync4 } from "fs";
|
|
528
536
|
import { execSync as execSync3 } from "child_process";
|
|
529
537
|
import path4 from "path";
|
|
530
|
-
|
|
538
|
+
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
539
|
+
if (!existsSync4(employeesPath)) return [];
|
|
540
|
+
try {
|
|
541
|
+
return JSON.parse(readFileSync4(employeesPath, "utf-8"));
|
|
542
|
+
} catch {
|
|
543
|
+
return [];
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
function getEmployee(employees, name) {
|
|
547
|
+
return employees.find((e) => e.name.toLowerCase() === name.toLowerCase());
|
|
548
|
+
}
|
|
549
|
+
function isMultiInstance(agentName, employees) {
|
|
550
|
+
const roster = employees ?? loadEmployeesSync();
|
|
551
|
+
const emp = getEmployee(roster, agentName);
|
|
552
|
+
if (!emp) return false;
|
|
553
|
+
return MULTI_INSTANCE_ROLES.has(emp.role.toLowerCase());
|
|
554
|
+
}
|
|
555
|
+
var EMPLOYEES_PATH, MULTI_INSTANCE_ROLES;
|
|
531
556
|
var init_employees = __esm({
|
|
532
557
|
"src/lib/employees.ts"() {
|
|
533
558
|
"use strict";
|
|
534
559
|
init_config();
|
|
535
560
|
EMPLOYEES_PATH = path4.join(EXE_AI_DIR, "exe-employees.json");
|
|
561
|
+
MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
|
|
536
562
|
}
|
|
537
563
|
});
|
|
538
564
|
|
|
539
565
|
// src/lib/license.ts
|
|
540
|
-
import { readFileSync as
|
|
566
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync5, mkdirSync as mkdirSync3 } from "fs";
|
|
541
567
|
import { randomUUID } from "crypto";
|
|
542
568
|
import path5 from "path";
|
|
543
569
|
import { jwtVerify, importSPKI } from "jose";
|
|
@@ -560,12 +586,12 @@ var init_license = __esm({
|
|
|
560
586
|
});
|
|
561
587
|
|
|
562
588
|
// src/lib/plan-limits.ts
|
|
563
|
-
import { readFileSync as
|
|
589
|
+
import { readFileSync as readFileSync6, existsSync as existsSync6 } from "fs";
|
|
564
590
|
import path6 from "path";
|
|
565
591
|
function getLicenseSync() {
|
|
566
592
|
try {
|
|
567
593
|
if (!existsSync6(CACHE_PATH2)) return freeLicense();
|
|
568
|
-
const raw = JSON.parse(
|
|
594
|
+
const raw = JSON.parse(readFileSync6(CACHE_PATH2, "utf8"));
|
|
569
595
|
if (!raw.token || typeof raw.token !== "string") return freeLicense();
|
|
570
596
|
const parts = raw.token.split(".");
|
|
571
597
|
if (parts.length !== 3) return freeLicense();
|
|
@@ -604,7 +630,7 @@ function assertEmployeeLimitSync(rosterPath) {
|
|
|
604
630
|
let count = 0;
|
|
605
631
|
try {
|
|
606
632
|
if (existsSync6(filePath)) {
|
|
607
|
-
const raw =
|
|
633
|
+
const raw = readFileSync6(filePath, "utf8");
|
|
608
634
|
const employees = JSON.parse(raw);
|
|
609
635
|
count = Array.isArray(employees) ? employees.length : 0;
|
|
610
636
|
}
|
|
@@ -642,7 +668,7 @@ import crypto from "crypto";
|
|
|
642
668
|
import path7 from "path";
|
|
643
669
|
import os4 from "os";
|
|
644
670
|
import {
|
|
645
|
-
readFileSync as
|
|
671
|
+
readFileSync as readFileSync7,
|
|
646
672
|
readdirSync,
|
|
647
673
|
unlinkSync,
|
|
648
674
|
existsSync as existsSync7,
|
|
@@ -728,7 +754,7 @@ import crypto3 from "crypto";
|
|
|
728
754
|
import path8 from "path";
|
|
729
755
|
import { execSync as execSync4 } from "child_process";
|
|
730
756
|
import { mkdir as mkdir3, writeFile as writeFile3, appendFile } from "fs/promises";
|
|
731
|
-
import { existsSync as existsSync8, readFileSync as
|
|
757
|
+
import { existsSync as existsSync8, readFileSync as readFileSync8 } from "fs";
|
|
732
758
|
async function writeCheckpoint(input) {
|
|
733
759
|
const client = getClient();
|
|
734
760
|
const row = await resolveTask(client, input.taskId);
|
|
@@ -1105,7 +1131,7 @@ async function ensureGitignoreExe(baseDir) {
|
|
|
1105
1131
|
const gitignorePath = path8.join(baseDir, ".gitignore");
|
|
1106
1132
|
try {
|
|
1107
1133
|
if (existsSync8(gitignorePath)) {
|
|
1108
|
-
const content =
|
|
1134
|
+
const content = readFileSync8(gitignorePath, "utf-8");
|
|
1109
1135
|
if (/^\/?exe\/?$/m.test(content)) return;
|
|
1110
1136
|
await appendFile(gitignorePath, "\n# Employee task assignments (private)\n/exe/\n");
|
|
1111
1137
|
} else {
|
|
@@ -1481,7 +1507,7 @@ async function dispatchTaskToEmployee(input) {
|
|
|
1481
1507
|
} else {
|
|
1482
1508
|
const projectDir = input.projectDir ?? process.cwd();
|
|
1483
1509
|
const result = ensureEmployee(input.assignedTo, exeSession, projectDir, {
|
|
1484
|
-
autoInstance: input.assignedTo
|
|
1510
|
+
autoInstance: isMultiInstance(input.assignedTo)
|
|
1485
1511
|
});
|
|
1486
1512
|
if (result.status === "failed") {
|
|
1487
1513
|
process.stderr.write(
|
|
@@ -1516,6 +1542,7 @@ var init_tasks_notify = __esm({
|
|
|
1516
1542
|
init_session_key();
|
|
1517
1543
|
init_notifications();
|
|
1518
1544
|
init_transport();
|
|
1545
|
+
init_employees();
|
|
1519
1546
|
}
|
|
1520
1547
|
});
|
|
1521
1548
|
|
|
@@ -2268,10 +2295,46 @@ var init_capacity_monitor = __esm({
|
|
|
2268
2295
|
|
|
2269
2296
|
// src/lib/tmux-routing.ts
|
|
2270
2297
|
import { execFileSync as execFileSync2, execSync as execSync6 } from "child_process";
|
|
2271
|
-
import { readFileSync as
|
|
2298
|
+
import { readFileSync as readFileSync9, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, existsSync as existsSync10, appendFileSync } from "fs";
|
|
2272
2299
|
import path13 from "path";
|
|
2273
2300
|
import os5 from "os";
|
|
2274
2301
|
import { fileURLToPath } from "url";
|
|
2302
|
+
import { unlinkSync as unlinkSync4 } from "fs";
|
|
2303
|
+
function spawnLockPath(sessionName) {
|
|
2304
|
+
return path13.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
|
|
2305
|
+
}
|
|
2306
|
+
function isProcessAlive(pid) {
|
|
2307
|
+
try {
|
|
2308
|
+
process.kill(pid, 0);
|
|
2309
|
+
return true;
|
|
2310
|
+
} catch {
|
|
2311
|
+
return false;
|
|
2312
|
+
}
|
|
2313
|
+
}
|
|
2314
|
+
function acquireSpawnLock(sessionName) {
|
|
2315
|
+
if (!existsSync10(SPAWN_LOCK_DIR)) {
|
|
2316
|
+
mkdirSync5(SPAWN_LOCK_DIR, { recursive: true });
|
|
2317
|
+
}
|
|
2318
|
+
const lockFile = spawnLockPath(sessionName);
|
|
2319
|
+
if (existsSync10(lockFile)) {
|
|
2320
|
+
try {
|
|
2321
|
+
const lock = JSON.parse(readFileSync9(lockFile, "utf8"));
|
|
2322
|
+
const age = Date.now() - lock.timestamp;
|
|
2323
|
+
if (isProcessAlive(lock.pid) && age < 6e4) {
|
|
2324
|
+
return false;
|
|
2325
|
+
}
|
|
2326
|
+
} catch {
|
|
2327
|
+
}
|
|
2328
|
+
}
|
|
2329
|
+
writeFileSync5(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
|
|
2330
|
+
return true;
|
|
2331
|
+
}
|
|
2332
|
+
function releaseSpawnLock(sessionName) {
|
|
2333
|
+
try {
|
|
2334
|
+
unlinkSync4(spawnLockPath(sessionName));
|
|
2335
|
+
} catch {
|
|
2336
|
+
}
|
|
2337
|
+
}
|
|
2275
2338
|
function resolveBehaviorsExporterScript() {
|
|
2276
2339
|
try {
|
|
2277
2340
|
const thisFile = fileURLToPath(import.meta.url);
|
|
@@ -2335,7 +2398,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
2335
2398
|
}
|
|
2336
2399
|
function getParentExe(sessionKey) {
|
|
2337
2400
|
try {
|
|
2338
|
-
const data = JSON.parse(
|
|
2401
|
+
const data = JSON.parse(readFileSync9(path13.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
2339
2402
|
return data.parentExe || null;
|
|
2340
2403
|
} catch {
|
|
2341
2404
|
return null;
|
|
@@ -2343,7 +2406,7 @@ function getParentExe(sessionKey) {
|
|
|
2343
2406
|
}
|
|
2344
2407
|
function getDispatchedBy(sessionKey) {
|
|
2345
2408
|
try {
|
|
2346
|
-
const data = JSON.parse(
|
|
2409
|
+
const data = JSON.parse(readFileSync9(
|
|
2347
2410
|
path13.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
|
|
2348
2411
|
"utf8"
|
|
2349
2412
|
));
|
|
@@ -2370,10 +2433,10 @@ function isEmployeeAlive(sessionName) {
|
|
|
2370
2433
|
}
|
|
2371
2434
|
function findFreeInstance(employeeName, exeSession, maxInstances = 10, isAlive = isEmployeeAlive) {
|
|
2372
2435
|
const base = employeeSessionName(employeeName, exeSession);
|
|
2373
|
-
if (!isAlive(base)) return 0;
|
|
2436
|
+
if (!isAlive(base) && acquireSpawnLock(base)) return 0;
|
|
2374
2437
|
for (let i = 2; i <= maxInstances; i++) {
|
|
2375
2438
|
const candidate = employeeSessionName(employeeName, exeSession, i);
|
|
2376
|
-
if (!isAlive(candidate)) return i;
|
|
2439
|
+
if (!isAlive(candidate) && acquireSpawnLock(candidate)) return i;
|
|
2377
2440
|
}
|
|
2378
2441
|
return null;
|
|
2379
2442
|
}
|
|
@@ -2406,7 +2469,7 @@ async function verifyPaneAtCapacity(sessionName) {
|
|
|
2406
2469
|
function readDebounceState() {
|
|
2407
2470
|
try {
|
|
2408
2471
|
if (!existsSync10(DEBOUNCE_FILE)) return {};
|
|
2409
|
-
return JSON.parse(
|
|
2472
|
+
return JSON.parse(readFileSync9(DEBOUNCE_FILE, "utf8"));
|
|
2410
2473
|
} catch {
|
|
2411
2474
|
return {};
|
|
2412
2475
|
}
|
|
@@ -2606,7 +2669,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
2606
2669
|
const claudeJsonPath = path13.join(os5.homedir(), ".claude.json");
|
|
2607
2670
|
let claudeJson = {};
|
|
2608
2671
|
try {
|
|
2609
|
-
claudeJson = JSON.parse(
|
|
2672
|
+
claudeJson = JSON.parse(readFileSync9(claudeJsonPath, "utf8"));
|
|
2610
2673
|
} catch {
|
|
2611
2674
|
}
|
|
2612
2675
|
if (!claudeJson.projects) claudeJson.projects = {};
|
|
@@ -2624,7 +2687,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
2624
2687
|
const settingsPath = path13.join(projSettingsDir, "settings.json");
|
|
2625
2688
|
let settings = {};
|
|
2626
2689
|
try {
|
|
2627
|
-
settings = JSON.parse(
|
|
2690
|
+
settings = JSON.parse(readFileSync9(settingsPath, "utf8"));
|
|
2628
2691
|
} catch {
|
|
2629
2692
|
}
|
|
2630
2693
|
const perms = settings.permissions ?? {};
|
|
@@ -2737,6 +2800,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
2737
2800
|
command: spawnCommand
|
|
2738
2801
|
});
|
|
2739
2802
|
if (spawnResult.error) {
|
|
2803
|
+
releaseSpawnLock(sessionName);
|
|
2740
2804
|
return { sessionName, error: `tmux new-session failed: ${spawnResult.error}` };
|
|
2741
2805
|
}
|
|
2742
2806
|
transport.pipeLog(sessionName, logFile);
|
|
@@ -2774,6 +2838,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
2774
2838
|
}
|
|
2775
2839
|
}
|
|
2776
2840
|
if (!booted) {
|
|
2841
|
+
releaseSpawnLock(sessionName);
|
|
2777
2842
|
return { sessionName, error: `${useExeAgent ? "exe-agent" : "claude"} did not boot within 15s` };
|
|
2778
2843
|
}
|
|
2779
2844
|
if (!useExeAgent) {
|
|
@@ -2790,9 +2855,10 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
2790
2855
|
pid: 0,
|
|
2791
2856
|
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2792
2857
|
});
|
|
2858
|
+
releaseSpawnLock(sessionName);
|
|
2793
2859
|
return { sessionName };
|
|
2794
2860
|
}
|
|
2795
|
-
var SESSION_CACHE, BEHAVIORS_EXPORT_TIMEOUT_MS, VERIFY_PANE_LINES, INTERCOM_DEBOUNCE_MS, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS, BUSY_PATTERN;
|
|
2861
|
+
var SPAWN_LOCK_DIR, SESSION_CACHE, BEHAVIORS_EXPORT_TIMEOUT_MS, VERIFY_PANE_LINES, INTERCOM_DEBOUNCE_MS, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS, BUSY_PATTERN;
|
|
2796
2862
|
var init_tmux_routing = __esm({
|
|
2797
2863
|
"src/lib/tmux-routing.ts"() {
|
|
2798
2864
|
init_session_registry();
|
|
@@ -2803,6 +2869,7 @@ var init_tmux_routing = __esm({
|
|
|
2803
2869
|
init_provider_table();
|
|
2804
2870
|
init_intercom_queue();
|
|
2805
2871
|
init_plan_limits();
|
|
2872
|
+
SPAWN_LOCK_DIR = path13.join(os5.homedir(), ".exe-os", "spawn-locks");
|
|
2806
2873
|
SESSION_CACHE = path13.join(os5.homedir(), ".exe-os", "session-cache");
|
|
2807
2874
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
2808
2875
|
VERIFY_PANE_LINES = 200;
|
|
@@ -2815,6 +2882,7 @@ var init_tmux_routing = __esm({
|
|
|
2815
2882
|
});
|
|
2816
2883
|
init_tmux_routing();
|
|
2817
2884
|
export {
|
|
2885
|
+
acquireSpawnLock,
|
|
2818
2886
|
employeeSessionName,
|
|
2819
2887
|
ensureEmployee,
|
|
2820
2888
|
extractRootExe,
|
|
@@ -2829,6 +2897,7 @@ export {
|
|
|
2829
2897
|
notifyParentExe,
|
|
2830
2898
|
parseParentExe,
|
|
2831
2899
|
registerParentExe,
|
|
2900
|
+
releaseSpawnLock,
|
|
2832
2901
|
resolveExeSession,
|
|
2833
2902
|
sendIntercom,
|
|
2834
2903
|
spawnEmployee,
|