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