@askexenow/exe-os 0.8.82 → 0.8.85
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 +97 -2
- package/dist/bin/cli.js +14360 -12525
- package/dist/bin/exe-agent.js +97 -88
- package/dist/bin/exe-assign.js +1003 -854
- package/dist/bin/exe-boot.js +1260 -323
- package/dist/bin/exe-call.js +10 -0
- package/dist/bin/exe-cloud.js +32 -9
- package/dist/bin/exe-dispatch.js +212 -36
- package/dist/bin/exe-doctor.js +403 -6
- package/dist/bin/exe-export-behaviors.js +175 -72
- package/dist/bin/exe-forget.js +97 -2
- package/dist/bin/exe-gateway.js +553 -174
- package/dist/bin/exe-healthcheck.js +1 -0
- package/dist/bin/exe-heartbeat.js +100 -5
- package/dist/bin/exe-kill.js +175 -72
- package/dist/bin/exe-launch-agent.js +189 -76
- package/dist/bin/exe-link.js +902 -80
- package/dist/bin/exe-new-employee.js +41 -11
- package/dist/bin/exe-pending-messages.js +96 -2
- package/dist/bin/exe-pending-notifications.js +97 -2
- package/dist/bin/exe-pending-reviews.js +98 -3
- package/dist/bin/exe-rename.js +577 -33
- package/dist/bin/exe-review.js +231 -73
- package/dist/bin/exe-search.js +989 -226
- package/dist/bin/exe-session-cleanup.js +4806 -1665
- package/dist/bin/exe-settings.js +20 -5
- package/dist/bin/exe-status.js +97 -2
- package/dist/bin/exe-team.js +97 -2
- package/dist/bin/git-sweep.js +901 -209
- package/dist/bin/graph-backfill.js +175 -72
- package/dist/bin/graph-export.js +175 -72
- package/dist/bin/install.js +38 -7
- package/dist/bin/list-providers.js +1 -0
- package/dist/bin/scan-tasks.js +906 -213
- package/dist/bin/setup.js +870 -271
- package/dist/bin/shard-migrate.js +175 -72
- package/dist/bin/update.js +4 -3
- package/dist/bin/wiki-sync.js +175 -72
- package/dist/gateway/index.js +550 -168
- package/dist/hooks/bug-report-worker.js +210 -25
- package/dist/hooks/commit-complete.js +899 -207
- package/dist/hooks/error-recall.js +988 -226
- package/dist/hooks/ingest-worker.js +1639 -1195
- 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 +714 -104
- package/dist/hooks/pre-compact.js +899 -207
- package/dist/hooks/pre-tool-use.js +742 -123
- package/dist/hooks/prompt-ingest-worker.js +245 -104
- package/dist/hooks/prompt-submit.js +995 -233
- package/dist/hooks/response-ingest-worker.js +245 -104
- package/dist/hooks/session-end.js +3941 -400
- package/dist/hooks/session-start.js +1001 -226
- package/dist/hooks/stop.js +725 -115
- package/dist/hooks/subagent-stop.js +714 -104
- package/dist/hooks/summary-worker.js +1970 -1336
- package/dist/index.js +1653 -1055
- package/dist/lib/cloud-sync.js +907 -86
- 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 +1957 -924
- package/dist/lib/hybrid-search.js +988 -226
- package/dist/lib/identity.js +87 -67
- package/dist/lib/keychain.js +9 -1
- package/dist/lib/license.js +3 -3
- package/dist/lib/messaging.js +8 -1
- package/dist/lib/reminders.js +91 -74
- package/dist/lib/schedules.js +96 -2
- package/dist/lib/skill-learning.js +103 -85
- package/dist/lib/store.js +234 -73
- package/dist/lib/tasks.js +113 -24
- package/dist/lib/tmux-routing.js +122 -33
- package/dist/lib/token-spend.js +273 -0
- package/dist/lib/ws-client.js +11 -0
- package/dist/mcp/server.js +10874 -5546
- 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 +810 -27
- 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 +31 -1
- package/dist/mcp/tools/send-message.js +8 -1
- package/dist/mcp/tools/update-task.js +39 -10
- package/dist/runtime/index.js +913 -221
- package/dist/tui/App.js +1000 -298
- package/package.json +6 -1
- package/src/commands/exe/build-adv.md +2 -2
|
@@ -244,6 +244,7 @@ __export(employees_exports, {
|
|
|
244
244
|
DEFAULT_COORDINATOR_TEMPLATE_NAME: () => DEFAULT_COORDINATOR_TEMPLATE_NAME,
|
|
245
245
|
EMPLOYEES_PATH: () => EMPLOYEES_PATH,
|
|
246
246
|
addEmployee: () => addEmployee,
|
|
247
|
+
baseAgentName: () => baseAgentName,
|
|
247
248
|
canCoordinate: () => canCoordinate,
|
|
248
249
|
getCoordinatorEmployee: () => getCoordinatorEmployee,
|
|
249
250
|
getCoordinatorName: () => getCoordinatorName,
|
|
@@ -340,6 +341,14 @@ function hasRole(agentName, role) {
|
|
|
340
341
|
const emp = getEmployee(employees, agentName);
|
|
341
342
|
return emp ? emp.role.toLowerCase() === role.toLowerCase() : false;
|
|
342
343
|
}
|
|
344
|
+
function baseAgentName(name, employees) {
|
|
345
|
+
const match = name.match(/^([a-zA-Z]+)\d+$/);
|
|
346
|
+
if (!match) return name;
|
|
347
|
+
const base = match[1];
|
|
348
|
+
const roster = employees ?? loadEmployeesSync();
|
|
349
|
+
if (getEmployee(roster, base)) return base;
|
|
350
|
+
return name;
|
|
351
|
+
}
|
|
343
352
|
function isMultiInstance(agentName, employees) {
|
|
344
353
|
const roster = employees ?? loadEmployeesSync();
|
|
345
354
|
const emp = getEmployee(roster, agentName);
|
|
@@ -651,6 +660,443 @@ var init_db_retry = __esm({
|
|
|
651
660
|
}
|
|
652
661
|
});
|
|
653
662
|
|
|
663
|
+
// src/lib/exe-daemon-client.ts
|
|
664
|
+
import net from "net";
|
|
665
|
+
import { spawn } from "child_process";
|
|
666
|
+
import { randomUUID } from "crypto";
|
|
667
|
+
import { existsSync as existsSync4, unlinkSync as unlinkSync3, readFileSync as readFileSync5, openSync, closeSync, statSync } from "fs";
|
|
668
|
+
import path6 from "path";
|
|
669
|
+
import { fileURLToPath } from "url";
|
|
670
|
+
function handleData(chunk) {
|
|
671
|
+
_buffer += chunk.toString();
|
|
672
|
+
if (_buffer.length > MAX_BUFFER) {
|
|
673
|
+
_buffer = "";
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
676
|
+
let newlineIdx;
|
|
677
|
+
while ((newlineIdx = _buffer.indexOf("\n")) !== -1) {
|
|
678
|
+
const line = _buffer.slice(0, newlineIdx).trim();
|
|
679
|
+
_buffer = _buffer.slice(newlineIdx + 1);
|
|
680
|
+
if (!line) continue;
|
|
681
|
+
try {
|
|
682
|
+
const response = JSON.parse(line);
|
|
683
|
+
const id = response.id;
|
|
684
|
+
if (!id) continue;
|
|
685
|
+
const entry = _pending.get(id);
|
|
686
|
+
if (entry) {
|
|
687
|
+
clearTimeout(entry.timer);
|
|
688
|
+
_pending.delete(id);
|
|
689
|
+
entry.resolve(response);
|
|
690
|
+
}
|
|
691
|
+
} catch {
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
function cleanupStaleFiles() {
|
|
696
|
+
if (existsSync4(PID_PATH)) {
|
|
697
|
+
try {
|
|
698
|
+
const pid = parseInt(readFileSync5(PID_PATH, "utf8").trim(), 10);
|
|
699
|
+
if (pid > 0) {
|
|
700
|
+
try {
|
|
701
|
+
process.kill(pid, 0);
|
|
702
|
+
return;
|
|
703
|
+
} catch {
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
} catch {
|
|
707
|
+
}
|
|
708
|
+
try {
|
|
709
|
+
unlinkSync3(PID_PATH);
|
|
710
|
+
} catch {
|
|
711
|
+
}
|
|
712
|
+
try {
|
|
713
|
+
unlinkSync3(SOCKET_PATH);
|
|
714
|
+
} catch {
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
function findPackageRoot() {
|
|
719
|
+
let dir = path6.dirname(fileURLToPath(import.meta.url));
|
|
720
|
+
const { root } = path6.parse(dir);
|
|
721
|
+
while (dir !== root) {
|
|
722
|
+
if (existsSync4(path6.join(dir, "package.json"))) return dir;
|
|
723
|
+
dir = path6.dirname(dir);
|
|
724
|
+
}
|
|
725
|
+
return null;
|
|
726
|
+
}
|
|
727
|
+
function spawnDaemon() {
|
|
728
|
+
const pkgRoot = findPackageRoot();
|
|
729
|
+
if (!pkgRoot) {
|
|
730
|
+
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
731
|
+
return;
|
|
732
|
+
}
|
|
733
|
+
const daemonPath = path6.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
734
|
+
if (!existsSync4(daemonPath)) {
|
|
735
|
+
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
736
|
+
`);
|
|
737
|
+
return;
|
|
738
|
+
}
|
|
739
|
+
const resolvedPath = daemonPath;
|
|
740
|
+
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
741
|
+
`);
|
|
742
|
+
const logPath = path6.join(path6.dirname(SOCKET_PATH), "exed.log");
|
|
743
|
+
let stderrFd = "ignore";
|
|
744
|
+
try {
|
|
745
|
+
stderrFd = openSync(logPath, "a");
|
|
746
|
+
} catch {
|
|
747
|
+
}
|
|
748
|
+
const child = spawn(process.execPath, [resolvedPath], {
|
|
749
|
+
detached: true,
|
|
750
|
+
stdio: ["ignore", "ignore", stderrFd],
|
|
751
|
+
env: {
|
|
752
|
+
...process.env,
|
|
753
|
+
TMUX: void 0,
|
|
754
|
+
// Daemon is global — must not inherit session scope
|
|
755
|
+
TMUX_PANE: void 0,
|
|
756
|
+
// Prevents resolveExeSession() from scoping to one session
|
|
757
|
+
EXE_DAEMON_SOCK: SOCKET_PATH,
|
|
758
|
+
EXE_DAEMON_PID: PID_PATH
|
|
759
|
+
}
|
|
760
|
+
});
|
|
761
|
+
child.unref();
|
|
762
|
+
if (typeof stderrFd === "number") {
|
|
763
|
+
try {
|
|
764
|
+
closeSync(stderrFd);
|
|
765
|
+
} catch {
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
function acquireSpawnLock() {
|
|
770
|
+
try {
|
|
771
|
+
const fd = openSync(SPAWN_LOCK_PATH, "wx");
|
|
772
|
+
closeSync(fd);
|
|
773
|
+
return true;
|
|
774
|
+
} catch {
|
|
775
|
+
try {
|
|
776
|
+
const stat = statSync(SPAWN_LOCK_PATH);
|
|
777
|
+
if (Date.now() - stat.mtimeMs > SPAWN_LOCK_STALE_MS) {
|
|
778
|
+
try {
|
|
779
|
+
unlinkSync3(SPAWN_LOCK_PATH);
|
|
780
|
+
} catch {
|
|
781
|
+
}
|
|
782
|
+
try {
|
|
783
|
+
const fd = openSync(SPAWN_LOCK_PATH, "wx");
|
|
784
|
+
closeSync(fd);
|
|
785
|
+
return true;
|
|
786
|
+
} catch {
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
} catch {
|
|
790
|
+
}
|
|
791
|
+
return false;
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
function releaseSpawnLock() {
|
|
795
|
+
try {
|
|
796
|
+
unlinkSync3(SPAWN_LOCK_PATH);
|
|
797
|
+
} catch {
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
function connectToSocket() {
|
|
801
|
+
return new Promise((resolve) => {
|
|
802
|
+
if (_socket && _connected) {
|
|
803
|
+
resolve(true);
|
|
804
|
+
return;
|
|
805
|
+
}
|
|
806
|
+
const socket = net.createConnection({ path: SOCKET_PATH });
|
|
807
|
+
const connectTimeout = setTimeout(() => {
|
|
808
|
+
socket.destroy();
|
|
809
|
+
resolve(false);
|
|
810
|
+
}, 2e3);
|
|
811
|
+
socket.on("connect", () => {
|
|
812
|
+
clearTimeout(connectTimeout);
|
|
813
|
+
_socket = socket;
|
|
814
|
+
_connected = true;
|
|
815
|
+
_buffer = "";
|
|
816
|
+
socket.on("data", handleData);
|
|
817
|
+
socket.on("close", () => {
|
|
818
|
+
_connected = false;
|
|
819
|
+
_socket = null;
|
|
820
|
+
for (const [id, entry] of _pending) {
|
|
821
|
+
clearTimeout(entry.timer);
|
|
822
|
+
_pending.delete(id);
|
|
823
|
+
entry.resolve({ error: "Connection closed" });
|
|
824
|
+
}
|
|
825
|
+
});
|
|
826
|
+
socket.on("error", () => {
|
|
827
|
+
_connected = false;
|
|
828
|
+
_socket = null;
|
|
829
|
+
});
|
|
830
|
+
resolve(true);
|
|
831
|
+
});
|
|
832
|
+
socket.on("error", () => {
|
|
833
|
+
clearTimeout(connectTimeout);
|
|
834
|
+
resolve(false);
|
|
835
|
+
});
|
|
836
|
+
});
|
|
837
|
+
}
|
|
838
|
+
async function connectEmbedDaemon() {
|
|
839
|
+
if (_socket && _connected) return true;
|
|
840
|
+
if (await connectToSocket()) return true;
|
|
841
|
+
if (acquireSpawnLock()) {
|
|
842
|
+
try {
|
|
843
|
+
cleanupStaleFiles();
|
|
844
|
+
spawnDaemon();
|
|
845
|
+
} finally {
|
|
846
|
+
releaseSpawnLock();
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
const start = Date.now();
|
|
850
|
+
let delay2 = 100;
|
|
851
|
+
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
852
|
+
await new Promise((r) => setTimeout(r, delay2));
|
|
853
|
+
if (await connectToSocket()) return true;
|
|
854
|
+
delay2 = Math.min(delay2 * 2, 3e3);
|
|
855
|
+
}
|
|
856
|
+
return false;
|
|
857
|
+
}
|
|
858
|
+
function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
859
|
+
return new Promise((resolve) => {
|
|
860
|
+
if (!_socket || !_connected) {
|
|
861
|
+
resolve({ error: "Not connected" });
|
|
862
|
+
return;
|
|
863
|
+
}
|
|
864
|
+
const id = randomUUID();
|
|
865
|
+
const timer = setTimeout(() => {
|
|
866
|
+
_pending.delete(id);
|
|
867
|
+
resolve({ error: "Request timeout" });
|
|
868
|
+
}, timeoutMs);
|
|
869
|
+
_pending.set(id, { resolve, timer });
|
|
870
|
+
try {
|
|
871
|
+
_socket.write(JSON.stringify({ id, ...payload }) + "\n");
|
|
872
|
+
} catch {
|
|
873
|
+
clearTimeout(timer);
|
|
874
|
+
_pending.delete(id);
|
|
875
|
+
resolve({ error: "Write failed" });
|
|
876
|
+
}
|
|
877
|
+
});
|
|
878
|
+
}
|
|
879
|
+
function isClientConnected() {
|
|
880
|
+
return _connected;
|
|
881
|
+
}
|
|
882
|
+
var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _pending, MAX_BUFFER;
|
|
883
|
+
var init_exe_daemon_client = __esm({
|
|
884
|
+
"src/lib/exe-daemon-client.ts"() {
|
|
885
|
+
"use strict";
|
|
886
|
+
init_config();
|
|
887
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path6.join(EXE_AI_DIR, "exed.sock");
|
|
888
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path6.join(EXE_AI_DIR, "exed.pid");
|
|
889
|
+
SPAWN_LOCK_PATH = path6.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
890
|
+
SPAWN_LOCK_STALE_MS = 3e4;
|
|
891
|
+
CONNECT_TIMEOUT_MS = 15e3;
|
|
892
|
+
REQUEST_TIMEOUT_MS = 3e4;
|
|
893
|
+
_socket = null;
|
|
894
|
+
_connected = false;
|
|
895
|
+
_buffer = "";
|
|
896
|
+
_pending = /* @__PURE__ */ new Map();
|
|
897
|
+
MAX_BUFFER = 1e7;
|
|
898
|
+
}
|
|
899
|
+
});
|
|
900
|
+
|
|
901
|
+
// src/lib/daemon-protocol.ts
|
|
902
|
+
function serializeValue(v) {
|
|
903
|
+
if (v === null || v === void 0) return null;
|
|
904
|
+
if (typeof v === "bigint") return Number(v);
|
|
905
|
+
if (typeof v === "boolean") return v ? 1 : 0;
|
|
906
|
+
if (v instanceof Uint8Array) {
|
|
907
|
+
return { __blob: Buffer.from(v).toString("base64") };
|
|
908
|
+
}
|
|
909
|
+
if (ArrayBuffer.isView(v)) {
|
|
910
|
+
return { __blob: Buffer.from(v.buffer, v.byteOffset, v.byteLength).toString("base64") };
|
|
911
|
+
}
|
|
912
|
+
if (v instanceof ArrayBuffer) {
|
|
913
|
+
return { __blob: Buffer.from(v).toString("base64") };
|
|
914
|
+
}
|
|
915
|
+
if (typeof v === "string" || typeof v === "number") return v;
|
|
916
|
+
return String(v);
|
|
917
|
+
}
|
|
918
|
+
function deserializeValue(v) {
|
|
919
|
+
if (v === null) return null;
|
|
920
|
+
if (typeof v === "object" && v !== null && "__blob" in v) {
|
|
921
|
+
const buf = Buffer.from(v.__blob, "base64");
|
|
922
|
+
return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
|
|
923
|
+
}
|
|
924
|
+
return v;
|
|
925
|
+
}
|
|
926
|
+
function deserializeResultSet(srs) {
|
|
927
|
+
const rows = srs.rows.map((obj) => {
|
|
928
|
+
const values = srs.columns.map(
|
|
929
|
+
(col) => deserializeValue(obj[col] ?? null)
|
|
930
|
+
);
|
|
931
|
+
const row = values;
|
|
932
|
+
for (let i = 0; i < srs.columns.length; i++) {
|
|
933
|
+
const col = srs.columns[i];
|
|
934
|
+
if (col !== void 0) {
|
|
935
|
+
row[col] = values[i] ?? null;
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
Object.defineProperty(row, "length", {
|
|
939
|
+
value: values.length,
|
|
940
|
+
enumerable: false
|
|
941
|
+
});
|
|
942
|
+
return row;
|
|
943
|
+
});
|
|
944
|
+
return {
|
|
945
|
+
columns: srs.columns,
|
|
946
|
+
columnTypes: srs.columnTypes ?? [],
|
|
947
|
+
rows,
|
|
948
|
+
rowsAffected: srs.rowsAffected,
|
|
949
|
+
lastInsertRowid: srs.lastInsertRowid != null ? BigInt(srs.lastInsertRowid) : void 0,
|
|
950
|
+
toJSON: () => ({
|
|
951
|
+
columns: srs.columns,
|
|
952
|
+
columnTypes: srs.columnTypes ?? [],
|
|
953
|
+
rows: srs.rows,
|
|
954
|
+
rowsAffected: srs.rowsAffected,
|
|
955
|
+
lastInsertRowid: srs.lastInsertRowid
|
|
956
|
+
})
|
|
957
|
+
};
|
|
958
|
+
}
|
|
959
|
+
var init_daemon_protocol = __esm({
|
|
960
|
+
"src/lib/daemon-protocol.ts"() {
|
|
961
|
+
"use strict";
|
|
962
|
+
}
|
|
963
|
+
});
|
|
964
|
+
|
|
965
|
+
// src/lib/db-daemon-client.ts
|
|
966
|
+
var db_daemon_client_exports = {};
|
|
967
|
+
__export(db_daemon_client_exports, {
|
|
968
|
+
createDaemonDbClient: () => createDaemonDbClient,
|
|
969
|
+
initDaemonDbClient: () => initDaemonDbClient
|
|
970
|
+
});
|
|
971
|
+
function normalizeStatement(stmt) {
|
|
972
|
+
if (typeof stmt === "string") {
|
|
973
|
+
return { sql: stmt, args: [] };
|
|
974
|
+
}
|
|
975
|
+
const sql = stmt.sql;
|
|
976
|
+
let args = [];
|
|
977
|
+
if (Array.isArray(stmt.args)) {
|
|
978
|
+
args = stmt.args.map((v) => serializeValue(v));
|
|
979
|
+
} else if (stmt.args && typeof stmt.args === "object") {
|
|
980
|
+
const named = {};
|
|
981
|
+
for (const [key, val] of Object.entries(stmt.args)) {
|
|
982
|
+
named[key] = serializeValue(val);
|
|
983
|
+
}
|
|
984
|
+
return { sql, args: named };
|
|
985
|
+
}
|
|
986
|
+
return { sql, args };
|
|
987
|
+
}
|
|
988
|
+
function createDaemonDbClient(fallbackClient) {
|
|
989
|
+
let _useDaemon = false;
|
|
990
|
+
const client = {
|
|
991
|
+
async execute(stmt) {
|
|
992
|
+
if (!_useDaemon || !isClientConnected()) {
|
|
993
|
+
return fallbackClient.execute(stmt);
|
|
994
|
+
}
|
|
995
|
+
const { sql, args } = normalizeStatement(stmt);
|
|
996
|
+
const response = await sendDaemonRequest({
|
|
997
|
+
type: "db-execute",
|
|
998
|
+
sql,
|
|
999
|
+
args
|
|
1000
|
+
});
|
|
1001
|
+
if (response.error) {
|
|
1002
|
+
const errMsg = String(response.error);
|
|
1003
|
+
if (errMsg === "Not connected" || errMsg === "Request timeout" || errMsg === "Write failed") {
|
|
1004
|
+
process.stderr.write(`[db-daemon] Transport error (${errMsg}), falling back to direct
|
|
1005
|
+
`);
|
|
1006
|
+
return fallbackClient.execute(stmt);
|
|
1007
|
+
}
|
|
1008
|
+
throw new Error(errMsg);
|
|
1009
|
+
}
|
|
1010
|
+
if (response.db) {
|
|
1011
|
+
return deserializeResultSet(response.db);
|
|
1012
|
+
}
|
|
1013
|
+
process.stderr.write("[db-daemon] Unexpected response shape, falling back to direct\n");
|
|
1014
|
+
return fallbackClient.execute(stmt);
|
|
1015
|
+
},
|
|
1016
|
+
async batch(stmts, mode) {
|
|
1017
|
+
if (!_useDaemon || !isClientConnected()) {
|
|
1018
|
+
return fallbackClient.batch(stmts, mode);
|
|
1019
|
+
}
|
|
1020
|
+
const statements = stmts.map(normalizeStatement);
|
|
1021
|
+
const response = await sendDaemonRequest({
|
|
1022
|
+
type: "db-batch",
|
|
1023
|
+
statements,
|
|
1024
|
+
mode: mode ?? "deferred"
|
|
1025
|
+
});
|
|
1026
|
+
if (response.error) {
|
|
1027
|
+
const errMsg = String(response.error);
|
|
1028
|
+
if (errMsg === "Not connected" || errMsg === "Request timeout" || errMsg === "Write failed") {
|
|
1029
|
+
process.stderr.write(`[db-daemon] Batch transport error (${errMsg}), falling back to direct
|
|
1030
|
+
`);
|
|
1031
|
+
return fallbackClient.batch(stmts, mode);
|
|
1032
|
+
}
|
|
1033
|
+
throw new Error(errMsg);
|
|
1034
|
+
}
|
|
1035
|
+
const batchResults = response["db-batch"];
|
|
1036
|
+
if (batchResults) {
|
|
1037
|
+
return batchResults.map(deserializeResultSet);
|
|
1038
|
+
}
|
|
1039
|
+
process.stderr.write("[db-daemon] Unexpected batch response shape, falling back to direct\n");
|
|
1040
|
+
return fallbackClient.batch(stmts, mode);
|
|
1041
|
+
},
|
|
1042
|
+
// Transaction support — delegate to fallback (transactions need direct connection)
|
|
1043
|
+
async transaction(mode) {
|
|
1044
|
+
return fallbackClient.transaction(mode);
|
|
1045
|
+
},
|
|
1046
|
+
// executeMultiple — delegate to fallback (used only for schema migrations)
|
|
1047
|
+
async executeMultiple(sql) {
|
|
1048
|
+
return fallbackClient.executeMultiple(sql);
|
|
1049
|
+
},
|
|
1050
|
+
// migrate — delegate to fallback
|
|
1051
|
+
async migrate(stmts) {
|
|
1052
|
+
return fallbackClient.migrate(stmts);
|
|
1053
|
+
},
|
|
1054
|
+
// Sync mode — delegate to fallback
|
|
1055
|
+
sync() {
|
|
1056
|
+
return fallbackClient.sync();
|
|
1057
|
+
},
|
|
1058
|
+
close() {
|
|
1059
|
+
_useDaemon = false;
|
|
1060
|
+
},
|
|
1061
|
+
get closed() {
|
|
1062
|
+
return fallbackClient.closed;
|
|
1063
|
+
},
|
|
1064
|
+
get protocol() {
|
|
1065
|
+
return fallbackClient.protocol;
|
|
1066
|
+
}
|
|
1067
|
+
};
|
|
1068
|
+
return {
|
|
1069
|
+
...client,
|
|
1070
|
+
/** Enable daemon routing (call after confirming daemon is connected) */
|
|
1071
|
+
_enableDaemon() {
|
|
1072
|
+
_useDaemon = true;
|
|
1073
|
+
},
|
|
1074
|
+
/** Check if daemon routing is active */
|
|
1075
|
+
_isDaemonActive() {
|
|
1076
|
+
return _useDaemon && isClientConnected();
|
|
1077
|
+
}
|
|
1078
|
+
};
|
|
1079
|
+
}
|
|
1080
|
+
async function initDaemonDbClient(fallbackClient) {
|
|
1081
|
+
if (process.env.EXE_IS_DAEMON === "1") return null;
|
|
1082
|
+
const connected = await connectEmbedDaemon();
|
|
1083
|
+
if (!connected) {
|
|
1084
|
+
process.stderr.write("[db-daemon] Daemon unavailable \u2014 using direct SQLite\n");
|
|
1085
|
+
return null;
|
|
1086
|
+
}
|
|
1087
|
+
const client = createDaemonDbClient(fallbackClient);
|
|
1088
|
+
client._enableDaemon();
|
|
1089
|
+
process.stderr.write("[db-daemon] DB routing through daemon (single-writer)\n");
|
|
1090
|
+
return client;
|
|
1091
|
+
}
|
|
1092
|
+
var init_db_daemon_client = __esm({
|
|
1093
|
+
"src/lib/db-daemon-client.ts"() {
|
|
1094
|
+
"use strict";
|
|
1095
|
+
init_exe_daemon_client();
|
|
1096
|
+
init_daemon_protocol();
|
|
1097
|
+
}
|
|
1098
|
+
});
|
|
1099
|
+
|
|
654
1100
|
// src/lib/database.ts
|
|
655
1101
|
var database_exports = {};
|
|
656
1102
|
__export(database_exports, {
|
|
@@ -659,6 +1105,7 @@ __export(database_exports, {
|
|
|
659
1105
|
ensureSchema: () => ensureSchema,
|
|
660
1106
|
getClient: () => getClient,
|
|
661
1107
|
getRawClient: () => getRawClient,
|
|
1108
|
+
initDaemonClient: () => initDaemonClient,
|
|
662
1109
|
initDatabase: () => initDatabase,
|
|
663
1110
|
initTurso: () => initTurso,
|
|
664
1111
|
isInitialized: () => isInitialized
|
|
@@ -686,8 +1133,27 @@ function getClient() {
|
|
|
686
1133
|
if (!_resilientClient) {
|
|
687
1134
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
688
1135
|
}
|
|
1136
|
+
if (process.env.EXE_IS_DAEMON === "1") {
|
|
1137
|
+
return _resilientClient;
|
|
1138
|
+
}
|
|
1139
|
+
if (_daemonClient && _daemonClient._isDaemonActive()) {
|
|
1140
|
+
return _daemonClient;
|
|
1141
|
+
}
|
|
689
1142
|
return _resilientClient;
|
|
690
1143
|
}
|
|
1144
|
+
async function initDaemonClient() {
|
|
1145
|
+
if (process.env.EXE_IS_DAEMON === "1") return;
|
|
1146
|
+
if (!_resilientClient) return;
|
|
1147
|
+
try {
|
|
1148
|
+
const { initDaemonDbClient: initDaemonDbClient2 } = await Promise.resolve().then(() => (init_db_daemon_client(), db_daemon_client_exports));
|
|
1149
|
+
_daemonClient = await initDaemonDbClient2(_resilientClient);
|
|
1150
|
+
} catch (err) {
|
|
1151
|
+
process.stderr.write(
|
|
1152
|
+
`[database] Daemon client init failed (non-fatal): ${err instanceof Error ? err.message : String(err)}
|
|
1153
|
+
`
|
|
1154
|
+
);
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
691
1157
|
function getRawClient() {
|
|
692
1158
|
if (!_client) {
|
|
693
1159
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
@@ -1174,6 +1640,12 @@ async function ensureSchema() {
|
|
|
1174
1640
|
} catch {
|
|
1175
1641
|
}
|
|
1176
1642
|
}
|
|
1643
|
+
try {
|
|
1644
|
+
await client.execute(
|
|
1645
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_content_hash ON memories(content_hash, agent_id)`
|
|
1646
|
+
);
|
|
1647
|
+
} catch {
|
|
1648
|
+
}
|
|
1177
1649
|
await client.executeMultiple(`
|
|
1178
1650
|
CREATE TABLE IF NOT EXISTS entities (
|
|
1179
1651
|
id TEXT PRIMARY KEY,
|
|
@@ -1226,7 +1698,30 @@ async function ensureSchema() {
|
|
|
1226
1698
|
entity_id TEXT NOT NULL,
|
|
1227
1699
|
PRIMARY KEY (hyperedge_id, entity_id)
|
|
1228
1700
|
);
|
|
1701
|
+
|
|
1702
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS entities_fts USING fts5(
|
|
1703
|
+
name,
|
|
1704
|
+
content=entities,
|
|
1705
|
+
content_rowid=rowid
|
|
1706
|
+
);
|
|
1707
|
+
|
|
1708
|
+
CREATE TRIGGER IF NOT EXISTS entities_fts_ai AFTER INSERT ON entities BEGIN
|
|
1709
|
+
INSERT INTO entities_fts(rowid, name) VALUES (new.rowid, new.name);
|
|
1710
|
+
END;
|
|
1711
|
+
|
|
1712
|
+
CREATE TRIGGER IF NOT EXISTS entities_fts_ad AFTER DELETE ON entities BEGIN
|
|
1713
|
+
INSERT INTO entities_fts(entities_fts, rowid, name) VALUES('delete', old.rowid, old.name);
|
|
1714
|
+
END;
|
|
1715
|
+
|
|
1716
|
+
CREATE TRIGGER IF NOT EXISTS entities_fts_au AFTER UPDATE ON entities BEGIN
|
|
1717
|
+
INSERT INTO entities_fts(entities_fts, rowid, name) VALUES('delete', old.rowid, old.name);
|
|
1718
|
+
INSERT INTO entities_fts(rowid, name) VALUES (new.rowid, new.name);
|
|
1719
|
+
END;
|
|
1229
1720
|
`);
|
|
1721
|
+
try {
|
|
1722
|
+
await client.execute("INSERT INTO entities_fts(entities_fts) VALUES('rebuild')");
|
|
1723
|
+
} catch {
|
|
1724
|
+
}
|
|
1230
1725
|
await client.executeMultiple(`
|
|
1231
1726
|
CREATE TABLE IF NOT EXISTS entity_aliases (
|
|
1232
1727
|
alias TEXT NOT NULL PRIMARY KEY,
|
|
@@ -1407,6 +1902,33 @@ async function ensureSchema() {
|
|
|
1407
1902
|
CREATE INDEX IF NOT EXISTS idx_conversations_channel
|
|
1408
1903
|
ON conversations(channel_id);
|
|
1409
1904
|
`);
|
|
1905
|
+
await client.executeMultiple(`
|
|
1906
|
+
CREATE TABLE IF NOT EXISTS session_agent_map (
|
|
1907
|
+
session_uuid TEXT PRIMARY KEY,
|
|
1908
|
+
agent_id TEXT NOT NULL,
|
|
1909
|
+
session_name TEXT,
|
|
1910
|
+
task_id TEXT,
|
|
1911
|
+
project_name TEXT,
|
|
1912
|
+
started_at TEXT NOT NULL
|
|
1913
|
+
);
|
|
1914
|
+
|
|
1915
|
+
CREATE INDEX IF NOT EXISTS idx_session_agent_map_agent
|
|
1916
|
+
ON session_agent_map(agent_id);
|
|
1917
|
+
`);
|
|
1918
|
+
try {
|
|
1919
|
+
const mapCount = await client.execute({ sql: `SELECT COUNT(*) as cnt FROM session_agent_map`, args: [] });
|
|
1920
|
+
if (Number(mapCount.rows[0]?.cnt ?? 0) === 0) {
|
|
1921
|
+
await client.execute({
|
|
1922
|
+
sql: `INSERT OR IGNORE INTO session_agent_map (session_uuid, agent_id, session_name, started_at)
|
|
1923
|
+
SELECT session_id, agent_id, '', MIN(timestamp)
|
|
1924
|
+
FROM memories
|
|
1925
|
+
WHERE session_id IS NOT NULL AND session_id != '' AND agent_id IS NOT NULL AND agent_id != ''
|
|
1926
|
+
GROUP BY session_id, agent_id`,
|
|
1927
|
+
args: []
|
|
1928
|
+
});
|
|
1929
|
+
}
|
|
1930
|
+
} catch {
|
|
1931
|
+
}
|
|
1410
1932
|
try {
|
|
1411
1933
|
await client.execute({
|
|
1412
1934
|
sql: `ALTER TABLE tasks ADD COLUMN budget_tokens INTEGER`,
|
|
@@ -1540,15 +2062,41 @@ async function ensureSchema() {
|
|
|
1540
2062
|
});
|
|
1541
2063
|
} catch {
|
|
1542
2064
|
}
|
|
2065
|
+
for (const col of [
|
|
2066
|
+
"ALTER TABLE memories ADD COLUMN intent TEXT",
|
|
2067
|
+
"ALTER TABLE memories ADD COLUMN outcome TEXT",
|
|
2068
|
+
"ALTER TABLE memories ADD COLUMN domain TEXT",
|
|
2069
|
+
"ALTER TABLE memories ADD COLUMN referenced_entities TEXT",
|
|
2070
|
+
"ALTER TABLE memories ADD COLUMN retrieval_count INTEGER DEFAULT 0",
|
|
2071
|
+
"ALTER TABLE memories ADD COLUMN chain_position TEXT",
|
|
2072
|
+
"ALTER TABLE memories ADD COLUMN review_status TEXT",
|
|
2073
|
+
"ALTER TABLE memories ADD COLUMN context_window_pct INTEGER",
|
|
2074
|
+
"ALTER TABLE memories ADD COLUMN file_paths TEXT",
|
|
2075
|
+
"ALTER TABLE memories ADD COLUMN commit_hash TEXT",
|
|
2076
|
+
"ALTER TABLE memories ADD COLUMN duration_ms INTEGER",
|
|
2077
|
+
"ALTER TABLE memories ADD COLUMN token_cost REAL",
|
|
2078
|
+
"ALTER TABLE memories ADD COLUMN audience TEXT",
|
|
2079
|
+
"ALTER TABLE memories ADD COLUMN language_type TEXT",
|
|
2080
|
+
"ALTER TABLE memories ADD COLUMN parent_memory_id TEXT"
|
|
2081
|
+
]) {
|
|
2082
|
+
try {
|
|
2083
|
+
await client.execute(col);
|
|
2084
|
+
} catch {
|
|
2085
|
+
}
|
|
2086
|
+
}
|
|
1543
2087
|
}
|
|
1544
2088
|
async function disposeDatabase() {
|
|
2089
|
+
if (_daemonClient) {
|
|
2090
|
+
_daemonClient.close();
|
|
2091
|
+
_daemonClient = null;
|
|
2092
|
+
}
|
|
1545
2093
|
if (_client) {
|
|
1546
2094
|
_client.close();
|
|
1547
2095
|
_client = null;
|
|
1548
2096
|
_resilientClient = null;
|
|
1549
2097
|
}
|
|
1550
2098
|
}
|
|
1551
|
-
var _client, _resilientClient, initTurso, disposeTurso;
|
|
2099
|
+
var _client, _resilientClient, _daemonClient, initTurso, disposeTurso;
|
|
1552
2100
|
var init_database = __esm({
|
|
1553
2101
|
"src/lib/database.ts"() {
|
|
1554
2102
|
"use strict";
|
|
@@ -1556,30 +2104,31 @@ var init_database = __esm({
|
|
|
1556
2104
|
init_employees();
|
|
1557
2105
|
_client = null;
|
|
1558
2106
|
_resilientClient = null;
|
|
2107
|
+
_daemonClient = null;
|
|
1559
2108
|
initTurso = initDatabase;
|
|
1560
2109
|
disposeTurso = disposeDatabase;
|
|
1561
2110
|
}
|
|
1562
2111
|
});
|
|
1563
2112
|
|
|
1564
2113
|
// src/lib/license.ts
|
|
1565
|
-
import { readFileSync as
|
|
1566
|
-
import { randomUUID } from "crypto";
|
|
1567
|
-
import
|
|
2114
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, existsSync as existsSync5, mkdirSync as mkdirSync3 } from "fs";
|
|
2115
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
2116
|
+
import path7 from "path";
|
|
1568
2117
|
import { jwtVerify, importSPKI } from "jose";
|
|
1569
2118
|
var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH;
|
|
1570
2119
|
var init_license = __esm({
|
|
1571
2120
|
"src/lib/license.ts"() {
|
|
1572
2121
|
"use strict";
|
|
1573
2122
|
init_config();
|
|
1574
|
-
LICENSE_PATH =
|
|
1575
|
-
CACHE_PATH =
|
|
1576
|
-
DEVICE_ID_PATH =
|
|
2123
|
+
LICENSE_PATH = path7.join(EXE_AI_DIR, "license.key");
|
|
2124
|
+
CACHE_PATH = path7.join(EXE_AI_DIR, "license-cache.json");
|
|
2125
|
+
DEVICE_ID_PATH = path7.join(EXE_AI_DIR, "device-id");
|
|
1577
2126
|
}
|
|
1578
2127
|
});
|
|
1579
2128
|
|
|
1580
2129
|
// src/lib/plan-limits.ts
|
|
1581
|
-
import { readFileSync as
|
|
1582
|
-
import
|
|
2130
|
+
import { readFileSync as readFileSync7, existsSync as existsSync6 } from "fs";
|
|
2131
|
+
import path8 from "path";
|
|
1583
2132
|
var CACHE_PATH2;
|
|
1584
2133
|
var init_plan_limits = __esm({
|
|
1585
2134
|
"src/lib/plan-limits.ts"() {
|
|
@@ -1588,15 +2137,15 @@ var init_plan_limits = __esm({
|
|
|
1588
2137
|
init_employees();
|
|
1589
2138
|
init_license();
|
|
1590
2139
|
init_config();
|
|
1591
|
-
CACHE_PATH2 =
|
|
2140
|
+
CACHE_PATH2 = path8.join(EXE_AI_DIR, "license-cache.json");
|
|
1592
2141
|
}
|
|
1593
2142
|
});
|
|
1594
2143
|
|
|
1595
2144
|
// src/lib/tmux-routing.ts
|
|
1596
|
-
import { readFileSync as
|
|
1597
|
-
import
|
|
2145
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync5, mkdirSync as mkdirSync4, existsSync as existsSync7, appendFileSync } from "fs";
|
|
2146
|
+
import path9 from "path";
|
|
1598
2147
|
import os5 from "os";
|
|
1599
|
-
import { fileURLToPath } from "url";
|
|
2148
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
1600
2149
|
function getMySession() {
|
|
1601
2150
|
return getTransport().getMySession();
|
|
1602
2151
|
}
|
|
@@ -1608,7 +2157,7 @@ function extractRootExe(name) {
|
|
|
1608
2157
|
}
|
|
1609
2158
|
function getParentExe(sessionKey) {
|
|
1610
2159
|
try {
|
|
1611
|
-
const data = JSON.parse(
|
|
2160
|
+
const data = JSON.parse(readFileSync8(path9.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
1612
2161
|
return data.parentExe || null;
|
|
1613
2162
|
} catch {
|
|
1614
2163
|
return null;
|
|
@@ -1640,10 +2189,10 @@ var init_tmux_routing = __esm({
|
|
|
1640
2189
|
init_intercom_queue();
|
|
1641
2190
|
init_plan_limits();
|
|
1642
2191
|
init_employees();
|
|
1643
|
-
SPAWN_LOCK_DIR =
|
|
1644
|
-
SESSION_CACHE =
|
|
1645
|
-
INTERCOM_LOG2 =
|
|
1646
|
-
DEBOUNCE_FILE =
|
|
2192
|
+
SPAWN_LOCK_DIR = path9.join(os5.homedir(), ".exe-os", "spawn-locks");
|
|
2193
|
+
SESSION_CACHE = path9.join(os5.homedir(), ".exe-os", "session-cache");
|
|
2194
|
+
INTERCOM_LOG2 = path9.join(os5.homedir(), ".exe-os", "intercom.log");
|
|
2195
|
+
DEBOUNCE_FILE = path9.join(SESSION_CACHE, "intercom-debounce.json");
|
|
1647
2196
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
1648
2197
|
}
|
|
1649
2198
|
});
|
|
@@ -1686,8 +2235,8 @@ __export(cto_delegation_gate_exports, {
|
|
|
1686
2235
|
scratchpadPath: () => scratchpadPath
|
|
1687
2236
|
});
|
|
1688
2237
|
import os6 from "os";
|
|
1689
|
-
import
|
|
1690
|
-
import { existsSync as
|
|
2238
|
+
import path10 from "path";
|
|
2239
|
+
import { existsSync as existsSync8, readFileSync as readFileSync9, statSync as statSync2 } from "fs";
|
|
1691
2240
|
function resolveGatedAgent() {
|
|
1692
2241
|
try {
|
|
1693
2242
|
const employees = loadEmployeesSync();
|
|
@@ -1701,12 +2250,12 @@ function getGatedAgent() {
|
|
|
1701
2250
|
return resolveGatedAgent() || GATED_AGENT;
|
|
1702
2251
|
}
|
|
1703
2252
|
function toWorkspaceRelative(filePath, cwd = process.cwd()) {
|
|
1704
|
-
const normalized =
|
|
1705
|
-
if (normalized.startsWith(cwd +
|
|
2253
|
+
const normalized = path10.normalize(filePath);
|
|
2254
|
+
if (normalized.startsWith(cwd + path10.sep)) {
|
|
1706
2255
|
return normalized.slice(cwd.length + 1);
|
|
1707
2256
|
}
|
|
1708
|
-
if (
|
|
1709
|
-
return
|
|
2257
|
+
if (path10.isAbsolute(normalized)) {
|
|
2258
|
+
return path10.basename(normalized);
|
|
1710
2259
|
}
|
|
1711
2260
|
return normalized;
|
|
1712
2261
|
}
|
|
@@ -1715,8 +2264,8 @@ function isDockerfile(basename) {
|
|
|
1715
2264
|
}
|
|
1716
2265
|
function classifyPath(filePath, cwd) {
|
|
1717
2266
|
const rel = toWorkspaceRelative(filePath, cwd);
|
|
1718
|
-
const basename =
|
|
1719
|
-
const ext =
|
|
2267
|
+
const basename = path10.basename(rel);
|
|
2268
|
+
const ext = path10.extname(rel).toLowerCase();
|
|
1720
2269
|
if (ext === ".md") {
|
|
1721
2270
|
for (const prefix of EXEMPT_MD_DIR_PREFIXES) {
|
|
1722
2271
|
if (rel.startsWith(prefix)) return "exempt";
|
|
@@ -1759,7 +2308,7 @@ async function hasRecentEngineerDispatch(now = Date.now()) {
|
|
|
1759
2308
|
}
|
|
1760
2309
|
}
|
|
1761
2310
|
function scratchpadPath(sessionId, homeDir = os6.homedir()) {
|
|
1762
|
-
return
|
|
2311
|
+
return path10.join(
|
|
1763
2312
|
homeDir,
|
|
1764
2313
|
".exe-os",
|
|
1765
2314
|
"session-cache",
|
|
@@ -1769,11 +2318,11 @@ function scratchpadPath(sessionId, homeDir = os6.homedir()) {
|
|
|
1769
2318
|
function hasValidScratchpadEscape(sessionId, now = Date.now(), homeDir = os6.homedir()) {
|
|
1770
2319
|
const paths = [scratchpadPath(sessionId, homeDir)];
|
|
1771
2320
|
for (const filePath of paths) {
|
|
1772
|
-
if (!
|
|
2321
|
+
if (!existsSync8(filePath)) continue;
|
|
1773
2322
|
try {
|
|
1774
|
-
const stat =
|
|
2323
|
+
const stat = statSync2(filePath);
|
|
1775
2324
|
if (now - stat.mtimeMs > SCRATCHPAD_MAX_AGE_MS) continue;
|
|
1776
|
-
const body =
|
|
2325
|
+
const body = readFileSync9(filePath, "utf-8");
|
|
1777
2326
|
if (SCRATCHPAD_ESCAPE_PATTERN.test(body)) return true;
|
|
1778
2327
|
} catch {
|
|
1779
2328
|
}
|
|
@@ -1864,14 +2413,14 @@ var init_memory = __esm({
|
|
|
1864
2413
|
|
|
1865
2414
|
// src/lib/keychain.ts
|
|
1866
2415
|
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
1867
|
-
import { existsSync as
|
|
1868
|
-
import
|
|
2416
|
+
import { existsSync as existsSync9 } from "fs";
|
|
2417
|
+
import path11 from "path";
|
|
1869
2418
|
import os7 from "os";
|
|
1870
2419
|
function getKeyDir() {
|
|
1871
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
2420
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path11.join(os7.homedir(), ".exe-os");
|
|
1872
2421
|
}
|
|
1873
2422
|
function getKeyPath() {
|
|
1874
|
-
return
|
|
2423
|
+
return path11.join(getKeyDir(), "master.key");
|
|
1875
2424
|
}
|
|
1876
2425
|
async function tryKeytar() {
|
|
1877
2426
|
try {
|
|
@@ -1892,13 +2441,21 @@ async function getMasterKey() {
|
|
|
1892
2441
|
}
|
|
1893
2442
|
}
|
|
1894
2443
|
const keyPath = getKeyPath();
|
|
1895
|
-
if (!
|
|
2444
|
+
if (!existsSync9(keyPath)) {
|
|
2445
|
+
process.stderr.write(
|
|
2446
|
+
`[keychain] Key not found at ${keyPath} (HOME=${os7.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
2447
|
+
`
|
|
2448
|
+
);
|
|
1896
2449
|
return null;
|
|
1897
2450
|
}
|
|
1898
2451
|
try {
|
|
1899
2452
|
const content = await readFile3(keyPath, "utf-8");
|
|
1900
2453
|
return Buffer.from(content.trim(), "base64");
|
|
1901
|
-
} catch {
|
|
2454
|
+
} catch (err) {
|
|
2455
|
+
process.stderr.write(
|
|
2456
|
+
`[keychain] Key read failed at ${keyPath}: ${err instanceof Error ? err.message : String(err)}
|
|
2457
|
+
`
|
|
2458
|
+
);
|
|
1902
2459
|
return null;
|
|
1903
2460
|
}
|
|
1904
2461
|
}
|
|
@@ -1979,12 +2536,12 @@ __export(shard_manager_exports, {
|
|
|
1979
2536
|
listShards: () => listShards,
|
|
1980
2537
|
shardExists: () => shardExists
|
|
1981
2538
|
});
|
|
1982
|
-
import
|
|
1983
|
-
import { existsSync as
|
|
2539
|
+
import path12 from "path";
|
|
2540
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync5, readdirSync as readdirSync2 } from "fs";
|
|
1984
2541
|
import { createClient as createClient2 } from "@libsql/client";
|
|
1985
2542
|
function initShardManager(encryptionKey) {
|
|
1986
2543
|
_encryptionKey = encryptionKey;
|
|
1987
|
-
if (!
|
|
2544
|
+
if (!existsSync10(SHARDS_DIR)) {
|
|
1988
2545
|
mkdirSync5(SHARDS_DIR, { recursive: true });
|
|
1989
2546
|
}
|
|
1990
2547
|
_shardingEnabled = true;
|
|
@@ -2005,7 +2562,7 @@ function getShardClient(projectName) {
|
|
|
2005
2562
|
}
|
|
2006
2563
|
const cached = _shards.get(safeName);
|
|
2007
2564
|
if (cached) return cached;
|
|
2008
|
-
const dbPath =
|
|
2565
|
+
const dbPath = path12.join(SHARDS_DIR, `${safeName}.db`);
|
|
2009
2566
|
const client = createClient2({
|
|
2010
2567
|
url: `file:${dbPath}`,
|
|
2011
2568
|
encryptionKey: _encryptionKey
|
|
@@ -2015,10 +2572,10 @@ function getShardClient(projectName) {
|
|
|
2015
2572
|
}
|
|
2016
2573
|
function shardExists(projectName) {
|
|
2017
2574
|
const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
2018
|
-
return
|
|
2575
|
+
return existsSync10(path12.join(SHARDS_DIR, `${safeName}.db`));
|
|
2019
2576
|
}
|
|
2020
2577
|
function listShards() {
|
|
2021
|
-
if (!
|
|
2578
|
+
if (!existsSync10(SHARDS_DIR)) return [];
|
|
2022
2579
|
return readdirSync2(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
2023
2580
|
}
|
|
2024
2581
|
async function ensureShardSchema(client) {
|
|
@@ -2204,7 +2761,7 @@ var init_shard_manager = __esm({
|
|
|
2204
2761
|
"src/lib/shard-manager.ts"() {
|
|
2205
2762
|
"use strict";
|
|
2206
2763
|
init_config();
|
|
2207
|
-
SHARDS_DIR =
|
|
2764
|
+
SHARDS_DIR = path12.join(EXE_AI_DIR, "shards");
|
|
2208
2765
|
_shards = /* @__PURE__ */ new Map();
|
|
2209
2766
|
_encryptionKey = null;
|
|
2210
2767
|
_shardingEnabled = false;
|
|
@@ -2329,7 +2886,7 @@ __export(global_procedures_exports, {
|
|
|
2329
2886
|
loadGlobalProcedures: () => loadGlobalProcedures,
|
|
2330
2887
|
storeGlobalProcedure: () => storeGlobalProcedure
|
|
2331
2888
|
});
|
|
2332
|
-
import { randomUUID as
|
|
2889
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
2333
2890
|
async function loadGlobalProcedures() {
|
|
2334
2891
|
const client = getClient();
|
|
2335
2892
|
const result = await client.execute({
|
|
@@ -2358,7 +2915,7 @@ ${sections.join("\n\n")}
|
|
|
2358
2915
|
`;
|
|
2359
2916
|
}
|
|
2360
2917
|
async function storeGlobalProcedure(input2) {
|
|
2361
|
-
const id =
|
|
2918
|
+
const id = randomUUID3();
|
|
2362
2919
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2363
2920
|
const client = getClient();
|
|
2364
2921
|
await client.execute({
|
|
@@ -2409,6 +2966,7 @@ __export(store_exports, {
|
|
|
2409
2966
|
vectorToBlob: () => vectorToBlob,
|
|
2410
2967
|
writeMemory: () => writeMemory
|
|
2411
2968
|
});
|
|
2969
|
+
import { createHash } from "crypto";
|
|
2412
2970
|
function isBusyError2(err) {
|
|
2413
2971
|
if (err instanceof Error) {
|
|
2414
2972
|
const msg = err.message.toLowerCase();
|
|
@@ -2482,12 +3040,52 @@ function classifyTier(record) {
|
|
|
2482
3040
|
if (["store_memory", "manual"].includes(record.tool_name ?? "") && (record.importance ?? 0) >= 5) return 2;
|
|
2483
3041
|
return 3;
|
|
2484
3042
|
}
|
|
3043
|
+
function inferFilePaths(record) {
|
|
3044
|
+
if (!["Read", "Write", "Edit"].includes(record.tool_name)) return null;
|
|
3045
|
+
const firstLine = record.raw_text.split("\n")[0] ?? "";
|
|
3046
|
+
const match = firstLine.match(/(\/[\w./-]+\.\w+)/);
|
|
3047
|
+
return match ? JSON.stringify([match[1]]) : null;
|
|
3048
|
+
}
|
|
3049
|
+
function inferCommitHash(record) {
|
|
3050
|
+
if (record.tool_name !== "Bash") return null;
|
|
3051
|
+
const match = record.raw_text.match(/\b([a-f0-9]{7,40})\b/);
|
|
3052
|
+
return match ? match[1] : null;
|
|
3053
|
+
}
|
|
3054
|
+
function inferLanguageType(record) {
|
|
3055
|
+
const text = record.raw_text;
|
|
3056
|
+
if (!text || text.length < 10) return null;
|
|
3057
|
+
const trimmed = text.trimStart();
|
|
3058
|
+
if (trimmed.startsWith("{") || trimmed.startsWith("[")) return "json";
|
|
3059
|
+
if (/\b(SELECT|INSERT|UPDATE|DELETE|CREATE TABLE|ALTER TABLE)\b/i.test(text)) return "sql";
|
|
3060
|
+
if (/\b(function |const |import |export |class |def |async |=>)\b/.test(text)) return "code";
|
|
3061
|
+
if (trimmed.startsWith("#") || trimmed.startsWith("*")) return "prose";
|
|
3062
|
+
return "mixed";
|
|
3063
|
+
}
|
|
3064
|
+
function inferDomain(record) {
|
|
3065
|
+
const proj = (record.project_name ?? "").toLowerCase();
|
|
3066
|
+
if (proj.includes("marketing") || proj.includes("content")) return "marketing";
|
|
3067
|
+
if (proj.includes("crm") || proj.includes("customer")) return "customer";
|
|
3068
|
+
return null;
|
|
3069
|
+
}
|
|
2485
3070
|
async function writeMemory(record) {
|
|
2486
3071
|
if (record.vector !== null && record.vector.length !== EMBEDDING_DIM) {
|
|
2487
3072
|
throw new Error(
|
|
2488
3073
|
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
2489
3074
|
);
|
|
2490
3075
|
}
|
|
3076
|
+
const contentHash = createHash("md5").update(record.raw_text).digest("hex");
|
|
3077
|
+
if (_pendingRecords.some((r) => r.content_hash === contentHash && r.agent_id === record.agent_id)) {
|
|
3078
|
+
return;
|
|
3079
|
+
}
|
|
3080
|
+
try {
|
|
3081
|
+
const client = getClient();
|
|
3082
|
+
const existing = await client.execute({
|
|
3083
|
+
sql: "SELECT id FROM memories WHERE content_hash = ? AND agent_id = ? LIMIT 1",
|
|
3084
|
+
args: [contentHash, record.agent_id]
|
|
3085
|
+
});
|
|
3086
|
+
if (existing.rows.length > 0) return;
|
|
3087
|
+
} catch {
|
|
3088
|
+
}
|
|
2491
3089
|
const dbRow = {
|
|
2492
3090
|
id: record.id,
|
|
2493
3091
|
agent_id: record.agent_id,
|
|
@@ -2517,7 +3115,23 @@ async function writeMemory(record) {
|
|
|
2517
3115
|
supersedes_id: record.supersedes_id ?? null,
|
|
2518
3116
|
draft: record.draft ? 1 : 0,
|
|
2519
3117
|
memory_type: record.memory_type ?? "raw",
|
|
2520
|
-
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null
|
|
3118
|
+
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
3119
|
+
content_hash: contentHash,
|
|
3120
|
+
intent: record.intent ?? null,
|
|
3121
|
+
outcome: record.outcome ?? null,
|
|
3122
|
+
domain: record.domain ?? inferDomain(record),
|
|
3123
|
+
referenced_entities: record.referenced_entities ?? null,
|
|
3124
|
+
retrieval_count: record.retrieval_count ?? 0,
|
|
3125
|
+
chain_position: record.chain_position ?? null,
|
|
3126
|
+
review_status: record.review_status ?? null,
|
|
3127
|
+
context_window_pct: record.context_window_pct ?? null,
|
|
3128
|
+
file_paths: record.file_paths ?? inferFilePaths(record),
|
|
3129
|
+
commit_hash: record.commit_hash ?? inferCommitHash(record),
|
|
3130
|
+
duration_ms: record.duration_ms ?? null,
|
|
3131
|
+
token_cost: record.token_cost ?? null,
|
|
3132
|
+
audience: record.audience ?? null,
|
|
3133
|
+
language_type: record.language_type ?? inferLanguageType(record),
|
|
3134
|
+
parent_memory_id: record.parent_memory_id ?? null
|
|
2521
3135
|
};
|
|
2522
3136
|
_pendingRecords.push(dbRow);
|
|
2523
3137
|
orgBus.emit({
|
|
@@ -2575,80 +3189,85 @@ async function flushBatch() {
|
|
|
2575
3189
|
const draft = row.draft ? 1 : 0;
|
|
2576
3190
|
const memoryType = row.memory_type ?? "raw";
|
|
2577
3191
|
const trajectory = row.trajectory ?? null;
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
3192
|
+
const contentHash = row.content_hash ?? null;
|
|
3193
|
+
const intent = row.intent ?? null;
|
|
3194
|
+
const outcome = row.outcome ?? null;
|
|
3195
|
+
const domain = row.domain ?? null;
|
|
3196
|
+
const referencedEntities = row.referenced_entities ?? null;
|
|
3197
|
+
const retrievalCount = row.retrieval_count ?? 0;
|
|
3198
|
+
const chainPosition = row.chain_position ?? null;
|
|
3199
|
+
const reviewStatus = row.review_status ?? null;
|
|
3200
|
+
const contextWindowPct = row.context_window_pct ?? null;
|
|
3201
|
+
const filePaths = row.file_paths ?? null;
|
|
3202
|
+
const commitHash = row.commit_hash ?? null;
|
|
3203
|
+
const durationMs = row.duration_ms ?? null;
|
|
3204
|
+
const tokenCost = row.token_cost ?? null;
|
|
3205
|
+
const audience = row.audience ?? null;
|
|
3206
|
+
const languageType = row.language_type ?? null;
|
|
3207
|
+
const parentMemoryId = row.parent_memory_id ?? null;
|
|
3208
|
+
const cols = `id, agent_id, agent_role, session_id, timestamp,
|
|
2581
3209
|
tool_name, project_name,
|
|
2582
3210
|
has_error, raw_text, vector, version, task_id, importance, status,
|
|
2583
3211
|
confidence, last_accessed,
|
|
2584
3212
|
workspace_id, document_id, user_id, char_offset, page_number,
|
|
2585
|
-
source_path, source_type, tier, supersedes_id, draft, memory_type, trajectory
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
pageNumber,
|
|
2644
|
-
sourcePath,
|
|
2645
|
-
sourceType,
|
|
2646
|
-
tier,
|
|
2647
|
-
supersedesId,
|
|
2648
|
-
draft,
|
|
2649
|
-
memoryType,
|
|
2650
|
-
trajectory
|
|
2651
|
-
]
|
|
3213
|
+
source_path, source_type, tier, supersedes_id, draft, memory_type, trajectory, content_hash,
|
|
3214
|
+
intent, outcome, domain, referenced_entities, retrieval_count,
|
|
3215
|
+
chain_position, review_status, context_window_pct, file_paths, commit_hash,
|
|
3216
|
+
duration_ms, token_cost, audience, language_type, parent_memory_id`;
|
|
3217
|
+
const metaArgs = [
|
|
3218
|
+
intent,
|
|
3219
|
+
outcome,
|
|
3220
|
+
domain,
|
|
3221
|
+
referencedEntities,
|
|
3222
|
+
retrievalCount,
|
|
3223
|
+
chainPosition,
|
|
3224
|
+
reviewStatus,
|
|
3225
|
+
contextWindowPct,
|
|
3226
|
+
filePaths,
|
|
3227
|
+
commitHash,
|
|
3228
|
+
durationMs,
|
|
3229
|
+
tokenCost,
|
|
3230
|
+
audience,
|
|
3231
|
+
languageType,
|
|
3232
|
+
parentMemoryId
|
|
3233
|
+
];
|
|
3234
|
+
const baseArgs = [
|
|
3235
|
+
row.id,
|
|
3236
|
+
row.agent_id,
|
|
3237
|
+
row.agent_role,
|
|
3238
|
+
row.session_id,
|
|
3239
|
+
row.timestamp,
|
|
3240
|
+
row.tool_name,
|
|
3241
|
+
row.project_name,
|
|
3242
|
+
row.has_error,
|
|
3243
|
+
row.raw_text
|
|
3244
|
+
];
|
|
3245
|
+
const sharedArgs = [
|
|
3246
|
+
row.version,
|
|
3247
|
+
taskId,
|
|
3248
|
+
importance,
|
|
3249
|
+
status,
|
|
3250
|
+
confidence,
|
|
3251
|
+
lastAccessed,
|
|
3252
|
+
workspaceId,
|
|
3253
|
+
documentId,
|
|
3254
|
+
userId,
|
|
3255
|
+
charOffset,
|
|
3256
|
+
pageNumber,
|
|
3257
|
+
sourcePath,
|
|
3258
|
+
sourceType,
|
|
3259
|
+
tier,
|
|
3260
|
+
supersedesId,
|
|
3261
|
+
draft,
|
|
3262
|
+
memoryType,
|
|
3263
|
+
trajectory,
|
|
3264
|
+
contentHash
|
|
3265
|
+
];
|
|
3266
|
+
return {
|
|
3267
|
+
sql: hasVector ? `INSERT OR IGNORE INTO memories (${cols})
|
|
3268
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories (${cols})
|
|
3269
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
3270
|
+
args: hasVector ? [...baseArgs, vectorToBlob(row.vector), ...sharedArgs, ...metaArgs] : [...baseArgs, ...sharedArgs, ...metaArgs]
|
|
2652
3271
|
};
|
|
2653
3272
|
};
|
|
2654
3273
|
const globalClient = getClient();
|
|
@@ -2908,7 +3527,7 @@ __export(review_gate_exports, {
|
|
|
2908
3527
|
runReviewGate: () => runReviewGate
|
|
2909
3528
|
});
|
|
2910
3529
|
import { execSync as execSync5 } from "child_process";
|
|
2911
|
-
import { existsSync as
|
|
3530
|
+
import { existsSync as existsSync11 } from "fs";
|
|
2912
3531
|
function checkCommitsExist(taskCreatedAt) {
|
|
2913
3532
|
try {
|
|
2914
3533
|
const since = new Date(taskCreatedAt).toISOString();
|
|
@@ -2971,7 +3590,7 @@ function checkTestCoverage(taskCreatedAt) {
|
|
|
2971
3590
|
const testPath = file.replace(/^src\//, "tests/").replace(/\.ts$/, ".test.ts");
|
|
2972
3591
|
const baseName = file.split("/").pop()?.replace(/\.ts$/, "") ?? "";
|
|
2973
3592
|
const testDir = testPath.substring(0, testPath.lastIndexOf("/"));
|
|
2974
|
-
const hasDirectTest =
|
|
3593
|
+
const hasDirectTest = existsSync11(testPath);
|
|
2975
3594
|
let hasRelatedTest = false;
|
|
2976
3595
|
try {
|
|
2977
3596
|
const related = execSync5(
|
|
@@ -3025,9 +3644,9 @@ var init_review_gate = __esm({
|
|
|
3025
3644
|
});
|
|
3026
3645
|
|
|
3027
3646
|
// src/adapters/claude/hooks/pre-tool-use.ts
|
|
3028
|
-
import { existsSync as
|
|
3647
|
+
import { existsSync as existsSync12, writeFileSync as writeFileSync6, mkdirSync as mkdirSync6 } from "fs";
|
|
3029
3648
|
import { execSync as execSync6 } from "child_process";
|
|
3030
|
-
import
|
|
3649
|
+
import path13 from "path";
|
|
3031
3650
|
|
|
3032
3651
|
// src/adapters/claude/active-agent.ts
|
|
3033
3652
|
init_config();
|
|
@@ -3140,17 +3759,17 @@ if (!process.env.AGENT_ID) {
|
|
|
3140
3759
|
}
|
|
3141
3760
|
var DELEGATION_TASK_THRESHOLD = 3;
|
|
3142
3761
|
var CTO_ROLES = ["CTO", "executive"];
|
|
3143
|
-
var CACHE_DIR2 =
|
|
3762
|
+
var CACHE_DIR2 = path13.join(EXE_AI_DIR, "session-cache");
|
|
3144
3763
|
var timeout = setTimeout(() => {
|
|
3145
3764
|
process.exit(0);
|
|
3146
3765
|
}, 5e3);
|
|
3147
3766
|
timeout.unref();
|
|
3148
3767
|
function getDelegationFlagPath() {
|
|
3149
|
-
return
|
|
3768
|
+
return path13.join(CACHE_DIR2, `delegation-checkpoint-${getSessionKey()}.json`);
|
|
3150
3769
|
}
|
|
3151
3770
|
function hasDelegationFired() {
|
|
3152
3771
|
try {
|
|
3153
|
-
return
|
|
3772
|
+
return existsSync12(getDelegationFlagPath());
|
|
3154
3773
|
} catch {
|
|
3155
3774
|
return false;
|
|
3156
3775
|
}
|