@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/runtime/index.js
CHANGED
|
@@ -544,18 +544,69 @@ var init_provider_table = __esm({
|
|
|
544
544
|
}
|
|
545
545
|
});
|
|
546
546
|
|
|
547
|
-
// src/lib/
|
|
548
|
-
|
|
547
|
+
// src/lib/runtime-table.ts
|
|
548
|
+
var RUNTIME_TABLE, DEFAULT_RUNTIME;
|
|
549
|
+
var init_runtime_table = __esm({
|
|
550
|
+
"src/lib/runtime-table.ts"() {
|
|
551
|
+
"use strict";
|
|
552
|
+
RUNTIME_TABLE = {
|
|
553
|
+
codex: {
|
|
554
|
+
binary: "codex",
|
|
555
|
+
launchMode: "exec",
|
|
556
|
+
autoApproveFlag: "--full-auto",
|
|
557
|
+
inlineFlag: "--no-alt-screen",
|
|
558
|
+
apiKeyEnv: "OPENAI_API_KEY",
|
|
559
|
+
defaultModel: "gpt-5.4"
|
|
560
|
+
}
|
|
561
|
+
};
|
|
562
|
+
DEFAULT_RUNTIME = "claude";
|
|
563
|
+
}
|
|
564
|
+
});
|
|
565
|
+
|
|
566
|
+
// src/lib/agent-config.ts
|
|
567
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync4, mkdirSync as mkdirSync2 } from "fs";
|
|
549
568
|
import path5 from "path";
|
|
569
|
+
function loadAgentConfig() {
|
|
570
|
+
if (!existsSync4(AGENT_CONFIG_PATH)) return {};
|
|
571
|
+
try {
|
|
572
|
+
return JSON.parse(readFileSync4(AGENT_CONFIG_PATH, "utf-8"));
|
|
573
|
+
} catch {
|
|
574
|
+
return {};
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
function getAgentRuntime(agentId) {
|
|
578
|
+
const config = loadAgentConfig();
|
|
579
|
+
const entry = config[agentId];
|
|
580
|
+
if (entry) return entry;
|
|
581
|
+
return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
|
|
582
|
+
}
|
|
583
|
+
var AGENT_CONFIG_PATH, DEFAULT_MODELS;
|
|
584
|
+
var init_agent_config = __esm({
|
|
585
|
+
"src/lib/agent-config.ts"() {
|
|
586
|
+
"use strict";
|
|
587
|
+
init_config();
|
|
588
|
+
init_runtime_table();
|
|
589
|
+
AGENT_CONFIG_PATH = path5.join(EXE_AI_DIR, "agent-config.json");
|
|
590
|
+
DEFAULT_MODELS = {
|
|
591
|
+
claude: "claude-opus-4",
|
|
592
|
+
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
593
|
+
opencode: "minimax-m2.7"
|
|
594
|
+
};
|
|
595
|
+
}
|
|
596
|
+
});
|
|
597
|
+
|
|
598
|
+
// src/lib/intercom-queue.ts
|
|
599
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, renameSync as renameSync3, existsSync as existsSync5, mkdirSync as mkdirSync3 } from "fs";
|
|
600
|
+
import path6 from "path";
|
|
550
601
|
import os5 from "os";
|
|
551
602
|
function ensureDir() {
|
|
552
|
-
const dir =
|
|
553
|
-
if (!
|
|
603
|
+
const dir = path6.dirname(QUEUE_PATH);
|
|
604
|
+
if (!existsSync5(dir)) mkdirSync3(dir, { recursive: true });
|
|
554
605
|
}
|
|
555
606
|
function readQueue() {
|
|
556
607
|
try {
|
|
557
|
-
if (!
|
|
558
|
-
return JSON.parse(
|
|
608
|
+
if (!existsSync5(QUEUE_PATH)) return [];
|
|
609
|
+
return JSON.parse(readFileSync5(QUEUE_PATH, "utf8"));
|
|
559
610
|
} catch {
|
|
560
611
|
return [];
|
|
561
612
|
}
|
|
@@ -563,7 +614,7 @@ function readQueue() {
|
|
|
563
614
|
function writeQueue(queue) {
|
|
564
615
|
ensureDir();
|
|
565
616
|
const tmp = `${QUEUE_PATH}.tmp`;
|
|
566
|
-
|
|
617
|
+
writeFileSync4(tmp, JSON.stringify(queue, null, 2));
|
|
567
618
|
renameSync3(tmp, QUEUE_PATH);
|
|
568
619
|
}
|
|
569
620
|
function queueIntercom(targetSession, reason) {
|
|
@@ -587,9 +638,9 @@ var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
|
|
|
587
638
|
var init_intercom_queue = __esm({
|
|
588
639
|
"src/lib/intercom-queue.ts"() {
|
|
589
640
|
"use strict";
|
|
590
|
-
QUEUE_PATH =
|
|
641
|
+
QUEUE_PATH = path6.join(os5.homedir(), ".exe-os", "intercom-queue.json");
|
|
591
642
|
TTL_MS = 60 * 60 * 1e3;
|
|
592
|
-
INTERCOM_LOG =
|
|
643
|
+
INTERCOM_LOG = path6.join(os5.homedir(), ".exe-os", "intercom.log");
|
|
593
644
|
}
|
|
594
645
|
});
|
|
595
646
|
|
|
@@ -648,6 +699,443 @@ var init_db_retry = __esm({
|
|
|
648
699
|
}
|
|
649
700
|
});
|
|
650
701
|
|
|
702
|
+
// src/lib/exe-daemon-client.ts
|
|
703
|
+
import net from "net";
|
|
704
|
+
import { spawn } from "child_process";
|
|
705
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
706
|
+
import { existsSync as existsSync6, unlinkSync as unlinkSync2, readFileSync as readFileSync6, openSync, closeSync, statSync } from "fs";
|
|
707
|
+
import path7 from "path";
|
|
708
|
+
import { fileURLToPath } from "url";
|
|
709
|
+
function handleData(chunk) {
|
|
710
|
+
_buffer += chunk.toString();
|
|
711
|
+
if (_buffer.length > MAX_BUFFER) {
|
|
712
|
+
_buffer = "";
|
|
713
|
+
return;
|
|
714
|
+
}
|
|
715
|
+
let newlineIdx;
|
|
716
|
+
while ((newlineIdx = _buffer.indexOf("\n")) !== -1) {
|
|
717
|
+
const line = _buffer.slice(0, newlineIdx).trim();
|
|
718
|
+
_buffer = _buffer.slice(newlineIdx + 1);
|
|
719
|
+
if (!line) continue;
|
|
720
|
+
try {
|
|
721
|
+
const response = JSON.parse(line);
|
|
722
|
+
const id = response.id;
|
|
723
|
+
if (!id) continue;
|
|
724
|
+
const entry = _pending.get(id);
|
|
725
|
+
if (entry) {
|
|
726
|
+
clearTimeout(entry.timer);
|
|
727
|
+
_pending.delete(id);
|
|
728
|
+
entry.resolve(response);
|
|
729
|
+
}
|
|
730
|
+
} catch {
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
function cleanupStaleFiles() {
|
|
735
|
+
if (existsSync6(PID_PATH)) {
|
|
736
|
+
try {
|
|
737
|
+
const pid = parseInt(readFileSync6(PID_PATH, "utf8").trim(), 10);
|
|
738
|
+
if (pid > 0) {
|
|
739
|
+
try {
|
|
740
|
+
process.kill(pid, 0);
|
|
741
|
+
return;
|
|
742
|
+
} catch {
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
} catch {
|
|
746
|
+
}
|
|
747
|
+
try {
|
|
748
|
+
unlinkSync2(PID_PATH);
|
|
749
|
+
} catch {
|
|
750
|
+
}
|
|
751
|
+
try {
|
|
752
|
+
unlinkSync2(SOCKET_PATH);
|
|
753
|
+
} catch {
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
function findPackageRoot() {
|
|
758
|
+
let dir = path7.dirname(fileURLToPath(import.meta.url));
|
|
759
|
+
const { root } = path7.parse(dir);
|
|
760
|
+
while (dir !== root) {
|
|
761
|
+
if (existsSync6(path7.join(dir, "package.json"))) return dir;
|
|
762
|
+
dir = path7.dirname(dir);
|
|
763
|
+
}
|
|
764
|
+
return null;
|
|
765
|
+
}
|
|
766
|
+
function spawnDaemon() {
|
|
767
|
+
const pkgRoot = findPackageRoot();
|
|
768
|
+
if (!pkgRoot) {
|
|
769
|
+
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
770
|
+
return;
|
|
771
|
+
}
|
|
772
|
+
const daemonPath = path7.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
773
|
+
if (!existsSync6(daemonPath)) {
|
|
774
|
+
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
775
|
+
`);
|
|
776
|
+
return;
|
|
777
|
+
}
|
|
778
|
+
const resolvedPath = daemonPath;
|
|
779
|
+
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
780
|
+
`);
|
|
781
|
+
const logPath = path7.join(path7.dirname(SOCKET_PATH), "exed.log");
|
|
782
|
+
let stderrFd = "ignore";
|
|
783
|
+
try {
|
|
784
|
+
stderrFd = openSync(logPath, "a");
|
|
785
|
+
} catch {
|
|
786
|
+
}
|
|
787
|
+
const child = spawn(process.execPath, [resolvedPath], {
|
|
788
|
+
detached: true,
|
|
789
|
+
stdio: ["ignore", "ignore", stderrFd],
|
|
790
|
+
env: {
|
|
791
|
+
...process.env,
|
|
792
|
+
TMUX: void 0,
|
|
793
|
+
// Daemon is global — must not inherit session scope
|
|
794
|
+
TMUX_PANE: void 0,
|
|
795
|
+
// Prevents resolveExeSession() from scoping to one session
|
|
796
|
+
EXE_DAEMON_SOCK: SOCKET_PATH,
|
|
797
|
+
EXE_DAEMON_PID: PID_PATH
|
|
798
|
+
}
|
|
799
|
+
});
|
|
800
|
+
child.unref();
|
|
801
|
+
if (typeof stderrFd === "number") {
|
|
802
|
+
try {
|
|
803
|
+
closeSync(stderrFd);
|
|
804
|
+
} catch {
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
function acquireSpawnLock() {
|
|
809
|
+
try {
|
|
810
|
+
const fd = openSync(SPAWN_LOCK_PATH, "wx");
|
|
811
|
+
closeSync(fd);
|
|
812
|
+
return true;
|
|
813
|
+
} catch {
|
|
814
|
+
try {
|
|
815
|
+
const stat = statSync(SPAWN_LOCK_PATH);
|
|
816
|
+
if (Date.now() - stat.mtimeMs > SPAWN_LOCK_STALE_MS) {
|
|
817
|
+
try {
|
|
818
|
+
unlinkSync2(SPAWN_LOCK_PATH);
|
|
819
|
+
} catch {
|
|
820
|
+
}
|
|
821
|
+
try {
|
|
822
|
+
const fd = openSync(SPAWN_LOCK_PATH, "wx");
|
|
823
|
+
closeSync(fd);
|
|
824
|
+
return true;
|
|
825
|
+
} catch {
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
} catch {
|
|
829
|
+
}
|
|
830
|
+
return false;
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
function releaseSpawnLock() {
|
|
834
|
+
try {
|
|
835
|
+
unlinkSync2(SPAWN_LOCK_PATH);
|
|
836
|
+
} catch {
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
function connectToSocket() {
|
|
840
|
+
return new Promise((resolve) => {
|
|
841
|
+
if (_socket && _connected) {
|
|
842
|
+
resolve(true);
|
|
843
|
+
return;
|
|
844
|
+
}
|
|
845
|
+
const socket = net.createConnection({ path: SOCKET_PATH });
|
|
846
|
+
const connectTimeout = setTimeout(() => {
|
|
847
|
+
socket.destroy();
|
|
848
|
+
resolve(false);
|
|
849
|
+
}, 2e3);
|
|
850
|
+
socket.on("connect", () => {
|
|
851
|
+
clearTimeout(connectTimeout);
|
|
852
|
+
_socket = socket;
|
|
853
|
+
_connected = true;
|
|
854
|
+
_buffer = "";
|
|
855
|
+
socket.on("data", handleData);
|
|
856
|
+
socket.on("close", () => {
|
|
857
|
+
_connected = false;
|
|
858
|
+
_socket = null;
|
|
859
|
+
for (const [id, entry] of _pending) {
|
|
860
|
+
clearTimeout(entry.timer);
|
|
861
|
+
_pending.delete(id);
|
|
862
|
+
entry.resolve({ error: "Connection closed" });
|
|
863
|
+
}
|
|
864
|
+
});
|
|
865
|
+
socket.on("error", () => {
|
|
866
|
+
_connected = false;
|
|
867
|
+
_socket = null;
|
|
868
|
+
});
|
|
869
|
+
resolve(true);
|
|
870
|
+
});
|
|
871
|
+
socket.on("error", () => {
|
|
872
|
+
clearTimeout(connectTimeout);
|
|
873
|
+
resolve(false);
|
|
874
|
+
});
|
|
875
|
+
});
|
|
876
|
+
}
|
|
877
|
+
async function connectEmbedDaemon() {
|
|
878
|
+
if (_socket && _connected) return true;
|
|
879
|
+
if (await connectToSocket()) return true;
|
|
880
|
+
if (acquireSpawnLock()) {
|
|
881
|
+
try {
|
|
882
|
+
cleanupStaleFiles();
|
|
883
|
+
spawnDaemon();
|
|
884
|
+
} finally {
|
|
885
|
+
releaseSpawnLock();
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
const start = Date.now();
|
|
889
|
+
let delay2 = 100;
|
|
890
|
+
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
891
|
+
await new Promise((r) => setTimeout(r, delay2));
|
|
892
|
+
if (await connectToSocket()) return true;
|
|
893
|
+
delay2 = Math.min(delay2 * 2, 3e3);
|
|
894
|
+
}
|
|
895
|
+
return false;
|
|
896
|
+
}
|
|
897
|
+
function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
898
|
+
return new Promise((resolve) => {
|
|
899
|
+
if (!_socket || !_connected) {
|
|
900
|
+
resolve({ error: "Not connected" });
|
|
901
|
+
return;
|
|
902
|
+
}
|
|
903
|
+
const id = randomUUID2();
|
|
904
|
+
const timer = setTimeout(() => {
|
|
905
|
+
_pending.delete(id);
|
|
906
|
+
resolve({ error: "Request timeout" });
|
|
907
|
+
}, timeoutMs);
|
|
908
|
+
_pending.set(id, { resolve, timer });
|
|
909
|
+
try {
|
|
910
|
+
_socket.write(JSON.stringify({ id, ...payload }) + "\n");
|
|
911
|
+
} catch {
|
|
912
|
+
clearTimeout(timer);
|
|
913
|
+
_pending.delete(id);
|
|
914
|
+
resolve({ error: "Write failed" });
|
|
915
|
+
}
|
|
916
|
+
});
|
|
917
|
+
}
|
|
918
|
+
function isClientConnected() {
|
|
919
|
+
return _connected;
|
|
920
|
+
}
|
|
921
|
+
var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _pending, MAX_BUFFER;
|
|
922
|
+
var init_exe_daemon_client = __esm({
|
|
923
|
+
"src/lib/exe-daemon-client.ts"() {
|
|
924
|
+
"use strict";
|
|
925
|
+
init_config();
|
|
926
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path7.join(EXE_AI_DIR, "exed.sock");
|
|
927
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path7.join(EXE_AI_DIR, "exed.pid");
|
|
928
|
+
SPAWN_LOCK_PATH = path7.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
929
|
+
SPAWN_LOCK_STALE_MS = 3e4;
|
|
930
|
+
CONNECT_TIMEOUT_MS = 15e3;
|
|
931
|
+
REQUEST_TIMEOUT_MS = 3e4;
|
|
932
|
+
_socket = null;
|
|
933
|
+
_connected = false;
|
|
934
|
+
_buffer = "";
|
|
935
|
+
_pending = /* @__PURE__ */ new Map();
|
|
936
|
+
MAX_BUFFER = 1e7;
|
|
937
|
+
}
|
|
938
|
+
});
|
|
939
|
+
|
|
940
|
+
// src/lib/daemon-protocol.ts
|
|
941
|
+
function serializeValue(v) {
|
|
942
|
+
if (v === null || v === void 0) return null;
|
|
943
|
+
if (typeof v === "bigint") return Number(v);
|
|
944
|
+
if (typeof v === "boolean") return v ? 1 : 0;
|
|
945
|
+
if (v instanceof Uint8Array) {
|
|
946
|
+
return { __blob: Buffer.from(v).toString("base64") };
|
|
947
|
+
}
|
|
948
|
+
if (ArrayBuffer.isView(v)) {
|
|
949
|
+
return { __blob: Buffer.from(v.buffer, v.byteOffset, v.byteLength).toString("base64") };
|
|
950
|
+
}
|
|
951
|
+
if (v instanceof ArrayBuffer) {
|
|
952
|
+
return { __blob: Buffer.from(v).toString("base64") };
|
|
953
|
+
}
|
|
954
|
+
if (typeof v === "string" || typeof v === "number") return v;
|
|
955
|
+
return String(v);
|
|
956
|
+
}
|
|
957
|
+
function deserializeValue(v) {
|
|
958
|
+
if (v === null) return null;
|
|
959
|
+
if (typeof v === "object" && v !== null && "__blob" in v) {
|
|
960
|
+
const buf = Buffer.from(v.__blob, "base64");
|
|
961
|
+
return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
|
|
962
|
+
}
|
|
963
|
+
return v;
|
|
964
|
+
}
|
|
965
|
+
function deserializeResultSet(srs) {
|
|
966
|
+
const rows = srs.rows.map((obj) => {
|
|
967
|
+
const values = srs.columns.map(
|
|
968
|
+
(col) => deserializeValue(obj[col] ?? null)
|
|
969
|
+
);
|
|
970
|
+
const row = values;
|
|
971
|
+
for (let i = 0; i < srs.columns.length; i++) {
|
|
972
|
+
const col = srs.columns[i];
|
|
973
|
+
if (col !== void 0) {
|
|
974
|
+
row[col] = values[i] ?? null;
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
Object.defineProperty(row, "length", {
|
|
978
|
+
value: values.length,
|
|
979
|
+
enumerable: false
|
|
980
|
+
});
|
|
981
|
+
return row;
|
|
982
|
+
});
|
|
983
|
+
return {
|
|
984
|
+
columns: srs.columns,
|
|
985
|
+
columnTypes: srs.columnTypes ?? [],
|
|
986
|
+
rows,
|
|
987
|
+
rowsAffected: srs.rowsAffected,
|
|
988
|
+
lastInsertRowid: srs.lastInsertRowid != null ? BigInt(srs.lastInsertRowid) : void 0,
|
|
989
|
+
toJSON: () => ({
|
|
990
|
+
columns: srs.columns,
|
|
991
|
+
columnTypes: srs.columnTypes ?? [],
|
|
992
|
+
rows: srs.rows,
|
|
993
|
+
rowsAffected: srs.rowsAffected,
|
|
994
|
+
lastInsertRowid: srs.lastInsertRowid
|
|
995
|
+
})
|
|
996
|
+
};
|
|
997
|
+
}
|
|
998
|
+
var init_daemon_protocol = __esm({
|
|
999
|
+
"src/lib/daemon-protocol.ts"() {
|
|
1000
|
+
"use strict";
|
|
1001
|
+
}
|
|
1002
|
+
});
|
|
1003
|
+
|
|
1004
|
+
// src/lib/db-daemon-client.ts
|
|
1005
|
+
var db_daemon_client_exports = {};
|
|
1006
|
+
__export(db_daemon_client_exports, {
|
|
1007
|
+
createDaemonDbClient: () => createDaemonDbClient,
|
|
1008
|
+
initDaemonDbClient: () => initDaemonDbClient
|
|
1009
|
+
});
|
|
1010
|
+
function normalizeStatement(stmt) {
|
|
1011
|
+
if (typeof stmt === "string") {
|
|
1012
|
+
return { sql: stmt, args: [] };
|
|
1013
|
+
}
|
|
1014
|
+
const sql = stmt.sql;
|
|
1015
|
+
let args = [];
|
|
1016
|
+
if (Array.isArray(stmt.args)) {
|
|
1017
|
+
args = stmt.args.map((v) => serializeValue(v));
|
|
1018
|
+
} else if (stmt.args && typeof stmt.args === "object") {
|
|
1019
|
+
const named = {};
|
|
1020
|
+
for (const [key, val] of Object.entries(stmt.args)) {
|
|
1021
|
+
named[key] = serializeValue(val);
|
|
1022
|
+
}
|
|
1023
|
+
return { sql, args: named };
|
|
1024
|
+
}
|
|
1025
|
+
return { sql, args };
|
|
1026
|
+
}
|
|
1027
|
+
function createDaemonDbClient(fallbackClient) {
|
|
1028
|
+
let _useDaemon = false;
|
|
1029
|
+
const client = {
|
|
1030
|
+
async execute(stmt) {
|
|
1031
|
+
if (!_useDaemon || !isClientConnected()) {
|
|
1032
|
+
return fallbackClient.execute(stmt);
|
|
1033
|
+
}
|
|
1034
|
+
const { sql, args } = normalizeStatement(stmt);
|
|
1035
|
+
const response = await sendDaemonRequest({
|
|
1036
|
+
type: "db-execute",
|
|
1037
|
+
sql,
|
|
1038
|
+
args
|
|
1039
|
+
});
|
|
1040
|
+
if (response.error) {
|
|
1041
|
+
const errMsg = String(response.error);
|
|
1042
|
+
if (errMsg === "Not connected" || errMsg === "Request timeout" || errMsg === "Write failed") {
|
|
1043
|
+
process.stderr.write(`[db-daemon] Transport error (${errMsg}), falling back to direct
|
|
1044
|
+
`);
|
|
1045
|
+
return fallbackClient.execute(stmt);
|
|
1046
|
+
}
|
|
1047
|
+
throw new Error(errMsg);
|
|
1048
|
+
}
|
|
1049
|
+
if (response.db) {
|
|
1050
|
+
return deserializeResultSet(response.db);
|
|
1051
|
+
}
|
|
1052
|
+
process.stderr.write("[db-daemon] Unexpected response shape, falling back to direct\n");
|
|
1053
|
+
return fallbackClient.execute(stmt);
|
|
1054
|
+
},
|
|
1055
|
+
async batch(stmts, mode) {
|
|
1056
|
+
if (!_useDaemon || !isClientConnected()) {
|
|
1057
|
+
return fallbackClient.batch(stmts, mode);
|
|
1058
|
+
}
|
|
1059
|
+
const statements = stmts.map(normalizeStatement);
|
|
1060
|
+
const response = await sendDaemonRequest({
|
|
1061
|
+
type: "db-batch",
|
|
1062
|
+
statements,
|
|
1063
|
+
mode: mode ?? "deferred"
|
|
1064
|
+
});
|
|
1065
|
+
if (response.error) {
|
|
1066
|
+
const errMsg = String(response.error);
|
|
1067
|
+
if (errMsg === "Not connected" || errMsg === "Request timeout" || errMsg === "Write failed") {
|
|
1068
|
+
process.stderr.write(`[db-daemon] Batch transport error (${errMsg}), falling back to direct
|
|
1069
|
+
`);
|
|
1070
|
+
return fallbackClient.batch(stmts, mode);
|
|
1071
|
+
}
|
|
1072
|
+
throw new Error(errMsg);
|
|
1073
|
+
}
|
|
1074
|
+
const batchResults = response["db-batch"];
|
|
1075
|
+
if (batchResults) {
|
|
1076
|
+
return batchResults.map(deserializeResultSet);
|
|
1077
|
+
}
|
|
1078
|
+
process.stderr.write("[db-daemon] Unexpected batch response shape, falling back to direct\n");
|
|
1079
|
+
return fallbackClient.batch(stmts, mode);
|
|
1080
|
+
},
|
|
1081
|
+
// Transaction support — delegate to fallback (transactions need direct connection)
|
|
1082
|
+
async transaction(mode) {
|
|
1083
|
+
return fallbackClient.transaction(mode);
|
|
1084
|
+
},
|
|
1085
|
+
// executeMultiple — delegate to fallback (used only for schema migrations)
|
|
1086
|
+
async executeMultiple(sql) {
|
|
1087
|
+
return fallbackClient.executeMultiple(sql);
|
|
1088
|
+
},
|
|
1089
|
+
// migrate — delegate to fallback
|
|
1090
|
+
async migrate(stmts) {
|
|
1091
|
+
return fallbackClient.migrate(stmts);
|
|
1092
|
+
},
|
|
1093
|
+
// Sync mode — delegate to fallback
|
|
1094
|
+
sync() {
|
|
1095
|
+
return fallbackClient.sync();
|
|
1096
|
+
},
|
|
1097
|
+
close() {
|
|
1098
|
+
_useDaemon = false;
|
|
1099
|
+
},
|
|
1100
|
+
get closed() {
|
|
1101
|
+
return fallbackClient.closed;
|
|
1102
|
+
},
|
|
1103
|
+
get protocol() {
|
|
1104
|
+
return fallbackClient.protocol;
|
|
1105
|
+
}
|
|
1106
|
+
};
|
|
1107
|
+
return {
|
|
1108
|
+
...client,
|
|
1109
|
+
/** Enable daemon routing (call after confirming daemon is connected) */
|
|
1110
|
+
_enableDaemon() {
|
|
1111
|
+
_useDaemon = true;
|
|
1112
|
+
},
|
|
1113
|
+
/** Check if daemon routing is active */
|
|
1114
|
+
_isDaemonActive() {
|
|
1115
|
+
return _useDaemon && isClientConnected();
|
|
1116
|
+
}
|
|
1117
|
+
};
|
|
1118
|
+
}
|
|
1119
|
+
async function initDaemonDbClient(fallbackClient) {
|
|
1120
|
+
if (process.env.EXE_IS_DAEMON === "1") return null;
|
|
1121
|
+
const connected = await connectEmbedDaemon();
|
|
1122
|
+
if (!connected) {
|
|
1123
|
+
process.stderr.write("[db-daemon] Daemon unavailable \u2014 using direct SQLite\n");
|
|
1124
|
+
return null;
|
|
1125
|
+
}
|
|
1126
|
+
const client = createDaemonDbClient(fallbackClient);
|
|
1127
|
+
client._enableDaemon();
|
|
1128
|
+
process.stderr.write("[db-daemon] DB routing through daemon (single-writer)\n");
|
|
1129
|
+
return client;
|
|
1130
|
+
}
|
|
1131
|
+
var init_db_daemon_client = __esm({
|
|
1132
|
+
"src/lib/db-daemon-client.ts"() {
|
|
1133
|
+
"use strict";
|
|
1134
|
+
init_exe_daemon_client();
|
|
1135
|
+
init_daemon_protocol();
|
|
1136
|
+
}
|
|
1137
|
+
});
|
|
1138
|
+
|
|
651
1139
|
// src/lib/database.ts
|
|
652
1140
|
var database_exports = {};
|
|
653
1141
|
__export(database_exports, {
|
|
@@ -656,6 +1144,7 @@ __export(database_exports, {
|
|
|
656
1144
|
ensureSchema: () => ensureSchema,
|
|
657
1145
|
getClient: () => getClient,
|
|
658
1146
|
getRawClient: () => getRawClient,
|
|
1147
|
+
initDaemonClient: () => initDaemonClient,
|
|
659
1148
|
initDatabase: () => initDatabase,
|
|
660
1149
|
initTurso: () => initTurso,
|
|
661
1150
|
isInitialized: () => isInitialized
|
|
@@ -683,8 +1172,27 @@ function getClient() {
|
|
|
683
1172
|
if (!_resilientClient) {
|
|
684
1173
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
685
1174
|
}
|
|
1175
|
+
if (process.env.EXE_IS_DAEMON === "1") {
|
|
1176
|
+
return _resilientClient;
|
|
1177
|
+
}
|
|
1178
|
+
if (_daemonClient && _daemonClient._isDaemonActive()) {
|
|
1179
|
+
return _daemonClient;
|
|
1180
|
+
}
|
|
686
1181
|
return _resilientClient;
|
|
687
1182
|
}
|
|
1183
|
+
async function initDaemonClient() {
|
|
1184
|
+
if (process.env.EXE_IS_DAEMON === "1") return;
|
|
1185
|
+
if (!_resilientClient) return;
|
|
1186
|
+
try {
|
|
1187
|
+
const { initDaemonDbClient: initDaemonDbClient2 } = await Promise.resolve().then(() => (init_db_daemon_client(), db_daemon_client_exports));
|
|
1188
|
+
_daemonClient = await initDaemonDbClient2(_resilientClient);
|
|
1189
|
+
} catch (err) {
|
|
1190
|
+
process.stderr.write(
|
|
1191
|
+
`[database] Daemon client init failed (non-fatal): ${err instanceof Error ? err.message : String(err)}
|
|
1192
|
+
`
|
|
1193
|
+
);
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
688
1196
|
function getRawClient() {
|
|
689
1197
|
if (!_client) {
|
|
690
1198
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
@@ -1171,6 +1679,12 @@ async function ensureSchema() {
|
|
|
1171
1679
|
} catch {
|
|
1172
1680
|
}
|
|
1173
1681
|
}
|
|
1682
|
+
try {
|
|
1683
|
+
await client.execute(
|
|
1684
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_content_hash ON memories(content_hash, agent_id)`
|
|
1685
|
+
);
|
|
1686
|
+
} catch {
|
|
1687
|
+
}
|
|
1174
1688
|
await client.executeMultiple(`
|
|
1175
1689
|
CREATE TABLE IF NOT EXISTS entities (
|
|
1176
1690
|
id TEXT PRIMARY KEY,
|
|
@@ -1223,7 +1737,30 @@ async function ensureSchema() {
|
|
|
1223
1737
|
entity_id TEXT NOT NULL,
|
|
1224
1738
|
PRIMARY KEY (hyperedge_id, entity_id)
|
|
1225
1739
|
);
|
|
1740
|
+
|
|
1741
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS entities_fts USING fts5(
|
|
1742
|
+
name,
|
|
1743
|
+
content=entities,
|
|
1744
|
+
content_rowid=rowid
|
|
1745
|
+
);
|
|
1746
|
+
|
|
1747
|
+
CREATE TRIGGER IF NOT EXISTS entities_fts_ai AFTER INSERT ON entities BEGIN
|
|
1748
|
+
INSERT INTO entities_fts(rowid, name) VALUES (new.rowid, new.name);
|
|
1749
|
+
END;
|
|
1750
|
+
|
|
1751
|
+
CREATE TRIGGER IF NOT EXISTS entities_fts_ad AFTER DELETE ON entities BEGIN
|
|
1752
|
+
INSERT INTO entities_fts(entities_fts, rowid, name) VALUES('delete', old.rowid, old.name);
|
|
1753
|
+
END;
|
|
1754
|
+
|
|
1755
|
+
CREATE TRIGGER IF NOT EXISTS entities_fts_au AFTER UPDATE ON entities BEGIN
|
|
1756
|
+
INSERT INTO entities_fts(entities_fts, rowid, name) VALUES('delete', old.rowid, old.name);
|
|
1757
|
+
INSERT INTO entities_fts(rowid, name) VALUES (new.rowid, new.name);
|
|
1758
|
+
END;
|
|
1226
1759
|
`);
|
|
1760
|
+
try {
|
|
1761
|
+
await client.execute("INSERT INTO entities_fts(entities_fts) VALUES('rebuild')");
|
|
1762
|
+
} catch {
|
|
1763
|
+
}
|
|
1227
1764
|
await client.executeMultiple(`
|
|
1228
1765
|
CREATE TABLE IF NOT EXISTS entity_aliases (
|
|
1229
1766
|
alias TEXT NOT NULL PRIMARY KEY,
|
|
@@ -1404,6 +1941,33 @@ async function ensureSchema() {
|
|
|
1404
1941
|
CREATE INDEX IF NOT EXISTS idx_conversations_channel
|
|
1405
1942
|
ON conversations(channel_id);
|
|
1406
1943
|
`);
|
|
1944
|
+
await client.executeMultiple(`
|
|
1945
|
+
CREATE TABLE IF NOT EXISTS session_agent_map (
|
|
1946
|
+
session_uuid TEXT PRIMARY KEY,
|
|
1947
|
+
agent_id TEXT NOT NULL,
|
|
1948
|
+
session_name TEXT,
|
|
1949
|
+
task_id TEXT,
|
|
1950
|
+
project_name TEXT,
|
|
1951
|
+
started_at TEXT NOT NULL
|
|
1952
|
+
);
|
|
1953
|
+
|
|
1954
|
+
CREATE INDEX IF NOT EXISTS idx_session_agent_map_agent
|
|
1955
|
+
ON session_agent_map(agent_id);
|
|
1956
|
+
`);
|
|
1957
|
+
try {
|
|
1958
|
+
const mapCount = await client.execute({ sql: `SELECT COUNT(*) as cnt FROM session_agent_map`, args: [] });
|
|
1959
|
+
if (Number(mapCount.rows[0]?.cnt ?? 0) === 0) {
|
|
1960
|
+
await client.execute({
|
|
1961
|
+
sql: `INSERT OR IGNORE INTO session_agent_map (session_uuid, agent_id, session_name, started_at)
|
|
1962
|
+
SELECT session_id, agent_id, '', MIN(timestamp)
|
|
1963
|
+
FROM memories
|
|
1964
|
+
WHERE session_id IS NOT NULL AND session_id != '' AND agent_id IS NOT NULL AND agent_id != ''
|
|
1965
|
+
GROUP BY session_id, agent_id`,
|
|
1966
|
+
args: []
|
|
1967
|
+
});
|
|
1968
|
+
}
|
|
1969
|
+
} catch {
|
|
1970
|
+
}
|
|
1407
1971
|
try {
|
|
1408
1972
|
await client.execute({
|
|
1409
1973
|
sql: `ALTER TABLE tasks ADD COLUMN budget_tokens INTEGER`,
|
|
@@ -1537,15 +2101,41 @@ async function ensureSchema() {
|
|
|
1537
2101
|
});
|
|
1538
2102
|
} catch {
|
|
1539
2103
|
}
|
|
2104
|
+
for (const col of [
|
|
2105
|
+
"ALTER TABLE memories ADD COLUMN intent TEXT",
|
|
2106
|
+
"ALTER TABLE memories ADD COLUMN outcome TEXT",
|
|
2107
|
+
"ALTER TABLE memories ADD COLUMN domain TEXT",
|
|
2108
|
+
"ALTER TABLE memories ADD COLUMN referenced_entities TEXT",
|
|
2109
|
+
"ALTER TABLE memories ADD COLUMN retrieval_count INTEGER DEFAULT 0",
|
|
2110
|
+
"ALTER TABLE memories ADD COLUMN chain_position TEXT",
|
|
2111
|
+
"ALTER TABLE memories ADD COLUMN review_status TEXT",
|
|
2112
|
+
"ALTER TABLE memories ADD COLUMN context_window_pct INTEGER",
|
|
2113
|
+
"ALTER TABLE memories ADD COLUMN file_paths TEXT",
|
|
2114
|
+
"ALTER TABLE memories ADD COLUMN commit_hash TEXT",
|
|
2115
|
+
"ALTER TABLE memories ADD COLUMN duration_ms INTEGER",
|
|
2116
|
+
"ALTER TABLE memories ADD COLUMN token_cost REAL",
|
|
2117
|
+
"ALTER TABLE memories ADD COLUMN audience TEXT",
|
|
2118
|
+
"ALTER TABLE memories ADD COLUMN language_type TEXT",
|
|
2119
|
+
"ALTER TABLE memories ADD COLUMN parent_memory_id TEXT"
|
|
2120
|
+
]) {
|
|
2121
|
+
try {
|
|
2122
|
+
await client.execute(col);
|
|
2123
|
+
} catch {
|
|
2124
|
+
}
|
|
2125
|
+
}
|
|
1540
2126
|
}
|
|
1541
2127
|
async function disposeDatabase() {
|
|
2128
|
+
if (_daemonClient) {
|
|
2129
|
+
_daemonClient.close();
|
|
2130
|
+
_daemonClient = null;
|
|
2131
|
+
}
|
|
1542
2132
|
if (_client) {
|
|
1543
2133
|
_client.close();
|
|
1544
2134
|
_client = null;
|
|
1545
2135
|
_resilientClient = null;
|
|
1546
2136
|
}
|
|
1547
2137
|
}
|
|
1548
|
-
var _client, _resilientClient, initTurso, disposeTurso;
|
|
2138
|
+
var _client, _resilientClient, _daemonClient, initTurso, disposeTurso;
|
|
1549
2139
|
var init_database = __esm({
|
|
1550
2140
|
"src/lib/database.ts"() {
|
|
1551
2141
|
"use strict";
|
|
@@ -1553,24 +2143,25 @@ var init_database = __esm({
|
|
|
1553
2143
|
init_employees();
|
|
1554
2144
|
_client = null;
|
|
1555
2145
|
_resilientClient = null;
|
|
2146
|
+
_daemonClient = null;
|
|
1556
2147
|
initTurso = initDatabase;
|
|
1557
2148
|
disposeTurso = disposeDatabase;
|
|
1558
2149
|
}
|
|
1559
2150
|
});
|
|
1560
2151
|
|
|
1561
2152
|
// src/lib/license.ts
|
|
1562
|
-
import { readFileSync as
|
|
1563
|
-
import { randomUUID as
|
|
1564
|
-
import
|
|
2153
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, existsSync as existsSync7, mkdirSync as mkdirSync4 } from "fs";
|
|
2154
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
2155
|
+
import path8 from "path";
|
|
1565
2156
|
import { jwtVerify, importSPKI } from "jose";
|
|
1566
2157
|
var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, PLAN_LIMITS;
|
|
1567
2158
|
var init_license = __esm({
|
|
1568
2159
|
"src/lib/license.ts"() {
|
|
1569
2160
|
"use strict";
|
|
1570
2161
|
init_config();
|
|
1571
|
-
LICENSE_PATH =
|
|
1572
|
-
CACHE_PATH =
|
|
1573
|
-
DEVICE_ID_PATH =
|
|
2162
|
+
LICENSE_PATH = path8.join(EXE_AI_DIR, "license.key");
|
|
2163
|
+
CACHE_PATH = path8.join(EXE_AI_DIR, "license-cache.json");
|
|
2164
|
+
DEVICE_ID_PATH = path8.join(EXE_AI_DIR, "device-id");
|
|
1574
2165
|
PLAN_LIMITS = {
|
|
1575
2166
|
free: { devices: 1, employees: 1, memories: 5e3 },
|
|
1576
2167
|
pro: { devices: 3, employees: 5, memories: 1e5 },
|
|
@@ -1582,12 +2173,12 @@ var init_license = __esm({
|
|
|
1582
2173
|
});
|
|
1583
2174
|
|
|
1584
2175
|
// src/lib/plan-limits.ts
|
|
1585
|
-
import { readFileSync as
|
|
1586
|
-
import
|
|
2176
|
+
import { readFileSync as readFileSync8, existsSync as existsSync8 } from "fs";
|
|
2177
|
+
import path9 from "path";
|
|
1587
2178
|
function getLicenseSync() {
|
|
1588
2179
|
try {
|
|
1589
|
-
if (!
|
|
1590
|
-
const raw = JSON.parse(
|
|
2180
|
+
if (!existsSync8(CACHE_PATH2)) return freeLicense();
|
|
2181
|
+
const raw = JSON.parse(readFileSync8(CACHE_PATH2, "utf8"));
|
|
1591
2182
|
if (!raw.token || typeof raw.token !== "string") return freeLicense();
|
|
1592
2183
|
const parts = raw.token.split(".");
|
|
1593
2184
|
if (parts.length !== 3) return freeLicense();
|
|
@@ -1625,8 +2216,8 @@ function assertEmployeeLimitSync(rosterPath) {
|
|
|
1625
2216
|
const filePath = rosterPath ?? EMPLOYEES_PATH;
|
|
1626
2217
|
let count = 0;
|
|
1627
2218
|
try {
|
|
1628
|
-
if (
|
|
1629
|
-
const raw =
|
|
2219
|
+
if (existsSync8(filePath)) {
|
|
2220
|
+
const raw = readFileSync8(filePath, "utf8");
|
|
1630
2221
|
const employees = JSON.parse(raw);
|
|
1631
2222
|
count = Array.isArray(employees) ? employees.length : 0;
|
|
1632
2223
|
}
|
|
@@ -1655,19 +2246,19 @@ var init_plan_limits = __esm({
|
|
|
1655
2246
|
this.name = "PlanLimitError";
|
|
1656
2247
|
}
|
|
1657
2248
|
};
|
|
1658
|
-
CACHE_PATH2 =
|
|
2249
|
+
CACHE_PATH2 = path9.join(EXE_AI_DIR, "license-cache.json");
|
|
1659
2250
|
}
|
|
1660
2251
|
});
|
|
1661
2252
|
|
|
1662
2253
|
// src/lib/notifications.ts
|
|
1663
2254
|
import crypto from "crypto";
|
|
1664
|
-
import
|
|
2255
|
+
import path10 from "path";
|
|
1665
2256
|
import os6 from "os";
|
|
1666
2257
|
import {
|
|
1667
|
-
readFileSync as
|
|
2258
|
+
readFileSync as readFileSync9,
|
|
1668
2259
|
readdirSync,
|
|
1669
|
-
unlinkSync as
|
|
1670
|
-
existsSync as
|
|
2260
|
+
unlinkSync as unlinkSync3,
|
|
2261
|
+
existsSync as existsSync9,
|
|
1671
2262
|
rmdirSync
|
|
1672
2263
|
} from "fs";
|
|
1673
2264
|
async function writeNotification(notification) {
|
|
@@ -1831,10 +2422,11 @@ var init_state_bus = __esm({
|
|
|
1831
2422
|
|
|
1832
2423
|
// src/lib/tasks-crud.ts
|
|
1833
2424
|
import crypto3 from "crypto";
|
|
1834
|
-
import
|
|
2425
|
+
import path11 from "path";
|
|
2426
|
+
import os7 from "os";
|
|
1835
2427
|
import { execSync as execSync5 } from "child_process";
|
|
1836
2428
|
import { mkdir as mkdir3, writeFile as writeFile3, appendFile } from "fs/promises";
|
|
1837
|
-
import { existsSync as
|
|
2429
|
+
import { existsSync as existsSync10, readFileSync as readFileSync10 } from "fs";
|
|
1838
2430
|
async function writeCheckpoint(input) {
|
|
1839
2431
|
const client = getClient();
|
|
1840
2432
|
const row = await resolveTask(client, input.taskId);
|
|
@@ -1875,6 +2467,35 @@ function extractParentFromContext(contextBody) {
|
|
|
1875
2467
|
function slugify(title) {
|
|
1876
2468
|
return title.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
1877
2469
|
}
|
|
2470
|
+
function buildKeywordIndex() {
|
|
2471
|
+
const idx = /* @__PURE__ */ new Map();
|
|
2472
|
+
for (const [role, keywords] of Object.entries(LANE_KEYWORDS)) {
|
|
2473
|
+
for (const kw of keywords) {
|
|
2474
|
+
const existing = idx.get(kw) ?? [];
|
|
2475
|
+
existing.push(role);
|
|
2476
|
+
idx.set(kw, existing);
|
|
2477
|
+
}
|
|
2478
|
+
}
|
|
2479
|
+
return idx;
|
|
2480
|
+
}
|
|
2481
|
+
function checkLaneAffinity(title, context, assigneeName) {
|
|
2482
|
+
const employees = loadEmployeesSync();
|
|
2483
|
+
const employee = employees.find((e) => e.name === assigneeName);
|
|
2484
|
+
if (!employee) return void 0;
|
|
2485
|
+
const assigneeRole = employee.role;
|
|
2486
|
+
const text = `${title} ${context}`.toLowerCase();
|
|
2487
|
+
const matchedRoles = /* @__PURE__ */ new Set();
|
|
2488
|
+
for (const [keyword, roles] of KEYWORD_INDEX) {
|
|
2489
|
+
if (text.includes(keyword)) {
|
|
2490
|
+
for (const role of roles) matchedRoles.add(role);
|
|
2491
|
+
}
|
|
2492
|
+
}
|
|
2493
|
+
if (matchedRoles.size === 0) return void 0;
|
|
2494
|
+
if (matchedRoles.has(assigneeRole)) return void 0;
|
|
2495
|
+
if (assigneeRole === "COO") return void 0;
|
|
2496
|
+
const expectedRoles = Array.from(matchedRoles).join(" or ");
|
|
2497
|
+
return `\u26A0\uFE0F Lane mismatch: task content suggests ${expectedRoles}, but assigned to ${assigneeName} (${assigneeRole}).`;
|
|
2498
|
+
}
|
|
1878
2499
|
async function resolveTask(client, identifier, scopeSession) {
|
|
1879
2500
|
const scope = sessionScopeFilter(scopeSession);
|
|
1880
2501
|
let result = await client.execute({
|
|
@@ -1924,7 +2545,14 @@ async function createTaskCore(input) {
|
|
|
1924
2545
|
const id = crypto3.randomUUID();
|
|
1925
2546
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1926
2547
|
const slug = slugify(input.title);
|
|
1927
|
-
|
|
2548
|
+
let earlySessionScope = null;
|
|
2549
|
+
try {
|
|
2550
|
+
const { resolveExeSession: resolveExeSession2 } = await Promise.resolve().then(() => (init_tmux_routing(), tmux_routing_exports));
|
|
2551
|
+
earlySessionScope = resolveExeSession2();
|
|
2552
|
+
} catch {
|
|
2553
|
+
}
|
|
2554
|
+
const scope = earlySessionScope ?? "default";
|
|
2555
|
+
const taskFile = input.taskFile ?? `tasks/${scope}/${input.assignedTo}/${slug}.md`;
|
|
1928
2556
|
let blockedById = null;
|
|
1929
2557
|
const initialStatus = input.blockedBy ? "blocked" : "open";
|
|
1930
2558
|
if (input.blockedBy) {
|
|
@@ -1964,22 +2592,24 @@ async function createTaskCore(input) {
|
|
|
1964
2592
|
if (dupCheck.rows.length > 0) {
|
|
1965
2593
|
warning = `similar active task already exists (${String(dupCheck.rows[0].id)}). Created new task anyway.`;
|
|
1966
2594
|
}
|
|
2595
|
+
if (!process.env.DISABLE_LANE_AFFINITY) {
|
|
2596
|
+
const laneWarning = checkLaneAffinity(input.title, input.context, input.assignedTo);
|
|
2597
|
+
if (laneWarning) {
|
|
2598
|
+
warning = warning ? `${warning}
|
|
2599
|
+
${laneWarning}` : laneWarning;
|
|
2600
|
+
}
|
|
2601
|
+
}
|
|
1967
2602
|
if (input.baseDir) {
|
|
1968
2603
|
try {
|
|
1969
|
-
await mkdir3(
|
|
1970
|
-
await mkdir3(
|
|
2604
|
+
await mkdir3(path11.join(input.baseDir, "exe", "output"), { recursive: true });
|
|
2605
|
+
await mkdir3(path11.join(input.baseDir, "exe", "research"), { recursive: true });
|
|
1971
2606
|
await ensureArchitectureDoc(input.baseDir, input.projectName);
|
|
1972
2607
|
await ensureGitignoreExe(input.baseDir);
|
|
1973
2608
|
} catch {
|
|
1974
2609
|
}
|
|
1975
2610
|
}
|
|
1976
2611
|
const complexity = input.complexity ?? "standard";
|
|
1977
|
-
|
|
1978
|
-
try {
|
|
1979
|
-
const { resolveExeSession: resolveExeSession2 } = await Promise.resolve().then(() => (init_tmux_routing(), tmux_routing_exports));
|
|
1980
|
-
sessionScope = resolveExeSession2();
|
|
1981
|
-
} catch {
|
|
1982
|
-
}
|
|
2612
|
+
const sessionScope = earlySessionScope;
|
|
1983
2613
|
await client.execute({
|
|
1984
2614
|
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)
|
|
1985
2615
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
@@ -2006,6 +2636,43 @@ async function createTaskCore(input) {
|
|
|
2006
2636
|
now
|
|
2007
2637
|
]
|
|
2008
2638
|
});
|
|
2639
|
+
if (input.baseDir) {
|
|
2640
|
+
try {
|
|
2641
|
+
const EXE_OS_DIR = path11.join(os7.homedir(), ".exe-os");
|
|
2642
|
+
const mdPath = path11.join(EXE_OS_DIR, taskFile);
|
|
2643
|
+
const mdDir = path11.dirname(mdPath);
|
|
2644
|
+
if (!existsSync10(mdDir)) await mkdir3(mdDir, { recursive: true });
|
|
2645
|
+
const reviewer = input.reviewer ?? input.assignedBy;
|
|
2646
|
+
const mdContent = `# ${input.title}
|
|
2647
|
+
|
|
2648
|
+
**ID:** ${id}
|
|
2649
|
+
**Status:** ${initialStatus}
|
|
2650
|
+
**Priority:** ${input.priority}
|
|
2651
|
+
**Assigned by:** ${input.assignedBy}
|
|
2652
|
+
**Assigned to:** ${input.assignedTo}
|
|
2653
|
+
**Project:** ${input.projectName}
|
|
2654
|
+
**Created:** ${now.split("T")[0]}${parentTaskId ? `
|
|
2655
|
+
**Parent task:** ${parentTaskId}` : ""}
|
|
2656
|
+
**Reviewer:** ${reviewer}
|
|
2657
|
+
|
|
2658
|
+
## Context
|
|
2659
|
+
|
|
2660
|
+
${input.context}
|
|
2661
|
+
|
|
2662
|
+
## MANDATORY: When done
|
|
2663
|
+
|
|
2664
|
+
You MUST call update_task with status "done" and a result summary when finished.
|
|
2665
|
+
If you skip this, your reviewer will not know you're done and your work won't be reviewed.
|
|
2666
|
+
Do NOT let a failed commit or any error prevent you from calling update_task(done).
|
|
2667
|
+
`;
|
|
2668
|
+
await writeFile3(mdPath, mdContent, "utf-8");
|
|
2669
|
+
} catch (err) {
|
|
2670
|
+
process.stderr.write(
|
|
2671
|
+
`[create-task] WARNING: .md file write failed for ${taskFile}: ${err instanceof Error ? err.message : String(err)}
|
|
2672
|
+
`
|
|
2673
|
+
);
|
|
2674
|
+
}
|
|
2675
|
+
}
|
|
2009
2676
|
return {
|
|
2010
2677
|
id,
|
|
2011
2678
|
title: input.title,
|
|
@@ -2198,7 +2865,7 @@ ${input.result}` : `\u26A0\uFE0F ${warning}`;
|
|
|
2198
2865
|
return { row, taskFile, now, taskId };
|
|
2199
2866
|
}
|
|
2200
2867
|
}
|
|
2201
|
-
if (curStatus === "in_progress" && input.callerAgentId && (input.callerAgentId === assignedBy || input.callerAgentId
|
|
2868
|
+
if (curStatus === "in_progress" && input.callerAgentId && (input.callerAgentId === assignedBy || isCoordinatorName(input.callerAgentId))) {
|
|
2202
2869
|
process.stderr.write(
|
|
2203
2870
|
`[tasks] Assigner override: ${input.callerAgentId} reclaiming ${taskId}
|
|
2204
2871
|
`
|
|
@@ -2263,9 +2930,9 @@ async function deleteTaskCore(taskId, _baseDir) {
|
|
|
2263
2930
|
return { taskFile, assignedTo, assignedBy, taskSlug };
|
|
2264
2931
|
}
|
|
2265
2932
|
async function ensureArchitectureDoc(baseDir, projectName) {
|
|
2266
|
-
const archPath =
|
|
2933
|
+
const archPath = path11.join(baseDir, "exe", "ARCHITECTURE.md");
|
|
2267
2934
|
try {
|
|
2268
|
-
if (
|
|
2935
|
+
if (existsSync10(archPath)) return;
|
|
2269
2936
|
const template = [
|
|
2270
2937
|
`# ${projectName} \u2014 System Architecture`,
|
|
2271
2938
|
"",
|
|
@@ -2298,10 +2965,10 @@ async function ensureArchitectureDoc(baseDir, projectName) {
|
|
|
2298
2965
|
}
|
|
2299
2966
|
}
|
|
2300
2967
|
async function ensureGitignoreExe(baseDir) {
|
|
2301
|
-
const gitignorePath =
|
|
2968
|
+
const gitignorePath = path11.join(baseDir, ".gitignore");
|
|
2302
2969
|
try {
|
|
2303
|
-
if (
|
|
2304
|
-
const content =
|
|
2970
|
+
if (existsSync10(gitignorePath)) {
|
|
2971
|
+
const content = readFileSync10(gitignorePath, "utf-8");
|
|
2305
2972
|
if (/^\/?exe\/?$/m.test(content)) return;
|
|
2306
2973
|
await appendFile(gitignorePath, "\n# Employee task assignments (private)\n/exe/\n");
|
|
2307
2974
|
} else {
|
|
@@ -2310,20 +2977,30 @@ async function ensureGitignoreExe(baseDir) {
|
|
|
2310
2977
|
} catch {
|
|
2311
2978
|
}
|
|
2312
2979
|
}
|
|
2313
|
-
var DELEGATION_KEYWORDS, TASK_ALREADY_CLAIMED_PREFIX;
|
|
2980
|
+
var LANE_KEYWORDS, KEYWORD_INDEX, DELEGATION_KEYWORDS, TASK_ALREADY_CLAIMED_PREFIX;
|
|
2314
2981
|
var init_tasks_crud = __esm({
|
|
2315
2982
|
"src/lib/tasks-crud.ts"() {
|
|
2316
2983
|
"use strict";
|
|
2317
2984
|
init_database();
|
|
2318
2985
|
init_task_scope();
|
|
2986
|
+
init_employees();
|
|
2987
|
+
LANE_KEYWORDS = {
|
|
2988
|
+
CMO: ["sales", "script", "pitch", "offer", "copy", "objection", "brand", "content", "seo", "marketing", "newsletter", "carousel", "social", "campaign"],
|
|
2989
|
+
CTO: ["spec", "architecture", "migration", "schema", "database", "design doc", "adr", "security audit", "tech stack"],
|
|
2990
|
+
"Principal Engineer": ["implement", "build", "fix", "commit", "refactor", "bug", "feature", "wire", "integration"],
|
|
2991
|
+
"Staff Code Reviewer": ["critique", "verdict", "review", "audit", "code quality"],
|
|
2992
|
+
"Content Production Specialist": ["render", "video", "image", "b-roll", "remotion", "animation", "thumbnail"],
|
|
2993
|
+
"AI Product Lead": ["competitive", "analysis", "benchmark", "compare", "scout", "evaluate", "poc"]
|
|
2994
|
+
};
|
|
2995
|
+
KEYWORD_INDEX = buildKeywordIndex();
|
|
2319
2996
|
DELEGATION_KEYWORDS = /parallel|delegate|wave|worktree|multi-instance/i;
|
|
2320
2997
|
TASK_ALREADY_CLAIMED_PREFIX = "TASK_ALREADY_CLAIMED";
|
|
2321
2998
|
}
|
|
2322
2999
|
});
|
|
2323
3000
|
|
|
2324
3001
|
// src/lib/tasks-review.ts
|
|
2325
|
-
import
|
|
2326
|
-
import { existsSync as
|
|
3002
|
+
import path12 from "path";
|
|
3003
|
+
import { existsSync as existsSync11, readdirSync as readdirSync2, unlinkSync as unlinkSync4 } from "fs";
|
|
2327
3004
|
async function countPendingReviews(sessionScope) {
|
|
2328
3005
|
const client = getClient();
|
|
2329
3006
|
if (sessionScope) {
|
|
@@ -2345,7 +3022,7 @@ async function countNewPendingReviewsSince(sinceIso, sessionScope) {
|
|
|
2345
3022
|
const result2 = await client.execute({
|
|
2346
3023
|
sql: `SELECT COUNT(*) as cnt FROM tasks
|
|
2347
3024
|
WHERE status = 'needs_review' AND updated_at > ?
|
|
2348
|
-
AND
|
|
3025
|
+
AND session_scope = ?`,
|
|
2349
3026
|
args: [sinceIso, sessionScope]
|
|
2350
3027
|
});
|
|
2351
3028
|
return Number(result2.rows[0]?.cnt) || 0;
|
|
@@ -2363,7 +3040,7 @@ async function listPendingReviews(limit, sessionScope) {
|
|
|
2363
3040
|
const result2 = await client.execute({
|
|
2364
3041
|
sql: `SELECT title, assigned_to, project_name FROM tasks
|
|
2365
3042
|
WHERE status = 'needs_review'
|
|
2366
|
-
AND
|
|
3043
|
+
AND session_scope = ?
|
|
2367
3044
|
ORDER BY priority ASC, created_at DESC LIMIT ?`,
|
|
2368
3045
|
args: [sessionScope, limit]
|
|
2369
3046
|
});
|
|
@@ -2484,14 +3161,14 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
2484
3161
|
if (parts.length >= 3 && parts[0] === "review") {
|
|
2485
3162
|
const agent = parts[1];
|
|
2486
3163
|
const slug = parts.slice(2).join("-");
|
|
2487
|
-
const
|
|
3164
|
+
const legacyTaskFile = `exe/${agent}/${slug}.md`;
|
|
2488
3165
|
const result = await client.execute({
|
|
2489
|
-
sql: "UPDATE tasks SET status = 'done', updated_at = ? WHERE task_file = ? AND status = 'needs_review'",
|
|
2490
|
-
args: [now,
|
|
3166
|
+
sql: "UPDATE tasks SET status = 'done', updated_at = ? WHERE (task_file = ? OR task_file LIKE ?) AND status = 'needs_review'",
|
|
3167
|
+
args: [now, legacyTaskFile, `tasks/%/${agent}/${slug}.md`]
|
|
2491
3168
|
});
|
|
2492
3169
|
if (result.rowsAffected > 0) {
|
|
2493
3170
|
process.stderr.write(
|
|
2494
|
-
`[review-cleanup] Cascaded original task to done
|
|
3171
|
+
`[review-cleanup] Cascaded original task to done: ${agent}/${slug}.md
|
|
2495
3172
|
`
|
|
2496
3173
|
);
|
|
2497
3174
|
}
|
|
@@ -2504,11 +3181,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
2504
3181
|
);
|
|
2505
3182
|
}
|
|
2506
3183
|
try {
|
|
2507
|
-
const cacheDir =
|
|
2508
|
-
if (
|
|
3184
|
+
const cacheDir = path12.join(EXE_AI_DIR, "session-cache");
|
|
3185
|
+
if (existsSync11(cacheDir)) {
|
|
2509
3186
|
for (const f of readdirSync2(cacheDir)) {
|
|
2510
3187
|
if (f.startsWith("review-notified-")) {
|
|
2511
|
-
|
|
3188
|
+
unlinkSync4(path12.join(cacheDir, f));
|
|
2512
3189
|
}
|
|
2513
3190
|
}
|
|
2514
3191
|
}
|
|
@@ -2529,7 +3206,7 @@ var init_tasks_review = __esm({
|
|
|
2529
3206
|
});
|
|
2530
3207
|
|
|
2531
3208
|
// src/lib/tasks-chain.ts
|
|
2532
|
-
import
|
|
3209
|
+
import path13 from "path";
|
|
2533
3210
|
import { readFile as readFile3, writeFile as writeFile4 } from "fs/promises";
|
|
2534
3211
|
async function cascadeUnblock(taskId, baseDir, now) {
|
|
2535
3212
|
const client = getClient();
|
|
@@ -2546,7 +3223,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
2546
3223
|
});
|
|
2547
3224
|
for (const ur of unblockedRows.rows) {
|
|
2548
3225
|
try {
|
|
2549
|
-
const ubFile =
|
|
3226
|
+
const ubFile = path13.join(baseDir, String(ur.task_file));
|
|
2550
3227
|
let ubContent = await readFile3(ubFile, "utf-8");
|
|
2551
3228
|
ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
|
|
2552
3229
|
ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
|
|
@@ -2615,7 +3292,7 @@ var init_tasks_chain = __esm({
|
|
|
2615
3292
|
|
|
2616
3293
|
// src/lib/project-name.ts
|
|
2617
3294
|
import { execSync as execSync6 } from "child_process";
|
|
2618
|
-
import
|
|
3295
|
+
import path14 from "path";
|
|
2619
3296
|
function getProjectName(cwd) {
|
|
2620
3297
|
const dir = cwd ?? process.cwd();
|
|
2621
3298
|
if (_cached2 && _cachedCwd === dir) return _cached2;
|
|
@@ -2628,7 +3305,7 @@ function getProjectName(cwd) {
|
|
|
2628
3305
|
timeout: 2e3,
|
|
2629
3306
|
stdio: ["pipe", "pipe", "pipe"]
|
|
2630
3307
|
}).trim();
|
|
2631
|
-
repoRoot =
|
|
3308
|
+
repoRoot = path14.dirname(gitCommonDir);
|
|
2632
3309
|
} catch {
|
|
2633
3310
|
repoRoot = execSync6("git rev-parse --show-toplevel", {
|
|
2634
3311
|
cwd: dir,
|
|
@@ -2637,11 +3314,11 @@ function getProjectName(cwd) {
|
|
|
2637
3314
|
stdio: ["pipe", "pipe", "pipe"]
|
|
2638
3315
|
}).trim();
|
|
2639
3316
|
}
|
|
2640
|
-
_cached2 =
|
|
3317
|
+
_cached2 = path14.basename(repoRoot);
|
|
2641
3318
|
_cachedCwd = dir;
|
|
2642
3319
|
return _cached2;
|
|
2643
3320
|
} catch {
|
|
2644
|
-
_cached2 =
|
|
3321
|
+
_cached2 = path14.basename(dir);
|
|
2645
3322
|
_cachedCwd = dir;
|
|
2646
3323
|
return _cached2;
|
|
2647
3324
|
}
|
|
@@ -2673,7 +3350,7 @@ function findSessionForProject(projectName) {
|
|
|
2673
3350
|
const sessions = listSessions();
|
|
2674
3351
|
for (const s of sessions) {
|
|
2675
3352
|
const proj = s.projectDir.split("/").filter(Boolean).pop();
|
|
2676
|
-
if (proj === projectName &&
|
|
3353
|
+
if (proj === projectName && isCoordinatorName(s.agentId)) return s;
|
|
2677
3354
|
}
|
|
2678
3355
|
return null;
|
|
2679
3356
|
}
|
|
@@ -2719,7 +3396,7 @@ var init_session_scope = __esm({
|
|
|
2719
3396
|
|
|
2720
3397
|
// src/lib/tasks-notify.ts
|
|
2721
3398
|
async function dispatchTaskToEmployee(input) {
|
|
2722
|
-
if (
|
|
3399
|
+
if (isCoordinatorName(input.assignedTo)) return { dispatched: "skipped" };
|
|
2723
3400
|
let crossProject = false;
|
|
2724
3401
|
if (input.projectName) {
|
|
2725
3402
|
try {
|
|
@@ -3176,8 +3853,8 @@ __export(tasks_exports, {
|
|
|
3176
3853
|
updateTaskStatus: () => updateTaskStatus,
|
|
3177
3854
|
writeCheckpoint: () => writeCheckpoint
|
|
3178
3855
|
});
|
|
3179
|
-
import
|
|
3180
|
-
import { writeFileSync as
|
|
3856
|
+
import path15 from "path";
|
|
3857
|
+
import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync5, unlinkSync as unlinkSync5 } from "fs";
|
|
3181
3858
|
async function createTask(input) {
|
|
3182
3859
|
const result = await createTaskCore(input);
|
|
3183
3860
|
if (!input.skipDispatch && result.status !== "blocked" && !process.env.VITEST) {
|
|
@@ -3196,14 +3873,14 @@ async function updateTask(input) {
|
|
|
3196
3873
|
const { row, taskFile, now, taskId } = await updateTaskStatus(input);
|
|
3197
3874
|
try {
|
|
3198
3875
|
const agent = String(row.assigned_to);
|
|
3199
|
-
const cacheDir =
|
|
3200
|
-
const cachePath =
|
|
3876
|
+
const cacheDir = path15.join(EXE_AI_DIR, "session-cache");
|
|
3877
|
+
const cachePath = path15.join(cacheDir, `current-task-${agent}.json`);
|
|
3201
3878
|
if (input.status === "in_progress") {
|
|
3202
|
-
|
|
3203
|
-
|
|
3879
|
+
mkdirSync5(cacheDir, { recursive: true });
|
|
3880
|
+
writeFileSync6(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
|
|
3204
3881
|
} else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled") {
|
|
3205
3882
|
try {
|
|
3206
|
-
|
|
3883
|
+
unlinkSync5(cachePath);
|
|
3207
3884
|
} catch {
|
|
3208
3885
|
}
|
|
3209
3886
|
}
|
|
@@ -3260,7 +3937,7 @@ async function updateTask(input) {
|
|
|
3260
3937
|
}
|
|
3261
3938
|
const isTerminal = input.status === "done" || input.status === "needs_review";
|
|
3262
3939
|
if (isTerminal) {
|
|
3263
|
-
const isCoordinator =
|
|
3940
|
+
const isCoordinator = isCoordinatorName(String(row.assigned_to));
|
|
3264
3941
|
if (!isCoordinator) {
|
|
3265
3942
|
notifyTaskDone();
|
|
3266
3943
|
}
|
|
@@ -3285,7 +3962,7 @@ async function updateTask(input) {
|
|
|
3285
3962
|
}
|
|
3286
3963
|
}
|
|
3287
3964
|
}
|
|
3288
|
-
if (input.status === "done" &&
|
|
3965
|
+
if (input.status === "done" && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
|
|
3289
3966
|
Promise.resolve().then(() => (init_skill_learning(), skill_learning_exports)).then(
|
|
3290
3967
|
({ captureAndLearn: captureAndLearn2 }) => captureAndLearn2({
|
|
3291
3968
|
taskId,
|
|
@@ -3301,7 +3978,7 @@ async function updateTask(input) {
|
|
|
3301
3978
|
});
|
|
3302
3979
|
}
|
|
3303
3980
|
let nextTask;
|
|
3304
|
-
if (isTerminal &&
|
|
3981
|
+
if (isTerminal && !isCoordinatorName(String(row.assigned_to))) {
|
|
3305
3982
|
try {
|
|
3306
3983
|
nextTask = await findNextTask(String(row.assigned_to));
|
|
3307
3984
|
} catch {
|
|
@@ -3645,7 +4322,7 @@ var init_capacity_monitor = __esm({
|
|
|
3645
4322
|
// src/lib/tmux-routing.ts
|
|
3646
4323
|
var tmux_routing_exports = {};
|
|
3647
4324
|
__export(tmux_routing_exports, {
|
|
3648
|
-
acquireSpawnLock: () =>
|
|
4325
|
+
acquireSpawnLock: () => acquireSpawnLock2,
|
|
3649
4326
|
employeeSessionName: () => employeeSessionName,
|
|
3650
4327
|
ensureEmployee: () => ensureEmployee,
|
|
3651
4328
|
extractRootExe: () => extractRootExe,
|
|
@@ -3660,20 +4337,20 @@ __export(tmux_routing_exports, {
|
|
|
3660
4337
|
notifyParentExe: () => notifyParentExe,
|
|
3661
4338
|
parseParentExe: () => parseParentExe,
|
|
3662
4339
|
registerParentExe: () => registerParentExe,
|
|
3663
|
-
releaseSpawnLock: () =>
|
|
4340
|
+
releaseSpawnLock: () => releaseSpawnLock2,
|
|
3664
4341
|
resolveExeSession: () => resolveExeSession,
|
|
3665
4342
|
sendIntercom: () => sendIntercom,
|
|
3666
4343
|
spawnEmployee: () => spawnEmployee,
|
|
3667
4344
|
verifyPaneAtCapacity: () => verifyPaneAtCapacity
|
|
3668
4345
|
});
|
|
3669
4346
|
import { execFileSync as execFileSync2, execSync as execSync7 } from "child_process";
|
|
3670
|
-
import { readFileSync as
|
|
3671
|
-
import
|
|
3672
|
-
import
|
|
3673
|
-
import { fileURLToPath } from "url";
|
|
3674
|
-
import { unlinkSync as
|
|
4347
|
+
import { readFileSync as readFileSync11, writeFileSync as writeFileSync7, mkdirSync as mkdirSync6, existsSync as existsSync12, appendFileSync } from "fs";
|
|
4348
|
+
import path16 from "path";
|
|
4349
|
+
import os8 from "os";
|
|
4350
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
4351
|
+
import { unlinkSync as unlinkSync6 } from "fs";
|
|
3675
4352
|
function spawnLockPath(sessionName) {
|
|
3676
|
-
return
|
|
4353
|
+
return path16.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
|
|
3677
4354
|
}
|
|
3678
4355
|
function isProcessAlive(pid) {
|
|
3679
4356
|
try {
|
|
@@ -3683,14 +4360,14 @@ function isProcessAlive(pid) {
|
|
|
3683
4360
|
return false;
|
|
3684
4361
|
}
|
|
3685
4362
|
}
|
|
3686
|
-
function
|
|
3687
|
-
if (!
|
|
3688
|
-
|
|
4363
|
+
function acquireSpawnLock2(sessionName) {
|
|
4364
|
+
if (!existsSync12(SPAWN_LOCK_DIR)) {
|
|
4365
|
+
mkdirSync6(SPAWN_LOCK_DIR, { recursive: true });
|
|
3689
4366
|
}
|
|
3690
4367
|
const lockFile = spawnLockPath(sessionName);
|
|
3691
|
-
if (
|
|
4368
|
+
if (existsSync12(lockFile)) {
|
|
3692
4369
|
try {
|
|
3693
|
-
const lock = JSON.parse(
|
|
4370
|
+
const lock = JSON.parse(readFileSync11(lockFile, "utf8"));
|
|
3694
4371
|
const age = Date.now() - lock.timestamp;
|
|
3695
4372
|
if (isProcessAlive(lock.pid) && age < 6e4) {
|
|
3696
4373
|
return false;
|
|
@@ -3698,25 +4375,25 @@ function acquireSpawnLock(sessionName) {
|
|
|
3698
4375
|
} catch {
|
|
3699
4376
|
}
|
|
3700
4377
|
}
|
|
3701
|
-
|
|
4378
|
+
writeFileSync7(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
|
|
3702
4379
|
return true;
|
|
3703
4380
|
}
|
|
3704
|
-
function
|
|
4381
|
+
function releaseSpawnLock2(sessionName) {
|
|
3705
4382
|
try {
|
|
3706
|
-
|
|
4383
|
+
unlinkSync6(spawnLockPath(sessionName));
|
|
3707
4384
|
} catch {
|
|
3708
4385
|
}
|
|
3709
4386
|
}
|
|
3710
4387
|
function resolveBehaviorsExporterScript() {
|
|
3711
4388
|
try {
|
|
3712
|
-
const thisFile =
|
|
3713
|
-
const scriptPath =
|
|
3714
|
-
|
|
4389
|
+
const thisFile = fileURLToPath2(import.meta.url);
|
|
4390
|
+
const scriptPath = path16.join(
|
|
4391
|
+
path16.dirname(thisFile),
|
|
3715
4392
|
"..",
|
|
3716
4393
|
"bin",
|
|
3717
4394
|
"exe-export-behaviors.js"
|
|
3718
4395
|
);
|
|
3719
|
-
return
|
|
4396
|
+
return existsSync12(scriptPath) ? scriptPath : null;
|
|
3720
4397
|
} catch {
|
|
3721
4398
|
return null;
|
|
3722
4399
|
}
|
|
@@ -3782,12 +4459,12 @@ function extractRootExe(name) {
|
|
|
3782
4459
|
return parts.length > 0 ? parts[parts.length - 1] : null;
|
|
3783
4460
|
}
|
|
3784
4461
|
function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
3785
|
-
if (!
|
|
3786
|
-
|
|
4462
|
+
if (!existsSync12(SESSION_CACHE)) {
|
|
4463
|
+
mkdirSync6(SESSION_CACHE, { recursive: true });
|
|
3787
4464
|
}
|
|
3788
4465
|
const rootExe = extractRootExe(parentExe) ?? parentExe;
|
|
3789
|
-
const filePath =
|
|
3790
|
-
|
|
4466
|
+
const filePath = path16.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
|
|
4467
|
+
writeFileSync7(filePath, JSON.stringify({
|
|
3791
4468
|
parentExe: rootExe,
|
|
3792
4469
|
dispatchedBy: dispatchedBy || rootExe,
|
|
3793
4470
|
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -3795,7 +4472,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
3795
4472
|
}
|
|
3796
4473
|
function getParentExe(sessionKey) {
|
|
3797
4474
|
try {
|
|
3798
|
-
const data = JSON.parse(
|
|
4475
|
+
const data = JSON.parse(readFileSync11(path16.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
3799
4476
|
return data.parentExe || null;
|
|
3800
4477
|
} catch {
|
|
3801
4478
|
return null;
|
|
@@ -3803,8 +4480,8 @@ function getParentExe(sessionKey) {
|
|
|
3803
4480
|
}
|
|
3804
4481
|
function getDispatchedBy(sessionKey) {
|
|
3805
4482
|
try {
|
|
3806
|
-
const data = JSON.parse(
|
|
3807
|
-
|
|
4483
|
+
const data = JSON.parse(readFileSync11(
|
|
4484
|
+
path16.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
|
|
3808
4485
|
"utf8"
|
|
3809
4486
|
));
|
|
3810
4487
|
return data.dispatchedBy ?? data.parentExe ?? null;
|
|
@@ -3830,10 +4507,10 @@ function isEmployeeAlive(sessionName) {
|
|
|
3830
4507
|
}
|
|
3831
4508
|
function findFreeInstance(employeeName, exeSession, maxInstances = 10, isAlive = isEmployeeAlive) {
|
|
3832
4509
|
const base = employeeSessionName(employeeName, exeSession);
|
|
3833
|
-
if (!isAlive(base) &&
|
|
4510
|
+
if (!isAlive(base) && acquireSpawnLock2(base)) return 0;
|
|
3834
4511
|
for (let i = 2; i <= maxInstances; i++) {
|
|
3835
4512
|
const candidate = employeeSessionName(employeeName, exeSession, i);
|
|
3836
|
-
if (!isAlive(candidate) &&
|
|
4513
|
+
if (!isAlive(candidate) && acquireSpawnLock2(candidate)) return i;
|
|
3837
4514
|
}
|
|
3838
4515
|
return null;
|
|
3839
4516
|
}
|
|
@@ -3865,32 +4542,50 @@ async function verifyPaneAtCapacity(sessionName) {
|
|
|
3865
4542
|
}
|
|
3866
4543
|
function readDebounceState() {
|
|
3867
4544
|
try {
|
|
3868
|
-
if (!
|
|
3869
|
-
|
|
4545
|
+
if (!existsSync12(DEBOUNCE_FILE)) return {};
|
|
4546
|
+
const raw = JSON.parse(readFileSync11(DEBOUNCE_FILE, "utf8"));
|
|
4547
|
+
const state = {};
|
|
4548
|
+
for (const [key, val] of Object.entries(raw)) {
|
|
4549
|
+
if (typeof val === "number") {
|
|
4550
|
+
state[key] = { lastSent: val, pending: 0 };
|
|
4551
|
+
} else if (val && typeof val === "object" && "lastSent" in val) {
|
|
4552
|
+
state[key] = val;
|
|
4553
|
+
}
|
|
4554
|
+
}
|
|
4555
|
+
return state;
|
|
3870
4556
|
} catch {
|
|
3871
4557
|
return {};
|
|
3872
4558
|
}
|
|
3873
4559
|
}
|
|
3874
4560
|
function writeDebounceState(state) {
|
|
3875
4561
|
try {
|
|
3876
|
-
if (!
|
|
3877
|
-
|
|
4562
|
+
if (!existsSync12(SESSION_CACHE)) mkdirSync6(SESSION_CACHE, { recursive: true });
|
|
4563
|
+
writeFileSync7(DEBOUNCE_FILE, JSON.stringify(state));
|
|
3878
4564
|
} catch {
|
|
3879
4565
|
}
|
|
3880
4566
|
}
|
|
3881
4567
|
function isDebounced(targetSession) {
|
|
3882
4568
|
const state = readDebounceState();
|
|
3883
|
-
const
|
|
3884
|
-
|
|
4569
|
+
const entry = state[targetSession];
|
|
4570
|
+
const lastSent = entry?.lastSent ?? 0;
|
|
4571
|
+
if (Date.now() - lastSent < INTERCOM_DEBOUNCE_MS) {
|
|
4572
|
+
if (!state[targetSession]) state[targetSession] = { lastSent, pending: 0 };
|
|
4573
|
+
state[targetSession].pending++;
|
|
4574
|
+
writeDebounceState(state);
|
|
4575
|
+
return true;
|
|
4576
|
+
}
|
|
4577
|
+
return false;
|
|
3885
4578
|
}
|
|
3886
4579
|
function recordDebounce(targetSession) {
|
|
3887
4580
|
const state = readDebounceState();
|
|
3888
|
-
state[targetSession]
|
|
4581
|
+
const batched = state[targetSession]?.pending ?? 0;
|
|
4582
|
+
state[targetSession] = { lastSent: Date.now(), pending: 0 };
|
|
3889
4583
|
const cutoff = Date.now() - DEBOUNCE_CLEANUP_AGE_MS;
|
|
3890
4584
|
for (const key of Object.keys(state)) {
|
|
3891
|
-
if ((state[key] ?? 0) < cutoff) delete state[key];
|
|
4585
|
+
if ((state[key]?.lastSent ?? 0) < cutoff) delete state[key];
|
|
3892
4586
|
}
|
|
3893
4587
|
writeDebounceState(state);
|
|
4588
|
+
return batched;
|
|
3894
4589
|
}
|
|
3895
4590
|
function logIntercom(msg) {
|
|
3896
4591
|
const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${msg}
|
|
@@ -3935,7 +4630,7 @@ function sendIntercom(targetSession) {
|
|
|
3935
4630
|
return "skipped_exe";
|
|
3936
4631
|
}
|
|
3937
4632
|
if (isDebounced(targetSession)) {
|
|
3938
|
-
logIntercom(`DEBOUNCE \u2192 ${targetSession} (
|
|
4633
|
+
logIntercom(`DEBOUNCE \u2192 ${targetSession} (nudge batched, task safe in DB)`);
|
|
3939
4634
|
return "debounced";
|
|
3940
4635
|
}
|
|
3941
4636
|
try {
|
|
@@ -3947,14 +4642,14 @@ function sendIntercom(targetSession) {
|
|
|
3947
4642
|
const sessionState = getSessionState(targetSession);
|
|
3948
4643
|
if (sessionState === "no_claude") {
|
|
3949
4644
|
queueIntercom(targetSession, "claude not running in session");
|
|
3950
|
-
recordDebounce(targetSession);
|
|
3951
|
-
logIntercom(`QUEUED \u2192 ${targetSession} (no claude process
|
|
4645
|
+
const batched2 = recordDebounce(targetSession);
|
|
4646
|
+
logIntercom(`QUEUED \u2192 ${targetSession} (no claude process)${batched2 > 0 ? ` [${batched2} batched]` : ""}`);
|
|
3952
4647
|
return "queued";
|
|
3953
4648
|
}
|
|
3954
4649
|
if (sessionState === "thinking" || sessionState === "tool") {
|
|
3955
4650
|
queueIntercom(targetSession, "session busy at send time");
|
|
3956
|
-
recordDebounce(targetSession);
|
|
3957
|
-
logIntercom(`QUEUED \u2192 ${targetSession} (session busy
|
|
4651
|
+
const batched2 = recordDebounce(targetSession);
|
|
4652
|
+
logIntercom(`QUEUED \u2192 ${targetSession} (session busy)${batched2 > 0 ? ` [${batched2} batched]` : ""}`);
|
|
3958
4653
|
return "queued";
|
|
3959
4654
|
}
|
|
3960
4655
|
if (transport.isPaneInCopyMode(targetSession)) {
|
|
@@ -3962,8 +4657,8 @@ function sendIntercom(targetSession) {
|
|
|
3962
4657
|
transport.sendKeys(targetSession, "q");
|
|
3963
4658
|
}
|
|
3964
4659
|
transport.sendKeys(targetSession, "/exe-intercom");
|
|
3965
|
-
recordDebounce(targetSession);
|
|
3966
|
-
logIntercom(`DELIVERED \u2192 ${targetSession} (fire-and-forget)`);
|
|
4660
|
+
const batched = recordDebounce(targetSession);
|
|
4661
|
+
logIntercom(`DELIVERED \u2192 ${targetSession}${batched > 0 ? ` [${batched} nudges batched during debounce]` : ""} (fire-and-forget)`);
|
|
3967
4662
|
return "delivered";
|
|
3968
4663
|
} catch {
|
|
3969
4664
|
logIntercom(`FAIL \u2192 ${targetSession}`);
|
|
@@ -3993,7 +4688,7 @@ function notifyParentExe(sessionKey) {
|
|
|
3993
4688
|
return true;
|
|
3994
4689
|
}
|
|
3995
4690
|
function ensureEmployee(employeeName, exeSession, projectDir, opts) {
|
|
3996
|
-
if (
|
|
4691
|
+
if (isCoordinatorName(employeeName)) {
|
|
3997
4692
|
return { status: "failed", sessionName: "", error: "The COO is not a dispatchable employee" };
|
|
3998
4693
|
}
|
|
3999
4694
|
try {
|
|
@@ -4065,26 +4760,26 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4065
4760
|
const transport = getTransport();
|
|
4066
4761
|
const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
|
|
4067
4762
|
const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
|
|
4068
|
-
const logDir =
|
|
4069
|
-
const logFile =
|
|
4070
|
-
if (!
|
|
4071
|
-
|
|
4763
|
+
const logDir = path16.join(os8.homedir(), ".exe-os", "session-logs");
|
|
4764
|
+
const logFile = path16.join(logDir, `${instanceLabel}-${Date.now()}.log`);
|
|
4765
|
+
if (!existsSync12(logDir)) {
|
|
4766
|
+
mkdirSync6(logDir, { recursive: true });
|
|
4072
4767
|
}
|
|
4073
4768
|
transport.kill(sessionName);
|
|
4074
4769
|
let cleanupSuffix = "";
|
|
4075
4770
|
try {
|
|
4076
|
-
const thisFile =
|
|
4077
|
-
const cleanupScript =
|
|
4078
|
-
if (
|
|
4771
|
+
const thisFile = fileURLToPath2(import.meta.url);
|
|
4772
|
+
const cleanupScript = path16.join(path16.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
|
|
4773
|
+
if (existsSync12(cleanupScript)) {
|
|
4079
4774
|
cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
|
|
4080
4775
|
}
|
|
4081
4776
|
} catch {
|
|
4082
4777
|
}
|
|
4083
4778
|
try {
|
|
4084
|
-
const claudeJsonPath =
|
|
4779
|
+
const claudeJsonPath = path16.join(os8.homedir(), ".claude.json");
|
|
4085
4780
|
let claudeJson = {};
|
|
4086
4781
|
try {
|
|
4087
|
-
claudeJson = JSON.parse(
|
|
4782
|
+
claudeJson = JSON.parse(readFileSync11(claudeJsonPath, "utf8"));
|
|
4088
4783
|
} catch {
|
|
4089
4784
|
}
|
|
4090
4785
|
if (!claudeJson.projects) claudeJson.projects = {};
|
|
@@ -4092,17 +4787,17 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4092
4787
|
const trustDir = opts?.cwd ?? projectDir;
|
|
4093
4788
|
if (!projects[trustDir]) projects[trustDir] = {};
|
|
4094
4789
|
projects[trustDir].hasTrustDialogAccepted = true;
|
|
4095
|
-
|
|
4790
|
+
writeFileSync7(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
4096
4791
|
} catch {
|
|
4097
4792
|
}
|
|
4098
4793
|
try {
|
|
4099
|
-
const settingsDir =
|
|
4794
|
+
const settingsDir = path16.join(os8.homedir(), ".claude", "projects");
|
|
4100
4795
|
const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
|
|
4101
|
-
const projSettingsDir =
|
|
4102
|
-
const settingsPath =
|
|
4796
|
+
const projSettingsDir = path16.join(settingsDir, normalizedKey);
|
|
4797
|
+
const settingsPath = path16.join(projSettingsDir, "settings.json");
|
|
4103
4798
|
let settings = {};
|
|
4104
4799
|
try {
|
|
4105
|
-
settings = JSON.parse(
|
|
4800
|
+
settings = JSON.parse(readFileSync11(settingsPath, "utf8"));
|
|
4106
4801
|
} catch {
|
|
4107
4802
|
}
|
|
4108
4803
|
const perms = settings.permissions ?? {};
|
|
@@ -4130,21 +4825,24 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4130
4825
|
if (changed) {
|
|
4131
4826
|
perms.allow = allow;
|
|
4132
4827
|
settings.permissions = perms;
|
|
4133
|
-
|
|
4134
|
-
|
|
4828
|
+
mkdirSync6(projSettingsDir, { recursive: true });
|
|
4829
|
+
writeFileSync7(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
4135
4830
|
}
|
|
4136
4831
|
} catch {
|
|
4137
4832
|
}
|
|
4138
4833
|
const spawnCwd = opts?.cwd ?? projectDir;
|
|
4139
4834
|
const useExeAgent = !!(opts?.model && opts?.provider);
|
|
4140
|
-
const
|
|
4835
|
+
const agentRtConfig = getAgentRuntime(employeeName);
|
|
4836
|
+
const useCodex = !useExeAgent && agentRtConfig.runtime === "codex";
|
|
4837
|
+
const useOpencode = !useExeAgent && !useCodex && agentRtConfig.runtime === "opencode";
|
|
4838
|
+
const ccProvider = useExeAgent || useCodex || useOpencode ? DEFAULT_PROVIDER : detectActiveProvider();
|
|
4141
4839
|
const useBinSymlink = ccProvider !== DEFAULT_PROVIDER;
|
|
4142
4840
|
let identityFlag = "";
|
|
4143
4841
|
let behaviorsFlag = "";
|
|
4144
4842
|
let legacyFallbackWarned = false;
|
|
4145
4843
|
if (!useExeAgent && !useBinSymlink) {
|
|
4146
|
-
const identityPath =
|
|
4147
|
-
|
|
4844
|
+
const identityPath = path16.join(
|
|
4845
|
+
os8.homedir(),
|
|
4148
4846
|
".exe-os",
|
|
4149
4847
|
"identity",
|
|
4150
4848
|
`${employeeName}.md`
|
|
@@ -4153,13 +4851,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4153
4851
|
const hasAgentFlag = claudeSupportsAgentFlag();
|
|
4154
4852
|
if (hasAgentFlag) {
|
|
4155
4853
|
identityFlag = ` --agent ${employeeName}`;
|
|
4156
|
-
} else if (
|
|
4854
|
+
} else if (existsSync12(identityPath)) {
|
|
4157
4855
|
identityFlag = ` --append-system-prompt-file ${identityPath}`;
|
|
4158
4856
|
legacyFallbackWarned = true;
|
|
4159
4857
|
}
|
|
4160
4858
|
const behaviorsFile = exportBehaviorsSync(
|
|
4161
4859
|
employeeName,
|
|
4162
|
-
|
|
4860
|
+
path16.basename(spawnCwd),
|
|
4163
4861
|
sessionName
|
|
4164
4862
|
);
|
|
4165
4863
|
if (behaviorsFile) {
|
|
@@ -4174,16 +4872,16 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4174
4872
|
}
|
|
4175
4873
|
let sessionContextFlag = "";
|
|
4176
4874
|
try {
|
|
4177
|
-
const ctxDir =
|
|
4178
|
-
|
|
4179
|
-
const ctxFile =
|
|
4875
|
+
const ctxDir = path16.join(os8.homedir(), ".exe-os", "session-cache");
|
|
4876
|
+
mkdirSync6(ctxDir, { recursive: true });
|
|
4877
|
+
const ctxFile = path16.join(ctxDir, `session-context-${sessionName}.md`);
|
|
4180
4878
|
const ctxContent = [
|
|
4181
4879
|
`## Session Context`,
|
|
4182
4880
|
`You are running in tmux session: ${sessionName}.`,
|
|
4183
4881
|
`Your parent coordinator session is ${exeSession}.`,
|
|
4184
4882
|
`Your employees (if any) use the -${exeSession} suffix.`
|
|
4185
4883
|
].join("\n");
|
|
4186
|
-
|
|
4884
|
+
writeFileSync7(ctxFile, ctxContent);
|
|
4187
4885
|
sessionContextFlag = ` --append-system-prompt-file ${ctxFile}`;
|
|
4188
4886
|
} catch {
|
|
4189
4887
|
}
|
|
@@ -4197,9 +4895,48 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4197
4895
|
}
|
|
4198
4896
|
}
|
|
4199
4897
|
}
|
|
4898
|
+
if (useCodex) {
|
|
4899
|
+
const codexCfg = RUNTIME_TABLE.codex;
|
|
4900
|
+
if (codexCfg?.apiKeyEnv) {
|
|
4901
|
+
const keyVal = process.env[codexCfg.apiKeyEnv];
|
|
4902
|
+
if (keyVal) {
|
|
4903
|
+
envPrefix = `${envPrefix} ${codexCfg.apiKeyEnv}=${keyVal}`;
|
|
4904
|
+
}
|
|
4905
|
+
}
|
|
4906
|
+
envPrefix = `${envPrefix} EXE_AGENT_MODEL=${agentRtConfig.model}`;
|
|
4907
|
+
}
|
|
4908
|
+
if (useOpencode) {
|
|
4909
|
+
const ocCfg = PROVIDER_TABLE.opencode;
|
|
4910
|
+
if (ocCfg?.apiKeyEnv) {
|
|
4911
|
+
const keyVal = process.env[ocCfg.apiKeyEnv];
|
|
4912
|
+
if (keyVal) {
|
|
4913
|
+
envPrefix = `${envPrefix} ${ocCfg.apiKeyEnv}=${keyVal}`;
|
|
4914
|
+
}
|
|
4915
|
+
}
|
|
4916
|
+
envPrefix = `${envPrefix} ANTHROPIC_MODEL=${agentRtConfig.model}`;
|
|
4917
|
+
}
|
|
4918
|
+
if (!useExeAgent && !useCodex && !useOpencode && !useBinSymlink) {
|
|
4919
|
+
const defaultClaudeModel = DEFAULT_MODELS.claude;
|
|
4920
|
+
if (agentRtConfig.runtime === "claude" && agentRtConfig.model !== defaultClaudeModel) {
|
|
4921
|
+
envPrefix = `${envPrefix} ANTHROPIC_MODEL=${agentRtConfig.model}`;
|
|
4922
|
+
}
|
|
4923
|
+
}
|
|
4200
4924
|
let spawnCommand;
|
|
4201
4925
|
if (useExeAgent) {
|
|
4202
4926
|
spawnCommand = `${envPrefix} exe-agent --employee ${employeeName} --model ${opts.model} --provider ${opts.provider}${cleanupSuffix}`;
|
|
4927
|
+
} else if (useCodex) {
|
|
4928
|
+
process.stderr.write(
|
|
4929
|
+
`[tmux-routing] agent-config: ${employeeName} \u2192 codex (${agentRtConfig.model})
|
|
4930
|
+
`
|
|
4931
|
+
);
|
|
4932
|
+
spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName}${cleanupSuffix}`;
|
|
4933
|
+
} else if (useOpencode) {
|
|
4934
|
+
const binName = `${employeeName}-opencode`;
|
|
4935
|
+
process.stderr.write(
|
|
4936
|
+
`[tmux-routing] agent-config: ${employeeName} \u2192 opencode (${agentRtConfig.model})
|
|
4937
|
+
`
|
|
4938
|
+
);
|
|
4939
|
+
spawnCommand = `${envPrefix} ${binName}${cleanupSuffix}`;
|
|
4203
4940
|
} else if (useBinSymlink) {
|
|
4204
4941
|
const binName = `${employeeName}-${ccProvider}`;
|
|
4205
4942
|
process.stderr.write(
|
|
@@ -4215,17 +4952,19 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4215
4952
|
command: spawnCommand
|
|
4216
4953
|
});
|
|
4217
4954
|
if (spawnResult.error) {
|
|
4218
|
-
|
|
4955
|
+
releaseSpawnLock2(sessionName);
|
|
4219
4956
|
return { sessionName, error: `tmux new-session failed: ${spawnResult.error}` };
|
|
4220
4957
|
}
|
|
4221
4958
|
transport.pipeLog(sessionName, logFile);
|
|
4222
4959
|
try {
|
|
4223
4960
|
const mySession = getMySession();
|
|
4224
|
-
const dispatchInfo =
|
|
4225
|
-
|
|
4961
|
+
const dispatchInfo = path16.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
|
|
4962
|
+
writeFileSync7(dispatchInfo, JSON.stringify({
|
|
4226
4963
|
dispatchedBy: mySession,
|
|
4227
4964
|
rootExe: exeSession,
|
|
4228
|
-
provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : "anthropic",
|
|
4965
|
+
provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : useCodex ? "openai" : useOpencode ? "opencode" : "anthropic",
|
|
4966
|
+
runtime: useCodex ? "codex" : useOpencode ? "opencode" : useExeAgent ? "exe-agent" : "claude",
|
|
4967
|
+
model: useCodex ? agentRtConfig.model : useOpencode ? agentRtConfig.model : void 0,
|
|
4229
4968
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4230
4969
|
}));
|
|
4231
4970
|
} catch {
|
|
@@ -4243,6 +4982,11 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4243
4982
|
booted = true;
|
|
4244
4983
|
break;
|
|
4245
4984
|
}
|
|
4985
|
+
} else if (useCodex) {
|
|
4986
|
+
if (pane.includes("codex") || pane.includes("Codex") || pane.includes("exe-start-codex")) {
|
|
4987
|
+
booted = true;
|
|
4988
|
+
break;
|
|
4989
|
+
}
|
|
4246
4990
|
} else {
|
|
4247
4991
|
if (pane.includes("Claude Code") || pane.includes("\u276F")) {
|
|
4248
4992
|
booted = true;
|
|
@@ -4253,10 +4997,11 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4253
4997
|
}
|
|
4254
4998
|
}
|
|
4255
4999
|
if (!booted) {
|
|
4256
|
-
|
|
4257
|
-
|
|
5000
|
+
releaseSpawnLock2(sessionName);
|
|
5001
|
+
const runtimeLabel = useExeAgent ? "exe-agent" : useCodex ? "codex" : "claude";
|
|
5002
|
+
return { sessionName, error: `${runtimeLabel} did not boot within 15s` };
|
|
4258
5003
|
}
|
|
4259
|
-
if (!useExeAgent) {
|
|
5004
|
+
if (!useExeAgent && !useCodex) {
|
|
4260
5005
|
try {
|
|
4261
5006
|
transport.sendKeys(sessionName, `/exe-call ${employeeName}`);
|
|
4262
5007
|
} catch {
|
|
@@ -4270,7 +5015,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4270
5015
|
pid: 0,
|
|
4271
5016
|
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4272
5017
|
});
|
|
4273
|
-
|
|
5018
|
+
releaseSpawnLock2(sessionName);
|
|
4274
5019
|
return { sessionName };
|
|
4275
5020
|
}
|
|
4276
5021
|
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;
|
|
@@ -4283,17 +5028,19 @@ var init_tmux_routing = __esm({
|
|
|
4283
5028
|
init_cc_agent_support();
|
|
4284
5029
|
init_mcp_prefix();
|
|
4285
5030
|
init_provider_table();
|
|
5031
|
+
init_agent_config();
|
|
5032
|
+
init_runtime_table();
|
|
4286
5033
|
init_intercom_queue();
|
|
4287
5034
|
init_plan_limits();
|
|
4288
5035
|
init_employees();
|
|
4289
|
-
SPAWN_LOCK_DIR =
|
|
4290
|
-
SESSION_CACHE =
|
|
5036
|
+
SPAWN_LOCK_DIR = path16.join(os8.homedir(), ".exe-os", "spawn-locks");
|
|
5037
|
+
SESSION_CACHE = path16.join(os8.homedir(), ".exe-os", "session-cache");
|
|
4291
5038
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
4292
5039
|
VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
|
|
4293
5040
|
VERIFY_PANE_LINES = 200;
|
|
4294
5041
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
4295
|
-
INTERCOM_LOG2 =
|
|
4296
|
-
DEBOUNCE_FILE =
|
|
5042
|
+
INTERCOM_LOG2 = path16.join(os8.homedir(), ".exe-os", "intercom.log");
|
|
5043
|
+
DEBOUNCE_FILE = path16.join(SESSION_CACHE, "intercom-debounce.json");
|
|
4297
5044
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
4298
5045
|
BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
|
|
4299
5046
|
}
|
|
@@ -4310,14 +5057,14 @@ var init_memory = __esm({
|
|
|
4310
5057
|
|
|
4311
5058
|
// src/lib/keychain.ts
|
|
4312
5059
|
import { readFile as readFile4, writeFile as writeFile5, unlink, mkdir as mkdir4, chmod as chmod2 } from "fs/promises";
|
|
4313
|
-
import { existsSync as
|
|
4314
|
-
import
|
|
4315
|
-
import
|
|
5060
|
+
import { existsSync as existsSync13 } from "fs";
|
|
5061
|
+
import path17 from "path";
|
|
5062
|
+
import os9 from "os";
|
|
4316
5063
|
function getKeyDir() {
|
|
4317
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
5064
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path17.join(os9.homedir(), ".exe-os");
|
|
4318
5065
|
}
|
|
4319
5066
|
function getKeyPath() {
|
|
4320
|
-
return
|
|
5067
|
+
return path17.join(getKeyDir(), "master.key");
|
|
4321
5068
|
}
|
|
4322
5069
|
async function tryKeytar() {
|
|
4323
5070
|
try {
|
|
@@ -4338,13 +5085,21 @@ async function getMasterKey() {
|
|
|
4338
5085
|
}
|
|
4339
5086
|
}
|
|
4340
5087
|
const keyPath = getKeyPath();
|
|
4341
|
-
if (!
|
|
5088
|
+
if (!existsSync13(keyPath)) {
|
|
5089
|
+
process.stderr.write(
|
|
5090
|
+
`[keychain] Key not found at ${keyPath} (HOME=${os9.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
5091
|
+
`
|
|
5092
|
+
);
|
|
4342
5093
|
return null;
|
|
4343
5094
|
}
|
|
4344
5095
|
try {
|
|
4345
5096
|
const content = await readFile4(keyPath, "utf-8");
|
|
4346
5097
|
return Buffer.from(content.trim(), "base64");
|
|
4347
|
-
} catch {
|
|
5098
|
+
} catch (err) {
|
|
5099
|
+
process.stderr.write(
|
|
5100
|
+
`[keychain] Key read failed at ${keyPath}: ${err instanceof Error ? err.message : String(err)}
|
|
5101
|
+
`
|
|
5102
|
+
);
|
|
4348
5103
|
return null;
|
|
4349
5104
|
}
|
|
4350
5105
|
}
|
|
@@ -4370,13 +5125,13 @@ __export(shard_manager_exports, {
|
|
|
4370
5125
|
listShards: () => listShards,
|
|
4371
5126
|
shardExists: () => shardExists
|
|
4372
5127
|
});
|
|
4373
|
-
import
|
|
4374
|
-
import { existsSync as
|
|
5128
|
+
import path18 from "path";
|
|
5129
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync7, readdirSync as readdirSync3 } from "fs";
|
|
4375
5130
|
import { createClient as createClient2 } from "@libsql/client";
|
|
4376
5131
|
function initShardManager(encryptionKey) {
|
|
4377
5132
|
_encryptionKey = encryptionKey;
|
|
4378
|
-
if (!
|
|
4379
|
-
|
|
5133
|
+
if (!existsSync14(SHARDS_DIR)) {
|
|
5134
|
+
mkdirSync7(SHARDS_DIR, { recursive: true });
|
|
4380
5135
|
}
|
|
4381
5136
|
_shardingEnabled = true;
|
|
4382
5137
|
}
|
|
@@ -4396,7 +5151,7 @@ function getShardClient(projectName) {
|
|
|
4396
5151
|
}
|
|
4397
5152
|
const cached = _shards.get(safeName);
|
|
4398
5153
|
if (cached) return cached;
|
|
4399
|
-
const dbPath =
|
|
5154
|
+
const dbPath = path18.join(SHARDS_DIR, `${safeName}.db`);
|
|
4400
5155
|
const client = createClient2({
|
|
4401
5156
|
url: `file:${dbPath}`,
|
|
4402
5157
|
encryptionKey: _encryptionKey
|
|
@@ -4406,10 +5161,10 @@ function getShardClient(projectName) {
|
|
|
4406
5161
|
}
|
|
4407
5162
|
function shardExists(projectName) {
|
|
4408
5163
|
const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
4409
|
-
return
|
|
5164
|
+
return existsSync14(path18.join(SHARDS_DIR, `${safeName}.db`));
|
|
4410
5165
|
}
|
|
4411
5166
|
function listShards() {
|
|
4412
|
-
if (!
|
|
5167
|
+
if (!existsSync14(SHARDS_DIR)) return [];
|
|
4413
5168
|
return readdirSync3(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
4414
5169
|
}
|
|
4415
5170
|
async function ensureShardSchema(client) {
|
|
@@ -4595,7 +5350,7 @@ var init_shard_manager = __esm({
|
|
|
4595
5350
|
"src/lib/shard-manager.ts"() {
|
|
4596
5351
|
"use strict";
|
|
4597
5352
|
init_config();
|
|
4598
|
-
SHARDS_DIR =
|
|
5353
|
+
SHARDS_DIR = path18.join(EXE_AI_DIR, "shards");
|
|
4599
5354
|
_shards = /* @__PURE__ */ new Map();
|
|
4600
5355
|
_encryptionKey = null;
|
|
4601
5356
|
_shardingEnabled = false;
|
|
@@ -4720,7 +5475,7 @@ __export(global_procedures_exports, {
|
|
|
4720
5475
|
loadGlobalProcedures: () => loadGlobalProcedures,
|
|
4721
5476
|
storeGlobalProcedure: () => storeGlobalProcedure
|
|
4722
5477
|
});
|
|
4723
|
-
import { randomUUID as
|
|
5478
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
4724
5479
|
async function loadGlobalProcedures() {
|
|
4725
5480
|
const client = getClient();
|
|
4726
5481
|
const result = await client.execute({
|
|
@@ -4749,7 +5504,7 @@ ${sections.join("\n\n")}
|
|
|
4749
5504
|
`;
|
|
4750
5505
|
}
|
|
4751
5506
|
async function storeGlobalProcedure(input) {
|
|
4752
|
-
const id =
|
|
5507
|
+
const id = randomUUID4();
|
|
4753
5508
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4754
5509
|
const client = getClient();
|
|
4755
5510
|
await client.execute({
|
|
@@ -4800,6 +5555,7 @@ __export(store_exports, {
|
|
|
4800
5555
|
vectorToBlob: () => vectorToBlob,
|
|
4801
5556
|
writeMemory: () => writeMemory
|
|
4802
5557
|
});
|
|
5558
|
+
import { createHash } from "crypto";
|
|
4803
5559
|
function isBusyError2(err) {
|
|
4804
5560
|
if (err instanceof Error) {
|
|
4805
5561
|
const msg = err.message.toLowerCase();
|
|
@@ -4873,12 +5629,52 @@ function classifyTier(record) {
|
|
|
4873
5629
|
if (["store_memory", "manual"].includes(record.tool_name ?? "") && (record.importance ?? 0) >= 5) return 2;
|
|
4874
5630
|
return 3;
|
|
4875
5631
|
}
|
|
5632
|
+
function inferFilePaths(record) {
|
|
5633
|
+
if (!["Read", "Write", "Edit"].includes(record.tool_name)) return null;
|
|
5634
|
+
const firstLine = record.raw_text.split("\n")[0] ?? "";
|
|
5635
|
+
const match = firstLine.match(/(\/[\w./-]+\.\w+)/);
|
|
5636
|
+
return match ? JSON.stringify([match[1]]) : null;
|
|
5637
|
+
}
|
|
5638
|
+
function inferCommitHash(record) {
|
|
5639
|
+
if (record.tool_name !== "Bash") return null;
|
|
5640
|
+
const match = record.raw_text.match(/\b([a-f0-9]{7,40})\b/);
|
|
5641
|
+
return match ? match[1] : null;
|
|
5642
|
+
}
|
|
5643
|
+
function inferLanguageType(record) {
|
|
5644
|
+
const text = record.raw_text;
|
|
5645
|
+
if (!text || text.length < 10) return null;
|
|
5646
|
+
const trimmed = text.trimStart();
|
|
5647
|
+
if (trimmed.startsWith("{") || trimmed.startsWith("[")) return "json";
|
|
5648
|
+
if (/\b(SELECT|INSERT|UPDATE|DELETE|CREATE TABLE|ALTER TABLE)\b/i.test(text)) return "sql";
|
|
5649
|
+
if (/\b(function |const |import |export |class |def |async |=>)\b/.test(text)) return "code";
|
|
5650
|
+
if (trimmed.startsWith("#") || trimmed.startsWith("*")) return "prose";
|
|
5651
|
+
return "mixed";
|
|
5652
|
+
}
|
|
5653
|
+
function inferDomain(record) {
|
|
5654
|
+
const proj = (record.project_name ?? "").toLowerCase();
|
|
5655
|
+
if (proj.includes("marketing") || proj.includes("content")) return "marketing";
|
|
5656
|
+
if (proj.includes("crm") || proj.includes("customer")) return "customer";
|
|
5657
|
+
return null;
|
|
5658
|
+
}
|
|
4876
5659
|
async function writeMemory(record) {
|
|
4877
5660
|
if (record.vector !== null && record.vector.length !== EMBEDDING_DIM) {
|
|
4878
5661
|
throw new Error(
|
|
4879
5662
|
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
4880
5663
|
);
|
|
4881
5664
|
}
|
|
5665
|
+
const contentHash = createHash("md5").update(record.raw_text).digest("hex");
|
|
5666
|
+
if (_pendingRecords.some((r) => r.content_hash === contentHash && r.agent_id === record.agent_id)) {
|
|
5667
|
+
return;
|
|
5668
|
+
}
|
|
5669
|
+
try {
|
|
5670
|
+
const client = getClient();
|
|
5671
|
+
const existing = await client.execute({
|
|
5672
|
+
sql: "SELECT id FROM memories WHERE content_hash = ? AND agent_id = ? LIMIT 1",
|
|
5673
|
+
args: [contentHash, record.agent_id]
|
|
5674
|
+
});
|
|
5675
|
+
if (existing.rows.length > 0) return;
|
|
5676
|
+
} catch {
|
|
5677
|
+
}
|
|
4882
5678
|
const dbRow = {
|
|
4883
5679
|
id: record.id,
|
|
4884
5680
|
agent_id: record.agent_id,
|
|
@@ -4908,7 +5704,23 @@ async function writeMemory(record) {
|
|
|
4908
5704
|
supersedes_id: record.supersedes_id ?? null,
|
|
4909
5705
|
draft: record.draft ? 1 : 0,
|
|
4910
5706
|
memory_type: record.memory_type ?? "raw",
|
|
4911
|
-
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null
|
|
5707
|
+
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
5708
|
+
content_hash: contentHash,
|
|
5709
|
+
intent: record.intent ?? null,
|
|
5710
|
+
outcome: record.outcome ?? null,
|
|
5711
|
+
domain: record.domain ?? inferDomain(record),
|
|
5712
|
+
referenced_entities: record.referenced_entities ?? null,
|
|
5713
|
+
retrieval_count: record.retrieval_count ?? 0,
|
|
5714
|
+
chain_position: record.chain_position ?? null,
|
|
5715
|
+
review_status: record.review_status ?? null,
|
|
5716
|
+
context_window_pct: record.context_window_pct ?? null,
|
|
5717
|
+
file_paths: record.file_paths ?? inferFilePaths(record),
|
|
5718
|
+
commit_hash: record.commit_hash ?? inferCommitHash(record),
|
|
5719
|
+
duration_ms: record.duration_ms ?? null,
|
|
5720
|
+
token_cost: record.token_cost ?? null,
|
|
5721
|
+
audience: record.audience ?? null,
|
|
5722
|
+
language_type: record.language_type ?? inferLanguageType(record),
|
|
5723
|
+
parent_memory_id: record.parent_memory_id ?? null
|
|
4912
5724
|
};
|
|
4913
5725
|
_pendingRecords.push(dbRow);
|
|
4914
5726
|
orgBus.emit({
|
|
@@ -4966,80 +5778,85 @@ async function flushBatch() {
|
|
|
4966
5778
|
const draft = row.draft ? 1 : 0;
|
|
4967
5779
|
const memoryType = row.memory_type ?? "raw";
|
|
4968
5780
|
const trajectory = row.trajectory ?? null;
|
|
4969
|
-
|
|
4970
|
-
|
|
4971
|
-
|
|
5781
|
+
const contentHash = row.content_hash ?? null;
|
|
5782
|
+
const intent = row.intent ?? null;
|
|
5783
|
+
const outcome = row.outcome ?? null;
|
|
5784
|
+
const domain = row.domain ?? null;
|
|
5785
|
+
const referencedEntities = row.referenced_entities ?? null;
|
|
5786
|
+
const retrievalCount = row.retrieval_count ?? 0;
|
|
5787
|
+
const chainPosition = row.chain_position ?? null;
|
|
5788
|
+
const reviewStatus = row.review_status ?? null;
|
|
5789
|
+
const contextWindowPct = row.context_window_pct ?? null;
|
|
5790
|
+
const filePaths = row.file_paths ?? null;
|
|
5791
|
+
const commitHash = row.commit_hash ?? null;
|
|
5792
|
+
const durationMs = row.duration_ms ?? null;
|
|
5793
|
+
const tokenCost = row.token_cost ?? null;
|
|
5794
|
+
const audience = row.audience ?? null;
|
|
5795
|
+
const languageType = row.language_type ?? null;
|
|
5796
|
+
const parentMemoryId = row.parent_memory_id ?? null;
|
|
5797
|
+
const cols = `id, agent_id, agent_role, session_id, timestamp,
|
|
4972
5798
|
tool_name, project_name,
|
|
4973
5799
|
has_error, raw_text, vector, version, task_id, importance, status,
|
|
4974
5800
|
confidence, last_accessed,
|
|
4975
5801
|
workspace_id, document_id, user_id, char_offset, page_number,
|
|
4976
|
-
source_path, source_type, tier, supersedes_id, draft, memory_type, trajectory
|
|
4977
|
-
|
|
4978
|
-
|
|
4979
|
-
|
|
4980
|
-
|
|
4981
|
-
|
|
4982
|
-
|
|
4983
|
-
|
|
4984
|
-
|
|
4985
|
-
|
|
4986
|
-
|
|
4987
|
-
|
|
4988
|
-
|
|
4989
|
-
|
|
4990
|
-
|
|
4991
|
-
|
|
4992
|
-
|
|
4993
|
-
|
|
4994
|
-
|
|
4995
|
-
|
|
4996
|
-
|
|
4997
|
-
|
|
4998
|
-
|
|
4999
|
-
|
|
5000
|
-
|
|
5001
|
-
|
|
5002
|
-
|
|
5003
|
-
|
|
5004
|
-
|
|
5005
|
-
|
|
5006
|
-
|
|
5007
|
-
|
|
5008
|
-
|
|
5009
|
-
|
|
5010
|
-
|
|
5011
|
-
|
|
5012
|
-
|
|
5013
|
-
|
|
5014
|
-
|
|
5015
|
-
|
|
5016
|
-
|
|
5017
|
-
|
|
5018
|
-
|
|
5019
|
-
|
|
5020
|
-
|
|
5021
|
-
|
|
5022
|
-
|
|
5023
|
-
|
|
5024
|
-
|
|
5025
|
-
|
|
5026
|
-
|
|
5027
|
-
|
|
5028
|
-
|
|
5029
|
-
|
|
5030
|
-
|
|
5031
|
-
|
|
5032
|
-
|
|
5033
|
-
|
|
5034
|
-
pageNumber,
|
|
5035
|
-
sourcePath,
|
|
5036
|
-
sourceType,
|
|
5037
|
-
tier,
|
|
5038
|
-
supersedesId,
|
|
5039
|
-
draft,
|
|
5040
|
-
memoryType,
|
|
5041
|
-
trajectory
|
|
5042
|
-
]
|
|
5802
|
+
source_path, source_type, tier, supersedes_id, draft, memory_type, trajectory, content_hash,
|
|
5803
|
+
intent, outcome, domain, referenced_entities, retrieval_count,
|
|
5804
|
+
chain_position, review_status, context_window_pct, file_paths, commit_hash,
|
|
5805
|
+
duration_ms, token_cost, audience, language_type, parent_memory_id`;
|
|
5806
|
+
const metaArgs = [
|
|
5807
|
+
intent,
|
|
5808
|
+
outcome,
|
|
5809
|
+
domain,
|
|
5810
|
+
referencedEntities,
|
|
5811
|
+
retrievalCount,
|
|
5812
|
+
chainPosition,
|
|
5813
|
+
reviewStatus,
|
|
5814
|
+
contextWindowPct,
|
|
5815
|
+
filePaths,
|
|
5816
|
+
commitHash,
|
|
5817
|
+
durationMs,
|
|
5818
|
+
tokenCost,
|
|
5819
|
+
audience,
|
|
5820
|
+
languageType,
|
|
5821
|
+
parentMemoryId
|
|
5822
|
+
];
|
|
5823
|
+
const baseArgs = [
|
|
5824
|
+
row.id,
|
|
5825
|
+
row.agent_id,
|
|
5826
|
+
row.agent_role,
|
|
5827
|
+
row.session_id,
|
|
5828
|
+
row.timestamp,
|
|
5829
|
+
row.tool_name,
|
|
5830
|
+
row.project_name,
|
|
5831
|
+
row.has_error,
|
|
5832
|
+
row.raw_text
|
|
5833
|
+
];
|
|
5834
|
+
const sharedArgs = [
|
|
5835
|
+
row.version,
|
|
5836
|
+
taskId,
|
|
5837
|
+
importance,
|
|
5838
|
+
status,
|
|
5839
|
+
confidence,
|
|
5840
|
+
lastAccessed,
|
|
5841
|
+
workspaceId,
|
|
5842
|
+
documentId,
|
|
5843
|
+
userId,
|
|
5844
|
+
charOffset,
|
|
5845
|
+
pageNumber,
|
|
5846
|
+
sourcePath,
|
|
5847
|
+
sourceType,
|
|
5848
|
+
tier,
|
|
5849
|
+
supersedesId,
|
|
5850
|
+
draft,
|
|
5851
|
+
memoryType,
|
|
5852
|
+
trajectory,
|
|
5853
|
+
contentHash
|
|
5854
|
+
];
|
|
5855
|
+
return {
|
|
5856
|
+
sql: hasVector ? `INSERT OR IGNORE INTO memories (${cols})
|
|
5857
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories (${cols})
|
|
5858
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
5859
|
+
args: hasVector ? [...baseArgs, vectorToBlob(row.vector), ...sharedArgs, ...metaArgs] : [...baseArgs, ...sharedArgs, ...metaArgs]
|
|
5043
5860
|
};
|
|
5044
5861
|
};
|
|
5045
5862
|
const globalClient = getClient();
|
|
@@ -5322,8 +6139,8 @@ function findContainingChunk(filePath, snippet) {
|
|
|
5322
6139
|
try {
|
|
5323
6140
|
const ext = filePath.split(".").pop()?.toLowerCase();
|
|
5324
6141
|
if (ext !== "ts" && ext !== "tsx" && ext !== "js" && ext !== "jsx") return "";
|
|
5325
|
-
const { readFileSync:
|
|
5326
|
-
const source =
|
|
6142
|
+
const { readFileSync: readFileSync12 } = __require("fs");
|
|
6143
|
+
const source = readFileSync12(filePath, "utf8");
|
|
5327
6144
|
const lines = source.split("\n");
|
|
5328
6145
|
const lowerSnippet = snippet.toLowerCase().slice(0, 80);
|
|
5329
6146
|
let matchLine = -1;
|
|
@@ -5389,9 +6206,9 @@ function extractBash(input, response) {
|
|
|
5389
6206
|
}
|
|
5390
6207
|
function extractGrep(input, response) {
|
|
5391
6208
|
const pattern = String(input.pattern ?? "");
|
|
5392
|
-
const
|
|
6209
|
+
const path19 = input.path ? String(input.path) : "";
|
|
5393
6210
|
const output = String(response.text ?? response.content ?? JSON.stringify(response).slice(0, MAX_OUTPUT));
|
|
5394
|
-
return `Searched for "${pattern}"${
|
|
6211
|
+
return `Searched for "${pattern}"${path19 ? ` in ${path19}` : ""}
|
|
5395
6212
|
${output.slice(0, MAX_OUTPUT)}`;
|
|
5396
6213
|
}
|
|
5397
6214
|
function extractGlob(input, response) {
|
|
@@ -7276,11 +8093,11 @@ function createQuietRenderer() {
|
|
|
7276
8093
|
init_state_bus();
|
|
7277
8094
|
|
|
7278
8095
|
// src/runtime/session-manager.ts
|
|
7279
|
-
import { randomUUID as
|
|
8096
|
+
import { randomUUID as randomUUID6 } from "crypto";
|
|
7280
8097
|
init_state_bus();
|
|
7281
8098
|
|
|
7282
8099
|
// src/runtime/exe-hooks.ts
|
|
7283
|
-
import { randomUUID as
|
|
8100
|
+
import { randomUUID as randomUUID5 } from "crypto";
|
|
7284
8101
|
function createExeOSHooks(config) {
|
|
7285
8102
|
let sessionRegistered = false;
|
|
7286
8103
|
return {
|
|
@@ -7339,7 +8156,7 @@ function createExeOSHooks(config) {
|
|
|
7339
8156
|
const toolResponse = result.isError ? { error: result.content } : { output: result.content };
|
|
7340
8157
|
const rawText = extractSemanticText2(toolName, toolInput, toolResponse);
|
|
7341
8158
|
await writeMemory2({
|
|
7342
|
-
id:
|
|
8159
|
+
id: randomUUID5(),
|
|
7343
8160
|
agent_id: config.agentId,
|
|
7344
8161
|
agent_role: "employee",
|
|
7345
8162
|
session_id: `api-${config.agentId}`,
|
|
@@ -7362,7 +8179,7 @@ function createExeOSHooks(config) {
|
|
|
7362
8179
|
const { writeMemory: writeMemory2 } = await Promise.resolve().then(() => (init_store(), store_exports));
|
|
7363
8180
|
if (summary) {
|
|
7364
8181
|
await writeMemory2({
|
|
7365
|
-
id:
|
|
8182
|
+
id: randomUUID5(),
|
|
7366
8183
|
agent_id: config.agentId,
|
|
7367
8184
|
agent_role: "employee",
|
|
7368
8185
|
session_id: `api-${config.agentId}`,
|
|
@@ -7435,7 +8252,7 @@ function createExeOSHooks(config) {
|
|
|
7435
8252
|
await client.execute({
|
|
7436
8253
|
sql: `INSERT OR IGNORE INTO notifications (id, type, source_agent, message, task_file, created_at, read)
|
|
7437
8254
|
VALUES (?, 'system', ?, ?, NULL, ?, 0)`,
|
|
7438
|
-
args: [
|
|
8255
|
+
args: [randomUUID5(), config.agentId, message.slice(0, 500), (/* @__PURE__ */ new Date()).toISOString()]
|
|
7439
8256
|
});
|
|
7440
8257
|
} catch {
|
|
7441
8258
|
}
|
|
@@ -7451,7 +8268,7 @@ function createExeOSHooks(config) {
|
|
|
7451
8268
|
try {
|
|
7452
8269
|
const { writeMemory: writeMemory2 } = await Promise.resolve().then(() => (init_store(), store_exports));
|
|
7453
8270
|
await writeMemory2({
|
|
7454
|
-
id:
|
|
8271
|
+
id: randomUUID5(),
|
|
7455
8272
|
agent_id: config.agentId,
|
|
7456
8273
|
agent_role: "employee",
|
|
7457
8274
|
session_id: `api-${config.agentId}`,
|
|
@@ -7473,7 +8290,7 @@ function createExeOSHooks(config) {
|
|
|
7473
8290
|
try {
|
|
7474
8291
|
const { writeMemory: writeMemory2 } = await Promise.resolve().then(() => (init_store(), store_exports));
|
|
7475
8292
|
await writeMemory2({
|
|
7476
|
-
id:
|
|
8293
|
+
id: randomUUID5(),
|
|
7477
8294
|
agent_id: config.agentId,
|
|
7478
8295
|
agent_role: "employee",
|
|
7479
8296
|
session_id: `api-${config.agentId}`,
|
|
@@ -7512,7 +8329,7 @@ function createExeOSHooks(config) {
|
|
|
7512
8329
|
if (tasks.rows.length > 0) {
|
|
7513
8330
|
const taskList = tasks.rows.map((r) => `- [${String(r.status)}] ${String(r.title)} (${String(r.task_file)})`).join("\n");
|
|
7514
8331
|
await writeMemory2({
|
|
7515
|
-
id:
|
|
8332
|
+
id: randomUUID5(),
|
|
7516
8333
|
agent_id: config.agentId,
|
|
7517
8334
|
agent_role: "employee",
|
|
7518
8335
|
session_id: `api-${config.agentId}`,
|
|
@@ -7538,7 +8355,7 @@ ${taskList}`,
|
|
|
7538
8355
|
try {
|
|
7539
8356
|
const { writeMemory: writeMemory2 } = await Promise.resolve().then(() => (init_store(), store_exports));
|
|
7540
8357
|
await writeMemory2({
|
|
7541
|
-
id:
|
|
8358
|
+
id: randomUUID5(),
|
|
7542
8359
|
agent_id: config.agentId,
|
|
7543
8360
|
agent_role: "employee",
|
|
7544
8361
|
session_id: `api-${config.agentId}`,
|
|
@@ -7565,7 +8382,7 @@ var SessionManager = class {
|
|
|
7565
8382
|
eventHandlers = /* @__PURE__ */ new Set();
|
|
7566
8383
|
/** Start a new agent session for an employee */
|
|
7567
8384
|
startSession(employeeId, config) {
|
|
7568
|
-
const sessionId =
|
|
8385
|
+
const sessionId = randomUUID6();
|
|
7569
8386
|
const abortController = new AbortController();
|
|
7570
8387
|
const session = {
|
|
7571
8388
|
info: {
|