@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
|
@@ -309,6 +309,12 @@ function getClient() {
|
|
|
309
309
|
if (!_resilientClient) {
|
|
310
310
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
311
311
|
}
|
|
312
|
+
if (process.env.EXE_IS_DAEMON === "1") {
|
|
313
|
+
return _resilientClient;
|
|
314
|
+
}
|
|
315
|
+
if (_daemonClient && _daemonClient._isDaemonActive()) {
|
|
316
|
+
return _daemonClient;
|
|
317
|
+
}
|
|
312
318
|
return _resilientClient;
|
|
313
319
|
}
|
|
314
320
|
function getRawClient() {
|
|
@@ -797,6 +803,12 @@ async function ensureSchema() {
|
|
|
797
803
|
} catch {
|
|
798
804
|
}
|
|
799
805
|
}
|
|
806
|
+
try {
|
|
807
|
+
await client.execute(
|
|
808
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_content_hash ON memories(content_hash, agent_id)`
|
|
809
|
+
);
|
|
810
|
+
} catch {
|
|
811
|
+
}
|
|
800
812
|
await client.executeMultiple(`
|
|
801
813
|
CREATE TABLE IF NOT EXISTS entities (
|
|
802
814
|
id TEXT PRIMARY KEY,
|
|
@@ -849,7 +861,30 @@ async function ensureSchema() {
|
|
|
849
861
|
entity_id TEXT NOT NULL,
|
|
850
862
|
PRIMARY KEY (hyperedge_id, entity_id)
|
|
851
863
|
);
|
|
864
|
+
|
|
865
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS entities_fts USING fts5(
|
|
866
|
+
name,
|
|
867
|
+
content=entities,
|
|
868
|
+
content_rowid=rowid
|
|
869
|
+
);
|
|
870
|
+
|
|
871
|
+
CREATE TRIGGER IF NOT EXISTS entities_fts_ai AFTER INSERT ON entities BEGIN
|
|
872
|
+
INSERT INTO entities_fts(rowid, name) VALUES (new.rowid, new.name);
|
|
873
|
+
END;
|
|
874
|
+
|
|
875
|
+
CREATE TRIGGER IF NOT EXISTS entities_fts_ad AFTER DELETE ON entities BEGIN
|
|
876
|
+
INSERT INTO entities_fts(entities_fts, rowid, name) VALUES('delete', old.rowid, old.name);
|
|
877
|
+
END;
|
|
878
|
+
|
|
879
|
+
CREATE TRIGGER IF NOT EXISTS entities_fts_au AFTER UPDATE ON entities BEGIN
|
|
880
|
+
INSERT INTO entities_fts(entities_fts, rowid, name) VALUES('delete', old.rowid, old.name);
|
|
881
|
+
INSERT INTO entities_fts(rowid, name) VALUES (new.rowid, new.name);
|
|
882
|
+
END;
|
|
852
883
|
`);
|
|
884
|
+
try {
|
|
885
|
+
await client.execute("INSERT INTO entities_fts(entities_fts) VALUES('rebuild')");
|
|
886
|
+
} catch {
|
|
887
|
+
}
|
|
853
888
|
await client.executeMultiple(`
|
|
854
889
|
CREATE TABLE IF NOT EXISTS entity_aliases (
|
|
855
890
|
alias TEXT NOT NULL PRIMARY KEY,
|
|
@@ -1030,6 +1065,33 @@ async function ensureSchema() {
|
|
|
1030
1065
|
CREATE INDEX IF NOT EXISTS idx_conversations_channel
|
|
1031
1066
|
ON conversations(channel_id);
|
|
1032
1067
|
`);
|
|
1068
|
+
await client.executeMultiple(`
|
|
1069
|
+
CREATE TABLE IF NOT EXISTS session_agent_map (
|
|
1070
|
+
session_uuid TEXT PRIMARY KEY,
|
|
1071
|
+
agent_id TEXT NOT NULL,
|
|
1072
|
+
session_name TEXT,
|
|
1073
|
+
task_id TEXT,
|
|
1074
|
+
project_name TEXT,
|
|
1075
|
+
started_at TEXT NOT NULL
|
|
1076
|
+
);
|
|
1077
|
+
|
|
1078
|
+
CREATE INDEX IF NOT EXISTS idx_session_agent_map_agent
|
|
1079
|
+
ON session_agent_map(agent_id);
|
|
1080
|
+
`);
|
|
1081
|
+
try {
|
|
1082
|
+
const mapCount = await client.execute({ sql: `SELECT COUNT(*) as cnt FROM session_agent_map`, args: [] });
|
|
1083
|
+
if (Number(mapCount.rows[0]?.cnt ?? 0) === 0) {
|
|
1084
|
+
await client.execute({
|
|
1085
|
+
sql: `INSERT OR IGNORE INTO session_agent_map (session_uuid, agent_id, session_name, started_at)
|
|
1086
|
+
SELECT session_id, agent_id, '', MIN(timestamp)
|
|
1087
|
+
FROM memories
|
|
1088
|
+
WHERE session_id IS NOT NULL AND session_id != '' AND agent_id IS NOT NULL AND agent_id != ''
|
|
1089
|
+
GROUP BY session_id, agent_id`,
|
|
1090
|
+
args: []
|
|
1091
|
+
});
|
|
1092
|
+
}
|
|
1093
|
+
} catch {
|
|
1094
|
+
}
|
|
1033
1095
|
try {
|
|
1034
1096
|
await client.execute({
|
|
1035
1097
|
sql: `ALTER TABLE tasks ADD COLUMN budget_tokens INTEGER`,
|
|
@@ -1163,8 +1225,30 @@ async function ensureSchema() {
|
|
|
1163
1225
|
});
|
|
1164
1226
|
} catch {
|
|
1165
1227
|
}
|
|
1228
|
+
for (const col of [
|
|
1229
|
+
"ALTER TABLE memories ADD COLUMN intent TEXT",
|
|
1230
|
+
"ALTER TABLE memories ADD COLUMN outcome TEXT",
|
|
1231
|
+
"ALTER TABLE memories ADD COLUMN domain TEXT",
|
|
1232
|
+
"ALTER TABLE memories ADD COLUMN referenced_entities TEXT",
|
|
1233
|
+
"ALTER TABLE memories ADD COLUMN retrieval_count INTEGER DEFAULT 0",
|
|
1234
|
+
"ALTER TABLE memories ADD COLUMN chain_position TEXT",
|
|
1235
|
+
"ALTER TABLE memories ADD COLUMN review_status TEXT",
|
|
1236
|
+
"ALTER TABLE memories ADD COLUMN context_window_pct INTEGER",
|
|
1237
|
+
"ALTER TABLE memories ADD COLUMN file_paths TEXT",
|
|
1238
|
+
"ALTER TABLE memories ADD COLUMN commit_hash TEXT",
|
|
1239
|
+
"ALTER TABLE memories ADD COLUMN duration_ms INTEGER",
|
|
1240
|
+
"ALTER TABLE memories ADD COLUMN token_cost REAL",
|
|
1241
|
+
"ALTER TABLE memories ADD COLUMN audience TEXT",
|
|
1242
|
+
"ALTER TABLE memories ADD COLUMN language_type TEXT",
|
|
1243
|
+
"ALTER TABLE memories ADD COLUMN parent_memory_id TEXT"
|
|
1244
|
+
]) {
|
|
1245
|
+
try {
|
|
1246
|
+
await client.execute(col);
|
|
1247
|
+
} catch {
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1166
1250
|
}
|
|
1167
|
-
var _client, _resilientClient, initTurso;
|
|
1251
|
+
var _client, _resilientClient, _daemonClient, initTurso;
|
|
1168
1252
|
var init_database = __esm({
|
|
1169
1253
|
"src/lib/database.ts"() {
|
|
1170
1254
|
"use strict";
|
|
@@ -1172,6 +1256,7 @@ var init_database = __esm({
|
|
|
1172
1256
|
init_employees();
|
|
1173
1257
|
_client = null;
|
|
1174
1258
|
_resilientClient = null;
|
|
1259
|
+
_daemonClient = null;
|
|
1175
1260
|
initTurso = initDatabase;
|
|
1176
1261
|
}
|
|
1177
1262
|
});
|
|
@@ -1602,546 +1687,135 @@ ${p.content}`).join("\n\n");
|
|
|
1602
1687
|
}
|
|
1603
1688
|
});
|
|
1604
1689
|
|
|
1605
|
-
// src/
|
|
1606
|
-
import
|
|
1607
|
-
import {
|
|
1608
|
-
import {
|
|
1609
|
-
import
|
|
1610
|
-
import
|
|
1611
|
-
import {
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
// src/lib/store.ts
|
|
1618
|
-
init_database();
|
|
1619
|
-
|
|
1620
|
-
// src/lib/keychain.ts
|
|
1621
|
-
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
1622
|
-
import { existsSync as existsSync3 } from "fs";
|
|
1623
|
-
import path3 from "path";
|
|
1624
|
-
import os3 from "os";
|
|
1625
|
-
var SERVICE = "exe-mem";
|
|
1626
|
-
var ACCOUNT = "master-key";
|
|
1627
|
-
function getKeyDir() {
|
|
1628
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path3.join(os3.homedir(), ".exe-os");
|
|
1629
|
-
}
|
|
1630
|
-
function getKeyPath() {
|
|
1631
|
-
return path3.join(getKeyDir(), "master.key");
|
|
1632
|
-
}
|
|
1633
|
-
async function tryKeytar() {
|
|
1634
|
-
try {
|
|
1635
|
-
return await import("keytar");
|
|
1636
|
-
} catch {
|
|
1637
|
-
return null;
|
|
1690
|
+
// src/lib/exe-daemon-client.ts
|
|
1691
|
+
import net from "net";
|
|
1692
|
+
import { spawn } from "child_process";
|
|
1693
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
1694
|
+
import { existsSync as existsSync5, unlinkSync as unlinkSync2, readFileSync as readFileSync3, openSync, closeSync, statSync } from "fs";
|
|
1695
|
+
import path5 from "path";
|
|
1696
|
+
import { fileURLToPath } from "url";
|
|
1697
|
+
function handleData(chunk) {
|
|
1698
|
+
_buffer += chunk.toString();
|
|
1699
|
+
if (_buffer.length > MAX_BUFFER) {
|
|
1700
|
+
_buffer = "";
|
|
1701
|
+
return;
|
|
1638
1702
|
}
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1703
|
+
let newlineIdx;
|
|
1704
|
+
while ((newlineIdx = _buffer.indexOf("\n")) !== -1) {
|
|
1705
|
+
const line = _buffer.slice(0, newlineIdx).trim();
|
|
1706
|
+
_buffer = _buffer.slice(newlineIdx + 1);
|
|
1707
|
+
if (!line) continue;
|
|
1643
1708
|
try {
|
|
1644
|
-
const
|
|
1645
|
-
|
|
1646
|
-
|
|
1709
|
+
const response = JSON.parse(line);
|
|
1710
|
+
const id = response.id;
|
|
1711
|
+
if (!id) continue;
|
|
1712
|
+
const entry = _pending.get(id);
|
|
1713
|
+
if (entry) {
|
|
1714
|
+
clearTimeout(entry.timer);
|
|
1715
|
+
_pending.delete(id);
|
|
1716
|
+
entry.resolve(response);
|
|
1647
1717
|
}
|
|
1648
1718
|
} catch {
|
|
1649
1719
|
}
|
|
1650
1720
|
}
|
|
1651
|
-
const keyPath = getKeyPath();
|
|
1652
|
-
if (!existsSync3(keyPath)) {
|
|
1653
|
-
return null;
|
|
1654
|
-
}
|
|
1655
|
-
try {
|
|
1656
|
-
const content = await readFile3(keyPath, "utf-8");
|
|
1657
|
-
return Buffer.from(content.trim(), "base64");
|
|
1658
|
-
} catch {
|
|
1659
|
-
return null;
|
|
1660
|
-
}
|
|
1661
1721
|
}
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
var StateBus = class {
|
|
1668
|
-
handlers = /* @__PURE__ */ new Map();
|
|
1669
|
-
globalHandlers = /* @__PURE__ */ new Set();
|
|
1670
|
-
/** Emit an event to all subscribers */
|
|
1671
|
-
emit(event) {
|
|
1672
|
-
const typeHandlers = this.handlers.get(event.type);
|
|
1673
|
-
if (typeHandlers) {
|
|
1674
|
-
for (const handler of typeHandlers) {
|
|
1722
|
+
function cleanupStaleFiles() {
|
|
1723
|
+
if (existsSync5(PID_PATH)) {
|
|
1724
|
+
try {
|
|
1725
|
+
const pid = parseInt(readFileSync3(PID_PATH, "utf8").trim(), 10);
|
|
1726
|
+
if (pid > 0) {
|
|
1675
1727
|
try {
|
|
1676
|
-
|
|
1728
|
+
process.kill(pid, 0);
|
|
1729
|
+
return;
|
|
1677
1730
|
} catch {
|
|
1678
1731
|
}
|
|
1679
1732
|
}
|
|
1733
|
+
} catch {
|
|
1680
1734
|
}
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
} catch {
|
|
1685
|
-
}
|
|
1735
|
+
try {
|
|
1736
|
+
unlinkSync2(PID_PATH);
|
|
1737
|
+
} catch {
|
|
1686
1738
|
}
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
if (!this.handlers.has(type)) {
|
|
1691
|
-
this.handlers.set(type, /* @__PURE__ */ new Set());
|
|
1739
|
+
try {
|
|
1740
|
+
unlinkSync2(SOCKET_PATH);
|
|
1741
|
+
} catch {
|
|
1692
1742
|
}
|
|
1693
|
-
this.handlers.get(type).add(handler);
|
|
1694
1743
|
}
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1744
|
+
}
|
|
1745
|
+
function findPackageRoot() {
|
|
1746
|
+
let dir = path5.dirname(fileURLToPath(import.meta.url));
|
|
1747
|
+
const { root } = path5.parse(dir);
|
|
1748
|
+
while (dir !== root) {
|
|
1749
|
+
if (existsSync5(path5.join(dir, "package.json"))) return dir;
|
|
1750
|
+
dir = path5.dirname(dir);
|
|
1698
1751
|
}
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1752
|
+
return null;
|
|
1753
|
+
}
|
|
1754
|
+
function spawnDaemon() {
|
|
1755
|
+
const pkgRoot = findPackageRoot();
|
|
1756
|
+
if (!pkgRoot) {
|
|
1757
|
+
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
1758
|
+
return;
|
|
1702
1759
|
}
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1760
|
+
const daemonPath = path5.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
1761
|
+
if (!existsSync5(daemonPath)) {
|
|
1762
|
+
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
1763
|
+
`);
|
|
1764
|
+
return;
|
|
1706
1765
|
}
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1766
|
+
const resolvedPath = daemonPath;
|
|
1767
|
+
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
1768
|
+
`);
|
|
1769
|
+
const logPath = path5.join(path5.dirname(SOCKET_PATH), "exed.log");
|
|
1770
|
+
let stderrFd = "ignore";
|
|
1771
|
+
try {
|
|
1772
|
+
stderrFd = openSync(logPath, "a");
|
|
1773
|
+
} catch {
|
|
1711
1774
|
}
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1775
|
+
const child = spawn(process.execPath, [resolvedPath], {
|
|
1776
|
+
detached: true,
|
|
1777
|
+
stdio: ["ignore", "ignore", stderrFd],
|
|
1778
|
+
env: {
|
|
1779
|
+
...process.env,
|
|
1780
|
+
TMUX: void 0,
|
|
1781
|
+
// Daemon is global — must not inherit session scope
|
|
1782
|
+
TMUX_PANE: void 0,
|
|
1783
|
+
// Prevents resolveExeSession() from scoping to one session
|
|
1784
|
+
EXE_DAEMON_SOCK: SOCKET_PATH,
|
|
1785
|
+
EXE_DAEMON_PID: PID_PATH
|
|
1786
|
+
}
|
|
1787
|
+
});
|
|
1788
|
+
child.unref();
|
|
1789
|
+
if (typeof stderrFd === "number") {
|
|
1790
|
+
try {
|
|
1791
|
+
closeSync(stderrFd);
|
|
1792
|
+
} catch {
|
|
1793
|
+
}
|
|
1722
1794
|
}
|
|
1723
|
-
return false;
|
|
1724
1795
|
}
|
|
1725
|
-
|
|
1726
|
-
|
|
1796
|
+
function acquireSpawnLock() {
|
|
1797
|
+
try {
|
|
1798
|
+
const fd = openSync(SPAWN_LOCK_PATH, "wx");
|
|
1799
|
+
closeSync(fd);
|
|
1800
|
+
return true;
|
|
1801
|
+
} catch {
|
|
1727
1802
|
try {
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1803
|
+
const stat2 = statSync(SPAWN_LOCK_PATH);
|
|
1804
|
+
if (Date.now() - stat2.mtimeMs > SPAWN_LOCK_STALE_MS) {
|
|
1805
|
+
try {
|
|
1806
|
+
unlinkSync2(SPAWN_LOCK_PATH);
|
|
1807
|
+
} catch {
|
|
1808
|
+
}
|
|
1809
|
+
try {
|
|
1810
|
+
const fd = openSync(SPAWN_LOCK_PATH, "wx");
|
|
1811
|
+
closeSync(fd);
|
|
1812
|
+
return true;
|
|
1813
|
+
} catch {
|
|
1814
|
+
}
|
|
1815
|
+
}
|
|
1816
|
+
} catch {
|
|
1736
1817
|
}
|
|
1737
|
-
|
|
1738
|
-
throw new Error("unreachable");
|
|
1739
|
-
}
|
|
1740
|
-
var _pendingRecords = [];
|
|
1741
|
-
var _batchSize = 20;
|
|
1742
|
-
var _flushIntervalMs = 1e4;
|
|
1743
|
-
var _flushTimer = null;
|
|
1744
|
-
var _flushing = false;
|
|
1745
|
-
var _nextVersion = 1;
|
|
1746
|
-
async function initStore(options) {
|
|
1747
|
-
if (_flushTimer !== null) {
|
|
1748
|
-
clearInterval(_flushTimer);
|
|
1749
|
-
_flushTimer = null;
|
|
1750
|
-
}
|
|
1751
|
-
_pendingRecords = [];
|
|
1752
|
-
_flushing = false;
|
|
1753
|
-
_batchSize = options?.batchSize ?? 20;
|
|
1754
|
-
_flushIntervalMs = options?.flushIntervalMs ?? 1e4;
|
|
1755
|
-
let dbPath = options?.dbPath;
|
|
1756
|
-
if (!dbPath) {
|
|
1757
|
-
const config = await loadConfig();
|
|
1758
|
-
dbPath = config.dbPath;
|
|
1759
|
-
}
|
|
1760
|
-
let masterKey = options?.masterKey ?? null;
|
|
1761
|
-
if (!masterKey) {
|
|
1762
|
-
masterKey = await getMasterKey();
|
|
1763
|
-
if (!masterKey) {
|
|
1764
|
-
throw new Error(
|
|
1765
|
-
"No encryption key found. Run /exe-setup to generate one."
|
|
1766
|
-
);
|
|
1767
|
-
}
|
|
1768
|
-
}
|
|
1769
|
-
const hexKey = masterKey.toString("hex");
|
|
1770
|
-
await initTurso({
|
|
1771
|
-
dbPath,
|
|
1772
|
-
encryptionKey: hexKey
|
|
1773
|
-
});
|
|
1774
|
-
await retryOnBusy2(() => ensureSchema(), "ensureSchema");
|
|
1775
|
-
try {
|
|
1776
|
-
const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
1777
|
-
initShardManager2(hexKey);
|
|
1778
|
-
} catch {
|
|
1779
|
-
}
|
|
1780
|
-
const client = getClient();
|
|
1781
|
-
const vResult = await retryOnBusy2(
|
|
1782
|
-
() => client.execute("SELECT MAX(version) as max_v FROM memories"),
|
|
1783
|
-
"version-query"
|
|
1784
|
-
);
|
|
1785
|
-
_nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
|
|
1786
|
-
try {
|
|
1787
|
-
const { loadGlobalProcedures: loadGlobalProcedures2 } = await Promise.resolve().then(() => (init_global_procedures(), global_procedures_exports));
|
|
1788
|
-
await loadGlobalProcedures2();
|
|
1789
|
-
} catch {
|
|
1790
|
-
}
|
|
1791
|
-
}
|
|
1792
|
-
function classifyTier(record) {
|
|
1793
|
-
if (record.tool_name === "commit_to_long_term_memory" && (record.importance ?? 0) >= 8) return 1;
|
|
1794
|
-
if (["store_memory", "manual"].includes(record.tool_name ?? "") && (record.importance ?? 0) >= 5) return 2;
|
|
1795
|
-
return 3;
|
|
1796
|
-
}
|
|
1797
|
-
async function writeMemory(record) {
|
|
1798
|
-
if (record.vector !== null && record.vector.length !== EMBEDDING_DIM) {
|
|
1799
|
-
throw new Error(
|
|
1800
|
-
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
1801
|
-
);
|
|
1802
|
-
}
|
|
1803
|
-
const dbRow = {
|
|
1804
|
-
id: record.id,
|
|
1805
|
-
agent_id: record.agent_id,
|
|
1806
|
-
agent_role: record.agent_role,
|
|
1807
|
-
session_id: record.session_id,
|
|
1808
|
-
timestamp: record.timestamp,
|
|
1809
|
-
tool_name: record.tool_name,
|
|
1810
|
-
project_name: record.project_name,
|
|
1811
|
-
has_error: record.has_error ? 1 : 0,
|
|
1812
|
-
raw_text: record.raw_text,
|
|
1813
|
-
vector: record.vector,
|
|
1814
|
-
version: 0,
|
|
1815
|
-
// Placeholder — assigned atomically at flush time
|
|
1816
|
-
task_id: record.task_id ?? null,
|
|
1817
|
-
importance: record.importance ?? 5,
|
|
1818
|
-
status: record.status ?? "active",
|
|
1819
|
-
confidence: record.confidence ?? 0.7,
|
|
1820
|
-
last_accessed: record.last_accessed ?? record.timestamp,
|
|
1821
|
-
workspace_id: record.workspace_id ?? null,
|
|
1822
|
-
document_id: record.document_id ?? null,
|
|
1823
|
-
user_id: record.user_id ?? null,
|
|
1824
|
-
char_offset: record.char_offset ?? null,
|
|
1825
|
-
page_number: record.page_number ?? null,
|
|
1826
|
-
source_path: record.source_path ?? null,
|
|
1827
|
-
source_type: record.source_type ?? null,
|
|
1828
|
-
tier: record.tier ?? classifyTier(record),
|
|
1829
|
-
supersedes_id: record.supersedes_id ?? null,
|
|
1830
|
-
draft: record.draft ? 1 : 0,
|
|
1831
|
-
memory_type: record.memory_type ?? "raw",
|
|
1832
|
-
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null
|
|
1833
|
-
};
|
|
1834
|
-
_pendingRecords.push(dbRow);
|
|
1835
|
-
orgBus.emit({
|
|
1836
|
-
type: "memory_stored",
|
|
1837
|
-
agentId: record.agent_id,
|
|
1838
|
-
project: record.project_name,
|
|
1839
|
-
timestamp: record.timestamp
|
|
1840
|
-
});
|
|
1841
|
-
const MAX_PENDING = 1e3;
|
|
1842
|
-
if (_pendingRecords.length > MAX_PENDING) {
|
|
1843
|
-
const dropped = _pendingRecords.length - MAX_PENDING;
|
|
1844
|
-
_pendingRecords = _pendingRecords.slice(-MAX_PENDING);
|
|
1845
|
-
console.warn(`[store] Dropped ${dropped} oldest pending records (overflow)`);
|
|
1846
|
-
}
|
|
1847
|
-
if (_flushTimer === null) {
|
|
1848
|
-
_flushTimer = setInterval(() => {
|
|
1849
|
-
void flushBatch();
|
|
1850
|
-
}, _flushIntervalMs);
|
|
1851
|
-
if (_flushTimer && typeof _flushTimer === "object" && "unref" in _flushTimer) {
|
|
1852
|
-
_flushTimer.unref();
|
|
1853
|
-
}
|
|
1854
|
-
}
|
|
1855
|
-
if (_pendingRecords.length >= _batchSize) {
|
|
1856
|
-
await flushBatch();
|
|
1857
|
-
}
|
|
1858
|
-
}
|
|
1859
|
-
async function flushBatch() {
|
|
1860
|
-
if (_flushing || _pendingRecords.length === 0) return 0;
|
|
1861
|
-
_flushing = true;
|
|
1862
|
-
try {
|
|
1863
|
-
const batch = _pendingRecords.slice(0);
|
|
1864
|
-
const client = getClient();
|
|
1865
|
-
const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
|
|
1866
|
-
let baseVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
|
|
1867
|
-
for (const row of batch) {
|
|
1868
|
-
row.version = baseVersion++;
|
|
1869
|
-
}
|
|
1870
|
-
_nextVersion = baseVersion;
|
|
1871
|
-
const buildStmt = (row) => {
|
|
1872
|
-
const hasVector = row.vector !== null;
|
|
1873
|
-
const taskId = row.task_id ?? null;
|
|
1874
|
-
const importance = row.importance ?? 5;
|
|
1875
|
-
const status = row.status ?? "active";
|
|
1876
|
-
const confidence = row.confidence ?? 0.7;
|
|
1877
|
-
const lastAccessed = row.last_accessed ?? row.timestamp;
|
|
1878
|
-
const workspaceId = row.workspace_id ?? null;
|
|
1879
|
-
const documentId = row.document_id ?? null;
|
|
1880
|
-
const userId = row.user_id ?? null;
|
|
1881
|
-
const charOffset = row.char_offset ?? null;
|
|
1882
|
-
const pageNumber = row.page_number ?? null;
|
|
1883
|
-
const sourcePath = row.source_path ?? null;
|
|
1884
|
-
const sourceType = row.source_type ?? null;
|
|
1885
|
-
const tier = row.tier ?? 3;
|
|
1886
|
-
const supersedesId = row.supersedes_id ?? null;
|
|
1887
|
-
const draft = row.draft ? 1 : 0;
|
|
1888
|
-
const memoryType = row.memory_type ?? "raw";
|
|
1889
|
-
const trajectory = row.trajectory ?? null;
|
|
1890
|
-
return {
|
|
1891
|
-
sql: hasVector ? `INSERT OR IGNORE INTO memories
|
|
1892
|
-
(id, agent_id, agent_role, session_id, timestamp,
|
|
1893
|
-
tool_name, project_name,
|
|
1894
|
-
has_error, raw_text, vector, version, task_id, importance, status,
|
|
1895
|
-
confidence, last_accessed,
|
|
1896
|
-
workspace_id, document_id, user_id, char_offset, page_number,
|
|
1897
|
-
source_path, source_type, tier, supersedes_id, draft, memory_type, trajectory)
|
|
1898
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories
|
|
1899
|
-
(id, agent_id, agent_role, session_id, timestamp,
|
|
1900
|
-
tool_name, project_name,
|
|
1901
|
-
has_error, raw_text, vector, version, task_id, importance, status,
|
|
1902
|
-
confidence, last_accessed,
|
|
1903
|
-
workspace_id, document_id, user_id, char_offset, page_number,
|
|
1904
|
-
source_path, source_type, tier, supersedes_id, draft, memory_type, trajectory)
|
|
1905
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
1906
|
-
args: hasVector ? [
|
|
1907
|
-
row.id,
|
|
1908
|
-
row.agent_id,
|
|
1909
|
-
row.agent_role,
|
|
1910
|
-
row.session_id,
|
|
1911
|
-
row.timestamp,
|
|
1912
|
-
row.tool_name,
|
|
1913
|
-
row.project_name,
|
|
1914
|
-
row.has_error,
|
|
1915
|
-
row.raw_text,
|
|
1916
|
-
vectorToBlob(row.vector),
|
|
1917
|
-
row.version,
|
|
1918
|
-
taskId,
|
|
1919
|
-
importance,
|
|
1920
|
-
status,
|
|
1921
|
-
confidence,
|
|
1922
|
-
lastAccessed,
|
|
1923
|
-
workspaceId,
|
|
1924
|
-
documentId,
|
|
1925
|
-
userId,
|
|
1926
|
-
charOffset,
|
|
1927
|
-
pageNumber,
|
|
1928
|
-
sourcePath,
|
|
1929
|
-
sourceType,
|
|
1930
|
-
tier,
|
|
1931
|
-
supersedesId,
|
|
1932
|
-
draft,
|
|
1933
|
-
memoryType,
|
|
1934
|
-
trajectory
|
|
1935
|
-
] : [
|
|
1936
|
-
row.id,
|
|
1937
|
-
row.agent_id,
|
|
1938
|
-
row.agent_role,
|
|
1939
|
-
row.session_id,
|
|
1940
|
-
row.timestamp,
|
|
1941
|
-
row.tool_name,
|
|
1942
|
-
row.project_name,
|
|
1943
|
-
row.has_error,
|
|
1944
|
-
row.raw_text,
|
|
1945
|
-
row.version,
|
|
1946
|
-
taskId,
|
|
1947
|
-
importance,
|
|
1948
|
-
status,
|
|
1949
|
-
confidence,
|
|
1950
|
-
lastAccessed,
|
|
1951
|
-
workspaceId,
|
|
1952
|
-
documentId,
|
|
1953
|
-
userId,
|
|
1954
|
-
charOffset,
|
|
1955
|
-
pageNumber,
|
|
1956
|
-
sourcePath,
|
|
1957
|
-
sourceType,
|
|
1958
|
-
tier,
|
|
1959
|
-
supersedesId,
|
|
1960
|
-
draft,
|
|
1961
|
-
memoryType,
|
|
1962
|
-
trajectory
|
|
1963
|
-
]
|
|
1964
|
-
};
|
|
1965
|
-
};
|
|
1966
|
-
const globalClient = getClient();
|
|
1967
|
-
const globalStmts = batch.map(buildStmt);
|
|
1968
|
-
await globalClient.batch(globalStmts, "write");
|
|
1969
|
-
_pendingRecords.splice(0, batch.length);
|
|
1970
|
-
try {
|
|
1971
|
-
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
1972
|
-
if (isShardingEnabled2()) {
|
|
1973
|
-
const byProject = /* @__PURE__ */ new Map();
|
|
1974
|
-
for (const row of batch) {
|
|
1975
|
-
const proj = row.project_name || "unknown";
|
|
1976
|
-
if (!byProject.has(proj)) byProject.set(proj, []);
|
|
1977
|
-
byProject.get(proj).push(row);
|
|
1978
|
-
}
|
|
1979
|
-
for (const [project, rows] of byProject) {
|
|
1980
|
-
try {
|
|
1981
|
-
const shardClient = await getReadyShardClient2(project);
|
|
1982
|
-
const shardStmts = rows.map(buildStmt);
|
|
1983
|
-
await shardClient.batch(shardStmts, "write");
|
|
1984
|
-
} catch (err) {
|
|
1985
|
-
process.stderr.write(
|
|
1986
|
-
`[store] Shard write failed for ${project}: ${err instanceof Error ? err.message : String(err)}
|
|
1987
|
-
`
|
|
1988
|
-
);
|
|
1989
|
-
}
|
|
1990
|
-
}
|
|
1991
|
-
}
|
|
1992
|
-
} catch {
|
|
1993
|
-
}
|
|
1994
|
-
return batch.length;
|
|
1995
|
-
} finally {
|
|
1996
|
-
_flushing = false;
|
|
1997
|
-
}
|
|
1998
|
-
}
|
|
1999
|
-
function vectorToBlob(vector) {
|
|
2000
|
-
const f32 = vector instanceof Float32Array ? vector : new Float32Array(vector);
|
|
2001
|
-
return JSON.stringify(Array.from(f32));
|
|
2002
|
-
}
|
|
2003
|
-
|
|
2004
|
-
// src/lib/exe-daemon-client.ts
|
|
2005
|
-
init_config();
|
|
2006
|
-
import net from "net";
|
|
2007
|
-
import { spawn } from "child_process";
|
|
2008
|
-
import { randomUUID as randomUUID2 } from "crypto";
|
|
2009
|
-
import { existsSync as existsSync5, unlinkSync as unlinkSync2, readFileSync as readFileSync3, openSync, closeSync, statSync } from "fs";
|
|
2010
|
-
import path5 from "path";
|
|
2011
|
-
import { fileURLToPath } from "url";
|
|
2012
|
-
var SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path5.join(EXE_AI_DIR, "exed.sock");
|
|
2013
|
-
var PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path5.join(EXE_AI_DIR, "exed.pid");
|
|
2014
|
-
var SPAWN_LOCK_PATH = path5.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
2015
|
-
var SPAWN_LOCK_STALE_MS = 3e4;
|
|
2016
|
-
var CONNECT_TIMEOUT_MS = 15e3;
|
|
2017
|
-
var REQUEST_TIMEOUT_MS = 3e4;
|
|
2018
|
-
var _socket = null;
|
|
2019
|
-
var _connected = false;
|
|
2020
|
-
var _buffer = "";
|
|
2021
|
-
var _requestCount = 0;
|
|
2022
|
-
var HEALTH_CHECK_INTERVAL = 100;
|
|
2023
|
-
var _pending = /* @__PURE__ */ new Map();
|
|
2024
|
-
var MAX_BUFFER = 1e7;
|
|
2025
|
-
function handleData(chunk) {
|
|
2026
|
-
_buffer += chunk.toString();
|
|
2027
|
-
if (_buffer.length > MAX_BUFFER) {
|
|
2028
|
-
_buffer = "";
|
|
2029
|
-
return;
|
|
2030
|
-
}
|
|
2031
|
-
let newlineIdx;
|
|
2032
|
-
while ((newlineIdx = _buffer.indexOf("\n")) !== -1) {
|
|
2033
|
-
const line = _buffer.slice(0, newlineIdx).trim();
|
|
2034
|
-
_buffer = _buffer.slice(newlineIdx + 1);
|
|
2035
|
-
if (!line) continue;
|
|
2036
|
-
try {
|
|
2037
|
-
const response = JSON.parse(line);
|
|
2038
|
-
const entry = _pending.get(response.id);
|
|
2039
|
-
if (entry) {
|
|
2040
|
-
clearTimeout(entry.timer);
|
|
2041
|
-
_pending.delete(response.id);
|
|
2042
|
-
entry.resolve(response);
|
|
2043
|
-
}
|
|
2044
|
-
} catch {
|
|
2045
|
-
}
|
|
2046
|
-
}
|
|
2047
|
-
}
|
|
2048
|
-
function cleanupStaleFiles() {
|
|
2049
|
-
if (existsSync5(PID_PATH)) {
|
|
2050
|
-
try {
|
|
2051
|
-
const pid = parseInt(readFileSync3(PID_PATH, "utf8").trim(), 10);
|
|
2052
|
-
if (pid > 0) {
|
|
2053
|
-
try {
|
|
2054
|
-
process.kill(pid, 0);
|
|
2055
|
-
return;
|
|
2056
|
-
} catch {
|
|
2057
|
-
}
|
|
2058
|
-
}
|
|
2059
|
-
} catch {
|
|
2060
|
-
}
|
|
2061
|
-
try {
|
|
2062
|
-
unlinkSync2(PID_PATH);
|
|
2063
|
-
} catch {
|
|
2064
|
-
}
|
|
2065
|
-
try {
|
|
2066
|
-
unlinkSync2(SOCKET_PATH);
|
|
2067
|
-
} catch {
|
|
2068
|
-
}
|
|
2069
|
-
}
|
|
2070
|
-
}
|
|
2071
|
-
function findPackageRoot() {
|
|
2072
|
-
let dir = path5.dirname(fileURLToPath(import.meta.url));
|
|
2073
|
-
const { root } = path5.parse(dir);
|
|
2074
|
-
while (dir !== root) {
|
|
2075
|
-
if (existsSync5(path5.join(dir, "package.json"))) return dir;
|
|
2076
|
-
dir = path5.dirname(dir);
|
|
2077
|
-
}
|
|
2078
|
-
return null;
|
|
2079
|
-
}
|
|
2080
|
-
function spawnDaemon() {
|
|
2081
|
-
const pkgRoot = findPackageRoot();
|
|
2082
|
-
if (!pkgRoot) {
|
|
2083
|
-
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
2084
|
-
return;
|
|
2085
|
-
}
|
|
2086
|
-
const daemonPath = path5.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
2087
|
-
if (!existsSync5(daemonPath)) {
|
|
2088
|
-
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
2089
|
-
`);
|
|
2090
|
-
return;
|
|
2091
|
-
}
|
|
2092
|
-
const resolvedPath = daemonPath;
|
|
2093
|
-
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
2094
|
-
`);
|
|
2095
|
-
const logPath = path5.join(path5.dirname(SOCKET_PATH), "exed.log");
|
|
2096
|
-
let stderrFd = "ignore";
|
|
2097
|
-
try {
|
|
2098
|
-
stderrFd = openSync(logPath, "a");
|
|
2099
|
-
} catch {
|
|
2100
|
-
}
|
|
2101
|
-
const child = spawn(process.execPath, [resolvedPath], {
|
|
2102
|
-
detached: true,
|
|
2103
|
-
stdio: ["ignore", "ignore", stderrFd],
|
|
2104
|
-
env: {
|
|
2105
|
-
...process.env,
|
|
2106
|
-
TMUX: void 0,
|
|
2107
|
-
// Daemon is global — must not inherit session scope
|
|
2108
|
-
TMUX_PANE: void 0,
|
|
2109
|
-
// Prevents resolveExeSession() from scoping to one session
|
|
2110
|
-
EXE_DAEMON_SOCK: SOCKET_PATH,
|
|
2111
|
-
EXE_DAEMON_PID: PID_PATH
|
|
2112
|
-
}
|
|
2113
|
-
});
|
|
2114
|
-
child.unref();
|
|
2115
|
-
if (typeof stderrFd === "number") {
|
|
2116
|
-
try {
|
|
2117
|
-
closeSync(stderrFd);
|
|
2118
|
-
} catch {
|
|
2119
|
-
}
|
|
2120
|
-
}
|
|
2121
|
-
}
|
|
2122
|
-
function acquireSpawnLock() {
|
|
2123
|
-
try {
|
|
2124
|
-
const fd = openSync(SPAWN_LOCK_PATH, "wx");
|
|
2125
|
-
closeSync(fd);
|
|
2126
|
-
return true;
|
|
2127
|
-
} catch {
|
|
2128
|
-
try {
|
|
2129
|
-
const stat2 = statSync(SPAWN_LOCK_PATH);
|
|
2130
|
-
if (Date.now() - stat2.mtimeMs > SPAWN_LOCK_STALE_MS) {
|
|
2131
|
-
try {
|
|
2132
|
-
unlinkSync2(SPAWN_LOCK_PATH);
|
|
2133
|
-
} catch {
|
|
2134
|
-
}
|
|
2135
|
-
try {
|
|
2136
|
-
const fd = openSync(SPAWN_LOCK_PATH, "wx");
|
|
2137
|
-
closeSync(fd);
|
|
2138
|
-
return true;
|
|
2139
|
-
} catch {
|
|
2140
|
-
}
|
|
2141
|
-
}
|
|
2142
|
-
} catch {
|
|
2143
|
-
}
|
|
2144
|
-
return false;
|
|
1818
|
+
return false;
|
|
2145
1819
|
}
|
|
2146
1820
|
}
|
|
2147
1821
|
function releaseSpawnLock() {
|
|
@@ -2209,6 +1883,9 @@ async function connectEmbedDaemon() {
|
|
|
2209
1883
|
return false;
|
|
2210
1884
|
}
|
|
2211
1885
|
function sendRequest(texts, priority) {
|
|
1886
|
+
return sendDaemonRequest({ texts, priority });
|
|
1887
|
+
}
|
|
1888
|
+
function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
2212
1889
|
return new Promise((resolve) => {
|
|
2213
1890
|
if (!_socket || !_connected) {
|
|
2214
1891
|
resolve({ error: "Not connected" });
|
|
@@ -2218,10 +1895,10 @@ function sendRequest(texts, priority) {
|
|
|
2218
1895
|
const timer = setTimeout(() => {
|
|
2219
1896
|
_pending.delete(id);
|
|
2220
1897
|
resolve({ error: "Request timeout" });
|
|
2221
|
-
},
|
|
1898
|
+
}, timeoutMs);
|
|
2222
1899
|
_pending.set(id, { resolve, timer });
|
|
2223
1900
|
try {
|
|
2224
|
-
_socket.write(JSON.stringify({ id,
|
|
1901
|
+
_socket.write(JSON.stringify({ id, ...payload }) + "\n");
|
|
2225
1902
|
} catch {
|
|
2226
1903
|
clearTimeout(timer);
|
|
2227
1904
|
_pending.delete(id);
|
|
@@ -2231,103 +1908,576 @@ function sendRequest(texts, priority) {
|
|
|
2231
1908
|
}
|
|
2232
1909
|
async function pingDaemon() {
|
|
2233
1910
|
if (!_socket || !_connected) return null;
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
1911
|
+
const response = await sendDaemonRequest({ type: "health" }, 5e3);
|
|
1912
|
+
if (response.health) {
|
|
1913
|
+
return response.health;
|
|
1914
|
+
}
|
|
1915
|
+
return null;
|
|
1916
|
+
}
|
|
1917
|
+
function killAndRespawnDaemon() {
|
|
1918
|
+
process.stderr.write("[exed-client] Killing daemon for restart...\n");
|
|
1919
|
+
if (existsSync5(PID_PATH)) {
|
|
1920
|
+
try {
|
|
1921
|
+
const pid = parseInt(readFileSync3(PID_PATH, "utf8").trim(), 10);
|
|
1922
|
+
if (pid > 0) {
|
|
1923
|
+
try {
|
|
1924
|
+
process.kill(pid, "SIGKILL");
|
|
1925
|
+
} catch {
|
|
2246
1926
|
}
|
|
2247
|
-
}
|
|
2248
|
-
|
|
2249
|
-
}
|
|
1927
|
+
}
|
|
1928
|
+
} catch {
|
|
1929
|
+
}
|
|
1930
|
+
}
|
|
1931
|
+
if (_socket) {
|
|
1932
|
+
_socket.destroy();
|
|
1933
|
+
_socket = null;
|
|
1934
|
+
}
|
|
1935
|
+
_connected = false;
|
|
1936
|
+
_buffer = "";
|
|
1937
|
+
try {
|
|
1938
|
+
unlinkSync2(PID_PATH);
|
|
1939
|
+
} catch {
|
|
1940
|
+
}
|
|
1941
|
+
try {
|
|
1942
|
+
unlinkSync2(SOCKET_PATH);
|
|
1943
|
+
} catch {
|
|
1944
|
+
}
|
|
1945
|
+
spawnDaemon();
|
|
1946
|
+
}
|
|
1947
|
+
async function embedViaClient(text, priority = "high") {
|
|
1948
|
+
if (!_connected && !await connectEmbedDaemon()) return null;
|
|
1949
|
+
_requestCount++;
|
|
1950
|
+
if (_requestCount % HEALTH_CHECK_INTERVAL === 0) {
|
|
1951
|
+
const health = await pingDaemon();
|
|
1952
|
+
if (!health) {
|
|
1953
|
+
process.stderr.write(`[exed-client] Periodic health check failed at request ${_requestCount} \u2014 restarting daemon
|
|
1954
|
+
`);
|
|
1955
|
+
killAndRespawnDaemon();
|
|
1956
|
+
const start = Date.now();
|
|
1957
|
+
let delay2 = 200;
|
|
1958
|
+
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
1959
|
+
await new Promise((r) => setTimeout(r, delay2));
|
|
1960
|
+
if (await connectToSocket()) break;
|
|
1961
|
+
delay2 = Math.min(delay2 * 2, 3e3);
|
|
1962
|
+
}
|
|
1963
|
+
if (!_connected) return null;
|
|
1964
|
+
}
|
|
1965
|
+
}
|
|
1966
|
+
const result = await sendRequest([text], priority);
|
|
1967
|
+
if (!result.error && result.vectors?.[0]) return result.vectors[0];
|
|
1968
|
+
if (result.error) {
|
|
1969
|
+
process.stderr.write(`[exed-client] Embed failed (${result.error}) \u2014 attempting restart
|
|
1970
|
+
`);
|
|
1971
|
+
killAndRespawnDaemon();
|
|
1972
|
+
const start = Date.now();
|
|
1973
|
+
let delay2 = 200;
|
|
1974
|
+
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
1975
|
+
await new Promise((r) => setTimeout(r, delay2));
|
|
1976
|
+
if (await connectToSocket()) break;
|
|
1977
|
+
delay2 = Math.min(delay2 * 2, 3e3);
|
|
1978
|
+
}
|
|
1979
|
+
if (!_connected) return null;
|
|
1980
|
+
const retry = await sendRequest([text], priority);
|
|
1981
|
+
if (!retry.error && retry.vectors?.[0]) return retry.vectors[0];
|
|
1982
|
+
process.stderr.write(`[exed-client] Embed retry also failed: ${retry.error ?? "no vector"}
|
|
1983
|
+
`);
|
|
1984
|
+
}
|
|
1985
|
+
return null;
|
|
1986
|
+
}
|
|
1987
|
+
var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _requestCount, HEALTH_CHECK_INTERVAL, _pending, MAX_BUFFER;
|
|
1988
|
+
var init_exe_daemon_client = __esm({
|
|
1989
|
+
"src/lib/exe-daemon-client.ts"() {
|
|
1990
|
+
"use strict";
|
|
1991
|
+
init_config();
|
|
1992
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path5.join(EXE_AI_DIR, "exed.sock");
|
|
1993
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path5.join(EXE_AI_DIR, "exed.pid");
|
|
1994
|
+
SPAWN_LOCK_PATH = path5.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
1995
|
+
SPAWN_LOCK_STALE_MS = 3e4;
|
|
1996
|
+
CONNECT_TIMEOUT_MS = 15e3;
|
|
1997
|
+
REQUEST_TIMEOUT_MS = 3e4;
|
|
1998
|
+
_socket = null;
|
|
1999
|
+
_connected = false;
|
|
2000
|
+
_buffer = "";
|
|
2001
|
+
_requestCount = 0;
|
|
2002
|
+
HEALTH_CHECK_INTERVAL = 100;
|
|
2003
|
+
_pending = /* @__PURE__ */ new Map();
|
|
2004
|
+
MAX_BUFFER = 1e7;
|
|
2005
|
+
}
|
|
2006
|
+
});
|
|
2007
|
+
|
|
2008
|
+
// src/bin/backfill-conversations.ts
|
|
2009
|
+
import crypto from "crypto";
|
|
2010
|
+
import { createReadStream } from "fs";
|
|
2011
|
+
import { readdir, stat } from "fs/promises";
|
|
2012
|
+
import path6 from "path";
|
|
2013
|
+
import { createInterface } from "readline";
|
|
2014
|
+
import { homedir } from "os";
|
|
2015
|
+
import { parseArgs } from "util";
|
|
2016
|
+
|
|
2017
|
+
// src/lib/store.ts
|
|
2018
|
+
import { createHash } from "crypto";
|
|
2019
|
+
|
|
2020
|
+
// src/types/memory.ts
|
|
2021
|
+
var EMBEDDING_DIM = 1024;
|
|
2022
|
+
|
|
2023
|
+
// src/lib/store.ts
|
|
2024
|
+
init_database();
|
|
2025
|
+
|
|
2026
|
+
// src/lib/keychain.ts
|
|
2027
|
+
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
2028
|
+
import { existsSync as existsSync3 } from "fs";
|
|
2029
|
+
import path3 from "path";
|
|
2030
|
+
import os3 from "os";
|
|
2031
|
+
var SERVICE = "exe-mem";
|
|
2032
|
+
var ACCOUNT = "master-key";
|
|
2033
|
+
function getKeyDir() {
|
|
2034
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path3.join(os3.homedir(), ".exe-os");
|
|
2035
|
+
}
|
|
2036
|
+
function getKeyPath() {
|
|
2037
|
+
return path3.join(getKeyDir(), "master.key");
|
|
2038
|
+
}
|
|
2039
|
+
async function tryKeytar() {
|
|
2040
|
+
try {
|
|
2041
|
+
return await import("keytar");
|
|
2042
|
+
} catch {
|
|
2043
|
+
return null;
|
|
2044
|
+
}
|
|
2045
|
+
}
|
|
2046
|
+
async function getMasterKey() {
|
|
2047
|
+
const keytar = await tryKeytar();
|
|
2048
|
+
if (keytar) {
|
|
2250
2049
|
try {
|
|
2251
|
-
|
|
2050
|
+
const stored = await keytar.getPassword(SERVICE, ACCOUNT);
|
|
2051
|
+
if (stored) {
|
|
2052
|
+
return Buffer.from(stored, "base64");
|
|
2053
|
+
}
|
|
2252
2054
|
} catch {
|
|
2253
|
-
clearTimeout(timer);
|
|
2254
|
-
_pending.delete(id);
|
|
2255
|
-
resolve(null);
|
|
2256
2055
|
}
|
|
2257
|
-
}
|
|
2056
|
+
}
|
|
2057
|
+
const keyPath = getKeyPath();
|
|
2058
|
+
if (!existsSync3(keyPath)) {
|
|
2059
|
+
process.stderr.write(
|
|
2060
|
+
`[keychain] Key not found at ${keyPath} (HOME=${os3.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
2061
|
+
`
|
|
2062
|
+
);
|
|
2063
|
+
return null;
|
|
2064
|
+
}
|
|
2065
|
+
try {
|
|
2066
|
+
const content = await readFile3(keyPath, "utf-8");
|
|
2067
|
+
return Buffer.from(content.trim(), "base64");
|
|
2068
|
+
} catch (err) {
|
|
2069
|
+
process.stderr.write(
|
|
2070
|
+
`[keychain] Key read failed at ${keyPath}: ${err instanceof Error ? err.message : String(err)}
|
|
2071
|
+
`
|
|
2072
|
+
);
|
|
2073
|
+
return null;
|
|
2074
|
+
}
|
|
2075
|
+
}
|
|
2076
|
+
|
|
2077
|
+
// src/lib/store.ts
|
|
2078
|
+
init_config();
|
|
2079
|
+
|
|
2080
|
+
// src/lib/state-bus.ts
|
|
2081
|
+
var StateBus = class {
|
|
2082
|
+
handlers = /* @__PURE__ */ new Map();
|
|
2083
|
+
globalHandlers = /* @__PURE__ */ new Set();
|
|
2084
|
+
/** Emit an event to all subscribers */
|
|
2085
|
+
emit(event) {
|
|
2086
|
+
const typeHandlers = this.handlers.get(event.type);
|
|
2087
|
+
if (typeHandlers) {
|
|
2088
|
+
for (const handler of typeHandlers) {
|
|
2089
|
+
try {
|
|
2090
|
+
handler(event);
|
|
2091
|
+
} catch {
|
|
2092
|
+
}
|
|
2093
|
+
}
|
|
2094
|
+
}
|
|
2095
|
+
for (const handler of this.globalHandlers) {
|
|
2096
|
+
try {
|
|
2097
|
+
handler(event);
|
|
2098
|
+
} catch {
|
|
2099
|
+
}
|
|
2100
|
+
}
|
|
2101
|
+
}
|
|
2102
|
+
/** Subscribe to a specific event type */
|
|
2103
|
+
on(type, handler) {
|
|
2104
|
+
if (!this.handlers.has(type)) {
|
|
2105
|
+
this.handlers.set(type, /* @__PURE__ */ new Set());
|
|
2106
|
+
}
|
|
2107
|
+
this.handlers.get(type).add(handler);
|
|
2108
|
+
}
|
|
2109
|
+
/** Subscribe to ALL events */
|
|
2110
|
+
onAny(handler) {
|
|
2111
|
+
this.globalHandlers.add(handler);
|
|
2112
|
+
}
|
|
2113
|
+
/** Unsubscribe from a specific event type */
|
|
2114
|
+
off(type, handler) {
|
|
2115
|
+
this.handlers.get(type)?.delete(handler);
|
|
2116
|
+
}
|
|
2117
|
+
/** Unsubscribe from ALL events */
|
|
2118
|
+
offAny(handler) {
|
|
2119
|
+
this.globalHandlers.delete(handler);
|
|
2120
|
+
}
|
|
2121
|
+
/** Remove all listeners */
|
|
2122
|
+
clear() {
|
|
2123
|
+
this.handlers.clear();
|
|
2124
|
+
this.globalHandlers.clear();
|
|
2125
|
+
}
|
|
2126
|
+
};
|
|
2127
|
+
var orgBus = new StateBus();
|
|
2128
|
+
|
|
2129
|
+
// src/lib/store.ts
|
|
2130
|
+
var INIT_MAX_RETRIES = 3;
|
|
2131
|
+
var INIT_RETRY_DELAY_MS = 1e3;
|
|
2132
|
+
function isBusyError2(err) {
|
|
2133
|
+
if (err instanceof Error) {
|
|
2134
|
+
const msg = err.message.toLowerCase();
|
|
2135
|
+
return msg.includes("sqlite_busy") || msg.includes("database is locked");
|
|
2136
|
+
}
|
|
2137
|
+
return false;
|
|
2258
2138
|
}
|
|
2259
|
-
function
|
|
2260
|
-
|
|
2261
|
-
if (existsSync5(PID_PATH)) {
|
|
2139
|
+
async function retryOnBusy2(fn, label) {
|
|
2140
|
+
for (let attempt = 0; attempt <= INIT_MAX_RETRIES; attempt++) {
|
|
2262
2141
|
try {
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
}
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2142
|
+
return await fn();
|
|
2143
|
+
} catch (err) {
|
|
2144
|
+
if (!isBusyError2(err) || attempt === INIT_MAX_RETRIES) throw err;
|
|
2145
|
+
process.stderr.write(
|
|
2146
|
+
`[store] SQLITE_BUSY during ${label}, retry ${attempt + 1}/${INIT_MAX_RETRIES}
|
|
2147
|
+
`
|
|
2148
|
+
);
|
|
2149
|
+
await new Promise((r) => setTimeout(r, INIT_RETRY_DELAY_MS * (attempt + 1)));
|
|
2271
2150
|
}
|
|
2272
2151
|
}
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2152
|
+
throw new Error("unreachable");
|
|
2153
|
+
}
|
|
2154
|
+
var _pendingRecords = [];
|
|
2155
|
+
var _batchSize = 20;
|
|
2156
|
+
var _flushIntervalMs = 1e4;
|
|
2157
|
+
var _flushTimer = null;
|
|
2158
|
+
var _flushing = false;
|
|
2159
|
+
var _nextVersion = 1;
|
|
2160
|
+
async function initStore(options) {
|
|
2161
|
+
if (_flushTimer !== null) {
|
|
2162
|
+
clearInterval(_flushTimer);
|
|
2163
|
+
_flushTimer = null;
|
|
2276
2164
|
}
|
|
2277
|
-
|
|
2278
|
-
|
|
2165
|
+
_pendingRecords = [];
|
|
2166
|
+
_flushing = false;
|
|
2167
|
+
_batchSize = options?.batchSize ?? 20;
|
|
2168
|
+
_flushIntervalMs = options?.flushIntervalMs ?? 1e4;
|
|
2169
|
+
let dbPath = options?.dbPath;
|
|
2170
|
+
if (!dbPath) {
|
|
2171
|
+
const config = await loadConfig();
|
|
2172
|
+
dbPath = config.dbPath;
|
|
2173
|
+
}
|
|
2174
|
+
let masterKey = options?.masterKey ?? null;
|
|
2175
|
+
if (!masterKey) {
|
|
2176
|
+
masterKey = await getMasterKey();
|
|
2177
|
+
if (!masterKey) {
|
|
2178
|
+
throw new Error(
|
|
2179
|
+
"No encryption key found. Run /exe-setup to generate one."
|
|
2180
|
+
);
|
|
2181
|
+
}
|
|
2182
|
+
}
|
|
2183
|
+
const hexKey = masterKey.toString("hex");
|
|
2184
|
+
await initTurso({
|
|
2185
|
+
dbPath,
|
|
2186
|
+
encryptionKey: hexKey
|
|
2187
|
+
});
|
|
2188
|
+
await retryOnBusy2(() => ensureSchema(), "ensureSchema");
|
|
2279
2189
|
try {
|
|
2280
|
-
|
|
2190
|
+
const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
2191
|
+
initShardManager2(hexKey);
|
|
2281
2192
|
} catch {
|
|
2282
2193
|
}
|
|
2194
|
+
const client = getClient();
|
|
2195
|
+
const vResult = await retryOnBusy2(
|
|
2196
|
+
() => client.execute("SELECT MAX(version) as max_v FROM memories"),
|
|
2197
|
+
"version-query"
|
|
2198
|
+
);
|
|
2199
|
+
_nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
|
|
2283
2200
|
try {
|
|
2284
|
-
|
|
2201
|
+
const { loadGlobalProcedures: loadGlobalProcedures2 } = await Promise.resolve().then(() => (init_global_procedures(), global_procedures_exports));
|
|
2202
|
+
await loadGlobalProcedures2();
|
|
2285
2203
|
} catch {
|
|
2286
2204
|
}
|
|
2287
|
-
spawnDaemon();
|
|
2288
2205
|
}
|
|
2289
|
-
|
|
2290
|
-
if (
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2206
|
+
function classifyTier(record) {
|
|
2207
|
+
if (record.tool_name === "commit_to_long_term_memory" && (record.importance ?? 0) >= 8) return 1;
|
|
2208
|
+
if (["store_memory", "manual"].includes(record.tool_name ?? "") && (record.importance ?? 0) >= 5) return 2;
|
|
2209
|
+
return 3;
|
|
2210
|
+
}
|
|
2211
|
+
function inferFilePaths(record) {
|
|
2212
|
+
if (!["Read", "Write", "Edit"].includes(record.tool_name)) return null;
|
|
2213
|
+
const firstLine = record.raw_text.split("\n")[0] ?? "";
|
|
2214
|
+
const match = firstLine.match(/(\/[\w./-]+\.\w+)/);
|
|
2215
|
+
return match ? JSON.stringify([match[1]]) : null;
|
|
2216
|
+
}
|
|
2217
|
+
function inferCommitHash(record) {
|
|
2218
|
+
if (record.tool_name !== "Bash") return null;
|
|
2219
|
+
const match = record.raw_text.match(/\b([a-f0-9]{7,40})\b/);
|
|
2220
|
+
return match ? match[1] : null;
|
|
2221
|
+
}
|
|
2222
|
+
function inferLanguageType(record) {
|
|
2223
|
+
const text = record.raw_text;
|
|
2224
|
+
if (!text || text.length < 10) return null;
|
|
2225
|
+
const trimmed = text.trimStart();
|
|
2226
|
+
if (trimmed.startsWith("{") || trimmed.startsWith("[")) return "json";
|
|
2227
|
+
if (/\b(SELECT|INSERT|UPDATE|DELETE|CREATE TABLE|ALTER TABLE)\b/i.test(text)) return "sql";
|
|
2228
|
+
if (/\b(function |const |import |export |class |def |async |=>)\b/.test(text)) return "code";
|
|
2229
|
+
if (trimmed.startsWith("#") || trimmed.startsWith("*")) return "prose";
|
|
2230
|
+
return "mixed";
|
|
2231
|
+
}
|
|
2232
|
+
function inferDomain(record) {
|
|
2233
|
+
const proj = (record.project_name ?? "").toLowerCase();
|
|
2234
|
+
if (proj.includes("marketing") || proj.includes("content")) return "marketing";
|
|
2235
|
+
if (proj.includes("crm") || proj.includes("customer")) return "customer";
|
|
2236
|
+
return null;
|
|
2237
|
+
}
|
|
2238
|
+
async function writeMemory(record) {
|
|
2239
|
+
if (record.vector !== null && record.vector.length !== EMBEDDING_DIM) {
|
|
2240
|
+
throw new Error(
|
|
2241
|
+
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
2242
|
+
);
|
|
2243
|
+
}
|
|
2244
|
+
const contentHash = createHash("md5").update(record.raw_text).digest("hex");
|
|
2245
|
+
if (_pendingRecords.some((r) => r.content_hash === contentHash && r.agent_id === record.agent_id)) {
|
|
2246
|
+
return;
|
|
2247
|
+
}
|
|
2248
|
+
try {
|
|
2249
|
+
const client = getClient();
|
|
2250
|
+
const existing = await client.execute({
|
|
2251
|
+
sql: "SELECT id FROM memories WHERE content_hash = ? AND agent_id = ? LIMIT 1",
|
|
2252
|
+
args: [contentHash, record.agent_id]
|
|
2253
|
+
});
|
|
2254
|
+
if (existing.rows.length > 0) return;
|
|
2255
|
+
} catch {
|
|
2256
|
+
}
|
|
2257
|
+
const dbRow = {
|
|
2258
|
+
id: record.id,
|
|
2259
|
+
agent_id: record.agent_id,
|
|
2260
|
+
agent_role: record.agent_role,
|
|
2261
|
+
session_id: record.session_id,
|
|
2262
|
+
timestamp: record.timestamp,
|
|
2263
|
+
tool_name: record.tool_name,
|
|
2264
|
+
project_name: record.project_name,
|
|
2265
|
+
has_error: record.has_error ? 1 : 0,
|
|
2266
|
+
raw_text: record.raw_text,
|
|
2267
|
+
vector: record.vector,
|
|
2268
|
+
version: 0,
|
|
2269
|
+
// Placeholder — assigned atomically at flush time
|
|
2270
|
+
task_id: record.task_id ?? null,
|
|
2271
|
+
importance: record.importance ?? 5,
|
|
2272
|
+
status: record.status ?? "active",
|
|
2273
|
+
confidence: record.confidence ?? 0.7,
|
|
2274
|
+
last_accessed: record.last_accessed ?? record.timestamp,
|
|
2275
|
+
workspace_id: record.workspace_id ?? null,
|
|
2276
|
+
document_id: record.document_id ?? null,
|
|
2277
|
+
user_id: record.user_id ?? null,
|
|
2278
|
+
char_offset: record.char_offset ?? null,
|
|
2279
|
+
page_number: record.page_number ?? null,
|
|
2280
|
+
source_path: record.source_path ?? null,
|
|
2281
|
+
source_type: record.source_type ?? null,
|
|
2282
|
+
tier: record.tier ?? classifyTier(record),
|
|
2283
|
+
supersedes_id: record.supersedes_id ?? null,
|
|
2284
|
+
draft: record.draft ? 1 : 0,
|
|
2285
|
+
memory_type: record.memory_type ?? "raw",
|
|
2286
|
+
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
2287
|
+
content_hash: contentHash,
|
|
2288
|
+
intent: record.intent ?? null,
|
|
2289
|
+
outcome: record.outcome ?? null,
|
|
2290
|
+
domain: record.domain ?? inferDomain(record),
|
|
2291
|
+
referenced_entities: record.referenced_entities ?? null,
|
|
2292
|
+
retrieval_count: record.retrieval_count ?? 0,
|
|
2293
|
+
chain_position: record.chain_position ?? null,
|
|
2294
|
+
review_status: record.review_status ?? null,
|
|
2295
|
+
context_window_pct: record.context_window_pct ?? null,
|
|
2296
|
+
file_paths: record.file_paths ?? inferFilePaths(record),
|
|
2297
|
+
commit_hash: record.commit_hash ?? inferCommitHash(record),
|
|
2298
|
+
duration_ms: record.duration_ms ?? null,
|
|
2299
|
+
token_cost: record.token_cost ?? null,
|
|
2300
|
+
audience: record.audience ?? null,
|
|
2301
|
+
language_type: record.language_type ?? inferLanguageType(record),
|
|
2302
|
+
parent_memory_id: record.parent_memory_id ?? null
|
|
2303
|
+
};
|
|
2304
|
+
_pendingRecords.push(dbRow);
|
|
2305
|
+
orgBus.emit({
|
|
2306
|
+
type: "memory_stored",
|
|
2307
|
+
agentId: record.agent_id,
|
|
2308
|
+
project: record.project_name,
|
|
2309
|
+
timestamp: record.timestamp
|
|
2310
|
+
});
|
|
2311
|
+
const MAX_PENDING = 1e3;
|
|
2312
|
+
if (_pendingRecords.length > MAX_PENDING) {
|
|
2313
|
+
const dropped = _pendingRecords.length - MAX_PENDING;
|
|
2314
|
+
_pendingRecords = _pendingRecords.slice(-MAX_PENDING);
|
|
2315
|
+
console.warn(`[store] Dropped ${dropped} oldest pending records (overflow)`);
|
|
2316
|
+
}
|
|
2317
|
+
if (_flushTimer === null) {
|
|
2318
|
+
_flushTimer = setInterval(() => {
|
|
2319
|
+
void flushBatch();
|
|
2320
|
+
}, _flushIntervalMs);
|
|
2321
|
+
if (_flushTimer && typeof _flushTimer === "object" && "unref" in _flushTimer) {
|
|
2322
|
+
_flushTimer.unref();
|
|
2306
2323
|
}
|
|
2307
2324
|
}
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2325
|
+
if (_pendingRecords.length >= _batchSize) {
|
|
2326
|
+
await flushBatch();
|
|
2327
|
+
}
|
|
2328
|
+
}
|
|
2329
|
+
async function flushBatch() {
|
|
2330
|
+
if (_flushing || _pendingRecords.length === 0) return 0;
|
|
2331
|
+
_flushing = true;
|
|
2332
|
+
try {
|
|
2333
|
+
const batch = _pendingRecords.slice(0);
|
|
2334
|
+
const client = getClient();
|
|
2335
|
+
const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
|
|
2336
|
+
let baseVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
|
|
2337
|
+
for (const row of batch) {
|
|
2338
|
+
row.version = baseVersion++;
|
|
2320
2339
|
}
|
|
2321
|
-
|
|
2322
|
-
const
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2340
|
+
_nextVersion = baseVersion;
|
|
2341
|
+
const buildStmt = (row) => {
|
|
2342
|
+
const hasVector = row.vector !== null;
|
|
2343
|
+
const taskId = row.task_id ?? null;
|
|
2344
|
+
const importance = row.importance ?? 5;
|
|
2345
|
+
const status = row.status ?? "active";
|
|
2346
|
+
const confidence = row.confidence ?? 0.7;
|
|
2347
|
+
const lastAccessed = row.last_accessed ?? row.timestamp;
|
|
2348
|
+
const workspaceId = row.workspace_id ?? null;
|
|
2349
|
+
const documentId = row.document_id ?? null;
|
|
2350
|
+
const userId = row.user_id ?? null;
|
|
2351
|
+
const charOffset = row.char_offset ?? null;
|
|
2352
|
+
const pageNumber = row.page_number ?? null;
|
|
2353
|
+
const sourcePath = row.source_path ?? null;
|
|
2354
|
+
const sourceType = row.source_type ?? null;
|
|
2355
|
+
const tier = row.tier ?? 3;
|
|
2356
|
+
const supersedesId = row.supersedes_id ?? null;
|
|
2357
|
+
const draft = row.draft ? 1 : 0;
|
|
2358
|
+
const memoryType = row.memory_type ?? "raw";
|
|
2359
|
+
const trajectory = row.trajectory ?? null;
|
|
2360
|
+
const contentHash = row.content_hash ?? null;
|
|
2361
|
+
const intent = row.intent ?? null;
|
|
2362
|
+
const outcome = row.outcome ?? null;
|
|
2363
|
+
const domain = row.domain ?? null;
|
|
2364
|
+
const referencedEntities = row.referenced_entities ?? null;
|
|
2365
|
+
const retrievalCount = row.retrieval_count ?? 0;
|
|
2366
|
+
const chainPosition = row.chain_position ?? null;
|
|
2367
|
+
const reviewStatus = row.review_status ?? null;
|
|
2368
|
+
const contextWindowPct = row.context_window_pct ?? null;
|
|
2369
|
+
const filePaths = row.file_paths ?? null;
|
|
2370
|
+
const commitHash = row.commit_hash ?? null;
|
|
2371
|
+
const durationMs = row.duration_ms ?? null;
|
|
2372
|
+
const tokenCost = row.token_cost ?? null;
|
|
2373
|
+
const audience = row.audience ?? null;
|
|
2374
|
+
const languageType = row.language_type ?? null;
|
|
2375
|
+
const parentMemoryId = row.parent_memory_id ?? null;
|
|
2376
|
+
const cols = `id, agent_id, agent_role, session_id, timestamp,
|
|
2377
|
+
tool_name, project_name,
|
|
2378
|
+
has_error, raw_text, vector, version, task_id, importance, status,
|
|
2379
|
+
confidence, last_accessed,
|
|
2380
|
+
workspace_id, document_id, user_id, char_offset, page_number,
|
|
2381
|
+
source_path, source_type, tier, supersedes_id, draft, memory_type, trajectory, content_hash,
|
|
2382
|
+
intent, outcome, domain, referenced_entities, retrieval_count,
|
|
2383
|
+
chain_position, review_status, context_window_pct, file_paths, commit_hash,
|
|
2384
|
+
duration_ms, token_cost, audience, language_type, parent_memory_id`;
|
|
2385
|
+
const metaArgs = [
|
|
2386
|
+
intent,
|
|
2387
|
+
outcome,
|
|
2388
|
+
domain,
|
|
2389
|
+
referencedEntities,
|
|
2390
|
+
retrievalCount,
|
|
2391
|
+
chainPosition,
|
|
2392
|
+
reviewStatus,
|
|
2393
|
+
contextWindowPct,
|
|
2394
|
+
filePaths,
|
|
2395
|
+
commitHash,
|
|
2396
|
+
durationMs,
|
|
2397
|
+
tokenCost,
|
|
2398
|
+
audience,
|
|
2399
|
+
languageType,
|
|
2400
|
+
parentMemoryId
|
|
2401
|
+
];
|
|
2402
|
+
const baseArgs = [
|
|
2403
|
+
row.id,
|
|
2404
|
+
row.agent_id,
|
|
2405
|
+
row.agent_role,
|
|
2406
|
+
row.session_id,
|
|
2407
|
+
row.timestamp,
|
|
2408
|
+
row.tool_name,
|
|
2409
|
+
row.project_name,
|
|
2410
|
+
row.has_error,
|
|
2411
|
+
row.raw_text
|
|
2412
|
+
];
|
|
2413
|
+
const sharedArgs = [
|
|
2414
|
+
row.version,
|
|
2415
|
+
taskId,
|
|
2416
|
+
importance,
|
|
2417
|
+
status,
|
|
2418
|
+
confidence,
|
|
2419
|
+
lastAccessed,
|
|
2420
|
+
workspaceId,
|
|
2421
|
+
documentId,
|
|
2422
|
+
userId,
|
|
2423
|
+
charOffset,
|
|
2424
|
+
pageNumber,
|
|
2425
|
+
sourcePath,
|
|
2426
|
+
sourceType,
|
|
2427
|
+
tier,
|
|
2428
|
+
supersedesId,
|
|
2429
|
+
draft,
|
|
2430
|
+
memoryType,
|
|
2431
|
+
trajectory,
|
|
2432
|
+
contentHash
|
|
2433
|
+
];
|
|
2434
|
+
return {
|
|
2435
|
+
sql: hasVector ? `INSERT OR IGNORE INTO memories (${cols})
|
|
2436
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories (${cols})
|
|
2437
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
2438
|
+
args: hasVector ? [...baseArgs, vectorToBlob(row.vector), ...sharedArgs, ...metaArgs] : [...baseArgs, ...sharedArgs, ...metaArgs]
|
|
2439
|
+
};
|
|
2440
|
+
};
|
|
2441
|
+
const globalClient = getClient();
|
|
2442
|
+
const globalStmts = batch.map(buildStmt);
|
|
2443
|
+
await globalClient.batch(globalStmts, "write");
|
|
2444
|
+
_pendingRecords.splice(0, batch.length);
|
|
2445
|
+
try {
|
|
2446
|
+
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
2447
|
+
if (isShardingEnabled2()) {
|
|
2448
|
+
const byProject = /* @__PURE__ */ new Map();
|
|
2449
|
+
for (const row of batch) {
|
|
2450
|
+
const proj = row.project_name || "unknown";
|
|
2451
|
+
if (!byProject.has(proj)) byProject.set(proj, []);
|
|
2452
|
+
byProject.get(proj).push(row);
|
|
2453
|
+
}
|
|
2454
|
+
for (const [project, rows] of byProject) {
|
|
2455
|
+
try {
|
|
2456
|
+
const shardClient = await getReadyShardClient2(project);
|
|
2457
|
+
const shardStmts = rows.map(buildStmt);
|
|
2458
|
+
await shardClient.batch(shardStmts, "write");
|
|
2459
|
+
} catch (err) {
|
|
2460
|
+
process.stderr.write(
|
|
2461
|
+
`[store] Shard write failed for ${project}: ${err instanceof Error ? err.message : String(err)}
|
|
2462
|
+
`
|
|
2463
|
+
);
|
|
2464
|
+
}
|
|
2465
|
+
}
|
|
2466
|
+
}
|
|
2467
|
+
} catch {
|
|
2468
|
+
}
|
|
2469
|
+
return batch.length;
|
|
2470
|
+
} finally {
|
|
2471
|
+
_flushing = false;
|
|
2326
2472
|
}
|
|
2327
|
-
|
|
2473
|
+
}
|
|
2474
|
+
function vectorToBlob(vector) {
|
|
2475
|
+
const f32 = vector instanceof Float32Array ? vector : new Float32Array(vector);
|
|
2476
|
+
return JSON.stringify(Array.from(f32));
|
|
2328
2477
|
}
|
|
2329
2478
|
|
|
2330
2479
|
// src/bin/backfill-conversations.ts
|
|
2480
|
+
init_exe_daemon_client();
|
|
2331
2481
|
init_database();
|
|
2332
2482
|
|
|
2333
2483
|
// src/lib/is-main.ts
|
|
@@ -2335,6 +2485,7 @@ import { realpathSync } from "fs";
|
|
|
2335
2485
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
2336
2486
|
function isMainModule(importMetaUrl) {
|
|
2337
2487
|
if (process.argv[1] == null) return false;
|
|
2488
|
+
if (process.argv[1].includes("mcp/server")) return false;
|
|
2338
2489
|
try {
|
|
2339
2490
|
const scriptPath = realpathSync(process.argv[1]);
|
|
2340
2491
|
const modulePath = realpathSync(fileURLToPath2(importMetaUrl));
|
|
@@ -2650,7 +2801,7 @@ async function backfillConversations(options) {
|
|
|
2650
2801
|
await writeMemory({
|
|
2651
2802
|
id: crypto.randomUUID(),
|
|
2652
2803
|
agent_id: conv.agentId,
|
|
2653
|
-
agent_role: isCoordinatorName(conv.agentId)
|
|
2804
|
+
agent_role: isCoordinatorName(conv.agentId) ? "COO" : "specialist",
|
|
2654
2805
|
session_id: conv.sessionId,
|
|
2655
2806
|
timestamp: conv.startTime ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
2656
2807
|
tool_name: TOOL_NAME,
|