@askexenow/exe-os 0.8.83 → 0.8.86
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 +746 -595
- package/dist/bin/backfill-responses.js +745 -594
- package/dist/bin/backfill-vectors.js +312 -226
- package/dist/bin/cleanup-stale-review-tasks.js +154 -21
- package/dist/bin/cli.js +14678 -12676
- package/dist/bin/exe-agent-config.js +242 -0
- package/dist/bin/exe-agent.js +100 -91
- package/dist/bin/exe-assign.js +1003 -854
- package/dist/bin/exe-boot.js +1420 -485
- package/dist/bin/exe-call.js +10 -0
- package/dist/bin/exe-cloud.js +29 -6
- package/dist/bin/exe-dispatch.js +572 -271
- package/dist/bin/exe-doctor.js +403 -6
- package/dist/bin/exe-export-behaviors.js +175 -72
- package/dist/bin/exe-forget.js +102 -3
- package/dist/bin/exe-gateway.js +796 -292
- package/dist/bin/exe-healthcheck.js +134 -1
- package/dist/bin/exe-heartbeat.js +172 -36
- package/dist/bin/exe-kill.js +175 -72
- package/dist/bin/exe-launch-agent.js +189 -76
- package/dist/bin/exe-link.js +927 -82
- package/dist/bin/exe-new-employee.js +60 -8
- package/dist/bin/exe-pending-messages.js +151 -19
- package/dist/bin/exe-pending-notifications.js +97 -2
- package/dist/bin/exe-pending-reviews.js +155 -22
- package/dist/bin/exe-rename.js +564 -23
- package/dist/bin/exe-review.js +231 -73
- package/dist/bin/exe-search.js +995 -228
- package/dist/bin/exe-session-cleanup.js +4930 -1664
- package/dist/bin/exe-settings.js +20 -5
- package/dist/bin/exe-start-codex.js +2598 -0
- package/dist/bin/exe-start.sh +15 -3
- package/dist/bin/exe-status.js +154 -21
- package/dist/bin/exe-team.js +97 -2
- package/dist/bin/git-sweep.js +1180 -363
- package/dist/bin/graph-backfill.js +175 -72
- package/dist/bin/graph-export.js +175 -72
- package/dist/bin/install.js +60 -7
- package/dist/bin/list-providers.js +1 -0
- package/dist/bin/scan-tasks.js +1185 -367
- package/dist/bin/setup.js +914 -270
- package/dist/bin/shard-migrate.js +175 -72
- package/dist/bin/update.js +1 -0
- package/dist/bin/wiki-sync.js +175 -72
- package/dist/gateway/index.js +792 -285
- package/dist/hooks/bug-report-worker.js +445 -135
- package/dist/hooks/commit-complete.js +1178 -361
- package/dist/hooks/error-recall.js +994 -228
- package/dist/hooks/ingest-worker.js +1799 -1234
- package/dist/hooks/ingest.js +3 -0
- package/dist/hooks/instructions-loaded.js +707 -97
- package/dist/hooks/notification.js +699 -89
- package/dist/hooks/post-compact.js +757 -109
- package/dist/hooks/pre-compact.js +1061 -244
- package/dist/hooks/pre-tool-use.js +787 -130
- package/dist/hooks/prompt-ingest-worker.js +242 -101
- package/dist/hooks/prompt-submit.js +1121 -299
- package/dist/hooks/response-ingest-worker.js +242 -101
- package/dist/hooks/session-end.js +4063 -397
- package/dist/hooks/session-start.js +1071 -254
- package/dist/hooks/stop.js +768 -120
- package/dist/hooks/subagent-stop.js +757 -109
- package/dist/hooks/summary-worker.js +1706 -1011
- package/dist/index.js +1821 -1098
- package/dist/lib/agent-config.js +167 -0
- package/dist/lib/cloud-sync.js +932 -88
- package/dist/lib/consolidation.js +2 -1
- package/dist/lib/database.js +642 -87
- package/dist/lib/db-daemon-client.js +503 -0
- package/dist/lib/device-registry.js +547 -7
- package/dist/lib/embedder.js +14 -28
- package/dist/lib/employee-templates.js +84 -74
- package/dist/lib/employees.js +9 -0
- package/dist/lib/exe-daemon-client.js +16 -29
- package/dist/lib/exe-daemon.js +2733 -1575
- package/dist/lib/hybrid-search.js +995 -228
- package/dist/lib/identity.js +87 -67
- package/dist/lib/keychain.js +9 -1
- package/dist/lib/messaging.js +103 -40
- package/dist/lib/reminders.js +91 -74
- package/dist/lib/runtime-table.js +16 -0
- package/dist/lib/schedules.js +96 -2
- package/dist/lib/session-wrappers.js +22 -0
- package/dist/lib/skill-learning.js +103 -85
- package/dist/lib/store.js +234 -73
- package/dist/lib/tasks.js +348 -134
- package/dist/lib/tmux-routing.js +422 -208
- package/dist/lib/token-spend.js +273 -0
- package/dist/lib/ws-client.js +11 -0
- package/dist/mcp/server.js +5742 -696
- package/dist/mcp/tools/complete-reminder.js +94 -77
- package/dist/mcp/tools/create-reminder.js +94 -77
- package/dist/mcp/tools/create-task.js +375 -152
- package/dist/mcp/tools/deactivate-behavior.js +95 -77
- package/dist/mcp/tools/list-reminders.js +94 -77
- package/dist/mcp/tools/list-tasks.js +99 -31
- package/dist/mcp/tools/send-message.js +108 -45
- package/dist/mcp/tools/update-task.js +162 -77
- package/dist/runtime/index.js +1075 -258
- package/dist/tui/App.js +1333 -506
- package/package.json +6 -1
- package/src/commands/exe/agent-config.md +27 -0
- package/src/commands/exe/cc-doctor.md +10 -0
package/dist/bin/exe-boot.js
CHANGED
|
@@ -427,6 +427,443 @@ var init_db_retry = __esm({
|
|
|
427
427
|
}
|
|
428
428
|
});
|
|
429
429
|
|
|
430
|
+
// src/lib/exe-daemon-client.ts
|
|
431
|
+
import net from "net";
|
|
432
|
+
import { spawn } from "child_process";
|
|
433
|
+
import { randomUUID } from "crypto";
|
|
434
|
+
import { existsSync as existsSync3, unlinkSync as unlinkSync2, readFileSync as readFileSync3, openSync, closeSync, statSync } from "fs";
|
|
435
|
+
import path3 from "path";
|
|
436
|
+
import { fileURLToPath } from "url";
|
|
437
|
+
function handleData(chunk) {
|
|
438
|
+
_buffer += chunk.toString();
|
|
439
|
+
if (_buffer.length > MAX_BUFFER) {
|
|
440
|
+
_buffer = "";
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
let newlineIdx;
|
|
444
|
+
while ((newlineIdx = _buffer.indexOf("\n")) !== -1) {
|
|
445
|
+
const line = _buffer.slice(0, newlineIdx).trim();
|
|
446
|
+
_buffer = _buffer.slice(newlineIdx + 1);
|
|
447
|
+
if (!line) continue;
|
|
448
|
+
try {
|
|
449
|
+
const response = JSON.parse(line);
|
|
450
|
+
const id = response.id;
|
|
451
|
+
if (!id) continue;
|
|
452
|
+
const entry = _pending.get(id);
|
|
453
|
+
if (entry) {
|
|
454
|
+
clearTimeout(entry.timer);
|
|
455
|
+
_pending.delete(id);
|
|
456
|
+
entry.resolve(response);
|
|
457
|
+
}
|
|
458
|
+
} catch {
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
function cleanupStaleFiles() {
|
|
463
|
+
if (existsSync3(PID_PATH)) {
|
|
464
|
+
try {
|
|
465
|
+
const pid = parseInt(readFileSync3(PID_PATH, "utf8").trim(), 10);
|
|
466
|
+
if (pid > 0) {
|
|
467
|
+
try {
|
|
468
|
+
process.kill(pid, 0);
|
|
469
|
+
return;
|
|
470
|
+
} catch {
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
} catch {
|
|
474
|
+
}
|
|
475
|
+
try {
|
|
476
|
+
unlinkSync2(PID_PATH);
|
|
477
|
+
} catch {
|
|
478
|
+
}
|
|
479
|
+
try {
|
|
480
|
+
unlinkSync2(SOCKET_PATH);
|
|
481
|
+
} catch {
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
function findPackageRoot() {
|
|
486
|
+
let dir = path3.dirname(fileURLToPath(import.meta.url));
|
|
487
|
+
const { root } = path3.parse(dir);
|
|
488
|
+
while (dir !== root) {
|
|
489
|
+
if (existsSync3(path3.join(dir, "package.json"))) return dir;
|
|
490
|
+
dir = path3.dirname(dir);
|
|
491
|
+
}
|
|
492
|
+
return null;
|
|
493
|
+
}
|
|
494
|
+
function spawnDaemon() {
|
|
495
|
+
const pkgRoot = findPackageRoot();
|
|
496
|
+
if (!pkgRoot) {
|
|
497
|
+
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
const daemonPath = path3.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
501
|
+
if (!existsSync3(daemonPath)) {
|
|
502
|
+
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
503
|
+
`);
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
const resolvedPath = daemonPath;
|
|
507
|
+
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
508
|
+
`);
|
|
509
|
+
const logPath = path3.join(path3.dirname(SOCKET_PATH), "exed.log");
|
|
510
|
+
let stderrFd = "ignore";
|
|
511
|
+
try {
|
|
512
|
+
stderrFd = openSync(logPath, "a");
|
|
513
|
+
} catch {
|
|
514
|
+
}
|
|
515
|
+
const child = spawn(process.execPath, [resolvedPath], {
|
|
516
|
+
detached: true,
|
|
517
|
+
stdio: ["ignore", "ignore", stderrFd],
|
|
518
|
+
env: {
|
|
519
|
+
...process.env,
|
|
520
|
+
TMUX: void 0,
|
|
521
|
+
// Daemon is global — must not inherit session scope
|
|
522
|
+
TMUX_PANE: void 0,
|
|
523
|
+
// Prevents resolveExeSession() from scoping to one session
|
|
524
|
+
EXE_DAEMON_SOCK: SOCKET_PATH,
|
|
525
|
+
EXE_DAEMON_PID: PID_PATH
|
|
526
|
+
}
|
|
527
|
+
});
|
|
528
|
+
child.unref();
|
|
529
|
+
if (typeof stderrFd === "number") {
|
|
530
|
+
try {
|
|
531
|
+
closeSync(stderrFd);
|
|
532
|
+
} catch {
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
function acquireSpawnLock() {
|
|
537
|
+
try {
|
|
538
|
+
const fd = openSync(SPAWN_LOCK_PATH, "wx");
|
|
539
|
+
closeSync(fd);
|
|
540
|
+
return true;
|
|
541
|
+
} catch {
|
|
542
|
+
try {
|
|
543
|
+
const stat = statSync(SPAWN_LOCK_PATH);
|
|
544
|
+
if (Date.now() - stat.mtimeMs > SPAWN_LOCK_STALE_MS) {
|
|
545
|
+
try {
|
|
546
|
+
unlinkSync2(SPAWN_LOCK_PATH);
|
|
547
|
+
} catch {
|
|
548
|
+
}
|
|
549
|
+
try {
|
|
550
|
+
const fd = openSync(SPAWN_LOCK_PATH, "wx");
|
|
551
|
+
closeSync(fd);
|
|
552
|
+
return true;
|
|
553
|
+
} catch {
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
} catch {
|
|
557
|
+
}
|
|
558
|
+
return false;
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
function releaseSpawnLock() {
|
|
562
|
+
try {
|
|
563
|
+
unlinkSync2(SPAWN_LOCK_PATH);
|
|
564
|
+
} catch {
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
function connectToSocket() {
|
|
568
|
+
return new Promise((resolve) => {
|
|
569
|
+
if (_socket && _connected) {
|
|
570
|
+
resolve(true);
|
|
571
|
+
return;
|
|
572
|
+
}
|
|
573
|
+
const socket = net.createConnection({ path: SOCKET_PATH });
|
|
574
|
+
const connectTimeout = setTimeout(() => {
|
|
575
|
+
socket.destroy();
|
|
576
|
+
resolve(false);
|
|
577
|
+
}, 2e3);
|
|
578
|
+
socket.on("connect", () => {
|
|
579
|
+
clearTimeout(connectTimeout);
|
|
580
|
+
_socket = socket;
|
|
581
|
+
_connected = true;
|
|
582
|
+
_buffer = "";
|
|
583
|
+
socket.on("data", handleData);
|
|
584
|
+
socket.on("close", () => {
|
|
585
|
+
_connected = false;
|
|
586
|
+
_socket = null;
|
|
587
|
+
for (const [id, entry] of _pending) {
|
|
588
|
+
clearTimeout(entry.timer);
|
|
589
|
+
_pending.delete(id);
|
|
590
|
+
entry.resolve({ error: "Connection closed" });
|
|
591
|
+
}
|
|
592
|
+
});
|
|
593
|
+
socket.on("error", () => {
|
|
594
|
+
_connected = false;
|
|
595
|
+
_socket = null;
|
|
596
|
+
});
|
|
597
|
+
resolve(true);
|
|
598
|
+
});
|
|
599
|
+
socket.on("error", () => {
|
|
600
|
+
clearTimeout(connectTimeout);
|
|
601
|
+
resolve(false);
|
|
602
|
+
});
|
|
603
|
+
});
|
|
604
|
+
}
|
|
605
|
+
async function connectEmbedDaemon() {
|
|
606
|
+
if (_socket && _connected) return true;
|
|
607
|
+
if (await connectToSocket()) return true;
|
|
608
|
+
if (acquireSpawnLock()) {
|
|
609
|
+
try {
|
|
610
|
+
cleanupStaleFiles();
|
|
611
|
+
spawnDaemon();
|
|
612
|
+
} finally {
|
|
613
|
+
releaseSpawnLock();
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
const start = Date.now();
|
|
617
|
+
let delay2 = 100;
|
|
618
|
+
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
619
|
+
await new Promise((r) => setTimeout(r, delay2));
|
|
620
|
+
if (await connectToSocket()) return true;
|
|
621
|
+
delay2 = Math.min(delay2 * 2, 3e3);
|
|
622
|
+
}
|
|
623
|
+
return false;
|
|
624
|
+
}
|
|
625
|
+
function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
626
|
+
return new Promise((resolve) => {
|
|
627
|
+
if (!_socket || !_connected) {
|
|
628
|
+
resolve({ error: "Not connected" });
|
|
629
|
+
return;
|
|
630
|
+
}
|
|
631
|
+
const id = randomUUID();
|
|
632
|
+
const timer = setTimeout(() => {
|
|
633
|
+
_pending.delete(id);
|
|
634
|
+
resolve({ error: "Request timeout" });
|
|
635
|
+
}, timeoutMs);
|
|
636
|
+
_pending.set(id, { resolve, timer });
|
|
637
|
+
try {
|
|
638
|
+
_socket.write(JSON.stringify({ id, ...payload }) + "\n");
|
|
639
|
+
} catch {
|
|
640
|
+
clearTimeout(timer);
|
|
641
|
+
_pending.delete(id);
|
|
642
|
+
resolve({ error: "Write failed" });
|
|
643
|
+
}
|
|
644
|
+
});
|
|
645
|
+
}
|
|
646
|
+
function isClientConnected() {
|
|
647
|
+
return _connected;
|
|
648
|
+
}
|
|
649
|
+
var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _pending, MAX_BUFFER;
|
|
650
|
+
var init_exe_daemon_client = __esm({
|
|
651
|
+
"src/lib/exe-daemon-client.ts"() {
|
|
652
|
+
"use strict";
|
|
653
|
+
init_config();
|
|
654
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path3.join(EXE_AI_DIR, "exed.sock");
|
|
655
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path3.join(EXE_AI_DIR, "exed.pid");
|
|
656
|
+
SPAWN_LOCK_PATH = path3.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
657
|
+
SPAWN_LOCK_STALE_MS = 3e4;
|
|
658
|
+
CONNECT_TIMEOUT_MS = 15e3;
|
|
659
|
+
REQUEST_TIMEOUT_MS = 3e4;
|
|
660
|
+
_socket = null;
|
|
661
|
+
_connected = false;
|
|
662
|
+
_buffer = "";
|
|
663
|
+
_pending = /* @__PURE__ */ new Map();
|
|
664
|
+
MAX_BUFFER = 1e7;
|
|
665
|
+
}
|
|
666
|
+
});
|
|
667
|
+
|
|
668
|
+
// src/lib/daemon-protocol.ts
|
|
669
|
+
function serializeValue(v) {
|
|
670
|
+
if (v === null || v === void 0) return null;
|
|
671
|
+
if (typeof v === "bigint") return Number(v);
|
|
672
|
+
if (typeof v === "boolean") return v ? 1 : 0;
|
|
673
|
+
if (v instanceof Uint8Array) {
|
|
674
|
+
return { __blob: Buffer.from(v).toString("base64") };
|
|
675
|
+
}
|
|
676
|
+
if (ArrayBuffer.isView(v)) {
|
|
677
|
+
return { __blob: Buffer.from(v.buffer, v.byteOffset, v.byteLength).toString("base64") };
|
|
678
|
+
}
|
|
679
|
+
if (v instanceof ArrayBuffer) {
|
|
680
|
+
return { __blob: Buffer.from(v).toString("base64") };
|
|
681
|
+
}
|
|
682
|
+
if (typeof v === "string" || typeof v === "number") return v;
|
|
683
|
+
return String(v);
|
|
684
|
+
}
|
|
685
|
+
function deserializeValue(v) {
|
|
686
|
+
if (v === null) return null;
|
|
687
|
+
if (typeof v === "object" && v !== null && "__blob" in v) {
|
|
688
|
+
const buf = Buffer.from(v.__blob, "base64");
|
|
689
|
+
return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
|
|
690
|
+
}
|
|
691
|
+
return v;
|
|
692
|
+
}
|
|
693
|
+
function deserializeResultSet(srs) {
|
|
694
|
+
const rows = srs.rows.map((obj) => {
|
|
695
|
+
const values = srs.columns.map(
|
|
696
|
+
(col) => deserializeValue(obj[col] ?? null)
|
|
697
|
+
);
|
|
698
|
+
const row = values;
|
|
699
|
+
for (let i = 0; i < srs.columns.length; i++) {
|
|
700
|
+
const col = srs.columns[i];
|
|
701
|
+
if (col !== void 0) {
|
|
702
|
+
row[col] = values[i] ?? null;
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
Object.defineProperty(row, "length", {
|
|
706
|
+
value: values.length,
|
|
707
|
+
enumerable: false
|
|
708
|
+
});
|
|
709
|
+
return row;
|
|
710
|
+
});
|
|
711
|
+
return {
|
|
712
|
+
columns: srs.columns,
|
|
713
|
+
columnTypes: srs.columnTypes ?? [],
|
|
714
|
+
rows,
|
|
715
|
+
rowsAffected: srs.rowsAffected,
|
|
716
|
+
lastInsertRowid: srs.lastInsertRowid != null ? BigInt(srs.lastInsertRowid) : void 0,
|
|
717
|
+
toJSON: () => ({
|
|
718
|
+
columns: srs.columns,
|
|
719
|
+
columnTypes: srs.columnTypes ?? [],
|
|
720
|
+
rows: srs.rows,
|
|
721
|
+
rowsAffected: srs.rowsAffected,
|
|
722
|
+
lastInsertRowid: srs.lastInsertRowid
|
|
723
|
+
})
|
|
724
|
+
};
|
|
725
|
+
}
|
|
726
|
+
var init_daemon_protocol = __esm({
|
|
727
|
+
"src/lib/daemon-protocol.ts"() {
|
|
728
|
+
"use strict";
|
|
729
|
+
}
|
|
730
|
+
});
|
|
731
|
+
|
|
732
|
+
// src/lib/db-daemon-client.ts
|
|
733
|
+
var db_daemon_client_exports = {};
|
|
734
|
+
__export(db_daemon_client_exports, {
|
|
735
|
+
createDaemonDbClient: () => createDaemonDbClient,
|
|
736
|
+
initDaemonDbClient: () => initDaemonDbClient
|
|
737
|
+
});
|
|
738
|
+
function normalizeStatement(stmt) {
|
|
739
|
+
if (typeof stmt === "string") {
|
|
740
|
+
return { sql: stmt, args: [] };
|
|
741
|
+
}
|
|
742
|
+
const sql = stmt.sql;
|
|
743
|
+
let args = [];
|
|
744
|
+
if (Array.isArray(stmt.args)) {
|
|
745
|
+
args = stmt.args.map((v) => serializeValue(v));
|
|
746
|
+
} else if (stmt.args && typeof stmt.args === "object") {
|
|
747
|
+
const named = {};
|
|
748
|
+
for (const [key, val] of Object.entries(stmt.args)) {
|
|
749
|
+
named[key] = serializeValue(val);
|
|
750
|
+
}
|
|
751
|
+
return { sql, args: named };
|
|
752
|
+
}
|
|
753
|
+
return { sql, args };
|
|
754
|
+
}
|
|
755
|
+
function createDaemonDbClient(fallbackClient) {
|
|
756
|
+
let _useDaemon = false;
|
|
757
|
+
const client = {
|
|
758
|
+
async execute(stmt) {
|
|
759
|
+
if (!_useDaemon || !isClientConnected()) {
|
|
760
|
+
return fallbackClient.execute(stmt);
|
|
761
|
+
}
|
|
762
|
+
const { sql, args } = normalizeStatement(stmt);
|
|
763
|
+
const response = await sendDaemonRequest({
|
|
764
|
+
type: "db-execute",
|
|
765
|
+
sql,
|
|
766
|
+
args
|
|
767
|
+
});
|
|
768
|
+
if (response.error) {
|
|
769
|
+
const errMsg = String(response.error);
|
|
770
|
+
if (errMsg === "Not connected" || errMsg === "Request timeout" || errMsg === "Write failed") {
|
|
771
|
+
process.stderr.write(`[db-daemon] Transport error (${errMsg}), falling back to direct
|
|
772
|
+
`);
|
|
773
|
+
return fallbackClient.execute(stmt);
|
|
774
|
+
}
|
|
775
|
+
throw new Error(errMsg);
|
|
776
|
+
}
|
|
777
|
+
if (response.db) {
|
|
778
|
+
return deserializeResultSet(response.db);
|
|
779
|
+
}
|
|
780
|
+
process.stderr.write("[db-daemon] Unexpected response shape, falling back to direct\n");
|
|
781
|
+
return fallbackClient.execute(stmt);
|
|
782
|
+
},
|
|
783
|
+
async batch(stmts, mode) {
|
|
784
|
+
if (!_useDaemon || !isClientConnected()) {
|
|
785
|
+
return fallbackClient.batch(stmts, mode);
|
|
786
|
+
}
|
|
787
|
+
const statements = stmts.map(normalizeStatement);
|
|
788
|
+
const response = await sendDaemonRequest({
|
|
789
|
+
type: "db-batch",
|
|
790
|
+
statements,
|
|
791
|
+
mode: mode ?? "deferred"
|
|
792
|
+
});
|
|
793
|
+
if (response.error) {
|
|
794
|
+
const errMsg = String(response.error);
|
|
795
|
+
if (errMsg === "Not connected" || errMsg === "Request timeout" || errMsg === "Write failed") {
|
|
796
|
+
process.stderr.write(`[db-daemon] Batch transport error (${errMsg}), falling back to direct
|
|
797
|
+
`);
|
|
798
|
+
return fallbackClient.batch(stmts, mode);
|
|
799
|
+
}
|
|
800
|
+
throw new Error(errMsg);
|
|
801
|
+
}
|
|
802
|
+
const batchResults = response["db-batch"];
|
|
803
|
+
if (batchResults) {
|
|
804
|
+
return batchResults.map(deserializeResultSet);
|
|
805
|
+
}
|
|
806
|
+
process.stderr.write("[db-daemon] Unexpected batch response shape, falling back to direct\n");
|
|
807
|
+
return fallbackClient.batch(stmts, mode);
|
|
808
|
+
},
|
|
809
|
+
// Transaction support — delegate to fallback (transactions need direct connection)
|
|
810
|
+
async transaction(mode) {
|
|
811
|
+
return fallbackClient.transaction(mode);
|
|
812
|
+
},
|
|
813
|
+
// executeMultiple — delegate to fallback (used only for schema migrations)
|
|
814
|
+
async executeMultiple(sql) {
|
|
815
|
+
return fallbackClient.executeMultiple(sql);
|
|
816
|
+
},
|
|
817
|
+
// migrate — delegate to fallback
|
|
818
|
+
async migrate(stmts) {
|
|
819
|
+
return fallbackClient.migrate(stmts);
|
|
820
|
+
},
|
|
821
|
+
// Sync mode — delegate to fallback
|
|
822
|
+
sync() {
|
|
823
|
+
return fallbackClient.sync();
|
|
824
|
+
},
|
|
825
|
+
close() {
|
|
826
|
+
_useDaemon = false;
|
|
827
|
+
},
|
|
828
|
+
get closed() {
|
|
829
|
+
return fallbackClient.closed;
|
|
830
|
+
},
|
|
831
|
+
get protocol() {
|
|
832
|
+
return fallbackClient.protocol;
|
|
833
|
+
}
|
|
834
|
+
};
|
|
835
|
+
return {
|
|
836
|
+
...client,
|
|
837
|
+
/** Enable daemon routing (call after confirming daemon is connected) */
|
|
838
|
+
_enableDaemon() {
|
|
839
|
+
_useDaemon = true;
|
|
840
|
+
},
|
|
841
|
+
/** Check if daemon routing is active */
|
|
842
|
+
_isDaemonActive() {
|
|
843
|
+
return _useDaemon && isClientConnected();
|
|
844
|
+
}
|
|
845
|
+
};
|
|
846
|
+
}
|
|
847
|
+
async function initDaemonDbClient(fallbackClient) {
|
|
848
|
+
if (process.env.EXE_IS_DAEMON === "1") return null;
|
|
849
|
+
const connected = await connectEmbedDaemon();
|
|
850
|
+
if (!connected) {
|
|
851
|
+
process.stderr.write("[db-daemon] Daemon unavailable \u2014 using direct SQLite\n");
|
|
852
|
+
return null;
|
|
853
|
+
}
|
|
854
|
+
const client = createDaemonDbClient(fallbackClient);
|
|
855
|
+
client._enableDaemon();
|
|
856
|
+
process.stderr.write("[db-daemon] DB routing through daemon (single-writer)\n");
|
|
857
|
+
return client;
|
|
858
|
+
}
|
|
859
|
+
var init_db_daemon_client = __esm({
|
|
860
|
+
"src/lib/db-daemon-client.ts"() {
|
|
861
|
+
"use strict";
|
|
862
|
+
init_exe_daemon_client();
|
|
863
|
+
init_daemon_protocol();
|
|
864
|
+
}
|
|
865
|
+
});
|
|
866
|
+
|
|
430
867
|
// src/lib/database.ts
|
|
431
868
|
var database_exports = {};
|
|
432
869
|
__export(database_exports, {
|
|
@@ -435,6 +872,7 @@ __export(database_exports, {
|
|
|
435
872
|
ensureSchema: () => ensureSchema,
|
|
436
873
|
getClient: () => getClient,
|
|
437
874
|
getRawClient: () => getRawClient,
|
|
875
|
+
initDaemonClient: () => initDaemonClient,
|
|
438
876
|
initDatabase: () => initDatabase,
|
|
439
877
|
initTurso: () => initTurso,
|
|
440
878
|
isInitialized: () => isInitialized
|
|
@@ -462,8 +900,27 @@ function getClient() {
|
|
|
462
900
|
if (!_resilientClient) {
|
|
463
901
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
464
902
|
}
|
|
903
|
+
if (process.env.EXE_IS_DAEMON === "1") {
|
|
904
|
+
return _resilientClient;
|
|
905
|
+
}
|
|
906
|
+
if (_daemonClient && _daemonClient._isDaemonActive()) {
|
|
907
|
+
return _daemonClient;
|
|
908
|
+
}
|
|
465
909
|
return _resilientClient;
|
|
466
910
|
}
|
|
911
|
+
async function initDaemonClient() {
|
|
912
|
+
if (process.env.EXE_IS_DAEMON === "1") return;
|
|
913
|
+
if (!_resilientClient) return;
|
|
914
|
+
try {
|
|
915
|
+
const { initDaemonDbClient: initDaemonDbClient2 } = await Promise.resolve().then(() => (init_db_daemon_client(), db_daemon_client_exports));
|
|
916
|
+
_daemonClient = await initDaemonDbClient2(_resilientClient);
|
|
917
|
+
} catch (err) {
|
|
918
|
+
process.stderr.write(
|
|
919
|
+
`[database] Daemon client init failed (non-fatal): ${err instanceof Error ? err.message : String(err)}
|
|
920
|
+
`
|
|
921
|
+
);
|
|
922
|
+
}
|
|
923
|
+
}
|
|
467
924
|
function getRawClient() {
|
|
468
925
|
if (!_client) {
|
|
469
926
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
@@ -950,6 +1407,12 @@ async function ensureSchema() {
|
|
|
950
1407
|
} catch {
|
|
951
1408
|
}
|
|
952
1409
|
}
|
|
1410
|
+
try {
|
|
1411
|
+
await client.execute(
|
|
1412
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_content_hash ON memories(content_hash, agent_id)`
|
|
1413
|
+
);
|
|
1414
|
+
} catch {
|
|
1415
|
+
}
|
|
953
1416
|
await client.executeMultiple(`
|
|
954
1417
|
CREATE TABLE IF NOT EXISTS entities (
|
|
955
1418
|
id TEXT PRIMARY KEY,
|
|
@@ -1002,7 +1465,30 @@ async function ensureSchema() {
|
|
|
1002
1465
|
entity_id TEXT NOT NULL,
|
|
1003
1466
|
PRIMARY KEY (hyperedge_id, entity_id)
|
|
1004
1467
|
);
|
|
1468
|
+
|
|
1469
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS entities_fts USING fts5(
|
|
1470
|
+
name,
|
|
1471
|
+
content=entities,
|
|
1472
|
+
content_rowid=rowid
|
|
1473
|
+
);
|
|
1474
|
+
|
|
1475
|
+
CREATE TRIGGER IF NOT EXISTS entities_fts_ai AFTER INSERT ON entities BEGIN
|
|
1476
|
+
INSERT INTO entities_fts(rowid, name) VALUES (new.rowid, new.name);
|
|
1477
|
+
END;
|
|
1478
|
+
|
|
1479
|
+
CREATE TRIGGER IF NOT EXISTS entities_fts_ad AFTER DELETE ON entities BEGIN
|
|
1480
|
+
INSERT INTO entities_fts(entities_fts, rowid, name) VALUES('delete', old.rowid, old.name);
|
|
1481
|
+
END;
|
|
1482
|
+
|
|
1483
|
+
CREATE TRIGGER IF NOT EXISTS entities_fts_au AFTER UPDATE ON entities BEGIN
|
|
1484
|
+
INSERT INTO entities_fts(entities_fts, rowid, name) VALUES('delete', old.rowid, old.name);
|
|
1485
|
+
INSERT INTO entities_fts(rowid, name) VALUES (new.rowid, new.name);
|
|
1486
|
+
END;
|
|
1005
1487
|
`);
|
|
1488
|
+
try {
|
|
1489
|
+
await client.execute("INSERT INTO entities_fts(entities_fts) VALUES('rebuild')");
|
|
1490
|
+
} catch {
|
|
1491
|
+
}
|
|
1006
1492
|
await client.executeMultiple(`
|
|
1007
1493
|
CREATE TABLE IF NOT EXISTS entity_aliases (
|
|
1008
1494
|
alias TEXT NOT NULL PRIMARY KEY,
|
|
@@ -1183,6 +1669,33 @@ async function ensureSchema() {
|
|
|
1183
1669
|
CREATE INDEX IF NOT EXISTS idx_conversations_channel
|
|
1184
1670
|
ON conversations(channel_id);
|
|
1185
1671
|
`);
|
|
1672
|
+
await client.executeMultiple(`
|
|
1673
|
+
CREATE TABLE IF NOT EXISTS session_agent_map (
|
|
1674
|
+
session_uuid TEXT PRIMARY KEY,
|
|
1675
|
+
agent_id TEXT NOT NULL,
|
|
1676
|
+
session_name TEXT,
|
|
1677
|
+
task_id TEXT,
|
|
1678
|
+
project_name TEXT,
|
|
1679
|
+
started_at TEXT NOT NULL
|
|
1680
|
+
);
|
|
1681
|
+
|
|
1682
|
+
CREATE INDEX IF NOT EXISTS idx_session_agent_map_agent
|
|
1683
|
+
ON session_agent_map(agent_id);
|
|
1684
|
+
`);
|
|
1685
|
+
try {
|
|
1686
|
+
const mapCount = await client.execute({ sql: `SELECT COUNT(*) as cnt FROM session_agent_map`, args: [] });
|
|
1687
|
+
if (Number(mapCount.rows[0]?.cnt ?? 0) === 0) {
|
|
1688
|
+
await client.execute({
|
|
1689
|
+
sql: `INSERT OR IGNORE INTO session_agent_map (session_uuid, agent_id, session_name, started_at)
|
|
1690
|
+
SELECT session_id, agent_id, '', MIN(timestamp)
|
|
1691
|
+
FROM memories
|
|
1692
|
+
WHERE session_id IS NOT NULL AND session_id != '' AND agent_id IS NOT NULL AND agent_id != ''
|
|
1693
|
+
GROUP BY session_id, agent_id`,
|
|
1694
|
+
args: []
|
|
1695
|
+
});
|
|
1696
|
+
}
|
|
1697
|
+
} catch {
|
|
1698
|
+
}
|
|
1186
1699
|
try {
|
|
1187
1700
|
await client.execute({
|
|
1188
1701
|
sql: `ALTER TABLE tasks ADD COLUMN budget_tokens INTEGER`,
|
|
@@ -1316,15 +1829,41 @@ async function ensureSchema() {
|
|
|
1316
1829
|
});
|
|
1317
1830
|
} catch {
|
|
1318
1831
|
}
|
|
1832
|
+
for (const col of [
|
|
1833
|
+
"ALTER TABLE memories ADD COLUMN intent TEXT",
|
|
1834
|
+
"ALTER TABLE memories ADD COLUMN outcome TEXT",
|
|
1835
|
+
"ALTER TABLE memories ADD COLUMN domain TEXT",
|
|
1836
|
+
"ALTER TABLE memories ADD COLUMN referenced_entities TEXT",
|
|
1837
|
+
"ALTER TABLE memories ADD COLUMN retrieval_count INTEGER DEFAULT 0",
|
|
1838
|
+
"ALTER TABLE memories ADD COLUMN chain_position TEXT",
|
|
1839
|
+
"ALTER TABLE memories ADD COLUMN review_status TEXT",
|
|
1840
|
+
"ALTER TABLE memories ADD COLUMN context_window_pct INTEGER",
|
|
1841
|
+
"ALTER TABLE memories ADD COLUMN file_paths TEXT",
|
|
1842
|
+
"ALTER TABLE memories ADD COLUMN commit_hash TEXT",
|
|
1843
|
+
"ALTER TABLE memories ADD COLUMN duration_ms INTEGER",
|
|
1844
|
+
"ALTER TABLE memories ADD COLUMN token_cost REAL",
|
|
1845
|
+
"ALTER TABLE memories ADD COLUMN audience TEXT",
|
|
1846
|
+
"ALTER TABLE memories ADD COLUMN language_type TEXT",
|
|
1847
|
+
"ALTER TABLE memories ADD COLUMN parent_memory_id TEXT"
|
|
1848
|
+
]) {
|
|
1849
|
+
try {
|
|
1850
|
+
await client.execute(col);
|
|
1851
|
+
} catch {
|
|
1852
|
+
}
|
|
1853
|
+
}
|
|
1319
1854
|
}
|
|
1320
1855
|
async function disposeDatabase() {
|
|
1856
|
+
if (_daemonClient) {
|
|
1857
|
+
_daemonClient.close();
|
|
1858
|
+
_daemonClient = null;
|
|
1859
|
+
}
|
|
1321
1860
|
if (_client) {
|
|
1322
1861
|
_client.close();
|
|
1323
1862
|
_client = null;
|
|
1324
1863
|
_resilientClient = null;
|
|
1325
1864
|
}
|
|
1326
1865
|
}
|
|
1327
|
-
var _client, _resilientClient, initTurso, disposeTurso;
|
|
1866
|
+
var _client, _resilientClient, _daemonClient, initTurso, disposeTurso;
|
|
1328
1867
|
var init_database = __esm({
|
|
1329
1868
|
"src/lib/database.ts"() {
|
|
1330
1869
|
"use strict";
|
|
@@ -1332,6 +1871,7 @@ var init_database = __esm({
|
|
|
1332
1871
|
init_employees();
|
|
1333
1872
|
_client = null;
|
|
1334
1873
|
_resilientClient = null;
|
|
1874
|
+
_daemonClient = null;
|
|
1335
1875
|
initTurso = initDatabase;
|
|
1336
1876
|
disposeTurso = disposeDatabase;
|
|
1337
1877
|
}
|
|
@@ -1455,7 +1995,7 @@ __export(global_procedures_exports, {
|
|
|
1455
1995
|
loadGlobalProcedures: () => loadGlobalProcedures,
|
|
1456
1996
|
storeGlobalProcedure: () => storeGlobalProcedure
|
|
1457
1997
|
});
|
|
1458
|
-
import { randomUUID } from "crypto";
|
|
1998
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
1459
1999
|
async function loadGlobalProcedures() {
|
|
1460
2000
|
const client = getClient();
|
|
1461
2001
|
const result = await client.execute({
|
|
@@ -1484,7 +2024,7 @@ ${sections.join("\n\n")}
|
|
|
1484
2024
|
`;
|
|
1485
2025
|
}
|
|
1486
2026
|
async function storeGlobalProcedure(input) {
|
|
1487
|
-
const id =
|
|
2027
|
+
const id = randomUUID2();
|
|
1488
2028
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1489
2029
|
const client = getClient();
|
|
1490
2030
|
await client.execute({
|
|
@@ -1535,14 +2075,14 @@ __export(keychain_exports, {
|
|
|
1535
2075
|
setMasterKey: () => setMasterKey
|
|
1536
2076
|
});
|
|
1537
2077
|
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
1538
|
-
import { existsSync as
|
|
1539
|
-
import
|
|
2078
|
+
import { existsSync as existsSync4 } from "fs";
|
|
2079
|
+
import path4 from "path";
|
|
1540
2080
|
import os3 from "os";
|
|
1541
2081
|
function getKeyDir() {
|
|
1542
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
2082
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path4.join(os3.homedir(), ".exe-os");
|
|
1543
2083
|
}
|
|
1544
2084
|
function getKeyPath() {
|
|
1545
|
-
return
|
|
2085
|
+
return path4.join(getKeyDir(), "master.key");
|
|
1546
2086
|
}
|
|
1547
2087
|
async function tryKeytar() {
|
|
1548
2088
|
try {
|
|
@@ -1563,13 +2103,21 @@ async function getMasterKey() {
|
|
|
1563
2103
|
}
|
|
1564
2104
|
}
|
|
1565
2105
|
const keyPath = getKeyPath();
|
|
1566
|
-
if (!
|
|
2106
|
+
if (!existsSync4(keyPath)) {
|
|
2107
|
+
process.stderr.write(
|
|
2108
|
+
`[keychain] Key not found at ${keyPath} (HOME=${os3.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
2109
|
+
`
|
|
2110
|
+
);
|
|
1567
2111
|
return null;
|
|
1568
2112
|
}
|
|
1569
2113
|
try {
|
|
1570
2114
|
const content = await readFile3(keyPath, "utf-8");
|
|
1571
2115
|
return Buffer.from(content.trim(), "base64");
|
|
1572
|
-
} catch {
|
|
2116
|
+
} catch (err) {
|
|
2117
|
+
process.stderr.write(
|
|
2118
|
+
`[keychain] Key read failed at ${keyPath}: ${err instanceof Error ? err.message : String(err)}
|
|
2119
|
+
`
|
|
2120
|
+
);
|
|
1573
2121
|
return null;
|
|
1574
2122
|
}
|
|
1575
2123
|
}
|
|
@@ -1598,7 +2146,7 @@ async function deleteMasterKey() {
|
|
|
1598
2146
|
}
|
|
1599
2147
|
}
|
|
1600
2148
|
const keyPath = getKeyPath();
|
|
1601
|
-
if (
|
|
2149
|
+
if (existsSync4(keyPath)) {
|
|
1602
2150
|
await unlink(keyPath);
|
|
1603
2151
|
}
|
|
1604
2152
|
}
|
|
@@ -1708,12 +2256,12 @@ __export(shard_manager_exports, {
|
|
|
1708
2256
|
listShards: () => listShards,
|
|
1709
2257
|
shardExists: () => shardExists
|
|
1710
2258
|
});
|
|
1711
|
-
import
|
|
1712
|
-
import { existsSync as
|
|
2259
|
+
import path5 from "path";
|
|
2260
|
+
import { existsSync as existsSync5, mkdirSync, readdirSync } from "fs";
|
|
1713
2261
|
import { createClient as createClient2 } from "@libsql/client";
|
|
1714
2262
|
function initShardManager(encryptionKey) {
|
|
1715
2263
|
_encryptionKey = encryptionKey;
|
|
1716
|
-
if (!
|
|
2264
|
+
if (!existsSync5(SHARDS_DIR)) {
|
|
1717
2265
|
mkdirSync(SHARDS_DIR, { recursive: true });
|
|
1718
2266
|
}
|
|
1719
2267
|
_shardingEnabled = true;
|
|
@@ -1734,7 +2282,7 @@ function getShardClient(projectName) {
|
|
|
1734
2282
|
}
|
|
1735
2283
|
const cached = _shards.get(safeName);
|
|
1736
2284
|
if (cached) return cached;
|
|
1737
|
-
const dbPath =
|
|
2285
|
+
const dbPath = path5.join(SHARDS_DIR, `${safeName}.db`);
|
|
1738
2286
|
const client = createClient2({
|
|
1739
2287
|
url: `file:${dbPath}`,
|
|
1740
2288
|
encryptionKey: _encryptionKey
|
|
@@ -1744,10 +2292,10 @@ function getShardClient(projectName) {
|
|
|
1744
2292
|
}
|
|
1745
2293
|
function shardExists(projectName) {
|
|
1746
2294
|
const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
1747
|
-
return
|
|
2295
|
+
return existsSync5(path5.join(SHARDS_DIR, `${safeName}.db`));
|
|
1748
2296
|
}
|
|
1749
2297
|
function listShards() {
|
|
1750
|
-
if (!
|
|
2298
|
+
if (!existsSync5(SHARDS_DIR)) return [];
|
|
1751
2299
|
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
1752
2300
|
}
|
|
1753
2301
|
async function ensureShardSchema(client) {
|
|
@@ -1933,7 +2481,7 @@ var init_shard_manager = __esm({
|
|
|
1933
2481
|
"src/lib/shard-manager.ts"() {
|
|
1934
2482
|
"use strict";
|
|
1935
2483
|
init_config();
|
|
1936
|
-
SHARDS_DIR =
|
|
2484
|
+
SHARDS_DIR = path5.join(EXE_AI_DIR, "shards");
|
|
1937
2485
|
_shards = /* @__PURE__ */ new Map();
|
|
1938
2486
|
_encryptionKey = null;
|
|
1939
2487
|
_shardingEnabled = false;
|
|
@@ -1941,6 +2489,7 @@ var init_shard_manager = __esm({
|
|
|
1941
2489
|
});
|
|
1942
2490
|
|
|
1943
2491
|
// src/lib/store.ts
|
|
2492
|
+
import { createHash } from "crypto";
|
|
1944
2493
|
function isBusyError2(err) {
|
|
1945
2494
|
if (err instanceof Error) {
|
|
1946
2495
|
const msg = err.message.toLowerCase();
|
|
@@ -2036,13 +2585,13 @@ __export(session_registry_exports, {
|
|
|
2036
2585
|
pruneStaleSessions: () => pruneStaleSessions,
|
|
2037
2586
|
registerSession: () => registerSession
|
|
2038
2587
|
});
|
|
2039
|
-
import { readFileSync as
|
|
2588
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, existsSync as existsSync6 } from "fs";
|
|
2040
2589
|
import { execSync as execSync2 } from "child_process";
|
|
2041
|
-
import
|
|
2590
|
+
import path6 from "path";
|
|
2042
2591
|
import os4 from "os";
|
|
2043
2592
|
function registerSession(entry) {
|
|
2044
|
-
const dir =
|
|
2045
|
-
if (!
|
|
2593
|
+
const dir = path6.dirname(REGISTRY_PATH);
|
|
2594
|
+
if (!existsSync6(dir)) {
|
|
2046
2595
|
mkdirSync2(dir, { recursive: true });
|
|
2047
2596
|
}
|
|
2048
2597
|
const sessions = listSessions();
|
|
@@ -2056,7 +2605,7 @@ function registerSession(entry) {
|
|
|
2056
2605
|
}
|
|
2057
2606
|
function listSessions() {
|
|
2058
2607
|
try {
|
|
2059
|
-
const raw =
|
|
2608
|
+
const raw = readFileSync4(REGISTRY_PATH, "utf8");
|
|
2060
2609
|
return JSON.parse(raw);
|
|
2061
2610
|
} catch {
|
|
2062
2611
|
return [];
|
|
@@ -2085,7 +2634,7 @@ var REGISTRY_PATH;
|
|
|
2085
2634
|
var init_session_registry = __esm({
|
|
2086
2635
|
"src/lib/session-registry.ts"() {
|
|
2087
2636
|
"use strict";
|
|
2088
|
-
REGISTRY_PATH =
|
|
2637
|
+
REGISTRY_PATH = path6.join(os4.homedir(), ".exe-os", "session-registry.json");
|
|
2089
2638
|
}
|
|
2090
2639
|
});
|
|
2091
2640
|
|
|
@@ -2304,18 +2853,69 @@ var init_provider_table = __esm({
|
|
|
2304
2853
|
}
|
|
2305
2854
|
});
|
|
2306
2855
|
|
|
2856
|
+
// src/lib/runtime-table.ts
|
|
2857
|
+
var RUNTIME_TABLE, DEFAULT_RUNTIME;
|
|
2858
|
+
var init_runtime_table = __esm({
|
|
2859
|
+
"src/lib/runtime-table.ts"() {
|
|
2860
|
+
"use strict";
|
|
2861
|
+
RUNTIME_TABLE = {
|
|
2862
|
+
codex: {
|
|
2863
|
+
binary: "codex",
|
|
2864
|
+
launchMode: "exec",
|
|
2865
|
+
autoApproveFlag: "--full-auto",
|
|
2866
|
+
inlineFlag: "--no-alt-screen",
|
|
2867
|
+
apiKeyEnv: "OPENAI_API_KEY",
|
|
2868
|
+
defaultModel: "gpt-5.4"
|
|
2869
|
+
}
|
|
2870
|
+
};
|
|
2871
|
+
DEFAULT_RUNTIME = "claude";
|
|
2872
|
+
}
|
|
2873
|
+
});
|
|
2874
|
+
|
|
2875
|
+
// src/lib/agent-config.ts
|
|
2876
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync7, mkdirSync as mkdirSync3 } from "fs";
|
|
2877
|
+
import path7 from "path";
|
|
2878
|
+
function loadAgentConfig() {
|
|
2879
|
+
if (!existsSync7(AGENT_CONFIG_PATH)) return {};
|
|
2880
|
+
try {
|
|
2881
|
+
return JSON.parse(readFileSync5(AGENT_CONFIG_PATH, "utf-8"));
|
|
2882
|
+
} catch {
|
|
2883
|
+
return {};
|
|
2884
|
+
}
|
|
2885
|
+
}
|
|
2886
|
+
function getAgentRuntime(agentId) {
|
|
2887
|
+
const config = loadAgentConfig();
|
|
2888
|
+
const entry = config[agentId];
|
|
2889
|
+
if (entry) return entry;
|
|
2890
|
+
return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
|
|
2891
|
+
}
|
|
2892
|
+
var AGENT_CONFIG_PATH, DEFAULT_MODELS;
|
|
2893
|
+
var init_agent_config = __esm({
|
|
2894
|
+
"src/lib/agent-config.ts"() {
|
|
2895
|
+
"use strict";
|
|
2896
|
+
init_config();
|
|
2897
|
+
init_runtime_table();
|
|
2898
|
+
AGENT_CONFIG_PATH = path7.join(EXE_AI_DIR, "agent-config.json");
|
|
2899
|
+
DEFAULT_MODELS = {
|
|
2900
|
+
claude: "claude-opus-4",
|
|
2901
|
+
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
2902
|
+
opencode: "minimax-m2.7"
|
|
2903
|
+
};
|
|
2904
|
+
}
|
|
2905
|
+
});
|
|
2906
|
+
|
|
2307
2907
|
// src/lib/intercom-queue.ts
|
|
2308
|
-
import { readFileSync as
|
|
2309
|
-
import
|
|
2908
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, renameSync as renameSync3, existsSync as existsSync8, mkdirSync as mkdirSync4 } from "fs";
|
|
2909
|
+
import path8 from "path";
|
|
2310
2910
|
import os5 from "os";
|
|
2311
2911
|
function ensureDir() {
|
|
2312
|
-
const dir =
|
|
2313
|
-
if (!
|
|
2912
|
+
const dir = path8.dirname(QUEUE_PATH);
|
|
2913
|
+
if (!existsSync8(dir)) mkdirSync4(dir, { recursive: true });
|
|
2314
2914
|
}
|
|
2315
2915
|
function readQueue() {
|
|
2316
2916
|
try {
|
|
2317
|
-
if (!
|
|
2318
|
-
return JSON.parse(
|
|
2917
|
+
if (!existsSync8(QUEUE_PATH)) return [];
|
|
2918
|
+
return JSON.parse(readFileSync6(QUEUE_PATH, "utf8"));
|
|
2319
2919
|
} catch {
|
|
2320
2920
|
return [];
|
|
2321
2921
|
}
|
|
@@ -2323,7 +2923,7 @@ function readQueue() {
|
|
|
2323
2923
|
function writeQueue(queue) {
|
|
2324
2924
|
ensureDir();
|
|
2325
2925
|
const tmp = `${QUEUE_PATH}.tmp`;
|
|
2326
|
-
|
|
2926
|
+
writeFileSync4(tmp, JSON.stringify(queue, null, 2));
|
|
2327
2927
|
renameSync3(tmp, QUEUE_PATH);
|
|
2328
2928
|
}
|
|
2329
2929
|
function queueIntercom(targetSession, reason) {
|
|
@@ -2347,9 +2947,9 @@ var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
|
|
|
2347
2947
|
var init_intercom_queue = __esm({
|
|
2348
2948
|
"src/lib/intercom-queue.ts"() {
|
|
2349
2949
|
"use strict";
|
|
2350
|
-
QUEUE_PATH =
|
|
2950
|
+
QUEUE_PATH = path8.join(os5.homedir(), ".exe-os", "intercom-queue.json");
|
|
2351
2951
|
TTL_MS = 60 * 60 * 1e3;
|
|
2352
|
-
INTERCOM_LOG =
|
|
2952
|
+
INTERCOM_LOG = path8.join(os5.homedir(), ".exe-os", "intercom.log");
|
|
2353
2953
|
}
|
|
2354
2954
|
});
|
|
2355
2955
|
|
|
@@ -2370,9 +2970,9 @@ __export(license_exports, {
|
|
|
2370
2970
|
stopLicenseRevalidation: () => stopLicenseRevalidation,
|
|
2371
2971
|
validateLicense: () => validateLicense
|
|
2372
2972
|
});
|
|
2373
|
-
import { readFileSync as
|
|
2374
|
-
import { randomUUID as
|
|
2375
|
-
import
|
|
2973
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, existsSync as existsSync9, mkdirSync as mkdirSync5 } from "fs";
|
|
2974
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
2975
|
+
import path9 from "path";
|
|
2376
2976
|
import { jwtVerify, importSPKI } from "jose";
|
|
2377
2977
|
async function fetchRetry(url, init) {
|
|
2378
2978
|
try {
|
|
@@ -2383,37 +2983,37 @@ async function fetchRetry(url, init) {
|
|
|
2383
2983
|
}
|
|
2384
2984
|
}
|
|
2385
2985
|
function loadDeviceId() {
|
|
2386
|
-
const deviceJsonPath =
|
|
2986
|
+
const deviceJsonPath = path9.join(EXE_AI_DIR, "device.json");
|
|
2387
2987
|
try {
|
|
2388
|
-
if (
|
|
2389
|
-
const data = JSON.parse(
|
|
2988
|
+
if (existsSync9(deviceJsonPath)) {
|
|
2989
|
+
const data = JSON.parse(readFileSync7(deviceJsonPath, "utf8"));
|
|
2390
2990
|
if (data.deviceId) return data.deviceId;
|
|
2391
2991
|
}
|
|
2392
2992
|
} catch {
|
|
2393
2993
|
}
|
|
2394
2994
|
try {
|
|
2395
|
-
if (
|
|
2396
|
-
const id2 =
|
|
2995
|
+
if (existsSync9(DEVICE_ID_PATH)) {
|
|
2996
|
+
const id2 = readFileSync7(DEVICE_ID_PATH, "utf8").trim();
|
|
2397
2997
|
if (id2) return id2;
|
|
2398
2998
|
}
|
|
2399
2999
|
} catch {
|
|
2400
3000
|
}
|
|
2401
|
-
const id =
|
|
2402
|
-
|
|
2403
|
-
|
|
3001
|
+
const id = randomUUID3();
|
|
3002
|
+
mkdirSync5(EXE_AI_DIR, { recursive: true });
|
|
3003
|
+
writeFileSync5(DEVICE_ID_PATH, id, "utf8");
|
|
2404
3004
|
return id;
|
|
2405
3005
|
}
|
|
2406
3006
|
function loadLicense() {
|
|
2407
3007
|
try {
|
|
2408
|
-
if (!
|
|
2409
|
-
return
|
|
3008
|
+
if (!existsSync9(LICENSE_PATH)) return null;
|
|
3009
|
+
return readFileSync7(LICENSE_PATH, "utf8").trim();
|
|
2410
3010
|
} catch {
|
|
2411
3011
|
return null;
|
|
2412
3012
|
}
|
|
2413
3013
|
}
|
|
2414
3014
|
function saveLicense(apiKey) {
|
|
2415
|
-
|
|
2416
|
-
|
|
3015
|
+
mkdirSync5(EXE_AI_DIR, { recursive: true });
|
|
3016
|
+
writeFileSync5(LICENSE_PATH, apiKey.trim(), { encoding: "utf8", mode: 384 });
|
|
2417
3017
|
}
|
|
2418
3018
|
async function verifyLicenseJwt(token) {
|
|
2419
3019
|
try {
|
|
@@ -2439,8 +3039,8 @@ async function verifyLicenseJwt(token) {
|
|
|
2439
3039
|
}
|
|
2440
3040
|
async function getCachedLicense() {
|
|
2441
3041
|
try {
|
|
2442
|
-
if (!
|
|
2443
|
-
const raw = JSON.parse(
|
|
3042
|
+
if (!existsSync9(CACHE_PATH)) return null;
|
|
3043
|
+
const raw = JSON.parse(readFileSync7(CACHE_PATH, "utf8"));
|
|
2444
3044
|
if (!raw.token || typeof raw.token !== "string") return null;
|
|
2445
3045
|
return await verifyLicenseJwt(raw.token);
|
|
2446
3046
|
} catch {
|
|
@@ -2449,8 +3049,8 @@ async function getCachedLicense() {
|
|
|
2449
3049
|
}
|
|
2450
3050
|
function readCachedToken() {
|
|
2451
3051
|
try {
|
|
2452
|
-
if (!
|
|
2453
|
-
const raw = JSON.parse(
|
|
3052
|
+
if (!existsSync9(CACHE_PATH)) return null;
|
|
3053
|
+
const raw = JSON.parse(readFileSync7(CACHE_PATH, "utf8"));
|
|
2454
3054
|
return typeof raw.token === "string" ? raw.token : null;
|
|
2455
3055
|
} catch {
|
|
2456
3056
|
return null;
|
|
@@ -2484,7 +3084,7 @@ function getRawCachedPlan() {
|
|
|
2484
3084
|
}
|
|
2485
3085
|
function cacheResponse(token) {
|
|
2486
3086
|
try {
|
|
2487
|
-
|
|
3087
|
+
writeFileSync5(CACHE_PATH, JSON.stringify({ token }), "utf8");
|
|
2488
3088
|
} catch {
|
|
2489
3089
|
}
|
|
2490
3090
|
}
|
|
@@ -2548,9 +3148,9 @@ async function checkLicense() {
|
|
|
2548
3148
|
let key = loadLicense();
|
|
2549
3149
|
if (!key) {
|
|
2550
3150
|
try {
|
|
2551
|
-
const configPath =
|
|
2552
|
-
if (
|
|
2553
|
-
const raw = JSON.parse(
|
|
3151
|
+
const configPath = path9.join(EXE_AI_DIR, "config.json");
|
|
3152
|
+
if (existsSync9(configPath)) {
|
|
3153
|
+
const raw = JSON.parse(readFileSync7(configPath, "utf8"));
|
|
2554
3154
|
const cloud = raw.cloud;
|
|
2555
3155
|
if (cloud?.apiKey) {
|
|
2556
3156
|
key = cloud.apiKey;
|
|
@@ -2709,9 +3309,9 @@ var init_license = __esm({
|
|
|
2709
3309
|
"src/lib/license.ts"() {
|
|
2710
3310
|
"use strict";
|
|
2711
3311
|
init_config();
|
|
2712
|
-
LICENSE_PATH =
|
|
2713
|
-
CACHE_PATH =
|
|
2714
|
-
DEVICE_ID_PATH =
|
|
3312
|
+
LICENSE_PATH = path9.join(EXE_AI_DIR, "license.key");
|
|
3313
|
+
CACHE_PATH = path9.join(EXE_AI_DIR, "license-cache.json");
|
|
3314
|
+
DEVICE_ID_PATH = path9.join(EXE_AI_DIR, "device-id");
|
|
2715
3315
|
API_BASE = "https://askexe.com/cloud";
|
|
2716
3316
|
RETRY_DELAY_MS = 500;
|
|
2717
3317
|
LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
|
|
@@ -2741,12 +3341,12 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
|
|
|
2741
3341
|
});
|
|
2742
3342
|
|
|
2743
3343
|
// src/lib/plan-limits.ts
|
|
2744
|
-
import { readFileSync as
|
|
2745
|
-
import
|
|
3344
|
+
import { readFileSync as readFileSync8, existsSync as existsSync10 } from "fs";
|
|
3345
|
+
import path10 from "path";
|
|
2746
3346
|
function getLicenseSync() {
|
|
2747
3347
|
try {
|
|
2748
|
-
if (!
|
|
2749
|
-
const raw = JSON.parse(
|
|
3348
|
+
if (!existsSync10(CACHE_PATH2)) return freeLicense();
|
|
3349
|
+
const raw = JSON.parse(readFileSync8(CACHE_PATH2, "utf8"));
|
|
2750
3350
|
if (!raw.token || typeof raw.token !== "string") return freeLicense();
|
|
2751
3351
|
const parts = raw.token.split(".");
|
|
2752
3352
|
if (parts.length !== 3) return freeLicense();
|
|
@@ -2784,8 +3384,8 @@ function assertEmployeeLimitSync(rosterPath) {
|
|
|
2784
3384
|
const filePath = rosterPath ?? EMPLOYEES_PATH;
|
|
2785
3385
|
let count = 0;
|
|
2786
3386
|
try {
|
|
2787
|
-
if (
|
|
2788
|
-
const raw =
|
|
3387
|
+
if (existsSync10(filePath)) {
|
|
3388
|
+
const raw = readFileSync8(filePath, "utf8");
|
|
2789
3389
|
const employees = JSON.parse(raw);
|
|
2790
3390
|
count = Array.isArray(employees) ? employees.length : 0;
|
|
2791
3391
|
}
|
|
@@ -2814,19 +3414,19 @@ var init_plan_limits = __esm({
|
|
|
2814
3414
|
this.name = "PlanLimitError";
|
|
2815
3415
|
}
|
|
2816
3416
|
};
|
|
2817
|
-
CACHE_PATH2 =
|
|
3417
|
+
CACHE_PATH2 = path10.join(EXE_AI_DIR, "license-cache.json");
|
|
2818
3418
|
}
|
|
2819
3419
|
});
|
|
2820
3420
|
|
|
2821
3421
|
// src/lib/notifications.ts
|
|
2822
3422
|
import crypto from "crypto";
|
|
2823
|
-
import
|
|
3423
|
+
import path11 from "path";
|
|
2824
3424
|
import os6 from "os";
|
|
2825
3425
|
import {
|
|
2826
|
-
readFileSync as
|
|
3426
|
+
readFileSync as readFileSync9,
|
|
2827
3427
|
readdirSync as readdirSync2,
|
|
2828
|
-
unlinkSync as
|
|
2829
|
-
existsSync as
|
|
3428
|
+
unlinkSync as unlinkSync3,
|
|
3429
|
+
existsSync as existsSync11,
|
|
2830
3430
|
rmdirSync
|
|
2831
3431
|
} from "fs";
|
|
2832
3432
|
async function writeNotification(notification) {
|
|
@@ -2939,9 +3539,9 @@ async function markDoneTaskNotificationsAsRead() {
|
|
|
2939
3539
|
}
|
|
2940
3540
|
}
|
|
2941
3541
|
async function migrateJsonNotifications() {
|
|
2942
|
-
const base = process.env.EXE_OS_DIR || process.env.EXE_MEM_DIR ||
|
|
2943
|
-
const notifDir =
|
|
2944
|
-
if (!
|
|
3542
|
+
const base = process.env.EXE_OS_DIR || process.env.EXE_MEM_DIR || path11.join(os6.homedir(), ".exe-os");
|
|
3543
|
+
const notifDir = path11.join(base, "notifications");
|
|
3544
|
+
if (!existsSync11(notifDir)) return 0;
|
|
2945
3545
|
let migrated = 0;
|
|
2946
3546
|
try {
|
|
2947
3547
|
const files = readdirSync2(notifDir).filter((f) => f.endsWith(".json"));
|
|
@@ -2949,8 +3549,8 @@ async function migrateJsonNotifications() {
|
|
|
2949
3549
|
const client = getClient();
|
|
2950
3550
|
for (const file of files) {
|
|
2951
3551
|
try {
|
|
2952
|
-
const filePath =
|
|
2953
|
-
const data = JSON.parse(
|
|
3552
|
+
const filePath = path11.join(notifDir, file);
|
|
3553
|
+
const data = JSON.parse(readFileSync9(filePath, "utf8"));
|
|
2954
3554
|
await client.execute({
|
|
2955
3555
|
sql: `INSERT OR IGNORE INTO notifications (id, agent_id, agent_role, event, project, summary, task_file, read, created_at)
|
|
2956
3556
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
@@ -2966,7 +3566,7 @@ async function migrateJsonNotifications() {
|
|
|
2966
3566
|
data.timestamp ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
2967
3567
|
]
|
|
2968
3568
|
});
|
|
2969
|
-
|
|
3569
|
+
unlinkSync3(filePath);
|
|
2970
3570
|
migrated++;
|
|
2971
3571
|
} catch {
|
|
2972
3572
|
}
|
|
@@ -3097,10 +3697,11 @@ var init_session_kill_telemetry = __esm({
|
|
|
3097
3697
|
|
|
3098
3698
|
// src/lib/tasks-crud.ts
|
|
3099
3699
|
import crypto3 from "crypto";
|
|
3100
|
-
import
|
|
3700
|
+
import path12 from "path";
|
|
3701
|
+
import os7 from "os";
|
|
3101
3702
|
import { execSync as execSync5 } from "child_process";
|
|
3102
3703
|
import { mkdir as mkdir4, writeFile as writeFile4, appendFile } from "fs/promises";
|
|
3103
|
-
import { existsSync as
|
|
3704
|
+
import { existsSync as existsSync12, readFileSync as readFileSync10 } from "fs";
|
|
3104
3705
|
async function writeCheckpoint(input) {
|
|
3105
3706
|
const client = getClient();
|
|
3106
3707
|
const row = await resolveTask(client, input.taskId);
|
|
@@ -3141,6 +3742,35 @@ function extractParentFromContext(contextBody) {
|
|
|
3141
3742
|
function slugify(title) {
|
|
3142
3743
|
return title.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
3143
3744
|
}
|
|
3745
|
+
function buildKeywordIndex() {
|
|
3746
|
+
const idx = /* @__PURE__ */ new Map();
|
|
3747
|
+
for (const [role, keywords] of Object.entries(LANE_KEYWORDS)) {
|
|
3748
|
+
for (const kw of keywords) {
|
|
3749
|
+
const existing = idx.get(kw) ?? [];
|
|
3750
|
+
existing.push(role);
|
|
3751
|
+
idx.set(kw, existing);
|
|
3752
|
+
}
|
|
3753
|
+
}
|
|
3754
|
+
return idx;
|
|
3755
|
+
}
|
|
3756
|
+
function checkLaneAffinity(title, context, assigneeName) {
|
|
3757
|
+
const employees = loadEmployeesSync();
|
|
3758
|
+
const employee = employees.find((e) => e.name === assigneeName);
|
|
3759
|
+
if (!employee) return void 0;
|
|
3760
|
+
const assigneeRole = employee.role;
|
|
3761
|
+
const text = `${title} ${context}`.toLowerCase();
|
|
3762
|
+
const matchedRoles = /* @__PURE__ */ new Set();
|
|
3763
|
+
for (const [keyword, roles] of KEYWORD_INDEX) {
|
|
3764
|
+
if (text.includes(keyword)) {
|
|
3765
|
+
for (const role of roles) matchedRoles.add(role);
|
|
3766
|
+
}
|
|
3767
|
+
}
|
|
3768
|
+
if (matchedRoles.size === 0) return void 0;
|
|
3769
|
+
if (matchedRoles.has(assigneeRole)) return void 0;
|
|
3770
|
+
if (assigneeRole === "COO") return void 0;
|
|
3771
|
+
const expectedRoles = Array.from(matchedRoles).join(" or ");
|
|
3772
|
+
return `\u26A0\uFE0F Lane mismatch: task content suggests ${expectedRoles}, but assigned to ${assigneeName} (${assigneeRole}).`;
|
|
3773
|
+
}
|
|
3144
3774
|
async function resolveTask(client, identifier, scopeSession) {
|
|
3145
3775
|
const scope = sessionScopeFilter(scopeSession);
|
|
3146
3776
|
let result = await client.execute({
|
|
@@ -3190,7 +3820,14 @@ async function createTaskCore(input) {
|
|
|
3190
3820
|
const id = crypto3.randomUUID();
|
|
3191
3821
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3192
3822
|
const slug = slugify(input.title);
|
|
3193
|
-
|
|
3823
|
+
let earlySessionScope = null;
|
|
3824
|
+
try {
|
|
3825
|
+
const { resolveExeSession: resolveExeSession2 } = await Promise.resolve().then(() => (init_tmux_routing(), tmux_routing_exports));
|
|
3826
|
+
earlySessionScope = resolveExeSession2();
|
|
3827
|
+
} catch {
|
|
3828
|
+
}
|
|
3829
|
+
const scope = earlySessionScope ?? "default";
|
|
3830
|
+
const taskFile = input.taskFile ?? `tasks/${scope}/${input.assignedTo}/${slug}.md`;
|
|
3194
3831
|
let blockedById = null;
|
|
3195
3832
|
const initialStatus = input.blockedBy ? "blocked" : "open";
|
|
3196
3833
|
if (input.blockedBy) {
|
|
@@ -3230,22 +3867,24 @@ async function createTaskCore(input) {
|
|
|
3230
3867
|
if (dupCheck.rows.length > 0) {
|
|
3231
3868
|
warning = `similar active task already exists (${String(dupCheck.rows[0].id)}). Created new task anyway.`;
|
|
3232
3869
|
}
|
|
3870
|
+
if (!process.env.DISABLE_LANE_AFFINITY) {
|
|
3871
|
+
const laneWarning = checkLaneAffinity(input.title, input.context, input.assignedTo);
|
|
3872
|
+
if (laneWarning) {
|
|
3873
|
+
warning = warning ? `${warning}
|
|
3874
|
+
${laneWarning}` : laneWarning;
|
|
3875
|
+
}
|
|
3876
|
+
}
|
|
3233
3877
|
if (input.baseDir) {
|
|
3234
3878
|
try {
|
|
3235
|
-
await mkdir4(
|
|
3236
|
-
await mkdir4(
|
|
3879
|
+
await mkdir4(path12.join(input.baseDir, "exe", "output"), { recursive: true });
|
|
3880
|
+
await mkdir4(path12.join(input.baseDir, "exe", "research"), { recursive: true });
|
|
3237
3881
|
await ensureArchitectureDoc(input.baseDir, input.projectName);
|
|
3238
3882
|
await ensureGitignoreExe(input.baseDir);
|
|
3239
3883
|
} catch {
|
|
3240
3884
|
}
|
|
3241
3885
|
}
|
|
3242
3886
|
const complexity = input.complexity ?? "standard";
|
|
3243
|
-
|
|
3244
|
-
try {
|
|
3245
|
-
const { resolveExeSession: resolveExeSession2 } = await Promise.resolve().then(() => (init_tmux_routing(), tmux_routing_exports));
|
|
3246
|
-
sessionScope = resolveExeSession2();
|
|
3247
|
-
} catch {
|
|
3248
|
-
}
|
|
3887
|
+
const sessionScope = earlySessionScope;
|
|
3249
3888
|
await client.execute({
|
|
3250
3889
|
sql: `INSERT INTO tasks (id, title, assigned_to, assigned_by, project_name, priority, status, task_file, blocked_by, parent_task_id, reviewer, context, complexity, budget_tokens, budget_fallback_model, tokens_used, tokens_warned_at, session_scope, created_at, updated_at)
|
|
3251
3890
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
@@ -3272,6 +3911,43 @@ async function createTaskCore(input) {
|
|
|
3272
3911
|
now
|
|
3273
3912
|
]
|
|
3274
3913
|
});
|
|
3914
|
+
if (input.baseDir) {
|
|
3915
|
+
try {
|
|
3916
|
+
const EXE_OS_DIR = path12.join(os7.homedir(), ".exe-os");
|
|
3917
|
+
const mdPath = path12.join(EXE_OS_DIR, taskFile);
|
|
3918
|
+
const mdDir = path12.dirname(mdPath);
|
|
3919
|
+
if (!existsSync12(mdDir)) await mkdir4(mdDir, { recursive: true });
|
|
3920
|
+
const reviewer = input.reviewer ?? input.assignedBy;
|
|
3921
|
+
const mdContent = `# ${input.title}
|
|
3922
|
+
|
|
3923
|
+
**ID:** ${id}
|
|
3924
|
+
**Status:** ${initialStatus}
|
|
3925
|
+
**Priority:** ${input.priority}
|
|
3926
|
+
**Assigned by:** ${input.assignedBy}
|
|
3927
|
+
**Assigned to:** ${input.assignedTo}
|
|
3928
|
+
**Project:** ${input.projectName}
|
|
3929
|
+
**Created:** ${now.split("T")[0]}${parentTaskId ? `
|
|
3930
|
+
**Parent task:** ${parentTaskId}` : ""}
|
|
3931
|
+
**Reviewer:** ${reviewer}
|
|
3932
|
+
|
|
3933
|
+
## Context
|
|
3934
|
+
|
|
3935
|
+
${input.context}
|
|
3936
|
+
|
|
3937
|
+
## MANDATORY: When done
|
|
3938
|
+
|
|
3939
|
+
You MUST call update_task with status "done" and a result summary when finished.
|
|
3940
|
+
If you skip this, your reviewer will not know you're done and your work won't be reviewed.
|
|
3941
|
+
Do NOT let a failed commit or any error prevent you from calling update_task(done).
|
|
3942
|
+
`;
|
|
3943
|
+
await writeFile4(mdPath, mdContent, "utf-8");
|
|
3944
|
+
} catch (err) {
|
|
3945
|
+
process.stderr.write(
|
|
3946
|
+
`[create-task] WARNING: .md file write failed for ${taskFile}: ${err instanceof Error ? err.message : String(err)}
|
|
3947
|
+
`
|
|
3948
|
+
);
|
|
3949
|
+
}
|
|
3950
|
+
}
|
|
3275
3951
|
return {
|
|
3276
3952
|
id,
|
|
3277
3953
|
title: input.title,
|
|
@@ -3464,7 +4140,7 @@ ${input.result}` : `\u26A0\uFE0F ${warning}`;
|
|
|
3464
4140
|
return { row, taskFile, now, taskId };
|
|
3465
4141
|
}
|
|
3466
4142
|
}
|
|
3467
|
-
if (curStatus === "in_progress" && input.callerAgentId && (input.callerAgentId === assignedBy || input.callerAgentId
|
|
4143
|
+
if (curStatus === "in_progress" && input.callerAgentId && (input.callerAgentId === assignedBy || isCoordinatorName(input.callerAgentId))) {
|
|
3468
4144
|
process.stderr.write(
|
|
3469
4145
|
`[tasks] Assigner override: ${input.callerAgentId} reclaiming ${taskId}
|
|
3470
4146
|
`
|
|
@@ -3529,9 +4205,9 @@ async function deleteTaskCore(taskId, _baseDir) {
|
|
|
3529
4205
|
return { taskFile, assignedTo, assignedBy, taskSlug };
|
|
3530
4206
|
}
|
|
3531
4207
|
async function ensureArchitectureDoc(baseDir, projectName) {
|
|
3532
|
-
const archPath =
|
|
4208
|
+
const archPath = path12.join(baseDir, "exe", "ARCHITECTURE.md");
|
|
3533
4209
|
try {
|
|
3534
|
-
if (
|
|
4210
|
+
if (existsSync12(archPath)) return;
|
|
3535
4211
|
const template = [
|
|
3536
4212
|
`# ${projectName} \u2014 System Architecture`,
|
|
3537
4213
|
"",
|
|
@@ -3564,10 +4240,10 @@ async function ensureArchitectureDoc(baseDir, projectName) {
|
|
|
3564
4240
|
}
|
|
3565
4241
|
}
|
|
3566
4242
|
async function ensureGitignoreExe(baseDir) {
|
|
3567
|
-
const gitignorePath =
|
|
4243
|
+
const gitignorePath = path12.join(baseDir, ".gitignore");
|
|
3568
4244
|
try {
|
|
3569
|
-
if (
|
|
3570
|
-
const content =
|
|
4245
|
+
if (existsSync12(gitignorePath)) {
|
|
4246
|
+
const content = readFileSync10(gitignorePath, "utf-8");
|
|
3571
4247
|
if (/^\/?exe\/?$/m.test(content)) return;
|
|
3572
4248
|
await appendFile(gitignorePath, "\n# Employee task assignments (private)\n/exe/\n");
|
|
3573
4249
|
} else {
|
|
@@ -3576,20 +4252,30 @@ async function ensureGitignoreExe(baseDir) {
|
|
|
3576
4252
|
} catch {
|
|
3577
4253
|
}
|
|
3578
4254
|
}
|
|
3579
|
-
var DELEGATION_KEYWORDS, TASK_ALREADY_CLAIMED_PREFIX;
|
|
4255
|
+
var LANE_KEYWORDS, KEYWORD_INDEX, DELEGATION_KEYWORDS, TASK_ALREADY_CLAIMED_PREFIX;
|
|
3580
4256
|
var init_tasks_crud = __esm({
|
|
3581
4257
|
"src/lib/tasks-crud.ts"() {
|
|
3582
4258
|
"use strict";
|
|
3583
4259
|
init_database();
|
|
3584
4260
|
init_task_scope();
|
|
4261
|
+
init_employees();
|
|
4262
|
+
LANE_KEYWORDS = {
|
|
4263
|
+
CMO: ["sales", "script", "pitch", "offer", "copy", "objection", "brand", "content", "seo", "marketing", "newsletter", "carousel", "social", "campaign"],
|
|
4264
|
+
CTO: ["spec", "architecture", "migration", "schema", "database", "design doc", "adr", "security audit", "tech stack"],
|
|
4265
|
+
"Principal Engineer": ["implement", "build", "fix", "commit", "refactor", "bug", "feature", "wire", "integration"],
|
|
4266
|
+
"Staff Code Reviewer": ["critique", "verdict", "review", "audit", "code quality"],
|
|
4267
|
+
"Content Production Specialist": ["render", "video", "image", "b-roll", "remotion", "animation", "thumbnail"],
|
|
4268
|
+
"AI Product Lead": ["competitive", "analysis", "benchmark", "compare", "scout", "evaluate", "poc"]
|
|
4269
|
+
};
|
|
4270
|
+
KEYWORD_INDEX = buildKeywordIndex();
|
|
3585
4271
|
DELEGATION_KEYWORDS = /parallel|delegate|wave|worktree|multi-instance/i;
|
|
3586
4272
|
TASK_ALREADY_CLAIMED_PREFIX = "TASK_ALREADY_CLAIMED";
|
|
3587
4273
|
}
|
|
3588
4274
|
});
|
|
3589
4275
|
|
|
3590
4276
|
// src/lib/tasks-review.ts
|
|
3591
|
-
import
|
|
3592
|
-
import { existsSync as
|
|
4277
|
+
import path13 from "path";
|
|
4278
|
+
import { existsSync as existsSync13, readdirSync as readdirSync3, unlinkSync as unlinkSync4 } from "fs";
|
|
3593
4279
|
async function countPendingReviews(sessionScope) {
|
|
3594
4280
|
const client = getClient();
|
|
3595
4281
|
if (sessionScope) {
|
|
@@ -3611,7 +4297,7 @@ async function countNewPendingReviewsSince(sinceIso, sessionScope) {
|
|
|
3611
4297
|
const result2 = await client.execute({
|
|
3612
4298
|
sql: `SELECT COUNT(*) as cnt FROM tasks
|
|
3613
4299
|
WHERE status = 'needs_review' AND updated_at > ?
|
|
3614
|
-
AND
|
|
4300
|
+
AND session_scope = ?`,
|
|
3615
4301
|
args: [sinceIso, sessionScope]
|
|
3616
4302
|
});
|
|
3617
4303
|
return Number(result2.rows[0]?.cnt) || 0;
|
|
@@ -3629,7 +4315,7 @@ async function listPendingReviews(limit, sessionScope) {
|
|
|
3629
4315
|
const result2 = await client.execute({
|
|
3630
4316
|
sql: `SELECT title, assigned_to, project_name FROM tasks
|
|
3631
4317
|
WHERE status = 'needs_review'
|
|
3632
|
-
AND
|
|
4318
|
+
AND session_scope = ?
|
|
3633
4319
|
ORDER BY priority ASC, created_at DESC LIMIT ?`,
|
|
3634
4320
|
args: [sessionScope, limit]
|
|
3635
4321
|
});
|
|
@@ -3750,14 +4436,14 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
3750
4436
|
if (parts.length >= 3 && parts[0] === "review") {
|
|
3751
4437
|
const agent = parts[1];
|
|
3752
4438
|
const slug = parts.slice(2).join("-");
|
|
3753
|
-
const
|
|
4439
|
+
const legacyTaskFile = `exe/${agent}/${slug}.md`;
|
|
3754
4440
|
const result = await client.execute({
|
|
3755
|
-
sql: "UPDATE tasks SET status = 'done', updated_at = ? WHERE task_file = ? AND status = 'needs_review'",
|
|
3756
|
-
args: [now,
|
|
4441
|
+
sql: "UPDATE tasks SET status = 'done', updated_at = ? WHERE (task_file = ? OR task_file LIKE ?) AND status = 'needs_review'",
|
|
4442
|
+
args: [now, legacyTaskFile, `tasks/%/${agent}/${slug}.md`]
|
|
3757
4443
|
});
|
|
3758
4444
|
if (result.rowsAffected > 0) {
|
|
3759
4445
|
process.stderr.write(
|
|
3760
|
-
`[review-cleanup] Cascaded original task to done
|
|
4446
|
+
`[review-cleanup] Cascaded original task to done: ${agent}/${slug}.md
|
|
3761
4447
|
`
|
|
3762
4448
|
);
|
|
3763
4449
|
}
|
|
@@ -3770,11 +4456,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
3770
4456
|
);
|
|
3771
4457
|
}
|
|
3772
4458
|
try {
|
|
3773
|
-
const cacheDir =
|
|
3774
|
-
if (
|
|
4459
|
+
const cacheDir = path13.join(EXE_AI_DIR, "session-cache");
|
|
4460
|
+
if (existsSync13(cacheDir)) {
|
|
3775
4461
|
for (const f of readdirSync3(cacheDir)) {
|
|
3776
4462
|
if (f.startsWith("review-notified-")) {
|
|
3777
|
-
|
|
4463
|
+
unlinkSync4(path13.join(cacheDir, f));
|
|
3778
4464
|
}
|
|
3779
4465
|
}
|
|
3780
4466
|
}
|
|
@@ -3795,7 +4481,7 @@ var init_tasks_review = __esm({
|
|
|
3795
4481
|
});
|
|
3796
4482
|
|
|
3797
4483
|
// src/lib/tasks-chain.ts
|
|
3798
|
-
import
|
|
4484
|
+
import path14 from "path";
|
|
3799
4485
|
import { readFile as readFile4, writeFile as writeFile5 } from "fs/promises";
|
|
3800
4486
|
async function cascadeUnblock(taskId, baseDir, now) {
|
|
3801
4487
|
const client = getClient();
|
|
@@ -3812,7 +4498,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
3812
4498
|
});
|
|
3813
4499
|
for (const ur of unblockedRows.rows) {
|
|
3814
4500
|
try {
|
|
3815
|
-
const ubFile =
|
|
4501
|
+
const ubFile = path14.join(baseDir, String(ur.task_file));
|
|
3816
4502
|
let ubContent = await readFile4(ubFile, "utf-8");
|
|
3817
4503
|
ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
|
|
3818
4504
|
ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
|
|
@@ -3886,7 +4572,7 @@ __export(project_name_exports, {
|
|
|
3886
4572
|
getProjectName: () => getProjectName
|
|
3887
4573
|
});
|
|
3888
4574
|
import { execSync as execSync6 } from "child_process";
|
|
3889
|
-
import
|
|
4575
|
+
import path15 from "path";
|
|
3890
4576
|
function getProjectName(cwd) {
|
|
3891
4577
|
const dir = cwd ?? process.cwd();
|
|
3892
4578
|
if (_cached2 && _cachedCwd === dir) return _cached2;
|
|
@@ -3899,7 +4585,7 @@ function getProjectName(cwd) {
|
|
|
3899
4585
|
timeout: 2e3,
|
|
3900
4586
|
stdio: ["pipe", "pipe", "pipe"]
|
|
3901
4587
|
}).trim();
|
|
3902
|
-
repoRoot =
|
|
4588
|
+
repoRoot = path15.dirname(gitCommonDir);
|
|
3903
4589
|
} catch {
|
|
3904
4590
|
repoRoot = execSync6("git rev-parse --show-toplevel", {
|
|
3905
4591
|
cwd: dir,
|
|
@@ -3908,11 +4594,11 @@ function getProjectName(cwd) {
|
|
|
3908
4594
|
stdio: ["pipe", "pipe", "pipe"]
|
|
3909
4595
|
}).trim();
|
|
3910
4596
|
}
|
|
3911
|
-
_cached2 =
|
|
4597
|
+
_cached2 = path15.basename(repoRoot);
|
|
3912
4598
|
_cachedCwd = dir;
|
|
3913
4599
|
return _cached2;
|
|
3914
4600
|
} catch {
|
|
3915
|
-
_cached2 =
|
|
4601
|
+
_cached2 = path15.basename(dir);
|
|
3916
4602
|
_cachedCwd = dir;
|
|
3917
4603
|
return _cached2;
|
|
3918
4604
|
}
|
|
@@ -3948,7 +4634,7 @@ function findSessionForProject(projectName) {
|
|
|
3948
4634
|
const sessions = listSessions();
|
|
3949
4635
|
for (const s of sessions) {
|
|
3950
4636
|
const proj = s.projectDir.split("/").filter(Boolean).pop();
|
|
3951
|
-
if (proj === projectName &&
|
|
4637
|
+
if (proj === projectName && isCoordinatorName(s.agentId)) return s;
|
|
3952
4638
|
}
|
|
3953
4639
|
return null;
|
|
3954
4640
|
}
|
|
@@ -3994,7 +4680,7 @@ var init_session_scope = __esm({
|
|
|
3994
4680
|
|
|
3995
4681
|
// src/lib/tasks-notify.ts
|
|
3996
4682
|
async function dispatchTaskToEmployee(input) {
|
|
3997
|
-
if (
|
|
4683
|
+
if (isCoordinatorName(input.assignedTo)) return { dispatched: "skipped" };
|
|
3998
4684
|
let crossProject = false;
|
|
3999
4685
|
if (input.projectName) {
|
|
4000
4686
|
try {
|
|
@@ -4389,8 +5075,8 @@ __export(tasks_exports, {
|
|
|
4389
5075
|
updateTaskStatus: () => updateTaskStatus,
|
|
4390
5076
|
writeCheckpoint: () => writeCheckpoint
|
|
4391
5077
|
});
|
|
4392
|
-
import
|
|
4393
|
-
import { writeFileSync as
|
|
5078
|
+
import path16 from "path";
|
|
5079
|
+
import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync6, unlinkSync as unlinkSync5 } from "fs";
|
|
4394
5080
|
async function createTask(input) {
|
|
4395
5081
|
const result = await createTaskCore(input);
|
|
4396
5082
|
if (!input.skipDispatch && result.status !== "blocked" && !process.env.VITEST) {
|
|
@@ -4409,14 +5095,14 @@ async function updateTask(input) {
|
|
|
4409
5095
|
const { row, taskFile, now, taskId } = await updateTaskStatus(input);
|
|
4410
5096
|
try {
|
|
4411
5097
|
const agent = String(row.assigned_to);
|
|
4412
|
-
const cacheDir =
|
|
4413
|
-
const cachePath =
|
|
5098
|
+
const cacheDir = path16.join(EXE_AI_DIR, "session-cache");
|
|
5099
|
+
const cachePath = path16.join(cacheDir, `current-task-${agent}.json`);
|
|
4414
5100
|
if (input.status === "in_progress") {
|
|
4415
|
-
|
|
4416
|
-
|
|
5101
|
+
mkdirSync6(cacheDir, { recursive: true });
|
|
5102
|
+
writeFileSync6(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
|
|
4417
5103
|
} else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled") {
|
|
4418
5104
|
try {
|
|
4419
|
-
|
|
5105
|
+
unlinkSync5(cachePath);
|
|
4420
5106
|
} catch {
|
|
4421
5107
|
}
|
|
4422
5108
|
}
|
|
@@ -4473,7 +5159,7 @@ async function updateTask(input) {
|
|
|
4473
5159
|
}
|
|
4474
5160
|
const isTerminal = input.status === "done" || input.status === "needs_review";
|
|
4475
5161
|
if (isTerminal) {
|
|
4476
|
-
const isCoordinator =
|
|
5162
|
+
const isCoordinator = isCoordinatorName(String(row.assigned_to));
|
|
4477
5163
|
if (!isCoordinator) {
|
|
4478
5164
|
notifyTaskDone();
|
|
4479
5165
|
}
|
|
@@ -4498,7 +5184,7 @@ async function updateTask(input) {
|
|
|
4498
5184
|
}
|
|
4499
5185
|
}
|
|
4500
5186
|
}
|
|
4501
|
-
if (input.status === "done" &&
|
|
5187
|
+
if (input.status === "done" && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
|
|
4502
5188
|
Promise.resolve().then(() => (init_skill_learning(), skill_learning_exports)).then(
|
|
4503
5189
|
({ captureAndLearn: captureAndLearn2 }) => captureAndLearn2({
|
|
4504
5190
|
taskId,
|
|
@@ -4514,7 +5200,7 @@ async function updateTask(input) {
|
|
|
4514
5200
|
});
|
|
4515
5201
|
}
|
|
4516
5202
|
let nextTask;
|
|
4517
|
-
if (isTerminal &&
|
|
5203
|
+
if (isTerminal && !isCoordinatorName(String(row.assigned_to))) {
|
|
4518
5204
|
try {
|
|
4519
5205
|
nextTask = await findNextTask(String(row.assigned_to));
|
|
4520
5206
|
} catch {
|
|
@@ -4858,7 +5544,7 @@ var init_capacity_monitor = __esm({
|
|
|
4858
5544
|
// src/lib/tmux-routing.ts
|
|
4859
5545
|
var tmux_routing_exports = {};
|
|
4860
5546
|
__export(tmux_routing_exports, {
|
|
4861
|
-
acquireSpawnLock: () =>
|
|
5547
|
+
acquireSpawnLock: () => acquireSpawnLock2,
|
|
4862
5548
|
employeeSessionName: () => employeeSessionName,
|
|
4863
5549
|
ensureEmployee: () => ensureEmployee,
|
|
4864
5550
|
extractRootExe: () => extractRootExe,
|
|
@@ -4873,20 +5559,20 @@ __export(tmux_routing_exports, {
|
|
|
4873
5559
|
notifyParentExe: () => notifyParentExe,
|
|
4874
5560
|
parseParentExe: () => parseParentExe,
|
|
4875
5561
|
registerParentExe: () => registerParentExe,
|
|
4876
|
-
releaseSpawnLock: () =>
|
|
5562
|
+
releaseSpawnLock: () => releaseSpawnLock2,
|
|
4877
5563
|
resolveExeSession: () => resolveExeSession,
|
|
4878
5564
|
sendIntercom: () => sendIntercom,
|
|
4879
5565
|
spawnEmployee: () => spawnEmployee,
|
|
4880
5566
|
verifyPaneAtCapacity: () => verifyPaneAtCapacity
|
|
4881
5567
|
});
|
|
4882
5568
|
import { execFileSync as execFileSync2, execSync as execSync7 } from "child_process";
|
|
4883
|
-
import { readFileSync as
|
|
4884
|
-
import
|
|
4885
|
-
import
|
|
4886
|
-
import { fileURLToPath } from "url";
|
|
4887
|
-
import { unlinkSync as
|
|
5569
|
+
import { readFileSync as readFileSync11, writeFileSync as writeFileSync7, mkdirSync as mkdirSync7, existsSync as existsSync14, appendFileSync } from "fs";
|
|
5570
|
+
import path17 from "path";
|
|
5571
|
+
import os8 from "os";
|
|
5572
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
5573
|
+
import { unlinkSync as unlinkSync6 } from "fs";
|
|
4888
5574
|
function spawnLockPath(sessionName) {
|
|
4889
|
-
return
|
|
5575
|
+
return path17.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
|
|
4890
5576
|
}
|
|
4891
5577
|
function isProcessAlive(pid) {
|
|
4892
5578
|
try {
|
|
@@ -4896,14 +5582,14 @@ function isProcessAlive(pid) {
|
|
|
4896
5582
|
return false;
|
|
4897
5583
|
}
|
|
4898
5584
|
}
|
|
4899
|
-
function
|
|
4900
|
-
if (!
|
|
4901
|
-
|
|
5585
|
+
function acquireSpawnLock2(sessionName) {
|
|
5586
|
+
if (!existsSync14(SPAWN_LOCK_DIR)) {
|
|
5587
|
+
mkdirSync7(SPAWN_LOCK_DIR, { recursive: true });
|
|
4902
5588
|
}
|
|
4903
5589
|
const lockFile = spawnLockPath(sessionName);
|
|
4904
|
-
if (
|
|
5590
|
+
if (existsSync14(lockFile)) {
|
|
4905
5591
|
try {
|
|
4906
|
-
const lock = JSON.parse(
|
|
5592
|
+
const lock = JSON.parse(readFileSync11(lockFile, "utf8"));
|
|
4907
5593
|
const age = Date.now() - lock.timestamp;
|
|
4908
5594
|
if (isProcessAlive(lock.pid) && age < 6e4) {
|
|
4909
5595
|
return false;
|
|
@@ -4911,25 +5597,25 @@ function acquireSpawnLock(sessionName) {
|
|
|
4911
5597
|
} catch {
|
|
4912
5598
|
}
|
|
4913
5599
|
}
|
|
4914
|
-
|
|
5600
|
+
writeFileSync7(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
|
|
4915
5601
|
return true;
|
|
4916
5602
|
}
|
|
4917
|
-
function
|
|
5603
|
+
function releaseSpawnLock2(sessionName) {
|
|
4918
5604
|
try {
|
|
4919
|
-
|
|
5605
|
+
unlinkSync6(spawnLockPath(sessionName));
|
|
4920
5606
|
} catch {
|
|
4921
5607
|
}
|
|
4922
5608
|
}
|
|
4923
5609
|
function resolveBehaviorsExporterScript() {
|
|
4924
5610
|
try {
|
|
4925
|
-
const thisFile =
|
|
4926
|
-
const scriptPath =
|
|
4927
|
-
|
|
5611
|
+
const thisFile = fileURLToPath2(import.meta.url);
|
|
5612
|
+
const scriptPath = path17.join(
|
|
5613
|
+
path17.dirname(thisFile),
|
|
4928
5614
|
"..",
|
|
4929
5615
|
"bin",
|
|
4930
5616
|
"exe-export-behaviors.js"
|
|
4931
5617
|
);
|
|
4932
|
-
return
|
|
5618
|
+
return existsSync14(scriptPath) ? scriptPath : null;
|
|
4933
5619
|
} catch {
|
|
4934
5620
|
return null;
|
|
4935
5621
|
}
|
|
@@ -4995,12 +5681,12 @@ function extractRootExe(name) {
|
|
|
4995
5681
|
return parts.length > 0 ? parts[parts.length - 1] : null;
|
|
4996
5682
|
}
|
|
4997
5683
|
function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
4998
|
-
if (!
|
|
4999
|
-
|
|
5684
|
+
if (!existsSync14(SESSION_CACHE)) {
|
|
5685
|
+
mkdirSync7(SESSION_CACHE, { recursive: true });
|
|
5000
5686
|
}
|
|
5001
5687
|
const rootExe = extractRootExe(parentExe) ?? parentExe;
|
|
5002
|
-
const filePath =
|
|
5003
|
-
|
|
5688
|
+
const filePath = path17.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
|
|
5689
|
+
writeFileSync7(filePath, JSON.stringify({
|
|
5004
5690
|
parentExe: rootExe,
|
|
5005
5691
|
dispatchedBy: dispatchedBy || rootExe,
|
|
5006
5692
|
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -5008,7 +5694,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
5008
5694
|
}
|
|
5009
5695
|
function getParentExe(sessionKey) {
|
|
5010
5696
|
try {
|
|
5011
|
-
const data = JSON.parse(
|
|
5697
|
+
const data = JSON.parse(readFileSync11(path17.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
5012
5698
|
return data.parentExe || null;
|
|
5013
5699
|
} catch {
|
|
5014
5700
|
return null;
|
|
@@ -5016,8 +5702,8 @@ function getParentExe(sessionKey) {
|
|
|
5016
5702
|
}
|
|
5017
5703
|
function getDispatchedBy(sessionKey) {
|
|
5018
5704
|
try {
|
|
5019
|
-
const data = JSON.parse(
|
|
5020
|
-
|
|
5705
|
+
const data = JSON.parse(readFileSync11(
|
|
5706
|
+
path17.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
|
|
5021
5707
|
"utf8"
|
|
5022
5708
|
));
|
|
5023
5709
|
return data.dispatchedBy ?? data.parentExe ?? null;
|
|
@@ -5043,10 +5729,10 @@ function isEmployeeAlive(sessionName) {
|
|
|
5043
5729
|
}
|
|
5044
5730
|
function findFreeInstance(employeeName, exeSession, maxInstances = 10, isAlive = isEmployeeAlive) {
|
|
5045
5731
|
const base = employeeSessionName(employeeName, exeSession);
|
|
5046
|
-
if (!isAlive(base) &&
|
|
5732
|
+
if (!isAlive(base) && acquireSpawnLock2(base)) return 0;
|
|
5047
5733
|
for (let i = 2; i <= maxInstances; i++) {
|
|
5048
5734
|
const candidate = employeeSessionName(employeeName, exeSession, i);
|
|
5049
|
-
if (!isAlive(candidate) &&
|
|
5735
|
+
if (!isAlive(candidate) && acquireSpawnLock2(candidate)) return i;
|
|
5050
5736
|
}
|
|
5051
5737
|
return null;
|
|
5052
5738
|
}
|
|
@@ -5078,32 +5764,50 @@ async function verifyPaneAtCapacity(sessionName) {
|
|
|
5078
5764
|
}
|
|
5079
5765
|
function readDebounceState() {
|
|
5080
5766
|
try {
|
|
5081
|
-
if (!
|
|
5082
|
-
|
|
5767
|
+
if (!existsSync14(DEBOUNCE_FILE)) return {};
|
|
5768
|
+
const raw = JSON.parse(readFileSync11(DEBOUNCE_FILE, "utf8"));
|
|
5769
|
+
const state = {};
|
|
5770
|
+
for (const [key, val] of Object.entries(raw)) {
|
|
5771
|
+
if (typeof val === "number") {
|
|
5772
|
+
state[key] = { lastSent: val, pending: 0 };
|
|
5773
|
+
} else if (val && typeof val === "object" && "lastSent" in val) {
|
|
5774
|
+
state[key] = val;
|
|
5775
|
+
}
|
|
5776
|
+
}
|
|
5777
|
+
return state;
|
|
5083
5778
|
} catch {
|
|
5084
5779
|
return {};
|
|
5085
5780
|
}
|
|
5086
5781
|
}
|
|
5087
5782
|
function writeDebounceState(state) {
|
|
5088
5783
|
try {
|
|
5089
|
-
if (!
|
|
5090
|
-
|
|
5784
|
+
if (!existsSync14(SESSION_CACHE)) mkdirSync7(SESSION_CACHE, { recursive: true });
|
|
5785
|
+
writeFileSync7(DEBOUNCE_FILE, JSON.stringify(state));
|
|
5091
5786
|
} catch {
|
|
5092
5787
|
}
|
|
5093
5788
|
}
|
|
5094
5789
|
function isDebounced(targetSession) {
|
|
5095
5790
|
const state = readDebounceState();
|
|
5096
|
-
const
|
|
5097
|
-
|
|
5791
|
+
const entry = state[targetSession];
|
|
5792
|
+
const lastSent = entry?.lastSent ?? 0;
|
|
5793
|
+
if (Date.now() - lastSent < INTERCOM_DEBOUNCE_MS) {
|
|
5794
|
+
if (!state[targetSession]) state[targetSession] = { lastSent, pending: 0 };
|
|
5795
|
+
state[targetSession].pending++;
|
|
5796
|
+
writeDebounceState(state);
|
|
5797
|
+
return true;
|
|
5798
|
+
}
|
|
5799
|
+
return false;
|
|
5098
5800
|
}
|
|
5099
5801
|
function recordDebounce(targetSession) {
|
|
5100
5802
|
const state = readDebounceState();
|
|
5101
|
-
state[targetSession]
|
|
5803
|
+
const batched = state[targetSession]?.pending ?? 0;
|
|
5804
|
+
state[targetSession] = { lastSent: Date.now(), pending: 0 };
|
|
5102
5805
|
const cutoff = Date.now() - DEBOUNCE_CLEANUP_AGE_MS;
|
|
5103
5806
|
for (const key of Object.keys(state)) {
|
|
5104
|
-
if ((state[key] ?? 0) < cutoff) delete state[key];
|
|
5807
|
+
if ((state[key]?.lastSent ?? 0) < cutoff) delete state[key];
|
|
5105
5808
|
}
|
|
5106
5809
|
writeDebounceState(state);
|
|
5810
|
+
return batched;
|
|
5107
5811
|
}
|
|
5108
5812
|
function logIntercom(msg) {
|
|
5109
5813
|
const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${msg}
|
|
@@ -5148,7 +5852,7 @@ function sendIntercom(targetSession) {
|
|
|
5148
5852
|
return "skipped_exe";
|
|
5149
5853
|
}
|
|
5150
5854
|
if (isDebounced(targetSession)) {
|
|
5151
|
-
logIntercom(`DEBOUNCE \u2192 ${targetSession} (
|
|
5855
|
+
logIntercom(`DEBOUNCE \u2192 ${targetSession} (nudge batched, task safe in DB)`);
|
|
5152
5856
|
return "debounced";
|
|
5153
5857
|
}
|
|
5154
5858
|
try {
|
|
@@ -5160,14 +5864,14 @@ function sendIntercom(targetSession) {
|
|
|
5160
5864
|
const sessionState = getSessionState(targetSession);
|
|
5161
5865
|
if (sessionState === "no_claude") {
|
|
5162
5866
|
queueIntercom(targetSession, "claude not running in session");
|
|
5163
|
-
recordDebounce(targetSession);
|
|
5164
|
-
logIntercom(`QUEUED \u2192 ${targetSession} (no claude process
|
|
5867
|
+
const batched2 = recordDebounce(targetSession);
|
|
5868
|
+
logIntercom(`QUEUED \u2192 ${targetSession} (no claude process)${batched2 > 0 ? ` [${batched2} batched]` : ""}`);
|
|
5165
5869
|
return "queued";
|
|
5166
5870
|
}
|
|
5167
5871
|
if (sessionState === "thinking" || sessionState === "tool") {
|
|
5168
5872
|
queueIntercom(targetSession, "session busy at send time");
|
|
5169
|
-
recordDebounce(targetSession);
|
|
5170
|
-
logIntercom(`QUEUED \u2192 ${targetSession} (session busy
|
|
5873
|
+
const batched2 = recordDebounce(targetSession);
|
|
5874
|
+
logIntercom(`QUEUED \u2192 ${targetSession} (session busy)${batched2 > 0 ? ` [${batched2} batched]` : ""}`);
|
|
5171
5875
|
return "queued";
|
|
5172
5876
|
}
|
|
5173
5877
|
if (transport.isPaneInCopyMode(targetSession)) {
|
|
@@ -5175,8 +5879,8 @@ function sendIntercom(targetSession) {
|
|
|
5175
5879
|
transport.sendKeys(targetSession, "q");
|
|
5176
5880
|
}
|
|
5177
5881
|
transport.sendKeys(targetSession, "/exe-intercom");
|
|
5178
|
-
recordDebounce(targetSession);
|
|
5179
|
-
logIntercom(`DELIVERED \u2192 ${targetSession} (fire-and-forget)`);
|
|
5882
|
+
const batched = recordDebounce(targetSession);
|
|
5883
|
+
logIntercom(`DELIVERED \u2192 ${targetSession}${batched > 0 ? ` [${batched} nudges batched during debounce]` : ""} (fire-and-forget)`);
|
|
5180
5884
|
return "delivered";
|
|
5181
5885
|
} catch {
|
|
5182
5886
|
logIntercom(`FAIL \u2192 ${targetSession}`);
|
|
@@ -5206,7 +5910,7 @@ function notifyParentExe(sessionKey) {
|
|
|
5206
5910
|
return true;
|
|
5207
5911
|
}
|
|
5208
5912
|
function ensureEmployee(employeeName, exeSession, projectDir, opts) {
|
|
5209
|
-
if (
|
|
5913
|
+
if (isCoordinatorName(employeeName)) {
|
|
5210
5914
|
return { status: "failed", sessionName: "", error: "The COO is not a dispatchable employee" };
|
|
5211
5915
|
}
|
|
5212
5916
|
try {
|
|
@@ -5278,26 +5982,26 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5278
5982
|
const transport = getTransport();
|
|
5279
5983
|
const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
|
|
5280
5984
|
const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
|
|
5281
|
-
const logDir =
|
|
5282
|
-
const logFile =
|
|
5283
|
-
if (!
|
|
5284
|
-
|
|
5985
|
+
const logDir = path17.join(os8.homedir(), ".exe-os", "session-logs");
|
|
5986
|
+
const logFile = path17.join(logDir, `${instanceLabel}-${Date.now()}.log`);
|
|
5987
|
+
if (!existsSync14(logDir)) {
|
|
5988
|
+
mkdirSync7(logDir, { recursive: true });
|
|
5285
5989
|
}
|
|
5286
5990
|
transport.kill(sessionName);
|
|
5287
5991
|
let cleanupSuffix = "";
|
|
5288
5992
|
try {
|
|
5289
|
-
const thisFile =
|
|
5290
|
-
const cleanupScript =
|
|
5291
|
-
if (
|
|
5993
|
+
const thisFile = fileURLToPath2(import.meta.url);
|
|
5994
|
+
const cleanupScript = path17.join(path17.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
|
|
5995
|
+
if (existsSync14(cleanupScript)) {
|
|
5292
5996
|
cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
|
|
5293
5997
|
}
|
|
5294
5998
|
} catch {
|
|
5295
5999
|
}
|
|
5296
6000
|
try {
|
|
5297
|
-
const claudeJsonPath =
|
|
6001
|
+
const claudeJsonPath = path17.join(os8.homedir(), ".claude.json");
|
|
5298
6002
|
let claudeJson = {};
|
|
5299
6003
|
try {
|
|
5300
|
-
claudeJson = JSON.parse(
|
|
6004
|
+
claudeJson = JSON.parse(readFileSync11(claudeJsonPath, "utf8"));
|
|
5301
6005
|
} catch {
|
|
5302
6006
|
}
|
|
5303
6007
|
if (!claudeJson.projects) claudeJson.projects = {};
|
|
@@ -5305,17 +6009,17 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5305
6009
|
const trustDir = opts?.cwd ?? projectDir;
|
|
5306
6010
|
if (!projects[trustDir]) projects[trustDir] = {};
|
|
5307
6011
|
projects[trustDir].hasTrustDialogAccepted = true;
|
|
5308
|
-
|
|
6012
|
+
writeFileSync7(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
5309
6013
|
} catch {
|
|
5310
6014
|
}
|
|
5311
6015
|
try {
|
|
5312
|
-
const settingsDir =
|
|
6016
|
+
const settingsDir = path17.join(os8.homedir(), ".claude", "projects");
|
|
5313
6017
|
const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
|
|
5314
|
-
const projSettingsDir =
|
|
5315
|
-
const settingsPath =
|
|
6018
|
+
const projSettingsDir = path17.join(settingsDir, normalizedKey);
|
|
6019
|
+
const settingsPath = path17.join(projSettingsDir, "settings.json");
|
|
5316
6020
|
let settings = {};
|
|
5317
6021
|
try {
|
|
5318
|
-
settings = JSON.parse(
|
|
6022
|
+
settings = JSON.parse(readFileSync11(settingsPath, "utf8"));
|
|
5319
6023
|
} catch {
|
|
5320
6024
|
}
|
|
5321
6025
|
const perms = settings.permissions ?? {};
|
|
@@ -5343,21 +6047,24 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5343
6047
|
if (changed) {
|
|
5344
6048
|
perms.allow = allow;
|
|
5345
6049
|
settings.permissions = perms;
|
|
5346
|
-
|
|
5347
|
-
|
|
6050
|
+
mkdirSync7(projSettingsDir, { recursive: true });
|
|
6051
|
+
writeFileSync7(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
5348
6052
|
}
|
|
5349
6053
|
} catch {
|
|
5350
6054
|
}
|
|
5351
6055
|
const spawnCwd = opts?.cwd ?? projectDir;
|
|
5352
6056
|
const useExeAgent = !!(opts?.model && opts?.provider);
|
|
5353
|
-
const
|
|
6057
|
+
const agentRtConfig = getAgentRuntime(employeeName);
|
|
6058
|
+
const useCodex = !useExeAgent && agentRtConfig.runtime === "codex";
|
|
6059
|
+
const useOpencode = !useExeAgent && !useCodex && agentRtConfig.runtime === "opencode";
|
|
6060
|
+
const ccProvider = useExeAgent || useCodex || useOpencode ? DEFAULT_PROVIDER : detectActiveProvider();
|
|
5354
6061
|
const useBinSymlink = ccProvider !== DEFAULT_PROVIDER;
|
|
5355
6062
|
let identityFlag = "";
|
|
5356
6063
|
let behaviorsFlag = "";
|
|
5357
6064
|
let legacyFallbackWarned = false;
|
|
5358
6065
|
if (!useExeAgent && !useBinSymlink) {
|
|
5359
|
-
const identityPath =
|
|
5360
|
-
|
|
6066
|
+
const identityPath = path17.join(
|
|
6067
|
+
os8.homedir(),
|
|
5361
6068
|
".exe-os",
|
|
5362
6069
|
"identity",
|
|
5363
6070
|
`${employeeName}.md`
|
|
@@ -5366,13 +6073,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5366
6073
|
const hasAgentFlag = claudeSupportsAgentFlag();
|
|
5367
6074
|
if (hasAgentFlag) {
|
|
5368
6075
|
identityFlag = ` --agent ${employeeName}`;
|
|
5369
|
-
} else if (
|
|
6076
|
+
} else if (existsSync14(identityPath)) {
|
|
5370
6077
|
identityFlag = ` --append-system-prompt-file ${identityPath}`;
|
|
5371
6078
|
legacyFallbackWarned = true;
|
|
5372
6079
|
}
|
|
5373
6080
|
const behaviorsFile = exportBehaviorsSync(
|
|
5374
6081
|
employeeName,
|
|
5375
|
-
|
|
6082
|
+
path17.basename(spawnCwd),
|
|
5376
6083
|
sessionName
|
|
5377
6084
|
);
|
|
5378
6085
|
if (behaviorsFile) {
|
|
@@ -5387,16 +6094,16 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5387
6094
|
}
|
|
5388
6095
|
let sessionContextFlag = "";
|
|
5389
6096
|
try {
|
|
5390
|
-
const ctxDir =
|
|
5391
|
-
|
|
5392
|
-
const ctxFile =
|
|
6097
|
+
const ctxDir = path17.join(os8.homedir(), ".exe-os", "session-cache");
|
|
6098
|
+
mkdirSync7(ctxDir, { recursive: true });
|
|
6099
|
+
const ctxFile = path17.join(ctxDir, `session-context-${sessionName}.md`);
|
|
5393
6100
|
const ctxContent = [
|
|
5394
6101
|
`## Session Context`,
|
|
5395
6102
|
`You are running in tmux session: ${sessionName}.`,
|
|
5396
6103
|
`Your parent coordinator session is ${exeSession}.`,
|
|
5397
6104
|
`Your employees (if any) use the -${exeSession} suffix.`
|
|
5398
6105
|
].join("\n");
|
|
5399
|
-
|
|
6106
|
+
writeFileSync7(ctxFile, ctxContent);
|
|
5400
6107
|
sessionContextFlag = ` --append-system-prompt-file ${ctxFile}`;
|
|
5401
6108
|
} catch {
|
|
5402
6109
|
}
|
|
@@ -5410,9 +6117,48 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5410
6117
|
}
|
|
5411
6118
|
}
|
|
5412
6119
|
}
|
|
6120
|
+
if (useCodex) {
|
|
6121
|
+
const codexCfg = RUNTIME_TABLE.codex;
|
|
6122
|
+
if (codexCfg?.apiKeyEnv) {
|
|
6123
|
+
const keyVal = process.env[codexCfg.apiKeyEnv];
|
|
6124
|
+
if (keyVal) {
|
|
6125
|
+
envPrefix = `${envPrefix} ${codexCfg.apiKeyEnv}=${keyVal}`;
|
|
6126
|
+
}
|
|
6127
|
+
}
|
|
6128
|
+
envPrefix = `${envPrefix} EXE_AGENT_MODEL=${agentRtConfig.model}`;
|
|
6129
|
+
}
|
|
6130
|
+
if (useOpencode) {
|
|
6131
|
+
const ocCfg = PROVIDER_TABLE.opencode;
|
|
6132
|
+
if (ocCfg?.apiKeyEnv) {
|
|
6133
|
+
const keyVal = process.env[ocCfg.apiKeyEnv];
|
|
6134
|
+
if (keyVal) {
|
|
6135
|
+
envPrefix = `${envPrefix} ${ocCfg.apiKeyEnv}=${keyVal}`;
|
|
6136
|
+
}
|
|
6137
|
+
}
|
|
6138
|
+
envPrefix = `${envPrefix} ANTHROPIC_MODEL=${agentRtConfig.model}`;
|
|
6139
|
+
}
|
|
6140
|
+
if (!useExeAgent && !useCodex && !useOpencode && !useBinSymlink) {
|
|
6141
|
+
const defaultClaudeModel = DEFAULT_MODELS.claude;
|
|
6142
|
+
if (agentRtConfig.runtime === "claude" && agentRtConfig.model !== defaultClaudeModel) {
|
|
6143
|
+
envPrefix = `${envPrefix} ANTHROPIC_MODEL=${agentRtConfig.model}`;
|
|
6144
|
+
}
|
|
6145
|
+
}
|
|
5413
6146
|
let spawnCommand;
|
|
5414
6147
|
if (useExeAgent) {
|
|
5415
6148
|
spawnCommand = `${envPrefix} exe-agent --employee ${employeeName} --model ${opts.model} --provider ${opts.provider}${cleanupSuffix}`;
|
|
6149
|
+
} else if (useCodex) {
|
|
6150
|
+
process.stderr.write(
|
|
6151
|
+
`[tmux-routing] agent-config: ${employeeName} \u2192 codex (${agentRtConfig.model})
|
|
6152
|
+
`
|
|
6153
|
+
);
|
|
6154
|
+
spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName}${cleanupSuffix}`;
|
|
6155
|
+
} else if (useOpencode) {
|
|
6156
|
+
const binName = `${employeeName}-opencode`;
|
|
6157
|
+
process.stderr.write(
|
|
6158
|
+
`[tmux-routing] agent-config: ${employeeName} \u2192 opencode (${agentRtConfig.model})
|
|
6159
|
+
`
|
|
6160
|
+
);
|
|
6161
|
+
spawnCommand = `${envPrefix} ${binName}${cleanupSuffix}`;
|
|
5416
6162
|
} else if (useBinSymlink) {
|
|
5417
6163
|
const binName = `${employeeName}-${ccProvider}`;
|
|
5418
6164
|
process.stderr.write(
|
|
@@ -5428,17 +6174,19 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5428
6174
|
command: spawnCommand
|
|
5429
6175
|
});
|
|
5430
6176
|
if (spawnResult.error) {
|
|
5431
|
-
|
|
6177
|
+
releaseSpawnLock2(sessionName);
|
|
5432
6178
|
return { sessionName, error: `tmux new-session failed: ${spawnResult.error}` };
|
|
5433
6179
|
}
|
|
5434
6180
|
transport.pipeLog(sessionName, logFile);
|
|
5435
6181
|
try {
|
|
5436
6182
|
const mySession = getMySession();
|
|
5437
|
-
const dispatchInfo =
|
|
5438
|
-
|
|
6183
|
+
const dispatchInfo = path17.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
|
|
6184
|
+
writeFileSync7(dispatchInfo, JSON.stringify({
|
|
5439
6185
|
dispatchedBy: mySession,
|
|
5440
6186
|
rootExe: exeSession,
|
|
5441
|
-
provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : "anthropic",
|
|
6187
|
+
provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : useCodex ? "openai" : useOpencode ? "opencode" : "anthropic",
|
|
6188
|
+
runtime: useCodex ? "codex" : useOpencode ? "opencode" : useExeAgent ? "exe-agent" : "claude",
|
|
6189
|
+
model: useCodex ? agentRtConfig.model : useOpencode ? agentRtConfig.model : void 0,
|
|
5442
6190
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5443
6191
|
}));
|
|
5444
6192
|
} catch {
|
|
@@ -5456,6 +6204,11 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5456
6204
|
booted = true;
|
|
5457
6205
|
break;
|
|
5458
6206
|
}
|
|
6207
|
+
} else if (useCodex) {
|
|
6208
|
+
if (pane.includes("codex") || pane.includes("Codex") || pane.includes("exe-start-codex")) {
|
|
6209
|
+
booted = true;
|
|
6210
|
+
break;
|
|
6211
|
+
}
|
|
5459
6212
|
} else {
|
|
5460
6213
|
if (pane.includes("Claude Code") || pane.includes("\u276F")) {
|
|
5461
6214
|
booted = true;
|
|
@@ -5466,10 +6219,11 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5466
6219
|
}
|
|
5467
6220
|
}
|
|
5468
6221
|
if (!booted) {
|
|
5469
|
-
|
|
5470
|
-
|
|
6222
|
+
releaseSpawnLock2(sessionName);
|
|
6223
|
+
const runtimeLabel = useExeAgent ? "exe-agent" : useCodex ? "codex" : "claude";
|
|
6224
|
+
return { sessionName, error: `${runtimeLabel} did not boot within 15s` };
|
|
5471
6225
|
}
|
|
5472
|
-
if (!useExeAgent) {
|
|
6226
|
+
if (!useExeAgent && !useCodex) {
|
|
5473
6227
|
try {
|
|
5474
6228
|
transport.sendKeys(sessionName, `/exe-call ${employeeName}`);
|
|
5475
6229
|
} catch {
|
|
@@ -5483,7 +6237,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5483
6237
|
pid: 0,
|
|
5484
6238
|
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5485
6239
|
});
|
|
5486
|
-
|
|
6240
|
+
releaseSpawnLock2(sessionName);
|
|
5487
6241
|
return { sessionName };
|
|
5488
6242
|
}
|
|
5489
6243
|
var SPAWN_LOCK_DIR, SESSION_CACHE, BEHAVIORS_EXPORT_TIMEOUT_MS, VALID_SESSION_NAME, VERIFY_PANE_LINES, INTERCOM_DEBOUNCE_MS, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS, BUSY_PATTERN;
|
|
@@ -5496,17 +6250,19 @@ var init_tmux_routing = __esm({
|
|
|
5496
6250
|
init_cc_agent_support();
|
|
5497
6251
|
init_mcp_prefix();
|
|
5498
6252
|
init_provider_table();
|
|
6253
|
+
init_agent_config();
|
|
6254
|
+
init_runtime_table();
|
|
5499
6255
|
init_intercom_queue();
|
|
5500
6256
|
init_plan_limits();
|
|
5501
6257
|
init_employees();
|
|
5502
|
-
SPAWN_LOCK_DIR =
|
|
5503
|
-
SESSION_CACHE =
|
|
6258
|
+
SPAWN_LOCK_DIR = path17.join(os8.homedir(), ".exe-os", "spawn-locks");
|
|
6259
|
+
SESSION_CACHE = path17.join(os8.homedir(), ".exe-os", "session-cache");
|
|
5504
6260
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
5505
6261
|
VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
|
|
5506
6262
|
VERIFY_PANE_LINES = 200;
|
|
5507
6263
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
5508
|
-
INTERCOM_LOG2 =
|
|
5509
|
-
DEBOUNCE_FILE =
|
|
6264
|
+
INTERCOM_LOG2 = path17.join(os8.homedir(), ".exe-os", "intercom.log");
|
|
6265
|
+
DEBOUNCE_FILE = path17.join(SESSION_CACHE, "intercom-debounce.json");
|
|
5510
6266
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
5511
6267
|
BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
|
|
5512
6268
|
}
|
|
@@ -5541,126 +6297,8 @@ var task_scanner_exports = {};
|
|
|
5541
6297
|
__export(task_scanner_exports, {
|
|
5542
6298
|
PRIORITY_RE: () => PRIORITY_RE,
|
|
5543
6299
|
STATUS_RE: () => STATUS_RE,
|
|
5544
|
-
TITLE_RE: () => TITLE_RE
|
|
5545
|
-
formatJson: () => formatJson,
|
|
5546
|
-
formatMandatory: () => formatMandatory,
|
|
5547
|
-
formatText: () => formatText,
|
|
5548
|
-
scanAgentTasks: () => scanAgentTasks
|
|
6300
|
+
TITLE_RE: () => TITLE_RE
|
|
5549
6301
|
});
|
|
5550
|
-
import { readdirSync as readdirSync5, readFileSync as readFileSync11, existsSync as existsSync13, statSync } from "fs";
|
|
5551
|
-
import { execSync as execSync9 } from "child_process";
|
|
5552
|
-
import path17 from "path";
|
|
5553
|
-
function getProjectRoot() {
|
|
5554
|
-
try {
|
|
5555
|
-
return execSync9("git rev-parse --show-toplevel", {
|
|
5556
|
-
encoding: "utf8",
|
|
5557
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
5558
|
-
timeout: 5e3
|
|
5559
|
-
}).trim();
|
|
5560
|
-
} catch {
|
|
5561
|
-
return process.cwd();
|
|
5562
|
-
}
|
|
5563
|
-
}
|
|
5564
|
-
function scanAgentTasks(agentId) {
|
|
5565
|
-
const taskDir = path17.join(getProjectRoot(), "exe", agentId);
|
|
5566
|
-
const open = [];
|
|
5567
|
-
const inProgress = [];
|
|
5568
|
-
let done = 0;
|
|
5569
|
-
let total = 0;
|
|
5570
|
-
if (!existsSync13(taskDir)) return { open, inProgress, done, total };
|
|
5571
|
-
try {
|
|
5572
|
-
const files = readdirSync5(taskDir).filter((f) => f.endsWith(".md"));
|
|
5573
|
-
total = files.length;
|
|
5574
|
-
for (const f of files) {
|
|
5575
|
-
try {
|
|
5576
|
-
const content = readFileSync11(path17.join(taskDir, f), "utf8");
|
|
5577
|
-
const statusMatch = content.match(STATUS_RE);
|
|
5578
|
-
const status = statusMatch ? statusMatch[1].toLowerCase() : null;
|
|
5579
|
-
if (status === "done") {
|
|
5580
|
-
done++;
|
|
5581
|
-
continue;
|
|
5582
|
-
}
|
|
5583
|
-
if (status !== "open" && status !== "in_progress") continue;
|
|
5584
|
-
const priMatch = content.match(PRIORITY_RE);
|
|
5585
|
-
const titleMatch = content.match(TITLE_RE);
|
|
5586
|
-
const task = {
|
|
5587
|
-
file: f,
|
|
5588
|
-
title: titleMatch ? titleMatch[1] : f.replace(".md", ""),
|
|
5589
|
-
priority: priMatch ? priMatch[1] : "P2",
|
|
5590
|
-
status,
|
|
5591
|
-
slug: f.replace(".md", "")
|
|
5592
|
-
};
|
|
5593
|
-
if (status === "in_progress") {
|
|
5594
|
-
inProgress.push(task);
|
|
5595
|
-
} else {
|
|
5596
|
-
open.push(task);
|
|
5597
|
-
}
|
|
5598
|
-
} catch {
|
|
5599
|
-
}
|
|
5600
|
-
}
|
|
5601
|
-
} catch {
|
|
5602
|
-
}
|
|
5603
|
-
open.sort((a, b) => a.priority.localeCompare(b.priority));
|
|
5604
|
-
inProgress.sort((a, b) => a.priority.localeCompare(b.priority));
|
|
5605
|
-
return { open, inProgress, done, total };
|
|
5606
|
-
}
|
|
5607
|
-
function formatText(agentId, result) {
|
|
5608
|
-
const lines = [];
|
|
5609
|
-
if (result.inProgress.length > 0) {
|
|
5610
|
-
lines.push(`IN_PROGRESS (${result.inProgress.length}):`);
|
|
5611
|
-
for (const t of result.inProgress) {
|
|
5612
|
-
lines.push(` [${t.priority}] ${t.title} \u2014 exe/${agentId}/${t.file}`);
|
|
5613
|
-
}
|
|
5614
|
-
}
|
|
5615
|
-
if (result.open.length > 0) {
|
|
5616
|
-
lines.push(`OPEN (${result.open.length}):`);
|
|
5617
|
-
for (const t of result.open) {
|
|
5618
|
-
lines.push(` [${t.priority}] ${t.title} \u2014 exe/${agentId}/${t.file}`);
|
|
5619
|
-
}
|
|
5620
|
-
}
|
|
5621
|
-
lines.push(`DONE: ${result.done}`);
|
|
5622
|
-
return lines.join("\n");
|
|
5623
|
-
}
|
|
5624
|
-
function formatMandatory(agentId, result) {
|
|
5625
|
-
const { open, inProgress } = result;
|
|
5626
|
-
if (open.length === 0 && inProgress.length === 0) return "";
|
|
5627
|
-
const lines = [];
|
|
5628
|
-
if (inProgress.length > 0) {
|
|
5629
|
-
const current = inProgress[0];
|
|
5630
|
-
let stale = false;
|
|
5631
|
-
try {
|
|
5632
|
-
const stat = statSync(path17.join(getProjectRoot(), "exe", agentId, current.file));
|
|
5633
|
-
const ageMin = (Date.now() - stat.mtimeMs) / 6e4;
|
|
5634
|
-
if (ageMin > 30) stale = true;
|
|
5635
|
-
} catch {
|
|
5636
|
-
}
|
|
5637
|
-
if (stale) {
|
|
5638
|
-
lines.push(`MANDATORY: Update task status for: ${current.title} [${current.priority}] (exe/${agentId}/${current.file})`);
|
|
5639
|
-
lines.push("This task has been in_progress for over 30 minutes without updates.");
|
|
5640
|
-
lines.push("If work is done, mark done. If blocked, update status to blocked.");
|
|
5641
|
-
} else {
|
|
5642
|
-
lines.push(`Continue working on: ${current.title} [${current.priority}] (exe/${agentId}/${current.file})`);
|
|
5643
|
-
}
|
|
5644
|
-
if (open.length > 0) {
|
|
5645
|
-
lines.push("Queued: " + open.map((t) => `${t.title} [${t.priority}]`).join(", "));
|
|
5646
|
-
}
|
|
5647
|
-
} else {
|
|
5648
|
-
const top = open[0];
|
|
5649
|
-
lines.push(`MANDATORY: You have ${open.length} unstarted task(s).`);
|
|
5650
|
-
lines.push(`Highest priority: ${top.title} [${top.priority}]`);
|
|
5651
|
-
lines.push(`File: exe/${agentId}/${top.file}`);
|
|
5652
|
-
lines.push("Read this task file and START WORKING NOW.");
|
|
5653
|
-
}
|
|
5654
|
-
return lines.join("\n");
|
|
5655
|
-
}
|
|
5656
|
-
function formatJson(result) {
|
|
5657
|
-
return JSON.stringify({
|
|
5658
|
-
open: result.open.map((t) => ({ file: t.file, title: t.title, priority: t.priority })),
|
|
5659
|
-
in_progress: result.inProgress.map((t) => ({ file: t.file, title: t.title, priority: t.priority })),
|
|
5660
|
-
done: result.done,
|
|
5661
|
-
total: result.total
|
|
5662
|
-
});
|
|
5663
|
-
}
|
|
5664
6302
|
var STATUS_RE, PRIORITY_RE, TITLE_RE;
|
|
5665
6303
|
var init_task_scanner = __esm({
|
|
5666
6304
|
"src/lib/task-scanner.ts"() {
|
|
@@ -5681,15 +6319,15 @@ __export(worker_gate_exports, {
|
|
|
5681
6319
|
tryAcquireBackfillLock: () => tryAcquireBackfillLock,
|
|
5682
6320
|
tryAcquireWorkerSlot: () => tryAcquireWorkerSlot
|
|
5683
6321
|
});
|
|
5684
|
-
import { readdirSync as
|
|
5685
|
-
import
|
|
6322
|
+
import { readdirSync as readdirSync5, writeFileSync as writeFileSync9, unlinkSync as unlinkSync8, mkdirSync as mkdirSync9, existsSync as existsSync15 } from "fs";
|
|
6323
|
+
import path19 from "path";
|
|
5686
6324
|
function tryAcquireWorkerSlot() {
|
|
5687
6325
|
try {
|
|
5688
|
-
|
|
6326
|
+
mkdirSync9(WORKER_PID_DIR, { recursive: true });
|
|
5689
6327
|
const reservationId = `res-${process.pid}-${Date.now()}`;
|
|
5690
|
-
const reservationPath =
|
|
5691
|
-
|
|
5692
|
-
const files =
|
|
6328
|
+
const reservationPath = path19.join(WORKER_PID_DIR, `${reservationId}.pid`);
|
|
6329
|
+
writeFileSync9(reservationPath, String(process.pid));
|
|
6330
|
+
const files = readdirSync5(WORKER_PID_DIR);
|
|
5693
6331
|
let alive = 0;
|
|
5694
6332
|
for (const f of files) {
|
|
5695
6333
|
if (!f.endsWith(".pid")) continue;
|
|
@@ -5705,20 +6343,20 @@ function tryAcquireWorkerSlot() {
|
|
|
5705
6343
|
alive++;
|
|
5706
6344
|
} catch {
|
|
5707
6345
|
try {
|
|
5708
|
-
|
|
6346
|
+
unlinkSync8(path19.join(WORKER_PID_DIR, f));
|
|
5709
6347
|
} catch {
|
|
5710
6348
|
}
|
|
5711
6349
|
}
|
|
5712
6350
|
}
|
|
5713
6351
|
if (alive > MAX_CONCURRENT_WORKERS) {
|
|
5714
6352
|
try {
|
|
5715
|
-
|
|
6353
|
+
unlinkSync8(reservationPath);
|
|
5716
6354
|
} catch {
|
|
5717
6355
|
}
|
|
5718
6356
|
return false;
|
|
5719
6357
|
}
|
|
5720
6358
|
try {
|
|
5721
|
-
|
|
6359
|
+
unlinkSync8(reservationPath);
|
|
5722
6360
|
} catch {
|
|
5723
6361
|
}
|
|
5724
6362
|
return true;
|
|
@@ -5728,21 +6366,21 @@ function tryAcquireWorkerSlot() {
|
|
|
5728
6366
|
}
|
|
5729
6367
|
function registerWorkerPid(pid) {
|
|
5730
6368
|
try {
|
|
5731
|
-
|
|
5732
|
-
|
|
6369
|
+
mkdirSync9(WORKER_PID_DIR, { recursive: true });
|
|
6370
|
+
writeFileSync9(path19.join(WORKER_PID_DIR, `worker-${pid}.pid`), String(pid));
|
|
5733
6371
|
} catch {
|
|
5734
6372
|
}
|
|
5735
6373
|
}
|
|
5736
6374
|
function cleanupWorkerPid() {
|
|
5737
6375
|
try {
|
|
5738
|
-
|
|
6376
|
+
unlinkSync8(path19.join(WORKER_PID_DIR, `worker-${process.pid}.pid`));
|
|
5739
6377
|
} catch {
|
|
5740
6378
|
}
|
|
5741
6379
|
}
|
|
5742
6380
|
function tryAcquireBackfillLock() {
|
|
5743
6381
|
try {
|
|
5744
|
-
|
|
5745
|
-
if (
|
|
6382
|
+
mkdirSync9(WORKER_PID_DIR, { recursive: true });
|
|
6383
|
+
if (existsSync15(BACKFILL_LOCK)) {
|
|
5746
6384
|
try {
|
|
5747
6385
|
const pid = parseInt(
|
|
5748
6386
|
__require("fs").readFileSync(BACKFILL_LOCK, "utf8").trim(),
|
|
@@ -5758,7 +6396,7 @@ function tryAcquireBackfillLock() {
|
|
|
5758
6396
|
} catch {
|
|
5759
6397
|
}
|
|
5760
6398
|
}
|
|
5761
|
-
|
|
6399
|
+
writeFileSync9(BACKFILL_LOCK, String(process.pid));
|
|
5762
6400
|
return true;
|
|
5763
6401
|
} catch {
|
|
5764
6402
|
return true;
|
|
@@ -5766,7 +6404,7 @@ function tryAcquireBackfillLock() {
|
|
|
5766
6404
|
}
|
|
5767
6405
|
function releaseBackfillLock() {
|
|
5768
6406
|
try {
|
|
5769
|
-
|
|
6407
|
+
unlinkSync8(BACKFILL_LOCK);
|
|
5770
6408
|
} catch {
|
|
5771
6409
|
}
|
|
5772
6410
|
}
|
|
@@ -5775,9 +6413,9 @@ var init_worker_gate = __esm({
|
|
|
5775
6413
|
"src/lib/worker-gate.ts"() {
|
|
5776
6414
|
"use strict";
|
|
5777
6415
|
init_config();
|
|
5778
|
-
WORKER_PID_DIR =
|
|
6416
|
+
WORKER_PID_DIR = path19.join(EXE_AI_DIR, "worker-pids");
|
|
5779
6417
|
MAX_CONCURRENT_WORKERS = 3;
|
|
5780
|
-
BACKFILL_LOCK =
|
|
6418
|
+
BACKFILL_LOCK = path19.join(WORKER_PID_DIR, "backfill.lock");
|
|
5781
6419
|
}
|
|
5782
6420
|
});
|
|
5783
6421
|
|
|
@@ -5860,6 +6498,232 @@ var init_compress = __esm({
|
|
|
5860
6498
|
}
|
|
5861
6499
|
});
|
|
5862
6500
|
|
|
6501
|
+
// src/lib/crdt-sync.ts
|
|
6502
|
+
var crdt_sync_exports = {};
|
|
6503
|
+
__export(crdt_sync_exports, {
|
|
6504
|
+
_setStatePath: () => _setStatePath,
|
|
6505
|
+
applyRemoteUpdate: () => applyRemoteUpdate,
|
|
6506
|
+
destroyCrdtDoc: () => destroyCrdtDoc,
|
|
6507
|
+
getDiffUpdate: () => getDiffUpdate,
|
|
6508
|
+
getFullState: () => getFullState,
|
|
6509
|
+
getStateVector: () => getStateVector,
|
|
6510
|
+
importExistingBehaviors: () => importExistingBehaviors,
|
|
6511
|
+
importExistingMemories: () => importExistingMemories,
|
|
6512
|
+
initCrdtDoc: () => initCrdtDoc,
|
|
6513
|
+
isCrdtSyncEnabled: () => isCrdtSyncEnabled,
|
|
6514
|
+
onUpdate: () => onUpdate,
|
|
6515
|
+
readAllBehaviors: () => readAllBehaviors,
|
|
6516
|
+
readAllMemories: () => readAllMemories,
|
|
6517
|
+
rebuildFromDb: () => rebuildFromDb
|
|
6518
|
+
});
|
|
6519
|
+
import * as Y from "yjs";
|
|
6520
|
+
import { readFileSync as readFileSync13, writeFileSync as writeFileSync10, existsSync as existsSync16, mkdirSync as mkdirSync10, unlinkSync as unlinkSync9 } from "fs";
|
|
6521
|
+
import path20 from "path";
|
|
6522
|
+
import { homedir } from "os";
|
|
6523
|
+
function getStatePath() {
|
|
6524
|
+
return _statePathOverride ?? DEFAULT_STATE_PATH;
|
|
6525
|
+
}
|
|
6526
|
+
function _setStatePath(p) {
|
|
6527
|
+
_statePathOverride = p;
|
|
6528
|
+
}
|
|
6529
|
+
function initCrdtDoc() {
|
|
6530
|
+
if (doc) return doc;
|
|
6531
|
+
doc = new Y.Doc();
|
|
6532
|
+
const sp = getStatePath();
|
|
6533
|
+
if (existsSync16(sp)) {
|
|
6534
|
+
try {
|
|
6535
|
+
const state = readFileSync13(sp);
|
|
6536
|
+
Y.applyUpdate(doc, new Uint8Array(state));
|
|
6537
|
+
} catch {
|
|
6538
|
+
console.warn("[crdt-sync] WARN: corrupted state file, rebuilding from DB");
|
|
6539
|
+
try {
|
|
6540
|
+
unlinkSync9(sp);
|
|
6541
|
+
} catch {
|
|
6542
|
+
}
|
|
6543
|
+
rebuildFromDb().catch((err) => {
|
|
6544
|
+
console.warn("[crdt-sync] rebuild from DB failed:", err);
|
|
6545
|
+
});
|
|
6546
|
+
}
|
|
6547
|
+
}
|
|
6548
|
+
doc.on("update", () => {
|
|
6549
|
+
persistState();
|
|
6550
|
+
});
|
|
6551
|
+
return doc;
|
|
6552
|
+
}
|
|
6553
|
+
function getMemoriesMap() {
|
|
6554
|
+
const d = initCrdtDoc();
|
|
6555
|
+
return d.getMap("memories");
|
|
6556
|
+
}
|
|
6557
|
+
function getBehaviorsMap() {
|
|
6558
|
+
const d = initCrdtDoc();
|
|
6559
|
+
return d.getMap("behaviors");
|
|
6560
|
+
}
|
|
6561
|
+
function applyRemoteUpdate(update) {
|
|
6562
|
+
const d = initCrdtDoc();
|
|
6563
|
+
Y.applyUpdate(d, update);
|
|
6564
|
+
}
|
|
6565
|
+
function getFullState() {
|
|
6566
|
+
const d = initCrdtDoc();
|
|
6567
|
+
return Y.encodeStateAsUpdate(d);
|
|
6568
|
+
}
|
|
6569
|
+
function getDiffUpdate(remoteStateVector) {
|
|
6570
|
+
const d = initCrdtDoc();
|
|
6571
|
+
return Y.encodeStateAsUpdate(d, remoteStateVector);
|
|
6572
|
+
}
|
|
6573
|
+
function getStateVector() {
|
|
6574
|
+
const d = initCrdtDoc();
|
|
6575
|
+
return Y.encodeStateVector(d);
|
|
6576
|
+
}
|
|
6577
|
+
function importExistingMemories(memories) {
|
|
6578
|
+
const map = getMemoriesMap();
|
|
6579
|
+
const d = initCrdtDoc();
|
|
6580
|
+
let imported = 0;
|
|
6581
|
+
d.transact(() => {
|
|
6582
|
+
for (const mem of memories) {
|
|
6583
|
+
if (!mem.id) continue;
|
|
6584
|
+
if (map.has(mem.id)) continue;
|
|
6585
|
+
const entry = new Y.Map();
|
|
6586
|
+
entry.set("id", mem.id);
|
|
6587
|
+
entry.set("agent_id", mem.agent_id ?? null);
|
|
6588
|
+
entry.set("agent_role", mem.agent_role ?? null);
|
|
6589
|
+
entry.set("session_id", mem.session_id ?? null);
|
|
6590
|
+
entry.set("timestamp", mem.timestamp ?? null);
|
|
6591
|
+
entry.set("tool_name", mem.tool_name ?? null);
|
|
6592
|
+
entry.set("project_name", mem.project_name ?? null);
|
|
6593
|
+
entry.set("has_error", mem.has_error ?? 0);
|
|
6594
|
+
entry.set("raw_text", mem.raw_text ?? "");
|
|
6595
|
+
entry.set("version", mem.version ?? 0);
|
|
6596
|
+
entry.set("author_device_id", mem.author_device_id ?? null);
|
|
6597
|
+
entry.set("scope", mem.scope ?? "business");
|
|
6598
|
+
map.set(mem.id, entry);
|
|
6599
|
+
imported++;
|
|
6600
|
+
}
|
|
6601
|
+
});
|
|
6602
|
+
return imported;
|
|
6603
|
+
}
|
|
6604
|
+
function importExistingBehaviors(behaviors) {
|
|
6605
|
+
const map = getBehaviorsMap();
|
|
6606
|
+
const d = initCrdtDoc();
|
|
6607
|
+
let imported = 0;
|
|
6608
|
+
d.transact(() => {
|
|
6609
|
+
for (const beh of behaviors) {
|
|
6610
|
+
if (!beh.id) continue;
|
|
6611
|
+
if (map.has(beh.id)) continue;
|
|
6612
|
+
const entry = new Y.Map();
|
|
6613
|
+
entry.set("id", beh.id);
|
|
6614
|
+
entry.set("agent_id", beh.agent_id ?? null);
|
|
6615
|
+
entry.set("project_name", beh.project_name ?? null);
|
|
6616
|
+
entry.set("domain", beh.domain ?? null);
|
|
6617
|
+
entry.set("content", beh.content ?? null);
|
|
6618
|
+
entry.set("active", beh.active ?? 1);
|
|
6619
|
+
entry.set("priority", beh.priority ?? "p1");
|
|
6620
|
+
entry.set("created_at", beh.created_at ?? null);
|
|
6621
|
+
entry.set("updated_at", beh.updated_at ?? null);
|
|
6622
|
+
map.set(beh.id, entry);
|
|
6623
|
+
imported++;
|
|
6624
|
+
}
|
|
6625
|
+
});
|
|
6626
|
+
return imported;
|
|
6627
|
+
}
|
|
6628
|
+
function readAllMemories() {
|
|
6629
|
+
const map = getMemoriesMap();
|
|
6630
|
+
const records = [];
|
|
6631
|
+
map.forEach((entry, id) => {
|
|
6632
|
+
records.push({
|
|
6633
|
+
id,
|
|
6634
|
+
agent_id: entry.get("agent_id"),
|
|
6635
|
+
agent_role: entry.get("agent_role"),
|
|
6636
|
+
session_id: entry.get("session_id"),
|
|
6637
|
+
timestamp: entry.get("timestamp"),
|
|
6638
|
+
tool_name: entry.get("tool_name"),
|
|
6639
|
+
project_name: entry.get("project_name"),
|
|
6640
|
+
has_error: entry.get("has_error"),
|
|
6641
|
+
raw_text: entry.get("raw_text"),
|
|
6642
|
+
version: entry.get("version"),
|
|
6643
|
+
author_device_id: entry.get("author_device_id"),
|
|
6644
|
+
scope: entry.get("scope")
|
|
6645
|
+
});
|
|
6646
|
+
});
|
|
6647
|
+
return records;
|
|
6648
|
+
}
|
|
6649
|
+
function readAllBehaviors() {
|
|
6650
|
+
const map = getBehaviorsMap();
|
|
6651
|
+
const records = [];
|
|
6652
|
+
map.forEach((entry, id) => {
|
|
6653
|
+
records.push({
|
|
6654
|
+
id,
|
|
6655
|
+
agent_id: entry.get("agent_id"),
|
|
6656
|
+
project_name: entry.get("project_name"),
|
|
6657
|
+
domain: entry.get("domain"),
|
|
6658
|
+
content: entry.get("content"),
|
|
6659
|
+
active: entry.get("active"),
|
|
6660
|
+
priority: entry.get("priority"),
|
|
6661
|
+
created_at: entry.get("created_at"),
|
|
6662
|
+
updated_at: entry.get("updated_at")
|
|
6663
|
+
});
|
|
6664
|
+
});
|
|
6665
|
+
return records;
|
|
6666
|
+
}
|
|
6667
|
+
function onUpdate(callback) {
|
|
6668
|
+
const d = initCrdtDoc();
|
|
6669
|
+
const handler = (update) => callback(update);
|
|
6670
|
+
d.on("update", handler);
|
|
6671
|
+
return () => d.off("update", handler);
|
|
6672
|
+
}
|
|
6673
|
+
function persistState() {
|
|
6674
|
+
if (!doc) return;
|
|
6675
|
+
try {
|
|
6676
|
+
const sp = getStatePath();
|
|
6677
|
+
const dir = path20.dirname(sp);
|
|
6678
|
+
if (!existsSync16(dir)) mkdirSync10(dir, { recursive: true });
|
|
6679
|
+
const state = Y.encodeStateAsUpdate(doc);
|
|
6680
|
+
writeFileSync10(sp, Buffer.from(state));
|
|
6681
|
+
} catch {
|
|
6682
|
+
}
|
|
6683
|
+
}
|
|
6684
|
+
function isCrdtSyncEnabled() {
|
|
6685
|
+
return process.env.EXE_CRDT_SYNC !== "0";
|
|
6686
|
+
}
|
|
6687
|
+
async function rebuildFromDb() {
|
|
6688
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
6689
|
+
const client = getClient2();
|
|
6690
|
+
const result = await client.execute(
|
|
6691
|
+
"SELECT id, agent_id, agent_role, session_id, timestamp, tool_name, project_name, has_error, raw_text, version, author_device_id, scope FROM memories"
|
|
6692
|
+
);
|
|
6693
|
+
const memories = result.rows.map((row) => ({
|
|
6694
|
+
id: String(row.id),
|
|
6695
|
+
agent_id: row.agent_id,
|
|
6696
|
+
agent_role: row.agent_role,
|
|
6697
|
+
session_id: row.session_id,
|
|
6698
|
+
timestamp: row.timestamp,
|
|
6699
|
+
tool_name: row.tool_name,
|
|
6700
|
+
project_name: row.project_name,
|
|
6701
|
+
has_error: row.has_error,
|
|
6702
|
+
raw_text: row.raw_text,
|
|
6703
|
+
version: row.version,
|
|
6704
|
+
author_device_id: row.author_device_id,
|
|
6705
|
+
scope: row.scope
|
|
6706
|
+
}));
|
|
6707
|
+
const count = importExistingMemories(memories);
|
|
6708
|
+
persistState();
|
|
6709
|
+
return count;
|
|
6710
|
+
}
|
|
6711
|
+
function destroyCrdtDoc() {
|
|
6712
|
+
if (doc) {
|
|
6713
|
+
doc.destroy();
|
|
6714
|
+
doc = null;
|
|
6715
|
+
}
|
|
6716
|
+
}
|
|
6717
|
+
var DEFAULT_STATE_PATH, _statePathOverride, doc;
|
|
6718
|
+
var init_crdt_sync = __esm({
|
|
6719
|
+
"src/lib/crdt-sync.ts"() {
|
|
6720
|
+
"use strict";
|
|
6721
|
+
DEFAULT_STATE_PATH = path20.join(homedir(), ".exe-os", "crdt-state.bin");
|
|
6722
|
+
_statePathOverride = null;
|
|
6723
|
+
doc = null;
|
|
6724
|
+
}
|
|
6725
|
+
});
|
|
6726
|
+
|
|
5863
6727
|
// src/lib/cloud-sync.ts
|
|
5864
6728
|
var cloud_sync_exports = {};
|
|
5865
6729
|
__export(cloud_sync_exports, {
|
|
@@ -5888,16 +6752,16 @@ __export(cloud_sync_exports, {
|
|
|
5888
6752
|
mergeRosterFromRemote: () => mergeRosterFromRemote,
|
|
5889
6753
|
recordRosterDeletion: () => recordRosterDeletion
|
|
5890
6754
|
});
|
|
5891
|
-
import { readFileSync as
|
|
6755
|
+
import { readFileSync as readFileSync14, writeFileSync as writeFileSync11, existsSync as existsSync17, readdirSync as readdirSync6, mkdirSync as mkdirSync11, appendFileSync as appendFileSync2, unlinkSync as unlinkSync10, openSync as openSync2, closeSync as closeSync2 } from "fs";
|
|
5892
6756
|
import crypto7 from "crypto";
|
|
5893
|
-
import
|
|
5894
|
-
import { homedir } from "os";
|
|
6757
|
+
import path21 from "path";
|
|
6758
|
+
import { homedir as homedir2 } from "os";
|
|
5895
6759
|
function sqlSafe(v) {
|
|
5896
6760
|
return v === void 0 ? null : v;
|
|
5897
6761
|
}
|
|
5898
6762
|
function logError(msg) {
|
|
5899
6763
|
try {
|
|
5900
|
-
const logPath =
|
|
6764
|
+
const logPath = path21.join(homedir2(), ".exe-os", "workers.log");
|
|
5901
6765
|
appendFileSync2(logPath, `${(/* @__PURE__ */ new Date()).toISOString()} ${msg}
|
|
5902
6766
|
`);
|
|
5903
6767
|
} catch {
|
|
@@ -5905,20 +6769,20 @@ function logError(msg) {
|
|
|
5905
6769
|
}
|
|
5906
6770
|
async function withRosterLock(fn) {
|
|
5907
6771
|
try {
|
|
5908
|
-
const fd =
|
|
5909
|
-
|
|
5910
|
-
|
|
6772
|
+
const fd = openSync2(ROSTER_LOCK_PATH, "wx");
|
|
6773
|
+
closeSync2(fd);
|
|
6774
|
+
writeFileSync11(ROSTER_LOCK_PATH, String(Date.now()));
|
|
5911
6775
|
} catch (err) {
|
|
5912
6776
|
if (err.code === "EEXIST") {
|
|
5913
6777
|
try {
|
|
5914
|
-
const ts = parseInt(
|
|
6778
|
+
const ts = parseInt(readFileSync14(ROSTER_LOCK_PATH, "utf-8"), 10);
|
|
5915
6779
|
if (Date.now() - ts < LOCK_STALE_MS) {
|
|
5916
6780
|
throw new Error("Roster merge already in progress \u2014 another sync is running");
|
|
5917
6781
|
}
|
|
5918
|
-
|
|
5919
|
-
const fd =
|
|
5920
|
-
|
|
5921
|
-
|
|
6782
|
+
unlinkSync10(ROSTER_LOCK_PATH);
|
|
6783
|
+
const fd = openSync2(ROSTER_LOCK_PATH, "wx");
|
|
6784
|
+
closeSync2(fd);
|
|
6785
|
+
writeFileSync11(ROSTER_LOCK_PATH, String(Date.now()));
|
|
5922
6786
|
} catch (retryErr) {
|
|
5923
6787
|
if (retryErr instanceof Error && retryErr.message.includes("already in progress")) throw retryErr;
|
|
5924
6788
|
throw new Error("Roster merge already in progress \u2014 another sync is running");
|
|
@@ -5931,7 +6795,7 @@ async function withRosterLock(fn) {
|
|
|
5931
6795
|
return await fn();
|
|
5932
6796
|
} finally {
|
|
5933
6797
|
try {
|
|
5934
|
-
|
|
6798
|
+
unlinkSync10(ROSTER_LOCK_PATH);
|
|
5935
6799
|
} catch {
|
|
5936
6800
|
}
|
|
5937
6801
|
}
|
|
@@ -6076,29 +6940,75 @@ async function cloudSync(config) {
|
|
|
6076
6940
|
const pullResult = await cloudPull(lastPullVersion, config);
|
|
6077
6941
|
let pulled = 0;
|
|
6078
6942
|
if (pullResult.records.length > 0) {
|
|
6079
|
-
|
|
6080
|
-
|
|
6081
|
-
|
|
6082
|
-
|
|
6083
|
-
|
|
6084
|
-
|
|
6085
|
-
|
|
6086
|
-
|
|
6087
|
-
|
|
6088
|
-
|
|
6089
|
-
|
|
6090
|
-
|
|
6091
|
-
|
|
6092
|
-
|
|
6093
|
-
|
|
6094
|
-
|
|
6095
|
-
|
|
6096
|
-
|
|
6097
|
-
|
|
6098
|
-
|
|
6099
|
-
|
|
6100
|
-
|
|
6101
|
-
|
|
6943
|
+
if (isCrdtSyncEnabled()) {
|
|
6944
|
+
const { initCrdtDoc: initCrdtDoc2, importExistingMemories: importExistingMemories2, readAllMemories: readAllMemories2 } = await Promise.resolve().then(() => (init_crdt_sync(), crdt_sync_exports));
|
|
6945
|
+
initCrdtDoc2();
|
|
6946
|
+
importExistingMemories2(
|
|
6947
|
+
pullResult.records.map((rec) => ({
|
|
6948
|
+
id: String(rec.id ?? ""),
|
|
6949
|
+
agent_id: rec.agent_id,
|
|
6950
|
+
agent_role: rec.agent_role,
|
|
6951
|
+
session_id: rec.session_id,
|
|
6952
|
+
timestamp: rec.timestamp,
|
|
6953
|
+
tool_name: rec.tool_name,
|
|
6954
|
+
project_name: rec.project_name,
|
|
6955
|
+
has_error: rec.has_error ?? 0,
|
|
6956
|
+
raw_text: rec.raw_text ?? "",
|
|
6957
|
+
version: rec.version ?? 0,
|
|
6958
|
+
author_device_id: rec.author_device_id,
|
|
6959
|
+
scope: rec.scope ?? "business"
|
|
6960
|
+
}))
|
|
6961
|
+
);
|
|
6962
|
+
const pulledIds = new Set(pullResult.records.map((r) => String(r.id ?? "")));
|
|
6963
|
+
const merged = readAllMemories2().filter((rec) => pulledIds.has(rec.id));
|
|
6964
|
+
const stmts = merged.map((rec) => ({
|
|
6965
|
+
sql: `INSERT OR REPLACE INTO memories
|
|
6966
|
+
(id, agent_id, agent_role, session_id, timestamp,
|
|
6967
|
+
tool_name, project_name, has_error, raw_text, version,
|
|
6968
|
+
author_device_id, scope)
|
|
6969
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
6970
|
+
args: [
|
|
6971
|
+
sqlSafe(rec.id),
|
|
6972
|
+
sqlSafe(rec.agent_id),
|
|
6973
|
+
sqlSafe(rec.agent_role),
|
|
6974
|
+
sqlSafe(rec.session_id),
|
|
6975
|
+
sqlSafe(rec.timestamp),
|
|
6976
|
+
sqlSafe(rec.tool_name),
|
|
6977
|
+
sqlSafe(rec.project_name),
|
|
6978
|
+
sqlSafe(rec.has_error ?? 0),
|
|
6979
|
+
sqlSafe(rec.raw_text ?? ""),
|
|
6980
|
+
sqlSafe(rec.version ?? 0),
|
|
6981
|
+
sqlSafe(rec.author_device_id),
|
|
6982
|
+
sqlSafe(rec.scope ?? "business")
|
|
6983
|
+
]
|
|
6984
|
+
}));
|
|
6985
|
+
if (stmts.length > 0) await client.batch(stmts, "write");
|
|
6986
|
+
pulled = pullResult.records.length;
|
|
6987
|
+
} else {
|
|
6988
|
+
const stmts = pullResult.records.map((rec) => ({
|
|
6989
|
+
sql: `INSERT OR REPLACE INTO memories
|
|
6990
|
+
(id, agent_id, agent_role, session_id, timestamp,
|
|
6991
|
+
tool_name, project_name, has_error, raw_text, version,
|
|
6992
|
+
author_device_id, scope)
|
|
6993
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
6994
|
+
args: [
|
|
6995
|
+
sqlSafe(rec.id),
|
|
6996
|
+
sqlSafe(rec.agent_id),
|
|
6997
|
+
sqlSafe(rec.agent_role),
|
|
6998
|
+
sqlSafe(rec.session_id),
|
|
6999
|
+
sqlSafe(rec.timestamp),
|
|
7000
|
+
sqlSafe(rec.tool_name),
|
|
7001
|
+
sqlSafe(rec.project_name),
|
|
7002
|
+
sqlSafe(rec.has_error ?? 0),
|
|
7003
|
+
sqlSafe(rec.raw_text ?? ""),
|
|
7004
|
+
sqlSafe(rec.version ?? 0),
|
|
7005
|
+
sqlSafe(rec.author_device_id),
|
|
7006
|
+
sqlSafe(rec.scope ?? "business")
|
|
7007
|
+
]
|
|
7008
|
+
}));
|
|
7009
|
+
await client.batch(stmts, "write");
|
|
7010
|
+
pulled = pullResult.records.length;
|
|
7011
|
+
}
|
|
6102
7012
|
}
|
|
6103
7013
|
if (pullResult.maxVersion > lastPullVersion) {
|
|
6104
7014
|
await client.execute({
|
|
@@ -6246,9 +7156,9 @@ async function cloudSync(config) {
|
|
|
6246
7156
|
try {
|
|
6247
7157
|
const employees = await loadEmployees();
|
|
6248
7158
|
rosterResult.employees = employees.length;
|
|
6249
|
-
const idDir =
|
|
6250
|
-
if (
|
|
6251
|
-
rosterResult.identities =
|
|
7159
|
+
const idDir = path21.join(EXE_AI_DIR, "identity");
|
|
7160
|
+
if (existsSync17(idDir)) {
|
|
7161
|
+
rosterResult.identities = readdirSync6(idDir).filter((f) => f.endsWith(".md")).length;
|
|
6252
7162
|
}
|
|
6253
7163
|
} catch {
|
|
6254
7164
|
}
|
|
@@ -6268,55 +7178,63 @@ async function cloudSync(config) {
|
|
|
6268
7178
|
function recordRosterDeletion(name) {
|
|
6269
7179
|
let deletions = [];
|
|
6270
7180
|
try {
|
|
6271
|
-
if (
|
|
6272
|
-
deletions = JSON.parse(
|
|
7181
|
+
if (existsSync17(ROSTER_DELETIONS_PATH)) {
|
|
7182
|
+
deletions = JSON.parse(readFileSync14(ROSTER_DELETIONS_PATH, "utf-8"));
|
|
6273
7183
|
}
|
|
6274
7184
|
} catch {
|
|
6275
7185
|
}
|
|
6276
7186
|
if (!deletions.includes(name)) deletions.push(name);
|
|
6277
|
-
|
|
7187
|
+
writeFileSync11(ROSTER_DELETIONS_PATH, JSON.stringify(deletions));
|
|
6278
7188
|
}
|
|
6279
7189
|
function consumeRosterDeletions() {
|
|
6280
7190
|
try {
|
|
6281
|
-
if (!
|
|
6282
|
-
const deletions = JSON.parse(
|
|
6283
|
-
|
|
7191
|
+
if (!existsSync17(ROSTER_DELETIONS_PATH)) return [];
|
|
7192
|
+
const deletions = JSON.parse(readFileSync14(ROSTER_DELETIONS_PATH, "utf-8"));
|
|
7193
|
+
writeFileSync11(ROSTER_DELETIONS_PATH, "[]");
|
|
6284
7194
|
return deletions;
|
|
6285
7195
|
} catch {
|
|
6286
7196
|
return [];
|
|
6287
7197
|
}
|
|
6288
7198
|
}
|
|
6289
7199
|
function buildRosterBlob(paths) {
|
|
6290
|
-
const rosterPath = paths?.rosterPath ??
|
|
6291
|
-
const identityDir = paths?.identityDir ??
|
|
6292
|
-
const configPath = paths?.configPath ??
|
|
7200
|
+
const rosterPath = paths?.rosterPath ?? path21.join(EXE_AI_DIR, "exe-employees.json");
|
|
7201
|
+
const identityDir = paths?.identityDir ?? path21.join(EXE_AI_DIR, "identity");
|
|
7202
|
+
const configPath = paths?.configPath ?? path21.join(EXE_AI_DIR, "config.json");
|
|
6293
7203
|
let roster = [];
|
|
6294
|
-
if (
|
|
7204
|
+
if (existsSync17(rosterPath)) {
|
|
6295
7205
|
try {
|
|
6296
|
-
roster = JSON.parse(
|
|
7206
|
+
roster = JSON.parse(readFileSync14(rosterPath, "utf-8"));
|
|
6297
7207
|
} catch {
|
|
6298
7208
|
}
|
|
6299
7209
|
}
|
|
6300
7210
|
const identities = {};
|
|
6301
|
-
if (
|
|
6302
|
-
for (const file of
|
|
7211
|
+
if (existsSync17(identityDir)) {
|
|
7212
|
+
for (const file of readdirSync6(identityDir).filter((f) => f.endsWith(".md"))) {
|
|
6303
7213
|
try {
|
|
6304
|
-
identities[file] =
|
|
7214
|
+
identities[file] = readFileSync14(path21.join(identityDir, file), "utf-8");
|
|
6305
7215
|
} catch {
|
|
6306
7216
|
}
|
|
6307
7217
|
}
|
|
6308
7218
|
}
|
|
6309
7219
|
let config;
|
|
6310
|
-
if (
|
|
7220
|
+
if (existsSync17(configPath)) {
|
|
6311
7221
|
try {
|
|
6312
|
-
config = JSON.parse(
|
|
7222
|
+
config = JSON.parse(readFileSync14(configPath, "utf-8"));
|
|
7223
|
+
} catch {
|
|
7224
|
+
}
|
|
7225
|
+
}
|
|
7226
|
+
let agentConfig;
|
|
7227
|
+
const agentConfigPath = path21.join(EXE_AI_DIR, "agent-config.json");
|
|
7228
|
+
if (existsSync17(agentConfigPath)) {
|
|
7229
|
+
try {
|
|
7230
|
+
agentConfig = JSON.parse(readFileSync14(agentConfigPath, "utf-8"));
|
|
6313
7231
|
} catch {
|
|
6314
7232
|
}
|
|
6315
7233
|
}
|
|
6316
7234
|
const deletedNames = consumeRosterDeletions();
|
|
6317
|
-
const content = JSON.stringify({ roster, identities, config, deletedNames });
|
|
7235
|
+
const content = JSON.stringify({ roster, identities, config, agentConfig, deletedNames });
|
|
6318
7236
|
const hash = crypto7.createHash("sha256").update(content).digest("hex").slice(0, 16);
|
|
6319
|
-
return { roster, identities, config, deletedNames, version: hash };
|
|
7237
|
+
return { roster, identities, config, agentConfig, deletedNames, version: hash };
|
|
6320
7238
|
}
|
|
6321
7239
|
async function cloudPushRoster(config) {
|
|
6322
7240
|
assertSecureEndpoint(config.endpoint);
|
|
@@ -6385,23 +7303,23 @@ async function cloudPullRoster(config) {
|
|
|
6385
7303
|
}
|
|
6386
7304
|
}
|
|
6387
7305
|
function mergeConfig(remoteConfig, configPath) {
|
|
6388
|
-
const cfgPath = configPath ??
|
|
7306
|
+
const cfgPath = configPath ?? path21.join(EXE_AI_DIR, "config.json");
|
|
6389
7307
|
let local = {};
|
|
6390
|
-
if (
|
|
7308
|
+
if (existsSync17(cfgPath)) {
|
|
6391
7309
|
try {
|
|
6392
|
-
local = JSON.parse(
|
|
7310
|
+
local = JSON.parse(readFileSync14(cfgPath, "utf-8"));
|
|
6393
7311
|
} catch {
|
|
6394
7312
|
}
|
|
6395
7313
|
}
|
|
6396
7314
|
const merged = { ...remoteConfig, ...local };
|
|
6397
|
-
const dir =
|
|
6398
|
-
if (!
|
|
6399
|
-
|
|
7315
|
+
const dir = path21.dirname(cfgPath);
|
|
7316
|
+
if (!existsSync17(dir)) mkdirSync11(dir, { recursive: true });
|
|
7317
|
+
writeFileSync11(cfgPath, JSON.stringify(merged, null, 2), "utf-8");
|
|
6400
7318
|
}
|
|
6401
7319
|
async function mergeRosterFromRemote(remote, paths) {
|
|
6402
7320
|
return withRosterLock(async () => {
|
|
6403
7321
|
const rosterPath = paths?.rosterPath ?? void 0;
|
|
6404
|
-
const identityDir = paths?.identityDir ??
|
|
7322
|
+
const identityDir = paths?.identityDir ?? path21.join(EXE_AI_DIR, "identity");
|
|
6405
7323
|
const localEmployees = await loadEmployees(rosterPath);
|
|
6406
7324
|
const localNames = new Set(localEmployees.map((e) => e.name));
|
|
6407
7325
|
let added = 0;
|
|
@@ -6422,15 +7340,15 @@ async function mergeRosterFromRemote(remote, paths) {
|
|
|
6422
7340
|
) ?? lookupKey;
|
|
6423
7341
|
const remoteIdentity = remote.identities[matchedKey];
|
|
6424
7342
|
if (remoteIdentity) {
|
|
6425
|
-
if (!
|
|
6426
|
-
const idPath =
|
|
7343
|
+
if (!existsSync17(identityDir)) mkdirSync11(identityDir, { recursive: true });
|
|
7344
|
+
const idPath = path21.join(identityDir, `${remoteEmp.name}.md`);
|
|
6427
7345
|
let localIdentity = null;
|
|
6428
7346
|
try {
|
|
6429
|
-
localIdentity =
|
|
7347
|
+
localIdentity = existsSync17(idPath) ? readFileSync14(idPath, "utf-8") : null;
|
|
6430
7348
|
} catch {
|
|
6431
7349
|
}
|
|
6432
7350
|
if (localIdentity !== remoteIdentity) {
|
|
6433
|
-
|
|
7351
|
+
writeFileSync11(idPath, remoteIdentity, "utf-8");
|
|
6434
7352
|
identitiesUpdated++;
|
|
6435
7353
|
}
|
|
6436
7354
|
}
|
|
@@ -6454,6 +7372,21 @@ async function mergeRosterFromRemote(remote, paths) {
|
|
|
6454
7372
|
} catch {
|
|
6455
7373
|
}
|
|
6456
7374
|
}
|
|
7375
|
+
if (remote.agentConfig && Object.keys(remote.agentConfig).length > 0) {
|
|
7376
|
+
try {
|
|
7377
|
+
const agentConfigPath = path21.join(EXE_AI_DIR, "agent-config.json");
|
|
7378
|
+
let local = {};
|
|
7379
|
+
if (existsSync17(agentConfigPath)) {
|
|
7380
|
+
try {
|
|
7381
|
+
local = JSON.parse(readFileSync14(agentConfigPath, "utf-8"));
|
|
7382
|
+
} catch {
|
|
7383
|
+
}
|
|
7384
|
+
}
|
|
7385
|
+
const merged = { ...remote.agentConfig, ...local };
|
|
7386
|
+
writeFileSync11(agentConfigPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
|
|
7387
|
+
} catch {
|
|
7388
|
+
}
|
|
7389
|
+
}
|
|
6457
7390
|
return { added, identitiesUpdated };
|
|
6458
7391
|
});
|
|
6459
7392
|
}
|
|
@@ -6883,13 +7816,14 @@ var init_cloud_sync = __esm({
|
|
|
6883
7816
|
init_compress();
|
|
6884
7817
|
init_license();
|
|
6885
7818
|
init_config();
|
|
7819
|
+
init_crdt_sync();
|
|
6886
7820
|
init_employees();
|
|
6887
7821
|
LOCALHOST_PATTERNS = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
|
|
6888
7822
|
FETCH_TIMEOUT_MS = 3e4;
|
|
6889
7823
|
PUSH_BATCH_SIZE = 5e3;
|
|
6890
|
-
ROSTER_LOCK_PATH =
|
|
7824
|
+
ROSTER_LOCK_PATH = path21.join(EXE_AI_DIR, "roster-merge.lock");
|
|
6891
7825
|
LOCK_STALE_MS = 3e4;
|
|
6892
|
-
ROSTER_DELETIONS_PATH =
|
|
7826
|
+
ROSTER_DELETIONS_PATH = path21.join(EXE_AI_DIR, "roster-deletions.json");
|
|
6893
7827
|
}
|
|
6894
7828
|
});
|
|
6895
7829
|
|
|
@@ -6902,7 +7836,7 @@ __export(schedules_exports, {
|
|
|
6902
7836
|
parseHumanCron: () => parseHumanCron
|
|
6903
7837
|
});
|
|
6904
7838
|
import crypto8 from "crypto";
|
|
6905
|
-
import { execSync as
|
|
7839
|
+
import { execSync as execSync9 } from "child_process";
|
|
6906
7840
|
async function ensureDb() {
|
|
6907
7841
|
if (!isInitialized()) {
|
|
6908
7842
|
await initStore();
|
|
@@ -7045,7 +7979,7 @@ function addToCrontab(id, cron, prompt, projectDir) {
|
|
|
7045
7979
|
const cwd = projectDir ? `cd ${JSON.stringify(projectDir)} && ` : "";
|
|
7046
7980
|
const escapedPrompt = prompt.replace(/"/g, '\\"');
|
|
7047
7981
|
const entry = `${cron} ${cwd}claude -p --dangerously-skip-permissions "${escapedPrompt}" # exe-schedule:${id}`;
|
|
7048
|
-
|
|
7982
|
+
execSync9(
|
|
7049
7983
|
`(crontab -l 2>/dev/null; echo ${JSON.stringify(entry)}) | crontab -`,
|
|
7050
7984
|
{ timeout: 5e3, stdio: "ignore" }
|
|
7051
7985
|
);
|
|
@@ -7054,7 +7988,7 @@ function addToCrontab(id, cron, prompt, projectDir) {
|
|
|
7054
7988
|
}
|
|
7055
7989
|
function removeFromCrontab(id) {
|
|
7056
7990
|
try {
|
|
7057
|
-
|
|
7991
|
+
execSync9(
|
|
7058
7992
|
`crontab -l 2>/dev/null | grep -v "exe-schedule:${id}" | crontab -`,
|
|
7059
7993
|
{ timeout: 5e3, stdio: "ignore" }
|
|
7060
7994
|
);
|
|
@@ -7071,10 +8005,10 @@ var init_schedules = __esm({
|
|
|
7071
8005
|
|
|
7072
8006
|
// src/bin/exe-boot.ts
|
|
7073
8007
|
init_employees();
|
|
7074
|
-
import
|
|
8008
|
+
import path22 from "path";
|
|
7075
8009
|
import { mkdir as mkdir5, writeFile as writeFile6 } from "fs/promises";
|
|
7076
|
-
import { existsSync as
|
|
7077
|
-
import
|
|
8010
|
+
import { existsSync as existsSync18, readFileSync as readFileSync15, readdirSync as readdirSync7, unlinkSync as unlinkSync11 } from "fs";
|
|
8011
|
+
import os9 from "os";
|
|
7078
8012
|
|
|
7079
8013
|
// src/lib/employee-templates.ts
|
|
7080
8014
|
init_global_procedures();
|
|
@@ -7532,12 +8466,13 @@ init_task_scope();
|
|
|
7532
8466
|
|
|
7533
8467
|
// src/lib/is-main.ts
|
|
7534
8468
|
import { realpathSync } from "fs";
|
|
7535
|
-
import { fileURLToPath as
|
|
8469
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
7536
8470
|
function isMainModule(importMetaUrl) {
|
|
7537
8471
|
if (process.argv[1] == null) return false;
|
|
8472
|
+
if (process.argv[1].includes("mcp/server")) return false;
|
|
7538
8473
|
try {
|
|
7539
8474
|
const scriptPath = realpathSync(process.argv[1]);
|
|
7540
|
-
const modulePath = realpathSync(
|
|
8475
|
+
const modulePath = realpathSync(fileURLToPath3(importMetaUrl));
|
|
7541
8476
|
return scriptPath === modulePath;
|
|
7542
8477
|
} catch {
|
|
7543
8478
|
return importMetaUrl === `file://${process.argv[1]}` || importMetaUrl === new URL(process.argv[1], "file://").href;
|
|
@@ -7549,24 +8484,24 @@ init_notifications();
|
|
|
7549
8484
|
|
|
7550
8485
|
// src/adapters/claude/active-agent.ts
|
|
7551
8486
|
init_config();
|
|
7552
|
-
import { readFileSync as
|
|
8487
|
+
import { readFileSync as readFileSync12, writeFileSync as writeFileSync8, mkdirSync as mkdirSync8, unlinkSync as unlinkSync7, readdirSync as readdirSync4 } from "fs";
|
|
7553
8488
|
import { execSync as execSync8 } from "child_process";
|
|
7554
|
-
import
|
|
8489
|
+
import path18 from "path";
|
|
7555
8490
|
|
|
7556
8491
|
// src/adapters/claude/session-key.ts
|
|
7557
8492
|
init_session_key();
|
|
7558
8493
|
|
|
7559
8494
|
// src/adapters/claude/active-agent.ts
|
|
7560
8495
|
init_employees();
|
|
7561
|
-
var CACHE_DIR =
|
|
8496
|
+
var CACHE_DIR = path18.join(EXE_AI_DIR, "session-cache");
|
|
7562
8497
|
var STALE_MS = 24 * 60 * 60 * 1e3;
|
|
7563
8498
|
function getMarkerPath() {
|
|
7564
|
-
return
|
|
8499
|
+
return path18.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
|
|
7565
8500
|
}
|
|
7566
8501
|
function writeActiveAgent(agentId, agentRole) {
|
|
7567
8502
|
try {
|
|
7568
|
-
|
|
7569
|
-
|
|
8503
|
+
mkdirSync8(CACHE_DIR, { recursive: true });
|
|
8504
|
+
writeFileSync8(
|
|
7570
8505
|
getMarkerPath(),
|
|
7571
8506
|
JSON.stringify({ agentId, agentRole, startedAt: (/* @__PURE__ */ new Date()).toISOString() })
|
|
7572
8507
|
);
|
|
@@ -7576,11 +8511,11 @@ function writeActiveAgent(agentId, agentRole) {
|
|
|
7576
8511
|
function cleanupSessionMarkers() {
|
|
7577
8512
|
const key = getSessionKey();
|
|
7578
8513
|
try {
|
|
7579
|
-
|
|
8514
|
+
unlinkSync7(path18.join(CACHE_DIR, `active-agent-${key}.json`));
|
|
7580
8515
|
} catch {
|
|
7581
8516
|
}
|
|
7582
8517
|
try {
|
|
7583
|
-
|
|
8518
|
+
unlinkSync7(path18.join(CACHE_DIR, "active-agent-undefined.json"));
|
|
7584
8519
|
} catch {
|
|
7585
8520
|
}
|
|
7586
8521
|
}
|
|
@@ -7662,18 +8597,18 @@ async function boot(options) {
|
|
|
7662
8597
|
} catch {
|
|
7663
8598
|
}
|
|
7664
8599
|
try {
|
|
7665
|
-
const { readdirSync:
|
|
8600
|
+
const { readdirSync: readdirSync8, readFileSync: readFs } = await import("fs");
|
|
7666
8601
|
const { STATUS_RE: STATUS_RE2, PRIORITY_RE: PRIORITY_RE2, TITLE_RE: TITLE_RE2 } = await Promise.resolve().then(() => (init_task_scanner(), task_scanner_exports));
|
|
7667
8602
|
const { getProjectName: getProjectName2 } = await Promise.resolve().then(() => (init_project_name(), project_name_exports));
|
|
7668
8603
|
const exeDir = "exe";
|
|
7669
|
-
const entries =
|
|
8604
|
+
const entries = readdirSync8(exeDir, { withFileTypes: true });
|
|
7670
8605
|
const employeeDirs = entries.filter((e) => e.isDirectory() && !["output", "research"].includes(e.name));
|
|
7671
8606
|
for (const dir of employeeDirs) {
|
|
7672
8607
|
const employee = dir.name;
|
|
7673
|
-
const taskDir =
|
|
8608
|
+
const taskDir = path22.join(exeDir, employee);
|
|
7674
8609
|
let files;
|
|
7675
8610
|
try {
|
|
7676
|
-
files =
|
|
8611
|
+
files = readdirSync8(taskDir).filter((f) => f.endsWith(".md"));
|
|
7677
8612
|
} catch {
|
|
7678
8613
|
continue;
|
|
7679
8614
|
}
|
|
@@ -7681,7 +8616,7 @@ async function boot(options) {
|
|
|
7681
8616
|
const taskFilePath = `exe/${employee}/${file}`;
|
|
7682
8617
|
let content;
|
|
7683
8618
|
try {
|
|
7684
|
-
content = readFs(
|
|
8619
|
+
content = readFs(path22.join(taskDir, file), "utf8");
|
|
7685
8620
|
} catch {
|
|
7686
8621
|
continue;
|
|
7687
8622
|
}
|
|
@@ -7767,12 +8702,12 @@ async function boot(options) {
|
|
|
7767
8702
|
}
|
|
7768
8703
|
try {
|
|
7769
8704
|
for (const reviewDirName of /* @__PURE__ */ new Set(["exe", coordinatorName])) {
|
|
7770
|
-
const reviewDir =
|
|
7771
|
-
if (
|
|
7772
|
-
for (const f of
|
|
8705
|
+
const reviewDir = path22.join(process.cwd(), "exe", reviewDirName);
|
|
8706
|
+
if (existsSync18(reviewDir)) {
|
|
8707
|
+
for (const f of readdirSync7(reviewDir)) {
|
|
7773
8708
|
if (f.startsWith("review-") && f.endsWith(".md")) {
|
|
7774
8709
|
try {
|
|
7775
|
-
|
|
8710
|
+
unlinkSync11(path22.join(reviewDir, f));
|
|
7776
8711
|
} catch {
|
|
7777
8712
|
}
|
|
7778
8713
|
}
|
|
@@ -7818,12 +8753,12 @@ async function boot(options) {
|
|
|
7818
8753
|
});
|
|
7819
8754
|
const taskFile = String(r.task_file);
|
|
7820
8755
|
try {
|
|
7821
|
-
const filePath =
|
|
7822
|
-
if (
|
|
7823
|
-
let content =
|
|
8756
|
+
const filePath = path22.join(process.cwd(), taskFile);
|
|
8757
|
+
if (existsSync18(filePath)) {
|
|
8758
|
+
let content = readFileSync15(filePath, "utf8");
|
|
7824
8759
|
content = content.replace(/\*\*Status:\*\* needs_review/, "**Status:** done");
|
|
7825
|
-
const { writeFileSync:
|
|
7826
|
-
|
|
8760
|
+
const { writeFileSync: writeFileSync12 } = await import("fs");
|
|
8761
|
+
writeFileSync12(filePath, content);
|
|
7827
8762
|
}
|
|
7828
8763
|
} catch {
|
|
7829
8764
|
}
|
|
@@ -8154,13 +9089,13 @@ async function boot(options) {
|
|
|
8154
9089
|
}));
|
|
8155
9090
|
const assignedResult = await client.execute({
|
|
8156
9091
|
sql: `SELECT COUNT(*) as cnt FROM tasks
|
|
8157
|
-
WHERE project_name = ? AND
|
|
9092
|
+
WHERE project_name = ? AND assigned_by = ?
|
|
8158
9093
|
AND created_at > datetime('now', '-7 days')${pScope.sql}`,
|
|
8159
9094
|
args: [p.projectName, coordinatorName, ...pScope.args]
|
|
8160
9095
|
});
|
|
8161
9096
|
const tasksAssigned = Number(assignedResult.rows[0]?.cnt) || 0;
|
|
8162
9097
|
if (tasksAssigned > 0) {
|
|
8163
|
-
const exeEntry = activity.find((a) => a.name === coordinatorName || a.name
|
|
9098
|
+
const exeEntry = activity.find((a) => a.name === coordinatorName || isCoordinatorName(a.name));
|
|
8164
9099
|
if (exeEntry) {
|
|
8165
9100
|
exeEntry.tasksAssigned = tasksAssigned;
|
|
8166
9101
|
} else {
|
|
@@ -8184,7 +9119,7 @@ async function boot(options) {
|
|
|
8184
9119
|
const projName = s.projectDir.split("/").pop() ?? "";
|
|
8185
9120
|
const proj = projects.find((p) => p.projectName === projName);
|
|
8186
9121
|
if (!proj || proj.sessionName) continue;
|
|
8187
|
-
if (s.agentId === coordinatorName || s.agentId
|
|
9122
|
+
if (s.agentId === coordinatorName || isCoordinatorName(s.agentId)) {
|
|
8188
9123
|
proj.sessionName = s.windowName;
|
|
8189
9124
|
} else if (s.parentExe) {
|
|
8190
9125
|
proj.sessionName = s.parentExe;
|
|
@@ -8268,8 +9203,8 @@ async function boot(options) {
|
|
|
8268
9203
|
updatedAt: String(row.updated_at)
|
|
8269
9204
|
}));
|
|
8270
9205
|
try {
|
|
8271
|
-
const { execSync:
|
|
8272
|
-
const gitLog =
|
|
9206
|
+
const { execSync: execSync10 } = await import("child_process");
|
|
9207
|
+
const gitLog = execSync10(
|
|
8273
9208
|
"git log --oneline -10 --grep='Co-Authored-By: Claude' --grep='task(' --all-match 2>/dev/null || git log --oneline -5 --grep='Co-Authored-By: Claude' 2>/dev/null || git log --oneline -5 --grep='^task(' 2>/dev/null",
|
|
8274
9209
|
{
|
|
8275
9210
|
encoding: "utf8",
|
|
@@ -8292,19 +9227,19 @@ async function boot(options) {
|
|
|
8292
9227
|
})()
|
|
8293
9228
|
]);
|
|
8294
9229
|
try {
|
|
8295
|
-
const configPath =
|
|
8296
|
-
process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
9230
|
+
const configPath = path22.join(
|
|
9231
|
+
process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path22.join(os9.homedir(), ".exe-os"),
|
|
8297
9232
|
"config.json"
|
|
8298
9233
|
);
|
|
8299
|
-
if (
|
|
8300
|
-
const raw = JSON.parse(
|
|
9234
|
+
if (existsSync18(configPath)) {
|
|
9235
|
+
const raw = JSON.parse(readFileSync15(configPath, "utf8"));
|
|
8301
9236
|
briefData.cloudConnected = !!(raw.cloud || raw.turso);
|
|
8302
9237
|
}
|
|
8303
9238
|
} catch {
|
|
8304
9239
|
}
|
|
8305
9240
|
try {
|
|
8306
|
-
const backfillFlagPath =
|
|
8307
|
-
const isBackfillNeeded = () =>
|
|
9241
|
+
const backfillFlagPath = path22.join(EXE_AI_DIR, "session-cache", "needs-backfill");
|
|
9242
|
+
const isBackfillNeeded = () => existsSync18(backfillFlagPath);
|
|
8308
9243
|
const coverageResult = await client.execute({
|
|
8309
9244
|
sql: `SELECT COUNT(*) as total,
|
|
8310
9245
|
SUM(CASE WHEN vector IS NOT NULL THEN 1 ELSE 0 END) as with_vectors
|
|
@@ -8326,16 +9261,16 @@ async function boot(options) {
|
|
|
8326
9261
|
let daemonRunning = false;
|
|
8327
9262
|
let daemonUptime;
|
|
8328
9263
|
let daemonRequestsServed;
|
|
8329
|
-
const socketPath =
|
|
8330
|
-
if (
|
|
9264
|
+
const socketPath = path22.join(EXE_AI_DIR, "exed.sock");
|
|
9265
|
+
if (existsSync18(socketPath)) {
|
|
8331
9266
|
try {
|
|
8332
|
-
const
|
|
9267
|
+
const net2 = await import("net");
|
|
8333
9268
|
const health = await new Promise((resolve) => {
|
|
8334
9269
|
const timer = setTimeout(() => {
|
|
8335
9270
|
sock.destroy();
|
|
8336
9271
|
resolve(null);
|
|
8337
9272
|
}, 2e3);
|
|
8338
|
-
const sock =
|
|
9273
|
+
const sock = net2.createConnection({ path: socketPath });
|
|
8339
9274
|
let buf = "";
|
|
8340
9275
|
sock.on("connect", () => {
|
|
8341
9276
|
sock.write(JSON.stringify({ id: "boot-health", type: "health" }) + "\n");
|
|
@@ -8369,10 +9304,10 @@ async function boot(options) {
|
|
|
8369
9304
|
}
|
|
8370
9305
|
}
|
|
8371
9306
|
if (!daemonRunning) {
|
|
8372
|
-
const pidPath =
|
|
8373
|
-
if (
|
|
9307
|
+
const pidPath = path22.join(EXE_AI_DIR, "exed.pid");
|
|
9308
|
+
if (existsSync18(pidPath)) {
|
|
8374
9309
|
try {
|
|
8375
|
-
const pid = parseInt(
|
|
9310
|
+
const pid = parseInt(readFileSync15(pidPath, "utf8").trim(), 10);
|
|
8376
9311
|
if (pid > 0) {
|
|
8377
9312
|
process.kill(pid, 0);
|
|
8378
9313
|
daemonRunning = true;
|
|
@@ -8383,10 +9318,10 @@ async function boot(options) {
|
|
|
8383
9318
|
}
|
|
8384
9319
|
if (nullCount === 0) {
|
|
8385
9320
|
try {
|
|
8386
|
-
const flagPath =
|
|
8387
|
-
if (
|
|
8388
|
-
const { unlinkSync:
|
|
8389
|
-
|
|
9321
|
+
const flagPath = path22.join(EXE_AI_DIR, "session-cache", "needs-backfill");
|
|
9322
|
+
if (existsSync18(flagPath)) {
|
|
9323
|
+
const { unlinkSync: unlinkSync12 } = await import("fs");
|
|
9324
|
+
unlinkSync12(flagPath);
|
|
8390
9325
|
}
|
|
8391
9326
|
} catch {
|
|
8392
9327
|
}
|
|
@@ -8407,26 +9342,26 @@ async function boot(options) {
|
|
|
8407
9342
|
if (!tryAcquireWorkerSlot2()) {
|
|
8408
9343
|
process.stderr.write("[exe-boot] Backfill needed but worker gate full \u2014 skipping\n");
|
|
8409
9344
|
} else {
|
|
8410
|
-
const { spawn } = await import("child_process");
|
|
8411
|
-
const { fileURLToPath:
|
|
8412
|
-
const thisFile =
|
|
8413
|
-
const backfillPath =
|
|
8414
|
-
if (
|
|
8415
|
-
const { openSync:
|
|
8416
|
-
const workerLogPath =
|
|
9345
|
+
const { spawn: spawn2 } = await import("child_process");
|
|
9346
|
+
const { fileURLToPath: fileURLToPath4 } = await import("url");
|
|
9347
|
+
const thisFile = fileURLToPath4(import.meta.url);
|
|
9348
|
+
const backfillPath = path22.resolve(path22.dirname(thisFile), "backfill-vectors.js");
|
|
9349
|
+
if (existsSync18(backfillPath)) {
|
|
9350
|
+
const { openSync: openSync3, closeSync: closeSync3 } = await import("fs");
|
|
9351
|
+
const workerLogPath = path22.join(EXE_AI_DIR, "workers.log");
|
|
8417
9352
|
let stderrFd = "ignore";
|
|
8418
9353
|
try {
|
|
8419
|
-
stderrFd =
|
|
9354
|
+
stderrFd = openSync3(workerLogPath, "a");
|
|
8420
9355
|
} catch {
|
|
8421
9356
|
}
|
|
8422
|
-
const child =
|
|
9357
|
+
const child = spawn2(process.execPath, [backfillPath], {
|
|
8423
9358
|
detached: true,
|
|
8424
9359
|
stdio: ["ignore", "ignore", stderrFd]
|
|
8425
9360
|
});
|
|
8426
9361
|
child.unref();
|
|
8427
9362
|
if (child.pid) registerWorkerPid2(child.pid);
|
|
8428
9363
|
if (typeof stderrFd === "number") try {
|
|
8429
|
-
|
|
9364
|
+
closeSync3(stderrFd);
|
|
8430
9365
|
} catch {
|
|
8431
9366
|
}
|
|
8432
9367
|
briefData.embedding.backfillRunning = true;
|
|
@@ -8438,13 +9373,13 @@ async function boot(options) {
|
|
|
8438
9373
|
} catch {
|
|
8439
9374
|
}
|
|
8440
9375
|
try {
|
|
8441
|
-
const { fileURLToPath:
|
|
8442
|
-
const thisFile =
|
|
9376
|
+
const { fileURLToPath: fileURLToPath4 } = await import("url");
|
|
9377
|
+
const thisFile = fileURLToPath4(import.meta.url);
|
|
8443
9378
|
const criticalBinaries = ["backfill-vectors.js", "scan-tasks.js"];
|
|
8444
9379
|
const missing = [];
|
|
8445
9380
|
for (const bin of criticalBinaries) {
|
|
8446
|
-
const binPath =
|
|
8447
|
-
if (!
|
|
9381
|
+
const binPath = path22.resolve(path22.dirname(thisFile), bin);
|
|
9382
|
+
if (!existsSync18(binPath)) {
|
|
8448
9383
|
missing.push(`dist/bin/${bin}`);
|
|
8449
9384
|
}
|
|
8450
9385
|
}
|
|
@@ -8473,7 +9408,7 @@ async function boot(options) {
|
|
|
8473
9408
|
console.log(brief);
|
|
8474
9409
|
return;
|
|
8475
9410
|
}
|
|
8476
|
-
const sessionDir =
|
|
9411
|
+
const sessionDir = path22.join(EXE_AI_DIR, "sessions", coordinatorName);
|
|
8477
9412
|
await mkdir5(sessionDir, { recursive: true });
|
|
8478
9413
|
const claudeMdContent = `${getSessionPrompt(coordinatorEmployee.systemPrompt)}
|
|
8479
9414
|
|
|
@@ -8482,7 +9417,7 @@ async function boot(options) {
|
|
|
8482
9417
|
# Status Brief
|
|
8483
9418
|
|
|
8484
9419
|
${brief}`;
|
|
8485
|
-
await writeFile6(
|
|
9420
|
+
await writeFile6(path22.join(sessionDir, "CLAUDE.md"), claudeMdContent, "utf-8");
|
|
8486
9421
|
const unread = await readUnreadNotifications();
|
|
8487
9422
|
if (unread.length > 0) {
|
|
8488
9423
|
console.log(`\u{1F4EC} ${unread.length} unread notification${unread.length === 1 ? "" : "s"}`);
|
|
@@ -8491,12 +9426,12 @@ ${brief}`;
|
|
|
8491
9426
|
await cleanupOldNotifications();
|
|
8492
9427
|
console.log(brief);
|
|
8493
9428
|
try {
|
|
8494
|
-
const configPath2 =
|
|
8495
|
-
process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
9429
|
+
const configPath2 = path22.join(
|
|
9430
|
+
process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path22.join(os9.homedir(), ".exe-os"),
|
|
8496
9431
|
"config.json"
|
|
8497
9432
|
);
|
|
8498
|
-
if (
|
|
8499
|
-
const rawCfg = JSON.parse(
|
|
9433
|
+
if (existsSync18(configPath2)) {
|
|
9434
|
+
const rawCfg = JSON.parse(readFileSync15(configPath2, "utf8"));
|
|
8500
9435
|
const cloudCfg = rawCfg.cloud;
|
|
8501
9436
|
if (cloudCfg?.apiKey) {
|
|
8502
9437
|
const { initSyncCrypto: initSyncCrypto2, isSyncCryptoInitialized: isSyncCryptoInitialized2 } = await Promise.resolve().then(() => (init_crypto(), crypto_exports));
|
|
@@ -8556,11 +9491,11 @@ async function updateIdleKillSuspectStreak(client, killsToday, liveSessions, tod
|
|
|
8556
9491
|
}
|
|
8557
9492
|
function runSplash() {
|
|
8558
9493
|
try {
|
|
8559
|
-
const { execSync:
|
|
9494
|
+
const { execSync: execSync10 } = __require("child_process");
|
|
8560
9495
|
const { loadConfigSync: loadConfigSync2 } = (init_config(), __toCommonJS(config_exports));
|
|
8561
9496
|
const config = loadConfigSync2();
|
|
8562
9497
|
if (!config.splashEffect) return;
|
|
8563
|
-
|
|
9498
|
+
execSync10(
|
|
8564
9499
|
'echo "EXE OS" | python3 -m terminaltexteffects decrypt --typing-speed 2 --ciphertext-colors 6B4C9A F5D76E --final-gradient-stops F5D76E F0EDE8 --final-gradient-direction vertical',
|
|
8565
9500
|
{ stdio: "inherit", timeout: 5e3 }
|
|
8566
9501
|
);
|