@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
|
@@ -408,6 +408,12 @@ function getClient() {
|
|
|
408
408
|
if (!_resilientClient) {
|
|
409
409
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
410
410
|
}
|
|
411
|
+
if (process.env.EXE_IS_DAEMON === "1") {
|
|
412
|
+
return _resilientClient;
|
|
413
|
+
}
|
|
414
|
+
if (_daemonClient && _daemonClient._isDaemonActive()) {
|
|
415
|
+
return _daemonClient;
|
|
416
|
+
}
|
|
411
417
|
return _resilientClient;
|
|
412
418
|
}
|
|
413
419
|
function getRawClient() {
|
|
@@ -896,6 +902,12 @@ async function ensureSchema() {
|
|
|
896
902
|
} catch {
|
|
897
903
|
}
|
|
898
904
|
}
|
|
905
|
+
try {
|
|
906
|
+
await client.execute(
|
|
907
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_content_hash ON memories(content_hash, agent_id)`
|
|
908
|
+
);
|
|
909
|
+
} catch {
|
|
910
|
+
}
|
|
899
911
|
await client.executeMultiple(`
|
|
900
912
|
CREATE TABLE IF NOT EXISTS entities (
|
|
901
913
|
id TEXT PRIMARY KEY,
|
|
@@ -948,7 +960,30 @@ async function ensureSchema() {
|
|
|
948
960
|
entity_id TEXT NOT NULL,
|
|
949
961
|
PRIMARY KEY (hyperedge_id, entity_id)
|
|
950
962
|
);
|
|
963
|
+
|
|
964
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS entities_fts USING fts5(
|
|
965
|
+
name,
|
|
966
|
+
content=entities,
|
|
967
|
+
content_rowid=rowid
|
|
968
|
+
);
|
|
969
|
+
|
|
970
|
+
CREATE TRIGGER IF NOT EXISTS entities_fts_ai AFTER INSERT ON entities BEGIN
|
|
971
|
+
INSERT INTO entities_fts(rowid, name) VALUES (new.rowid, new.name);
|
|
972
|
+
END;
|
|
973
|
+
|
|
974
|
+
CREATE TRIGGER IF NOT EXISTS entities_fts_ad AFTER DELETE ON entities BEGIN
|
|
975
|
+
INSERT INTO entities_fts(entities_fts, rowid, name) VALUES('delete', old.rowid, old.name);
|
|
976
|
+
END;
|
|
977
|
+
|
|
978
|
+
CREATE TRIGGER IF NOT EXISTS entities_fts_au AFTER UPDATE ON entities BEGIN
|
|
979
|
+
INSERT INTO entities_fts(entities_fts, rowid, name) VALUES('delete', old.rowid, old.name);
|
|
980
|
+
INSERT INTO entities_fts(rowid, name) VALUES (new.rowid, new.name);
|
|
981
|
+
END;
|
|
951
982
|
`);
|
|
983
|
+
try {
|
|
984
|
+
await client.execute("INSERT INTO entities_fts(entities_fts) VALUES('rebuild')");
|
|
985
|
+
} catch {
|
|
986
|
+
}
|
|
952
987
|
await client.executeMultiple(`
|
|
953
988
|
CREATE TABLE IF NOT EXISTS entity_aliases (
|
|
954
989
|
alias TEXT NOT NULL PRIMARY KEY,
|
|
@@ -1129,6 +1164,33 @@ async function ensureSchema() {
|
|
|
1129
1164
|
CREATE INDEX IF NOT EXISTS idx_conversations_channel
|
|
1130
1165
|
ON conversations(channel_id);
|
|
1131
1166
|
`);
|
|
1167
|
+
await client.executeMultiple(`
|
|
1168
|
+
CREATE TABLE IF NOT EXISTS session_agent_map (
|
|
1169
|
+
session_uuid TEXT PRIMARY KEY,
|
|
1170
|
+
agent_id TEXT NOT NULL,
|
|
1171
|
+
session_name TEXT,
|
|
1172
|
+
task_id TEXT,
|
|
1173
|
+
project_name TEXT,
|
|
1174
|
+
started_at TEXT NOT NULL
|
|
1175
|
+
);
|
|
1176
|
+
|
|
1177
|
+
CREATE INDEX IF NOT EXISTS idx_session_agent_map_agent
|
|
1178
|
+
ON session_agent_map(agent_id);
|
|
1179
|
+
`);
|
|
1180
|
+
try {
|
|
1181
|
+
const mapCount = await client.execute({ sql: `SELECT COUNT(*) as cnt FROM session_agent_map`, args: [] });
|
|
1182
|
+
if (Number(mapCount.rows[0]?.cnt ?? 0) === 0) {
|
|
1183
|
+
await client.execute({
|
|
1184
|
+
sql: `INSERT OR IGNORE INTO session_agent_map (session_uuid, agent_id, session_name, started_at)
|
|
1185
|
+
SELECT session_id, agent_id, '', MIN(timestamp)
|
|
1186
|
+
FROM memories
|
|
1187
|
+
WHERE session_id IS NOT NULL AND session_id != '' AND agent_id IS NOT NULL AND agent_id != ''
|
|
1188
|
+
GROUP BY session_id, agent_id`,
|
|
1189
|
+
args: []
|
|
1190
|
+
});
|
|
1191
|
+
}
|
|
1192
|
+
} catch {
|
|
1193
|
+
}
|
|
1132
1194
|
try {
|
|
1133
1195
|
await client.execute({
|
|
1134
1196
|
sql: `ALTER TABLE tasks ADD COLUMN budget_tokens INTEGER`,
|
|
@@ -1262,15 +1324,41 @@ async function ensureSchema() {
|
|
|
1262
1324
|
});
|
|
1263
1325
|
} catch {
|
|
1264
1326
|
}
|
|
1327
|
+
for (const col of [
|
|
1328
|
+
"ALTER TABLE memories ADD COLUMN intent TEXT",
|
|
1329
|
+
"ALTER TABLE memories ADD COLUMN outcome TEXT",
|
|
1330
|
+
"ALTER TABLE memories ADD COLUMN domain TEXT",
|
|
1331
|
+
"ALTER TABLE memories ADD COLUMN referenced_entities TEXT",
|
|
1332
|
+
"ALTER TABLE memories ADD COLUMN retrieval_count INTEGER DEFAULT 0",
|
|
1333
|
+
"ALTER TABLE memories ADD COLUMN chain_position TEXT",
|
|
1334
|
+
"ALTER TABLE memories ADD COLUMN review_status TEXT",
|
|
1335
|
+
"ALTER TABLE memories ADD COLUMN context_window_pct INTEGER",
|
|
1336
|
+
"ALTER TABLE memories ADD COLUMN file_paths TEXT",
|
|
1337
|
+
"ALTER TABLE memories ADD COLUMN commit_hash TEXT",
|
|
1338
|
+
"ALTER TABLE memories ADD COLUMN duration_ms INTEGER",
|
|
1339
|
+
"ALTER TABLE memories ADD COLUMN token_cost REAL",
|
|
1340
|
+
"ALTER TABLE memories ADD COLUMN audience TEXT",
|
|
1341
|
+
"ALTER TABLE memories ADD COLUMN language_type TEXT",
|
|
1342
|
+
"ALTER TABLE memories ADD COLUMN parent_memory_id TEXT"
|
|
1343
|
+
]) {
|
|
1344
|
+
try {
|
|
1345
|
+
await client.execute(col);
|
|
1346
|
+
} catch {
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1265
1349
|
}
|
|
1266
1350
|
async function disposeDatabase() {
|
|
1351
|
+
if (_daemonClient) {
|
|
1352
|
+
_daemonClient.close();
|
|
1353
|
+
_daemonClient = null;
|
|
1354
|
+
}
|
|
1267
1355
|
if (_client) {
|
|
1268
1356
|
_client.close();
|
|
1269
1357
|
_client = null;
|
|
1270
1358
|
_resilientClient = null;
|
|
1271
1359
|
}
|
|
1272
1360
|
}
|
|
1273
|
-
var _client, _resilientClient, initTurso, disposeTurso;
|
|
1361
|
+
var _client, _resilientClient, _daemonClient, initTurso, disposeTurso;
|
|
1274
1362
|
var init_database = __esm({
|
|
1275
1363
|
"src/lib/database.ts"() {
|
|
1276
1364
|
"use strict";
|
|
@@ -1278,6 +1366,7 @@ var init_database = __esm({
|
|
|
1278
1366
|
init_employees();
|
|
1279
1367
|
_client = null;
|
|
1280
1368
|
_resilientClient = null;
|
|
1369
|
+
_daemonClient = null;
|
|
1281
1370
|
initTurso = initDatabase;
|
|
1282
1371
|
disposeTurso = disposeDatabase;
|
|
1283
1372
|
}
|
|
@@ -1314,12 +1403,20 @@ async function getMasterKey() {
|
|
|
1314
1403
|
}
|
|
1315
1404
|
const keyPath = getKeyPath();
|
|
1316
1405
|
if (!existsSync3(keyPath)) {
|
|
1406
|
+
process.stderr.write(
|
|
1407
|
+
`[keychain] Key not found at ${keyPath} (HOME=${os3.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
1408
|
+
`
|
|
1409
|
+
);
|
|
1317
1410
|
return null;
|
|
1318
1411
|
}
|
|
1319
1412
|
try {
|
|
1320
1413
|
const content = await readFile3(keyPath, "utf-8");
|
|
1321
1414
|
return Buffer.from(content.trim(), "base64");
|
|
1322
|
-
} catch {
|
|
1415
|
+
} catch (err) {
|
|
1416
|
+
process.stderr.write(
|
|
1417
|
+
`[keychain] Key read failed at ${keyPath}: ${err instanceof Error ? err.message : String(err)}
|
|
1418
|
+
`
|
|
1419
|
+
);
|
|
1323
1420
|
return null;
|
|
1324
1421
|
}
|
|
1325
1422
|
}
|
|
@@ -1830,6 +1927,7 @@ __export(store_exports, {
|
|
|
1830
1927
|
vectorToBlob: () => vectorToBlob,
|
|
1831
1928
|
writeMemory: () => writeMemory
|
|
1832
1929
|
});
|
|
1930
|
+
import { createHash } from "crypto";
|
|
1833
1931
|
function isBusyError2(err) {
|
|
1834
1932
|
if (err instanceof Error) {
|
|
1835
1933
|
const msg = err.message.toLowerCase();
|
|
@@ -1903,12 +2001,52 @@ function classifyTier(record) {
|
|
|
1903
2001
|
if (["store_memory", "manual"].includes(record.tool_name ?? "") && (record.importance ?? 0) >= 5) return 2;
|
|
1904
2002
|
return 3;
|
|
1905
2003
|
}
|
|
2004
|
+
function inferFilePaths(record) {
|
|
2005
|
+
if (!["Read", "Write", "Edit"].includes(record.tool_name)) return null;
|
|
2006
|
+
const firstLine = record.raw_text.split("\n")[0] ?? "";
|
|
2007
|
+
const match = firstLine.match(/(\/[\w./-]+\.\w+)/);
|
|
2008
|
+
return match ? JSON.stringify([match[1]]) : null;
|
|
2009
|
+
}
|
|
2010
|
+
function inferCommitHash(record) {
|
|
2011
|
+
if (record.tool_name !== "Bash") return null;
|
|
2012
|
+
const match = record.raw_text.match(/\b([a-f0-9]{7,40})\b/);
|
|
2013
|
+
return match ? match[1] : null;
|
|
2014
|
+
}
|
|
2015
|
+
function inferLanguageType(record) {
|
|
2016
|
+
const text = record.raw_text;
|
|
2017
|
+
if (!text || text.length < 10) return null;
|
|
2018
|
+
const trimmed = text.trimStart();
|
|
2019
|
+
if (trimmed.startsWith("{") || trimmed.startsWith("[")) return "json";
|
|
2020
|
+
if (/\b(SELECT|INSERT|UPDATE|DELETE|CREATE TABLE|ALTER TABLE)\b/i.test(text)) return "sql";
|
|
2021
|
+
if (/\b(function |const |import |export |class |def |async |=>)\b/.test(text)) return "code";
|
|
2022
|
+
if (trimmed.startsWith("#") || trimmed.startsWith("*")) return "prose";
|
|
2023
|
+
return "mixed";
|
|
2024
|
+
}
|
|
2025
|
+
function inferDomain(record) {
|
|
2026
|
+
const proj = (record.project_name ?? "").toLowerCase();
|
|
2027
|
+
if (proj.includes("marketing") || proj.includes("content")) return "marketing";
|
|
2028
|
+
if (proj.includes("crm") || proj.includes("customer")) return "customer";
|
|
2029
|
+
return null;
|
|
2030
|
+
}
|
|
1906
2031
|
async function writeMemory(record) {
|
|
1907
2032
|
if (record.vector !== null && record.vector.length !== EMBEDDING_DIM) {
|
|
1908
2033
|
throw new Error(
|
|
1909
2034
|
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
1910
2035
|
);
|
|
1911
2036
|
}
|
|
2037
|
+
const contentHash = createHash("md5").update(record.raw_text).digest("hex");
|
|
2038
|
+
if (_pendingRecords.some((r) => r.content_hash === contentHash && r.agent_id === record.agent_id)) {
|
|
2039
|
+
return;
|
|
2040
|
+
}
|
|
2041
|
+
try {
|
|
2042
|
+
const client = getClient();
|
|
2043
|
+
const existing = await client.execute({
|
|
2044
|
+
sql: "SELECT id FROM memories WHERE content_hash = ? AND agent_id = ? LIMIT 1",
|
|
2045
|
+
args: [contentHash, record.agent_id]
|
|
2046
|
+
});
|
|
2047
|
+
if (existing.rows.length > 0) return;
|
|
2048
|
+
} catch {
|
|
2049
|
+
}
|
|
1912
2050
|
const dbRow = {
|
|
1913
2051
|
id: record.id,
|
|
1914
2052
|
agent_id: record.agent_id,
|
|
@@ -1938,7 +2076,23 @@ async function writeMemory(record) {
|
|
|
1938
2076
|
supersedes_id: record.supersedes_id ?? null,
|
|
1939
2077
|
draft: record.draft ? 1 : 0,
|
|
1940
2078
|
memory_type: record.memory_type ?? "raw",
|
|
1941
|
-
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null
|
|
2079
|
+
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
2080
|
+
content_hash: contentHash,
|
|
2081
|
+
intent: record.intent ?? null,
|
|
2082
|
+
outcome: record.outcome ?? null,
|
|
2083
|
+
domain: record.domain ?? inferDomain(record),
|
|
2084
|
+
referenced_entities: record.referenced_entities ?? null,
|
|
2085
|
+
retrieval_count: record.retrieval_count ?? 0,
|
|
2086
|
+
chain_position: record.chain_position ?? null,
|
|
2087
|
+
review_status: record.review_status ?? null,
|
|
2088
|
+
context_window_pct: record.context_window_pct ?? null,
|
|
2089
|
+
file_paths: record.file_paths ?? inferFilePaths(record),
|
|
2090
|
+
commit_hash: record.commit_hash ?? inferCommitHash(record),
|
|
2091
|
+
duration_ms: record.duration_ms ?? null,
|
|
2092
|
+
token_cost: record.token_cost ?? null,
|
|
2093
|
+
audience: record.audience ?? null,
|
|
2094
|
+
language_type: record.language_type ?? inferLanguageType(record),
|
|
2095
|
+
parent_memory_id: record.parent_memory_id ?? null
|
|
1942
2096
|
};
|
|
1943
2097
|
_pendingRecords.push(dbRow);
|
|
1944
2098
|
orgBus.emit({
|
|
@@ -1996,80 +2150,85 @@ async function flushBatch() {
|
|
|
1996
2150
|
const draft = row.draft ? 1 : 0;
|
|
1997
2151
|
const memoryType = row.memory_type ?? "raw";
|
|
1998
2152
|
const trajectory = row.trajectory ?? null;
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2153
|
+
const contentHash = row.content_hash ?? null;
|
|
2154
|
+
const intent = row.intent ?? null;
|
|
2155
|
+
const outcome = row.outcome ?? null;
|
|
2156
|
+
const domain = row.domain ?? null;
|
|
2157
|
+
const referencedEntities = row.referenced_entities ?? null;
|
|
2158
|
+
const retrievalCount = row.retrieval_count ?? 0;
|
|
2159
|
+
const chainPosition = row.chain_position ?? null;
|
|
2160
|
+
const reviewStatus = row.review_status ?? null;
|
|
2161
|
+
const contextWindowPct = row.context_window_pct ?? null;
|
|
2162
|
+
const filePaths = row.file_paths ?? null;
|
|
2163
|
+
const commitHash = row.commit_hash ?? null;
|
|
2164
|
+
const durationMs = row.duration_ms ?? null;
|
|
2165
|
+
const tokenCost = row.token_cost ?? null;
|
|
2166
|
+
const audience = row.audience ?? null;
|
|
2167
|
+
const languageType = row.language_type ?? null;
|
|
2168
|
+
const parentMemoryId = row.parent_memory_id ?? null;
|
|
2169
|
+
const cols = `id, agent_id, agent_role, session_id, timestamp,
|
|
2002
2170
|
tool_name, project_name,
|
|
2003
2171
|
has_error, raw_text, vector, version, task_id, importance, status,
|
|
2004
2172
|
confidence, last_accessed,
|
|
2005
2173
|
workspace_id, document_id, user_id, char_offset, page_number,
|
|
2006
|
-
source_path, source_type, tier, supersedes_id, draft, memory_type, trajectory
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
pageNumber,
|
|
2065
|
-
sourcePath,
|
|
2066
|
-
sourceType,
|
|
2067
|
-
tier,
|
|
2068
|
-
supersedesId,
|
|
2069
|
-
draft,
|
|
2070
|
-
memoryType,
|
|
2071
|
-
trajectory
|
|
2072
|
-
]
|
|
2174
|
+
source_path, source_type, tier, supersedes_id, draft, memory_type, trajectory, content_hash,
|
|
2175
|
+
intent, outcome, domain, referenced_entities, retrieval_count,
|
|
2176
|
+
chain_position, review_status, context_window_pct, file_paths, commit_hash,
|
|
2177
|
+
duration_ms, token_cost, audience, language_type, parent_memory_id`;
|
|
2178
|
+
const metaArgs = [
|
|
2179
|
+
intent,
|
|
2180
|
+
outcome,
|
|
2181
|
+
domain,
|
|
2182
|
+
referencedEntities,
|
|
2183
|
+
retrievalCount,
|
|
2184
|
+
chainPosition,
|
|
2185
|
+
reviewStatus,
|
|
2186
|
+
contextWindowPct,
|
|
2187
|
+
filePaths,
|
|
2188
|
+
commitHash,
|
|
2189
|
+
durationMs,
|
|
2190
|
+
tokenCost,
|
|
2191
|
+
audience,
|
|
2192
|
+
languageType,
|
|
2193
|
+
parentMemoryId
|
|
2194
|
+
];
|
|
2195
|
+
const baseArgs = [
|
|
2196
|
+
row.id,
|
|
2197
|
+
row.agent_id,
|
|
2198
|
+
row.agent_role,
|
|
2199
|
+
row.session_id,
|
|
2200
|
+
row.timestamp,
|
|
2201
|
+
row.tool_name,
|
|
2202
|
+
row.project_name,
|
|
2203
|
+
row.has_error,
|
|
2204
|
+
row.raw_text
|
|
2205
|
+
];
|
|
2206
|
+
const sharedArgs = [
|
|
2207
|
+
row.version,
|
|
2208
|
+
taskId,
|
|
2209
|
+
importance,
|
|
2210
|
+
status,
|
|
2211
|
+
confidence,
|
|
2212
|
+
lastAccessed,
|
|
2213
|
+
workspaceId,
|
|
2214
|
+
documentId,
|
|
2215
|
+
userId,
|
|
2216
|
+
charOffset,
|
|
2217
|
+
pageNumber,
|
|
2218
|
+
sourcePath,
|
|
2219
|
+
sourceType,
|
|
2220
|
+
tier,
|
|
2221
|
+
supersedesId,
|
|
2222
|
+
draft,
|
|
2223
|
+
memoryType,
|
|
2224
|
+
trajectory,
|
|
2225
|
+
contentHash
|
|
2226
|
+
];
|
|
2227
|
+
return {
|
|
2228
|
+
sql: hasVector ? `INSERT OR IGNORE INTO memories (${cols})
|
|
2229
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories (${cols})
|
|
2230
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
2231
|
+
args: hasVector ? [...baseArgs, vectorToBlob(row.vector), ...sharedArgs, ...metaArgs] : [...baseArgs, ...sharedArgs, ...metaArgs]
|
|
2073
2232
|
};
|
|
2074
2233
|
};
|
|
2075
2234
|
const globalClient = getClient();
|
|
@@ -2408,12 +2567,141 @@ var init_self_query_router = __esm({
|
|
|
2408
2567
|
}
|
|
2409
2568
|
});
|
|
2410
2569
|
|
|
2570
|
+
// src/lib/reranker.ts
|
|
2571
|
+
var reranker_exports = {};
|
|
2572
|
+
__export(reranker_exports, {
|
|
2573
|
+
disposeReranker: () => disposeReranker,
|
|
2574
|
+
getRerankerModelPath: () => getRerankerModelPath,
|
|
2575
|
+
isRerankerAvailable: () => isRerankerAvailable,
|
|
2576
|
+
rerank: () => rerank,
|
|
2577
|
+
rerankWithContext: () => rerankWithContext,
|
|
2578
|
+
rerankWithScores: () => rerankWithScores
|
|
2579
|
+
});
|
|
2580
|
+
import path5 from "path";
|
|
2581
|
+
import { existsSync as existsSync5 } from "fs";
|
|
2582
|
+
function resetIdleTimer() {
|
|
2583
|
+
if (_idleTimer) clearTimeout(_idleTimer);
|
|
2584
|
+
_idleTimer = setTimeout(() => {
|
|
2585
|
+
void disposeReranker();
|
|
2586
|
+
}, IDLE_TIMEOUT_MS);
|
|
2587
|
+
if (_idleTimer && typeof _idleTimer === "object" && "unref" in _idleTimer) {
|
|
2588
|
+
_idleTimer.unref();
|
|
2589
|
+
}
|
|
2590
|
+
}
|
|
2591
|
+
function isRerankerAvailable() {
|
|
2592
|
+
return existsSync5(path5.join(MODELS_DIR, RERANKER_MODEL_FILE));
|
|
2593
|
+
}
|
|
2594
|
+
function getRerankerModelPath() {
|
|
2595
|
+
return path5.join(MODELS_DIR, RERANKER_MODEL_FILE);
|
|
2596
|
+
}
|
|
2597
|
+
async function ensureLoaded() {
|
|
2598
|
+
if (_rerankerContext) {
|
|
2599
|
+
resetIdleTimer();
|
|
2600
|
+
return;
|
|
2601
|
+
}
|
|
2602
|
+
const modelPath = path5.join(MODELS_DIR, RERANKER_MODEL_FILE);
|
|
2603
|
+
if (!existsSync5(modelPath)) {
|
|
2604
|
+
throw new Error(
|
|
2605
|
+
`Reranker model not found at ${modelPath}. Run /exe-setup to download it.`
|
|
2606
|
+
);
|
|
2607
|
+
}
|
|
2608
|
+
process.stderr.write("[reranker] Loading Jina Reranker v3...\n");
|
|
2609
|
+
const { getLlama } = await import("node-llama-cpp");
|
|
2610
|
+
const llama = await getLlama();
|
|
2611
|
+
_rerankerModel = await llama.loadModel({ modelPath });
|
|
2612
|
+
_rerankerContext = await _rerankerModel.createEmbeddingContext();
|
|
2613
|
+
process.stderr.write("[reranker] Jina Reranker v3 loaded.\n");
|
|
2614
|
+
resetIdleTimer();
|
|
2615
|
+
}
|
|
2616
|
+
async function disposeReranker() {
|
|
2617
|
+
if (_idleTimer) {
|
|
2618
|
+
clearTimeout(_idleTimer);
|
|
2619
|
+
_idleTimer = null;
|
|
2620
|
+
}
|
|
2621
|
+
if (_rerankerContext) {
|
|
2622
|
+
try {
|
|
2623
|
+
await _rerankerContext.dispose();
|
|
2624
|
+
} catch {
|
|
2625
|
+
}
|
|
2626
|
+
_rerankerContext = null;
|
|
2627
|
+
}
|
|
2628
|
+
if (_rerankerModel) {
|
|
2629
|
+
try {
|
|
2630
|
+
await _rerankerModel.dispose();
|
|
2631
|
+
} catch {
|
|
2632
|
+
}
|
|
2633
|
+
_rerankerModel = null;
|
|
2634
|
+
}
|
|
2635
|
+
process.stderr.write("[reranker] Unloaded (idle timeout).\n");
|
|
2636
|
+
}
|
|
2637
|
+
async function rerankWithScores(query, texts, topK) {
|
|
2638
|
+
if (texts.length === 0) return [];
|
|
2639
|
+
await ensureLoaded();
|
|
2640
|
+
const ctx = _rerankerContext;
|
|
2641
|
+
const scored = [];
|
|
2642
|
+
for (let i = 0; i < texts.length; i++) {
|
|
2643
|
+
const text = texts[i] ?? "";
|
|
2644
|
+
try {
|
|
2645
|
+
const input2 = `query: ${query} document: ${text.slice(0, 512)}`;
|
|
2646
|
+
const embedding = await ctx.getEmbeddingFor(input2);
|
|
2647
|
+
const score = embedding.vector[0] ?? 0;
|
|
2648
|
+
scored.push({ text, score, index: i });
|
|
2649
|
+
} catch {
|
|
2650
|
+
scored.push({ text, score: -1, index: i });
|
|
2651
|
+
}
|
|
2652
|
+
}
|
|
2653
|
+
scored.sort((a, b) => b.score - a.score);
|
|
2654
|
+
return typeof topK === "number" ? scored.slice(0, topK) : scored;
|
|
2655
|
+
}
|
|
2656
|
+
async function rerank(query, candidates, topK = 5) {
|
|
2657
|
+
if (candidates.length === 0) return [];
|
|
2658
|
+
if (candidates.length <= topK) return candidates;
|
|
2659
|
+
const scored = await rerankWithScores(
|
|
2660
|
+
query,
|
|
2661
|
+
candidates.map((c) => c.raw_text),
|
|
2662
|
+
topK
|
|
2663
|
+
);
|
|
2664
|
+
return scored.map((s) => candidates[s.index]);
|
|
2665
|
+
}
|
|
2666
|
+
async function rerankWithContext(query, candidates, topK) {
|
|
2667
|
+
if (candidates.length === 0) return [];
|
|
2668
|
+
await ensureLoaded();
|
|
2669
|
+
const ctx = _rerankerContext;
|
|
2670
|
+
const scored = [];
|
|
2671
|
+
for (let i = 0; i < candidates.length; i++) {
|
|
2672
|
+
const candidate = candidates[i];
|
|
2673
|
+
try {
|
|
2674
|
+
const docText = candidate.context ? `[${candidate.context}] ${candidate.text.slice(0, 460)}` : candidate.text.slice(0, 512);
|
|
2675
|
+
const input2 = `query: ${query} document: ${docText}`;
|
|
2676
|
+
const embedding = await ctx.getEmbeddingFor(input2);
|
|
2677
|
+
const score = embedding.vector[0] ?? 0;
|
|
2678
|
+
scored.push({ text: candidate.text, score, index: i });
|
|
2679
|
+
} catch {
|
|
2680
|
+
scored.push({ text: candidate.text, score: -1, index: i });
|
|
2681
|
+
}
|
|
2682
|
+
}
|
|
2683
|
+
scored.sort((a, b) => b.score - a.score);
|
|
2684
|
+
return typeof topK === "number" ? scored.slice(0, topK) : scored;
|
|
2685
|
+
}
|
|
2686
|
+
var RERANKER_MODEL_FILE, IDLE_TIMEOUT_MS, _rerankerContext, _rerankerModel, _idleTimer;
|
|
2687
|
+
var init_reranker = __esm({
|
|
2688
|
+
"src/lib/reranker.ts"() {
|
|
2689
|
+
"use strict";
|
|
2690
|
+
init_config();
|
|
2691
|
+
RERANKER_MODEL_FILE = "jina-reranker-v3-q4_k_m.gguf";
|
|
2692
|
+
IDLE_TIMEOUT_MS = 6e4;
|
|
2693
|
+
_rerankerContext = null;
|
|
2694
|
+
_rerankerModel = null;
|
|
2695
|
+
_idleTimer = null;
|
|
2696
|
+
}
|
|
2697
|
+
});
|
|
2698
|
+
|
|
2411
2699
|
// src/lib/exe-daemon-client.ts
|
|
2412
2700
|
import net from "net";
|
|
2413
2701
|
import { spawn } from "child_process";
|
|
2414
2702
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
2415
|
-
import { existsSync as
|
|
2416
|
-
import
|
|
2703
|
+
import { existsSync as existsSync6, unlinkSync as unlinkSync2, readFileSync as readFileSync3, openSync, closeSync, statSync } from "fs";
|
|
2704
|
+
import path6 from "path";
|
|
2417
2705
|
import { fileURLToPath } from "url";
|
|
2418
2706
|
function handleData(chunk) {
|
|
2419
2707
|
_buffer += chunk.toString();
|
|
@@ -2428,10 +2716,12 @@ function handleData(chunk) {
|
|
|
2428
2716
|
if (!line) continue;
|
|
2429
2717
|
try {
|
|
2430
2718
|
const response = JSON.parse(line);
|
|
2431
|
-
const
|
|
2719
|
+
const id = response.id;
|
|
2720
|
+
if (!id) continue;
|
|
2721
|
+
const entry = _pending.get(id);
|
|
2432
2722
|
if (entry) {
|
|
2433
2723
|
clearTimeout(entry.timer);
|
|
2434
|
-
_pending.delete(
|
|
2724
|
+
_pending.delete(id);
|
|
2435
2725
|
entry.resolve(response);
|
|
2436
2726
|
}
|
|
2437
2727
|
} catch {
|
|
@@ -2439,7 +2729,7 @@ function handleData(chunk) {
|
|
|
2439
2729
|
}
|
|
2440
2730
|
}
|
|
2441
2731
|
function cleanupStaleFiles() {
|
|
2442
|
-
if (
|
|
2732
|
+
if (existsSync6(PID_PATH)) {
|
|
2443
2733
|
try {
|
|
2444
2734
|
const pid = parseInt(readFileSync3(PID_PATH, "utf8").trim(), 10);
|
|
2445
2735
|
if (pid > 0) {
|
|
@@ -2462,11 +2752,11 @@ function cleanupStaleFiles() {
|
|
|
2462
2752
|
}
|
|
2463
2753
|
}
|
|
2464
2754
|
function findPackageRoot() {
|
|
2465
|
-
let dir =
|
|
2466
|
-
const { root } =
|
|
2755
|
+
let dir = path6.dirname(fileURLToPath(import.meta.url));
|
|
2756
|
+
const { root } = path6.parse(dir);
|
|
2467
2757
|
while (dir !== root) {
|
|
2468
|
-
if (
|
|
2469
|
-
dir =
|
|
2758
|
+
if (existsSync6(path6.join(dir, "package.json"))) return dir;
|
|
2759
|
+
dir = path6.dirname(dir);
|
|
2470
2760
|
}
|
|
2471
2761
|
return null;
|
|
2472
2762
|
}
|
|
@@ -2476,8 +2766,8 @@ function spawnDaemon() {
|
|
|
2476
2766
|
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
2477
2767
|
return;
|
|
2478
2768
|
}
|
|
2479
|
-
const daemonPath =
|
|
2480
|
-
if (!
|
|
2769
|
+
const daemonPath = path6.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
2770
|
+
if (!existsSync6(daemonPath)) {
|
|
2481
2771
|
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
2482
2772
|
`);
|
|
2483
2773
|
return;
|
|
@@ -2485,7 +2775,7 @@ function spawnDaemon() {
|
|
|
2485
2775
|
const resolvedPath = daemonPath;
|
|
2486
2776
|
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
2487
2777
|
`);
|
|
2488
|
-
const logPath =
|
|
2778
|
+
const logPath = path6.join(path6.dirname(SOCKET_PATH), "exed.log");
|
|
2489
2779
|
let stderrFd = "ignore";
|
|
2490
2780
|
try {
|
|
2491
2781
|
stderrFd = openSync(logPath, "a");
|
|
@@ -2602,6 +2892,9 @@ async function connectEmbedDaemon() {
|
|
|
2602
2892
|
return false;
|
|
2603
2893
|
}
|
|
2604
2894
|
function sendRequest(texts, priority) {
|
|
2895
|
+
return sendDaemonRequest({ texts, priority });
|
|
2896
|
+
}
|
|
2897
|
+
function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
2605
2898
|
return new Promise((resolve) => {
|
|
2606
2899
|
if (!_socket || !_connected) {
|
|
2607
2900
|
resolve({ error: "Not connected" });
|
|
@@ -2611,10 +2904,10 @@ function sendRequest(texts, priority) {
|
|
|
2611
2904
|
const timer = setTimeout(() => {
|
|
2612
2905
|
_pending.delete(id);
|
|
2613
2906
|
resolve({ error: "Request timeout" });
|
|
2614
|
-
},
|
|
2907
|
+
}, timeoutMs);
|
|
2615
2908
|
_pending.set(id, { resolve, timer });
|
|
2616
2909
|
try {
|
|
2617
|
-
_socket.write(JSON.stringify({ id,
|
|
2910
|
+
_socket.write(JSON.stringify({ id, ...payload }) + "\n");
|
|
2618
2911
|
} catch {
|
|
2619
2912
|
clearTimeout(timer);
|
|
2620
2913
|
_pending.delete(id);
|
|
@@ -2624,34 +2917,15 @@ function sendRequest(texts, priority) {
|
|
|
2624
2917
|
}
|
|
2625
2918
|
async function pingDaemon() {
|
|
2626
2919
|
if (!_socket || !_connected) return null;
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
}, 5e3);
|
|
2633
|
-
_pending.set(id, {
|
|
2634
|
-
resolve: (data) => {
|
|
2635
|
-
if (data.health) {
|
|
2636
|
-
resolve(data.health);
|
|
2637
|
-
} else {
|
|
2638
|
-
resolve(null);
|
|
2639
|
-
}
|
|
2640
|
-
},
|
|
2641
|
-
timer
|
|
2642
|
-
});
|
|
2643
|
-
try {
|
|
2644
|
-
_socket.write(JSON.stringify({ id, type: "health" }) + "\n");
|
|
2645
|
-
} catch {
|
|
2646
|
-
clearTimeout(timer);
|
|
2647
|
-
_pending.delete(id);
|
|
2648
|
-
resolve(null);
|
|
2649
|
-
}
|
|
2650
|
-
});
|
|
2920
|
+
const response = await sendDaemonRequest({ type: "health" }, 5e3);
|
|
2921
|
+
if (response.health) {
|
|
2922
|
+
return response.health;
|
|
2923
|
+
}
|
|
2924
|
+
return null;
|
|
2651
2925
|
}
|
|
2652
2926
|
function killAndRespawnDaemon() {
|
|
2653
2927
|
process.stderr.write("[exed-client] Killing daemon for restart...\n");
|
|
2654
|
-
if (
|
|
2928
|
+
if (existsSync6(PID_PATH)) {
|
|
2655
2929
|
try {
|
|
2656
2930
|
const pid = parseInt(readFileSync3(PID_PATH, "utf8").trim(), 10);
|
|
2657
2931
|
if (pid > 0) {
|
|
@@ -2737,9 +3011,9 @@ var init_exe_daemon_client = __esm({
|
|
|
2737
3011
|
"src/lib/exe-daemon-client.ts"() {
|
|
2738
3012
|
"use strict";
|
|
2739
3013
|
init_config();
|
|
2740
|
-
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ??
|
|
2741
|
-
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ??
|
|
2742
|
-
SPAWN_LOCK_PATH =
|
|
3014
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path6.join(EXE_AI_DIR, "exed.sock");
|
|
3015
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path6.join(EXE_AI_DIR, "exed.pid");
|
|
3016
|
+
SPAWN_LOCK_PATH = path6.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
2743
3017
|
SPAWN_LOCK_STALE_MS = 3e4;
|
|
2744
3018
|
CONNECT_TIMEOUT_MS = 15e3;
|
|
2745
3019
|
REQUEST_TIMEOUT_MS = 3e4;
|
|
@@ -2790,10 +3064,10 @@ async function disposeEmbedder() {
|
|
|
2790
3064
|
async function embedDirect(text) {
|
|
2791
3065
|
const llamaCpp = await import("node-llama-cpp");
|
|
2792
3066
|
const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
2793
|
-
const { existsSync:
|
|
2794
|
-
const
|
|
2795
|
-
const modelPath =
|
|
2796
|
-
if (!
|
|
3067
|
+
const { existsSync: existsSync16 } = await import("fs");
|
|
3068
|
+
const path19 = await import("path");
|
|
3069
|
+
const modelPath = path19.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
|
|
3070
|
+
if (!existsSync16(modelPath)) {
|
|
2797
3071
|
throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
|
|
2798
3072
|
}
|
|
2799
3073
|
const llama = await llamaCpp.getLlama();
|
|
@@ -2828,7 +3102,7 @@ __export(project_name_exports, {
|
|
|
2828
3102
|
getProjectName: () => getProjectName
|
|
2829
3103
|
});
|
|
2830
3104
|
import { execSync as execSync2 } from "child_process";
|
|
2831
|
-
import
|
|
3105
|
+
import path7 from "path";
|
|
2832
3106
|
function getProjectName(cwd) {
|
|
2833
3107
|
const dir = cwd ?? process.cwd();
|
|
2834
3108
|
if (_cached && _cachedCwd === dir) return _cached;
|
|
@@ -2841,7 +3115,7 @@ function getProjectName(cwd) {
|
|
|
2841
3115
|
timeout: 2e3,
|
|
2842
3116
|
stdio: ["pipe", "pipe", "pipe"]
|
|
2843
3117
|
}).trim();
|
|
2844
|
-
repoRoot =
|
|
3118
|
+
repoRoot = path7.dirname(gitCommonDir);
|
|
2845
3119
|
} catch {
|
|
2846
3120
|
repoRoot = execSync2("git rev-parse --show-toplevel", {
|
|
2847
3121
|
cwd: dir,
|
|
@@ -2850,11 +3124,11 @@ function getProjectName(cwd) {
|
|
|
2850
3124
|
stdio: ["pipe", "pipe", "pipe"]
|
|
2851
3125
|
}).trim();
|
|
2852
3126
|
}
|
|
2853
|
-
_cached =
|
|
3127
|
+
_cached = path7.basename(repoRoot);
|
|
2854
3128
|
_cachedCwd = dir;
|
|
2855
3129
|
return _cached;
|
|
2856
3130
|
} catch {
|
|
2857
|
-
_cached =
|
|
3131
|
+
_cached = path7.basename(dir);
|
|
2858
3132
|
_cachedCwd = dir;
|
|
2859
3133
|
return _cached;
|
|
2860
3134
|
}
|
|
@@ -2878,8 +3152,8 @@ __export(file_grep_exports, {
|
|
|
2878
3152
|
grepProjectFiles: () => grepProjectFiles
|
|
2879
3153
|
});
|
|
2880
3154
|
import { execSync as execSync3 } from "child_process";
|
|
2881
|
-
import { readFileSync as readFileSync4, readdirSync as readdirSync2, statSync as statSync2, existsSync as
|
|
2882
|
-
import
|
|
3155
|
+
import { readFileSync as readFileSync4, readdirSync as readdirSync2, statSync as statSync2, existsSync as existsSync7 } from "fs";
|
|
3156
|
+
import path8 from "path";
|
|
2883
3157
|
import crypto from "crypto";
|
|
2884
3158
|
function hasRipgrep() {
|
|
2885
3159
|
if (_hasRg === null) {
|
|
@@ -2919,7 +3193,7 @@ async function grepProjectFiles(query, projectRoot, options) {
|
|
|
2919
3193
|
session_id: "file-grep",
|
|
2920
3194
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2921
3195
|
tool_name: "file_grep",
|
|
2922
|
-
project_name:
|
|
3196
|
+
project_name: path8.basename(projectRoot),
|
|
2923
3197
|
has_error: false,
|
|
2924
3198
|
raw_text: `${prefix} ${buildSnippet(hit, projectRoot)}`,
|
|
2925
3199
|
vector: null,
|
|
@@ -2993,7 +3267,7 @@ function grepWithNodeFs(pattern, projectRoot, patterns) {
|
|
|
2993
3267
|
const files = collectFiles(projectRoot, patterns ?? DEFAULT_PATTERNS);
|
|
2994
3268
|
const hits = [];
|
|
2995
3269
|
for (const filePath of files.slice(0, MAX_FILES)) {
|
|
2996
|
-
const absPath =
|
|
3270
|
+
const absPath = path8.join(projectRoot, filePath);
|
|
2997
3271
|
try {
|
|
2998
3272
|
const stat = statSync2(absPath);
|
|
2999
3273
|
if (stat.size > MAX_FILE_SIZE) continue;
|
|
@@ -3020,15 +3294,15 @@ function collectFiles(root, patterns) {
|
|
|
3020
3294
|
const files = [];
|
|
3021
3295
|
function walk(dir, relative) {
|
|
3022
3296
|
if (files.length >= MAX_FILES) return;
|
|
3023
|
-
const basename =
|
|
3297
|
+
const basename = path8.basename(dir);
|
|
3024
3298
|
if (EXCLUDE_DIRS.includes(basename)) return;
|
|
3025
3299
|
try {
|
|
3026
3300
|
const entries = readdirSync2(dir, { withFileTypes: true });
|
|
3027
3301
|
for (const entry of entries) {
|
|
3028
3302
|
if (files.length >= MAX_FILES) return;
|
|
3029
|
-
const rel =
|
|
3303
|
+
const rel = path8.join(relative, entry.name);
|
|
3030
3304
|
if (entry.isDirectory()) {
|
|
3031
|
-
walk(
|
|
3305
|
+
walk(path8.join(dir, entry.name), rel);
|
|
3032
3306
|
} else if (entry.isFile()) {
|
|
3033
3307
|
for (const pat of patterns) {
|
|
3034
3308
|
if (matchGlob(rel, pat)) {
|
|
@@ -3060,7 +3334,7 @@ function matchGlob(filePath, pattern) {
|
|
|
3060
3334
|
if (slashIdx !== -1) {
|
|
3061
3335
|
const dir = pattern.slice(0, slashIdx);
|
|
3062
3336
|
const ext2 = pattern.slice(slashIdx + 1).replace("*", "");
|
|
3063
|
-
const fileDir =
|
|
3337
|
+
const fileDir = path8.dirname(filePath);
|
|
3064
3338
|
return fileDir === dir && filePath.endsWith(ext2);
|
|
3065
3339
|
}
|
|
3066
3340
|
const ext = pattern.replace("*", "");
|
|
@@ -3068,8 +3342,8 @@ function matchGlob(filePath, pattern) {
|
|
|
3068
3342
|
}
|
|
3069
3343
|
function buildSnippet(hit, projectRoot) {
|
|
3070
3344
|
try {
|
|
3071
|
-
const absPath =
|
|
3072
|
-
if (!
|
|
3345
|
+
const absPath = path8.join(projectRoot, hit.filePath);
|
|
3346
|
+
if (!existsSync7(absPath)) return hit.matchLine;
|
|
3073
3347
|
const lines = readFileSync4(absPath, "utf8").split("\n");
|
|
3074
3348
|
const start = Math.max(0, hit.lineNumber - 3);
|
|
3075
3349
|
const end = Math.min(lines.length, hit.lineNumber + 2);
|
|
@@ -3095,111 +3369,574 @@ var init_file_grep = __esm({
|
|
|
3095
3369
|
}
|
|
3096
3370
|
});
|
|
3097
3371
|
|
|
3098
|
-
// src/lib/
|
|
3099
|
-
var
|
|
3100
|
-
__export(
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3372
|
+
// src/lib/graph-query.ts
|
|
3373
|
+
var graph_query_exports = {};
|
|
3374
|
+
__export(graph_query_exports, {
|
|
3375
|
+
getConversationPartners: () => getConversationPartners,
|
|
3376
|
+
getEntityByName: () => getEntityByName,
|
|
3377
|
+
getEntityNeighbors: () => getEntityNeighbors,
|
|
3378
|
+
getEntityTimeline: () => getEntityTimeline,
|
|
3379
|
+
getGraphStats: () => getGraphStats,
|
|
3380
|
+
getHotEntities: () => getHotEntities,
|
|
3381
|
+
getRelationshipFrequency: () => getRelationshipFrequency,
|
|
3382
|
+
getRelationships: () => getRelationships,
|
|
3383
|
+
searchEntities: () => searchEntities,
|
|
3384
|
+
traverseChain: () => traverseChain
|
|
3106
3385
|
});
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3386
|
+
async function getEntityByName(client, name, type) {
|
|
3387
|
+
const sql = type ? `SELECT * FROM entities WHERE LOWER(name) = LOWER(?) AND type = ? LIMIT 1` : `SELECT * FROM entities WHERE LOWER(name) = LOWER(?) LIMIT 1`;
|
|
3388
|
+
const args = type ? [name, type] : [name];
|
|
3389
|
+
const result = await client.execute({ sql, args });
|
|
3390
|
+
if (result.rows.length === 0) return null;
|
|
3391
|
+
const row = result.rows[0];
|
|
3392
|
+
return {
|
|
3393
|
+
id: String(row.id),
|
|
3394
|
+
name: String(row.name),
|
|
3395
|
+
type: String(row.type),
|
|
3396
|
+
firstSeen: String(row.first_seen),
|
|
3397
|
+
lastSeen: String(row.last_seen),
|
|
3398
|
+
properties: JSON.parse(String(row.properties ?? "{}"))
|
|
3399
|
+
};
|
|
3400
|
+
}
|
|
3401
|
+
async function searchEntities(client, query, limit = 10) {
|
|
3402
|
+
const result = await client.execute({
|
|
3403
|
+
sql: `SELECT * FROM entities WHERE LOWER(name) LIKE ? ORDER BY last_seen DESC LIMIT ?`,
|
|
3404
|
+
args: [`%${query.toLowerCase()}%`, limit]
|
|
3405
|
+
});
|
|
3406
|
+
return result.rows.map((row) => ({
|
|
3407
|
+
id: String(row.id),
|
|
3408
|
+
name: String(row.name),
|
|
3409
|
+
type: String(row.type),
|
|
3410
|
+
firstSeen: String(row.first_seen),
|
|
3411
|
+
lastSeen: String(row.last_seen),
|
|
3412
|
+
properties: JSON.parse(String(row.properties ?? "{}"))
|
|
3413
|
+
}));
|
|
3414
|
+
}
|
|
3415
|
+
async function getRelationships(client, entityId, options) {
|
|
3416
|
+
const direction = options?.direction ?? "both";
|
|
3417
|
+
let sql;
|
|
3418
|
+
const args = [];
|
|
3419
|
+
if (direction === "outgoing") {
|
|
3420
|
+
sql = `SELECT r.*, s.name as source_name, t.name as target_name
|
|
3421
|
+
FROM relationships r
|
|
3422
|
+
JOIN entities s ON r.source_entity_id = s.id
|
|
3423
|
+
JOIN entities t ON r.target_entity_id = t.id
|
|
3424
|
+
WHERE r.source_entity_id = ?`;
|
|
3425
|
+
args.push(entityId);
|
|
3426
|
+
} else if (direction === "incoming") {
|
|
3427
|
+
sql = `SELECT r.*, s.name as source_name, t.name as target_name
|
|
3428
|
+
FROM relationships r
|
|
3429
|
+
JOIN entities s ON r.source_entity_id = s.id
|
|
3430
|
+
JOIN entities t ON r.target_entity_id = t.id
|
|
3431
|
+
WHERE r.target_entity_id = ?`;
|
|
3432
|
+
args.push(entityId);
|
|
3433
|
+
} else {
|
|
3434
|
+
sql = `SELECT r.*, s.name as source_name, t.name as target_name
|
|
3435
|
+
FROM relationships r
|
|
3436
|
+
JOIN entities s ON r.source_entity_id = s.id
|
|
3437
|
+
JOIN entities t ON r.target_entity_id = t.id
|
|
3438
|
+
WHERE r.source_entity_id = ? OR r.target_entity_id = ?`;
|
|
3439
|
+
args.push(entityId, entityId);
|
|
3440
|
+
}
|
|
3441
|
+
if (options?.type) {
|
|
3442
|
+
sql += ` AND r.type = ?`;
|
|
3443
|
+
args.push(options.type);
|
|
3444
|
+
}
|
|
3445
|
+
sql += ` ORDER BY r.weight DESC, r.timestamp DESC`;
|
|
3446
|
+
const result = await client.execute({ sql, args });
|
|
3447
|
+
return result.rows.map((row) => ({
|
|
3448
|
+
id: String(row.id),
|
|
3449
|
+
sourceEntityId: String(row.source_entity_id),
|
|
3450
|
+
targetEntityId: String(row.target_entity_id),
|
|
3451
|
+
type: String(row.type),
|
|
3452
|
+
weight: Number(row.weight),
|
|
3453
|
+
timestamp: String(row.timestamp),
|
|
3454
|
+
properties: JSON.parse(String(row.properties ?? "{}")),
|
|
3455
|
+
confidence: Number(row.confidence ?? 1),
|
|
3456
|
+
confidenceLabel: String(row.confidence_label ?? "extracted"),
|
|
3457
|
+
sourceName: String(row.source_name),
|
|
3458
|
+
targetName: String(row.target_name)
|
|
3459
|
+
}));
|
|
3460
|
+
}
|
|
3461
|
+
async function traverseChain(client, startEntityId, relationshipType, maxDepth = 3) {
|
|
3462
|
+
const result = await client.execute({
|
|
3463
|
+
sql: `WITH RECURSIVE chain(entity_id, depth, rel_type) AS (
|
|
3464
|
+
SELECT ?, 0, ''
|
|
3465
|
+
UNION ALL
|
|
3466
|
+
SELECT r.target_entity_id, c.depth + 1, r.type
|
|
3467
|
+
FROM chain c
|
|
3468
|
+
JOIN relationships r ON r.source_entity_id = c.entity_id
|
|
3469
|
+
WHERE r.type = ? AND c.depth < ?
|
|
3470
|
+
)
|
|
3471
|
+
SELECT DISTINCT e.*, chain.depth, chain.rel_type
|
|
3472
|
+
FROM chain
|
|
3473
|
+
JOIN entities e ON e.id = chain.entity_id
|
|
3474
|
+
ORDER BY chain.depth ASC`,
|
|
3475
|
+
args: [startEntityId, relationshipType, maxDepth]
|
|
3476
|
+
});
|
|
3477
|
+
return result.rows.map((row) => ({
|
|
3478
|
+
entity: {
|
|
3479
|
+
id: String(row.id),
|
|
3480
|
+
name: String(row.name),
|
|
3481
|
+
type: String(row.type),
|
|
3482
|
+
firstSeen: String(row.first_seen),
|
|
3483
|
+
lastSeen: String(row.last_seen),
|
|
3484
|
+
properties: JSON.parse(String(row.properties ?? "{}"))
|
|
3485
|
+
},
|
|
3486
|
+
depth: Number(row.depth),
|
|
3487
|
+
relationship: String(row.rel_type)
|
|
3488
|
+
}));
|
|
3489
|
+
}
|
|
3490
|
+
async function getEntityNeighbors(client, entityId, maxHops = 2) {
|
|
3491
|
+
const result = await client.execute({
|
|
3492
|
+
sql: `WITH RECURSIVE neighborhood(entity_id, depth, rel_type, rel_confidence) AS (
|
|
3493
|
+
SELECT ?, 0, '', 1.0
|
|
3494
|
+
UNION ALL
|
|
3495
|
+
SELECT CASE
|
|
3496
|
+
WHEN r.source_entity_id = n.entity_id THEN r.target_entity_id
|
|
3497
|
+
ELSE r.source_entity_id
|
|
3498
|
+
END, n.depth + 1, r.type, COALESCE(r.confidence, 1.0)
|
|
3499
|
+
FROM neighborhood n
|
|
3500
|
+
JOIN relationships r ON r.source_entity_id = n.entity_id
|
|
3501
|
+
OR r.target_entity_id = n.entity_id
|
|
3502
|
+
WHERE n.depth < ?
|
|
3503
|
+
)
|
|
3504
|
+
SELECT DISTINCT e.*, MIN(neighborhood.depth) as depth,
|
|
3505
|
+
neighborhood.rel_type, neighborhood.rel_confidence
|
|
3506
|
+
FROM neighborhood
|
|
3507
|
+
JOIN entities e ON e.id = neighborhood.entity_id
|
|
3508
|
+
GROUP BY e.id
|
|
3509
|
+
ORDER BY depth ASC, e.last_seen DESC
|
|
3510
|
+
LIMIT 50`,
|
|
3511
|
+
args: [entityId, maxHops]
|
|
3512
|
+
});
|
|
3513
|
+
return result.rows.map((row) => ({
|
|
3514
|
+
entity: {
|
|
3515
|
+
id: String(row.id),
|
|
3516
|
+
name: String(row.name),
|
|
3517
|
+
type: String(row.type),
|
|
3518
|
+
firstSeen: String(row.first_seen),
|
|
3519
|
+
lastSeen: String(row.last_seen),
|
|
3520
|
+
properties: JSON.parse(String(row.properties ?? "{}"))
|
|
3521
|
+
},
|
|
3522
|
+
depth: Number(row.depth),
|
|
3523
|
+
relType: String(row.rel_type ?? ""),
|
|
3524
|
+
relConfidence: Number(row.rel_confidence ?? 1)
|
|
3525
|
+
}));
|
|
3526
|
+
}
|
|
3527
|
+
async function getGraphStats(client) {
|
|
3528
|
+
const entityCount = await client.execute("SELECT COUNT(*) as cnt FROM entities");
|
|
3529
|
+
const relCount = await client.execute("SELECT COUNT(*) as cnt FROM relationships");
|
|
3530
|
+
const typeResult = await client.execute(
|
|
3531
|
+
"SELECT type, COUNT(*) as cnt FROM entities GROUP BY type ORDER BY cnt DESC"
|
|
3532
|
+
);
|
|
3533
|
+
const types = {};
|
|
3534
|
+
for (const row of typeResult.rows) {
|
|
3535
|
+
types[String(row.type)] = Number(row.cnt);
|
|
3116
3536
|
}
|
|
3537
|
+
return {
|
|
3538
|
+
entities: Number(entityCount.rows[0]?.cnt ?? 0),
|
|
3539
|
+
relationships: Number(relCount.rows[0]?.cnt ?? 0),
|
|
3540
|
+
types
|
|
3541
|
+
};
|
|
3117
3542
|
}
|
|
3118
|
-
function
|
|
3119
|
-
|
|
3543
|
+
async function getEntityTimeline(client, entityId, since) {
|
|
3544
|
+
const args = [entityId, entityId];
|
|
3545
|
+
let sinceClause = "";
|
|
3546
|
+
if (since) {
|
|
3547
|
+
sinceClause = " AND r.timestamp >= ?";
|
|
3548
|
+
args.push(since.toISOString());
|
|
3549
|
+
}
|
|
3550
|
+
const result = await client.execute({
|
|
3551
|
+
sql: `SELECT r.*, s.name as source_name, t.name as target_name
|
|
3552
|
+
FROM relationships r
|
|
3553
|
+
JOIN entities s ON r.source_entity_id = s.id
|
|
3554
|
+
JOIN entities t ON r.target_entity_id = t.id
|
|
3555
|
+
WHERE (r.source_entity_id = ? OR r.target_entity_id = ?)${sinceClause}
|
|
3556
|
+
ORDER BY r.timestamp DESC`,
|
|
3557
|
+
args
|
|
3558
|
+
});
|
|
3559
|
+
return result.rows.map((row) => ({
|
|
3560
|
+
relationship: {
|
|
3561
|
+
id: String(row.id),
|
|
3562
|
+
sourceEntityId: String(row.source_entity_id),
|
|
3563
|
+
targetEntityId: String(row.target_entity_id),
|
|
3564
|
+
type: String(row.type),
|
|
3565
|
+
weight: Number(row.weight),
|
|
3566
|
+
timestamp: String(row.timestamp),
|
|
3567
|
+
properties: JSON.parse(String(row.properties ?? "{}")),
|
|
3568
|
+
sourceName: String(row.source_name),
|
|
3569
|
+
targetName: String(row.target_name)
|
|
3570
|
+
},
|
|
3571
|
+
timestamp: String(row.timestamp)
|
|
3572
|
+
}));
|
|
3120
3573
|
}
|
|
3121
|
-
function
|
|
3122
|
-
|
|
3574
|
+
async function getRelationshipFrequency(client, entityId, options) {
|
|
3575
|
+
const granularity = options?.granularity ?? "day";
|
|
3576
|
+
const args = [entityId, entityId];
|
|
3577
|
+
let typeClause = "";
|
|
3578
|
+
if (options?.type) {
|
|
3579
|
+
typeClause = " AND r.type = ?";
|
|
3580
|
+
args.push(options.type);
|
|
3581
|
+
}
|
|
3582
|
+
let bucketExpr;
|
|
3583
|
+
switch (granularity) {
|
|
3584
|
+
case "week":
|
|
3585
|
+
bucketExpr = `strftime('%Y', r.timestamp) || '-W' || strftime('%W', r.timestamp)`;
|
|
3586
|
+
break;
|
|
3587
|
+
case "month":
|
|
3588
|
+
bucketExpr = `strftime('%Y-%m', r.timestamp)`;
|
|
3589
|
+
break;
|
|
3590
|
+
default:
|
|
3591
|
+
bucketExpr = `strftime('%Y-%m-%d', r.timestamp)`;
|
|
3592
|
+
}
|
|
3593
|
+
const result = await client.execute({
|
|
3594
|
+
sql: `SELECT ${bucketExpr} as bucket, COUNT(*) as cnt, r.type as rel_type
|
|
3595
|
+
FROM relationships r
|
|
3596
|
+
WHERE (r.source_entity_id = ? OR r.target_entity_id = ?)${typeClause}
|
|
3597
|
+
GROUP BY bucket${options?.type ? "" : ", r.type"}
|
|
3598
|
+
ORDER BY bucket DESC`,
|
|
3599
|
+
args
|
|
3600
|
+
});
|
|
3601
|
+
return result.rows.map((row) => ({
|
|
3602
|
+
bucket: String(row.bucket),
|
|
3603
|
+
count: Number(row.cnt),
|
|
3604
|
+
relationshipType: options?.type ? options.type : String(row.rel_type)
|
|
3605
|
+
}));
|
|
3123
3606
|
}
|
|
3124
|
-
async function
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3607
|
+
async function getConversationPartners(client, contactEntityId, since) {
|
|
3608
|
+
const args = [contactEntityId, contactEntityId];
|
|
3609
|
+
let sinceClause = "";
|
|
3610
|
+
if (since) {
|
|
3611
|
+
sinceClause = " AND r.timestamp >= ?";
|
|
3612
|
+
args.push(since.toISOString());
|
|
3128
3613
|
}
|
|
3129
|
-
const
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3614
|
+
const result = await client.execute({
|
|
3615
|
+
sql: `SELECT
|
|
3616
|
+
CASE
|
|
3617
|
+
WHEN r.source_entity_id = ? THEN r.target_entity_id
|
|
3618
|
+
ELSE r.source_entity_id
|
|
3619
|
+
END as partner_id,
|
|
3620
|
+
COUNT(*) as interaction_count,
|
|
3621
|
+
MAX(r.timestamp) as last_interaction
|
|
3622
|
+
FROM relationships r
|
|
3623
|
+
WHERE (r.source_entity_id = ? OR r.target_entity_id = ?)${sinceClause}
|
|
3624
|
+
GROUP BY partner_id
|
|
3625
|
+
ORDER BY interaction_count DESC`,
|
|
3626
|
+
args: [contactEntityId, ...args]
|
|
3627
|
+
});
|
|
3628
|
+
const partners = [];
|
|
3629
|
+
for (const row of result.rows) {
|
|
3630
|
+
const partnerId = String(row.partner_id);
|
|
3631
|
+
const entityResult = await client.execute({
|
|
3632
|
+
sql: "SELECT * FROM entities WHERE id = ?",
|
|
3633
|
+
args: [partnerId]
|
|
3634
|
+
});
|
|
3635
|
+
if (entityResult.rows.length === 0) continue;
|
|
3636
|
+
const e = entityResult.rows[0];
|
|
3637
|
+
partners.push({
|
|
3638
|
+
agentEntity: {
|
|
3639
|
+
id: String(e.id),
|
|
3640
|
+
name: String(e.name),
|
|
3641
|
+
type: String(e.type),
|
|
3642
|
+
firstSeen: String(e.first_seen),
|
|
3643
|
+
lastSeen: String(e.last_seen),
|
|
3644
|
+
properties: JSON.parse(String(e.properties ?? "{}"))
|
|
3645
|
+
},
|
|
3646
|
+
interactionCount: Number(row.interaction_count),
|
|
3647
|
+
lastInteraction: String(row.last_interaction)
|
|
3648
|
+
});
|
|
3134
3649
|
}
|
|
3135
|
-
|
|
3136
|
-
const { getLlama } = await import("node-llama-cpp");
|
|
3137
|
-
const llama = await getLlama();
|
|
3138
|
-
_rerankerModel = await llama.loadModel({ modelPath });
|
|
3139
|
-
_rerankerContext = await _rerankerModel.createEmbeddingContext();
|
|
3140
|
-
process.stderr.write("[reranker] Jina Reranker v3 loaded.\n");
|
|
3141
|
-
resetIdleTimer();
|
|
3650
|
+
return partners;
|
|
3142
3651
|
}
|
|
3143
|
-
async function
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3652
|
+
async function getHotEntities(client, since, limit = 10) {
|
|
3653
|
+
const sinceISO = since.toISOString();
|
|
3654
|
+
const result = await client.execute({
|
|
3655
|
+
sql: `SELECT entity_id, COUNT(*) as rel_count
|
|
3656
|
+
FROM (
|
|
3657
|
+
SELECT source_entity_id as entity_id FROM relationships WHERE timestamp >= ?
|
|
3658
|
+
UNION ALL
|
|
3659
|
+
SELECT target_entity_id as entity_id FROM relationships WHERE timestamp >= ?
|
|
3660
|
+
)
|
|
3661
|
+
GROUP BY entity_id
|
|
3662
|
+
ORDER BY rel_count DESC
|
|
3663
|
+
LIMIT ?`,
|
|
3664
|
+
args: [sinceISO, sinceISO, limit]
|
|
3665
|
+
});
|
|
3666
|
+
const hotEntities = [];
|
|
3667
|
+
for (const row of result.rows) {
|
|
3668
|
+
const eid = String(row.entity_id);
|
|
3669
|
+
const entityResult = await client.execute({
|
|
3670
|
+
sql: "SELECT * FROM entities WHERE id = ?",
|
|
3671
|
+
args: [eid]
|
|
3672
|
+
});
|
|
3673
|
+
if (entityResult.rows.length === 0) continue;
|
|
3674
|
+
const e = entityResult.rows[0];
|
|
3675
|
+
hotEntities.push({
|
|
3676
|
+
entity: {
|
|
3677
|
+
id: String(e.id),
|
|
3678
|
+
name: String(e.name),
|
|
3679
|
+
type: String(e.type),
|
|
3680
|
+
firstSeen: String(e.first_seen),
|
|
3681
|
+
lastSeen: String(e.last_seen),
|
|
3682
|
+
properties: JSON.parse(String(e.properties ?? "{}"))
|
|
3683
|
+
},
|
|
3684
|
+
newRelationships: Number(row.rel_count)
|
|
3685
|
+
});
|
|
3147
3686
|
}
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3687
|
+
return hotEntities;
|
|
3688
|
+
}
|
|
3689
|
+
var init_graph_query = __esm({
|
|
3690
|
+
"src/lib/graph-query.ts"() {
|
|
3691
|
+
"use strict";
|
|
3692
|
+
}
|
|
3693
|
+
});
|
|
3694
|
+
|
|
3695
|
+
// src/lib/entity-boost.ts
|
|
3696
|
+
var entity_boost_exports = {};
|
|
3697
|
+
__export(entity_boost_exports, {
|
|
3698
|
+
applyEntityBoost: () => applyEntityBoost
|
|
3699
|
+
});
|
|
3700
|
+
function getRelTypeWeights() {
|
|
3701
|
+
const override = process.env.RELATIONSHIP_TYPE_WEIGHTS;
|
|
3702
|
+
if (!override) return DEFAULT_RELATIONSHIP_WEIGHTS;
|
|
3703
|
+
try {
|
|
3704
|
+
return { ...DEFAULT_RELATIONSHIP_WEIGHTS, ...JSON.parse(override) };
|
|
3705
|
+
} catch {
|
|
3706
|
+
return DEFAULT_RELATIONSHIP_WEIGHTS;
|
|
3707
|
+
}
|
|
3708
|
+
}
|
|
3709
|
+
async function matchEntities(query, client) {
|
|
3710
|
+
const words = query.toLowerCase().split(/\s+/).filter((w) => w.length >= MIN_WORD_LENGTH).map((w) => w.replace(/[^a-z0-9_-]/g, "")).filter((w) => w.length >= MIN_WORD_LENGTH);
|
|
3711
|
+
if (words.length === 0) return [];
|
|
3712
|
+
try {
|
|
3713
|
+
const matchExpr = words.map((w) => `${w}*`).join(" OR ");
|
|
3714
|
+
const result = await client.execute({
|
|
3715
|
+
sql: `SELECT e.id, e.name FROM entities e
|
|
3716
|
+
JOIN entities_fts fts ON e.rowid = fts.rowid
|
|
3717
|
+
WHERE entities_fts MATCH ?
|
|
3718
|
+
ORDER BY rank
|
|
3719
|
+
LIMIT ?`,
|
|
3720
|
+
args: [matchExpr, MAX_ENTITY_MATCHES]
|
|
3721
|
+
});
|
|
3722
|
+
if (result.rows.length > 0) {
|
|
3723
|
+
return result.rows.map((row) => ({
|
|
3724
|
+
entityId: String(row.id),
|
|
3725
|
+
name: String(row.name)
|
|
3726
|
+
}));
|
|
3152
3727
|
}
|
|
3153
|
-
|
|
3728
|
+
} catch {
|
|
3154
3729
|
}
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3730
|
+
const conditions = words.map(() => `LOWER(name) LIKE ?`);
|
|
3731
|
+
const args = words.map((w) => `%${w}%`);
|
|
3732
|
+
try {
|
|
3733
|
+
const result = await client.execute({
|
|
3734
|
+
sql: `SELECT id, name FROM entities
|
|
3735
|
+
WHERE ${conditions.join(" OR ")}
|
|
3736
|
+
LIMIT ?`,
|
|
3737
|
+
args: [...args, MAX_ENTITY_MATCHES]
|
|
3738
|
+
});
|
|
3739
|
+
return result.rows.map((row) => ({
|
|
3740
|
+
entityId: String(row.id),
|
|
3741
|
+
name: String(row.name)
|
|
3742
|
+
}));
|
|
3743
|
+
} catch {
|
|
3744
|
+
return [];
|
|
3745
|
+
}
|
|
3746
|
+
}
|
|
3747
|
+
async function getLinkedMemories(entityIds, client) {
|
|
3748
|
+
const linked = /* @__PURE__ */ new Map();
|
|
3749
|
+
if (entityIds.length === 0) return linked;
|
|
3750
|
+
const placeholders = entityIds.map(() => "?").join(",");
|
|
3751
|
+
try {
|
|
3752
|
+
const result = await client.execute({
|
|
3753
|
+
sql: `SELECT entity_id, memory_id FROM entity_memories
|
|
3754
|
+
WHERE entity_id IN (${placeholders})`,
|
|
3755
|
+
args: entityIds
|
|
3756
|
+
});
|
|
3757
|
+
for (const row of result.rows) {
|
|
3758
|
+
const entityId = String(row.entity_id);
|
|
3759
|
+
const memoryId = String(row.memory_id);
|
|
3760
|
+
const entry = linked.get(entityId) ?? {
|
|
3761
|
+
entityId,
|
|
3762
|
+
memoryIds: /* @__PURE__ */ new Set(),
|
|
3763
|
+
count: 0
|
|
3764
|
+
};
|
|
3765
|
+
entry.memoryIds.add(memoryId);
|
|
3766
|
+
entry.count = entry.memoryIds.size;
|
|
3767
|
+
linked.set(entityId, entry);
|
|
3159
3768
|
}
|
|
3160
|
-
|
|
3769
|
+
} catch {
|
|
3161
3770
|
}
|
|
3162
|
-
|
|
3771
|
+
return linked;
|
|
3163
3772
|
}
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
const
|
|
3169
|
-
|
|
3170
|
-
|
|
3773
|
+
function spreadAttenuation(numLinked) {
|
|
3774
|
+
return 1 / (1 + 1e-3 * (numLinked - 1) ** 2);
|
|
3775
|
+
}
|
|
3776
|
+
async function traverseAndScore(entities, client, boostMap, resultIds, graphContextMap) {
|
|
3777
|
+
const topEntities = entities.slice(0, TOP_ENTITIES_TO_TRAVERSE);
|
|
3778
|
+
if (topEntities.length === 0) return;
|
|
3779
|
+
const { getEntityNeighbors: getEntityNeighbors2 } = await Promise.resolve().then(() => (init_graph_query(), graph_query_exports));
|
|
3780
|
+
const relWeights = getRelTypeWeights();
|
|
3781
|
+
const neighborEntityIds = [];
|
|
3782
|
+
const neighborMeta = /* @__PURE__ */ new Map();
|
|
3783
|
+
for (const entity of topEntities) {
|
|
3171
3784
|
try {
|
|
3172
|
-
const
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
|
|
3785
|
+
const neighbors = await getEntityNeighbors2(
|
|
3786
|
+
client,
|
|
3787
|
+
entity.entityId,
|
|
3788
|
+
GRAPH_TRAVERSAL_MAX_HOPS
|
|
3789
|
+
);
|
|
3790
|
+
for (const n of neighbors) {
|
|
3791
|
+
if (n.depth === 0) continue;
|
|
3792
|
+
if (neighborMeta.has(n.entity.id)) continue;
|
|
3793
|
+
neighborEntityIds.push(n.entity.id);
|
|
3794
|
+
neighborMeta.set(n.entity.id, {
|
|
3795
|
+
depth: n.depth,
|
|
3796
|
+
relType: n.relType,
|
|
3797
|
+
relConfidence: n.relConfidence,
|
|
3798
|
+
sourceName: entity.name
|
|
3799
|
+
});
|
|
3800
|
+
}
|
|
3176
3801
|
} catch {
|
|
3177
|
-
scored.push({ text, score: -1, index: i });
|
|
3178
3802
|
}
|
|
3179
3803
|
}
|
|
3180
|
-
|
|
3181
|
-
|
|
3804
|
+
if (neighborEntityIds.length === 0) return;
|
|
3805
|
+
const neighborLinked = await getLinkedMemories(neighborEntityIds, client);
|
|
3806
|
+
for (const [entityId, entry] of neighborLinked) {
|
|
3807
|
+
const meta = neighborMeta.get(entityId);
|
|
3808
|
+
if (!meta) continue;
|
|
3809
|
+
const relWeight = relWeights[meta.relType] ?? DEFAULT_REL_WEIGHT;
|
|
3810
|
+
const depthDecay = 1 / (1 + meta.depth);
|
|
3811
|
+
const neighborBoost = ENTITY_BOOST_WEIGHT * meta.relConfidence * depthDecay * relWeight;
|
|
3812
|
+
const attenuation = spreadAttenuation(entry.count);
|
|
3813
|
+
const finalBoost = neighborBoost * attenuation;
|
|
3814
|
+
for (const memoryId of entry.memoryIds) {
|
|
3815
|
+
if (resultIds.has(memoryId)) {
|
|
3816
|
+
boostMap.set(memoryId, (boostMap.get(memoryId) ?? 0) + finalBoost);
|
|
3817
|
+
const contextPath = `${meta.sourceName} ${meta.relType} ${entityId}`;
|
|
3818
|
+
if (!graphContextMap.has(memoryId)) {
|
|
3819
|
+
graphContextMap.set(memoryId, contextPath);
|
|
3820
|
+
}
|
|
3821
|
+
}
|
|
3822
|
+
}
|
|
3823
|
+
}
|
|
3182
3824
|
}
|
|
3183
|
-
async function
|
|
3184
|
-
if (
|
|
3185
|
-
|
|
3186
|
-
const
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3825
|
+
async function applyHyperedgeBoost(entities, client, boostMap, resultIds) {
|
|
3826
|
+
if (entities.length < 2) return;
|
|
3827
|
+
const entityIds = entities.map((e) => e.entityId);
|
|
3828
|
+
const placeholders = entityIds.map(() => "?").join(",");
|
|
3829
|
+
try {
|
|
3830
|
+
const result = await client.execute({
|
|
3831
|
+
sql: `SELECT hyperedge_id, entity_id FROM hyperedge_nodes
|
|
3832
|
+
WHERE entity_id IN (${placeholders})`,
|
|
3833
|
+
args: entityIds
|
|
3834
|
+
});
|
|
3835
|
+
const hyperedgeEntities = /* @__PURE__ */ new Map();
|
|
3836
|
+
for (const row of result.rows) {
|
|
3837
|
+
const hid = String(row.hyperedge_id);
|
|
3838
|
+
const eid = String(row.entity_id);
|
|
3839
|
+
const set = hyperedgeEntities.get(hid) ?? /* @__PURE__ */ new Set();
|
|
3840
|
+
set.add(eid);
|
|
3841
|
+
hyperedgeEntities.set(hid, set);
|
|
3842
|
+
}
|
|
3843
|
+
const sharedHyperedgeIds = [];
|
|
3844
|
+
for (const [hid, eids] of hyperedgeEntities) {
|
|
3845
|
+
if (eids.size >= 2) {
|
|
3846
|
+
sharedHyperedgeIds.push(hid);
|
|
3847
|
+
}
|
|
3848
|
+
}
|
|
3849
|
+
if (sharedHyperedgeIds.length === 0) return;
|
|
3850
|
+
const hPlaceholders = sharedHyperedgeIds.map(() => "?").join(",");
|
|
3851
|
+
const allNodesResult = await client.execute({
|
|
3852
|
+
sql: `SELECT DISTINCT entity_id FROM hyperedge_nodes
|
|
3853
|
+
WHERE hyperedge_id IN (${hPlaceholders})`,
|
|
3854
|
+
args: sharedHyperedgeIds
|
|
3855
|
+
});
|
|
3856
|
+
const allEntityIds = allNodesResult.rows.map((r) => String(r.entity_id));
|
|
3857
|
+
if (allEntityIds.length === 0) return;
|
|
3858
|
+
const linked = await getLinkedMemories(allEntityIds, client);
|
|
3859
|
+
for (const [, entry] of linked) {
|
|
3860
|
+
const attenuation = spreadAttenuation(entry.count);
|
|
3861
|
+
const boost = ENTITY_BOOST_WEIGHT * attenuation;
|
|
3862
|
+
for (const memoryId of entry.memoryIds) {
|
|
3863
|
+
if (resultIds.has(memoryId)) {
|
|
3864
|
+
boostMap.set(memoryId, (boostMap.get(memoryId) ?? 0) + boost);
|
|
3865
|
+
}
|
|
3866
|
+
}
|
|
3867
|
+
}
|
|
3868
|
+
} catch {
|
|
3869
|
+
}
|
|
3870
|
+
}
|
|
3871
|
+
async function applyEntityBoost(results, query, client) {
|
|
3872
|
+
const emptyResult = { results, graphContext: /* @__PURE__ */ new Map() };
|
|
3873
|
+
if (ENTITY_BOOST_WEIGHT === 0 || results.length === 0) {
|
|
3874
|
+
return emptyResult;
|
|
3875
|
+
}
|
|
3876
|
+
console.time("entity-boost");
|
|
3877
|
+
const entities = await matchEntities(query, client);
|
|
3878
|
+
if (entities.length === 0) {
|
|
3879
|
+
console.timeEnd("entity-boost");
|
|
3880
|
+
return emptyResult;
|
|
3881
|
+
}
|
|
3882
|
+
const boostMap = /* @__PURE__ */ new Map();
|
|
3883
|
+
const resultIds = new Set(results.map((r) => r.id));
|
|
3884
|
+
const graphContextMap = /* @__PURE__ */ new Map();
|
|
3885
|
+
const directLinked = await getLinkedMemories(
|
|
3886
|
+
entities.map((e) => e.entityId),
|
|
3887
|
+
client
|
|
3190
3888
|
);
|
|
3191
|
-
|
|
3889
|
+
for (const [, entry] of directLinked) {
|
|
3890
|
+
const attenuation = spreadAttenuation(entry.count);
|
|
3891
|
+
const boost = ENTITY_BOOST_WEIGHT * attenuation;
|
|
3892
|
+
for (const memoryId of entry.memoryIds) {
|
|
3893
|
+
if (resultIds.has(memoryId)) {
|
|
3894
|
+
boostMap.set(memoryId, (boostMap.get(memoryId) ?? 0) + boost);
|
|
3895
|
+
}
|
|
3896
|
+
}
|
|
3897
|
+
}
|
|
3898
|
+
await traverseAndScore(entities, client, boostMap, resultIds, graphContextMap);
|
|
3899
|
+
await applyHyperedgeBoost(entities, client, boostMap, resultIds);
|
|
3900
|
+
if (boostMap.size === 0) {
|
|
3901
|
+
console.timeEnd("entity-boost");
|
|
3902
|
+
return emptyResult;
|
|
3903
|
+
}
|
|
3904
|
+
const scored = results.map((r, i) => ({
|
|
3905
|
+
record: r,
|
|
3906
|
+
baseScore: 1 / (1 + i * 0.1),
|
|
3907
|
+
entityBoost: boostMap.get(r.id) ?? 0
|
|
3908
|
+
}));
|
|
3909
|
+
scored.sort(
|
|
3910
|
+
(a, b) => b.baseScore + b.entityBoost - (a.baseScore + a.entityBoost)
|
|
3911
|
+
);
|
|
3912
|
+
console.timeEnd("entity-boost");
|
|
3913
|
+
return {
|
|
3914
|
+
results: scored.map((s) => s.record),
|
|
3915
|
+
graphContext: graphContextMap
|
|
3916
|
+
};
|
|
3192
3917
|
}
|
|
3193
|
-
var
|
|
3194
|
-
var
|
|
3195
|
-
"src/lib/
|
|
3918
|
+
var ENTITY_BOOST_WEIGHT, MIN_WORD_LENGTH, MAX_ENTITY_MATCHES, GRAPH_TRAVERSAL_MAX_HOPS, TOP_ENTITIES_TO_TRAVERSE, DEFAULT_RELATIONSHIP_WEIGHTS, DEFAULT_REL_WEIGHT;
|
|
3919
|
+
var init_entity_boost = __esm({
|
|
3920
|
+
"src/lib/entity-boost.ts"() {
|
|
3196
3921
|
"use strict";
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
|
|
3922
|
+
ENTITY_BOOST_WEIGHT = parseFloat(
|
|
3923
|
+
process.env.ENTITY_BOOST_WEIGHT ?? "0.5"
|
|
3924
|
+
);
|
|
3925
|
+
MIN_WORD_LENGTH = 3;
|
|
3926
|
+
MAX_ENTITY_MATCHES = 20;
|
|
3927
|
+
GRAPH_TRAVERSAL_MAX_HOPS = Math.min(
|
|
3928
|
+
parseInt(process.env.GRAPH_TRAVERSAL_MAX_HOPS ?? "2", 10),
|
|
3929
|
+
3
|
|
3930
|
+
);
|
|
3931
|
+
TOP_ENTITIES_TO_TRAVERSE = 5;
|
|
3932
|
+
DEFAULT_RELATIONSHIP_WEIGHTS = {
|
|
3933
|
+
decided: 1,
|
|
3934
|
+
depends_on: 0.8,
|
|
3935
|
+
part_of: 0.7,
|
|
3936
|
+
related_to: 0.5,
|
|
3937
|
+
mentioned_in: 0.3
|
|
3938
|
+
};
|
|
3939
|
+
DEFAULT_REL_WEIGHT = 0.4;
|
|
3203
3940
|
}
|
|
3204
3941
|
});
|
|
3205
3942
|
|
|
@@ -3393,6 +4130,42 @@ var init_provider_table = __esm({
|
|
|
3393
4130
|
}
|
|
3394
4131
|
});
|
|
3395
4132
|
|
|
4133
|
+
// src/lib/runtime-table.ts
|
|
4134
|
+
var RUNTIME_TABLE;
|
|
4135
|
+
var init_runtime_table = __esm({
|
|
4136
|
+
"src/lib/runtime-table.ts"() {
|
|
4137
|
+
"use strict";
|
|
4138
|
+
RUNTIME_TABLE = {
|
|
4139
|
+
codex: {
|
|
4140
|
+
binary: "codex",
|
|
4141
|
+
launchMode: "exec",
|
|
4142
|
+
autoApproveFlag: "--full-auto",
|
|
4143
|
+
inlineFlag: "--no-alt-screen",
|
|
4144
|
+
apiKeyEnv: "OPENAI_API_KEY",
|
|
4145
|
+
defaultModel: "gpt-5.4"
|
|
4146
|
+
}
|
|
4147
|
+
};
|
|
4148
|
+
}
|
|
4149
|
+
});
|
|
4150
|
+
|
|
4151
|
+
// src/lib/agent-config.ts
|
|
4152
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync3, existsSync as existsSync8, mkdirSync as mkdirSync3 } from "fs";
|
|
4153
|
+
import path11 from "path";
|
|
4154
|
+
var AGENT_CONFIG_PATH, DEFAULT_MODELS;
|
|
4155
|
+
var init_agent_config = __esm({
|
|
4156
|
+
"src/lib/agent-config.ts"() {
|
|
4157
|
+
"use strict";
|
|
4158
|
+
init_config();
|
|
4159
|
+
init_runtime_table();
|
|
4160
|
+
AGENT_CONFIG_PATH = path11.join(EXE_AI_DIR, "agent-config.json");
|
|
4161
|
+
DEFAULT_MODELS = {
|
|
4162
|
+
claude: "claude-opus-4",
|
|
4163
|
+
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
4164
|
+
opencode: "minimax-m2.7"
|
|
4165
|
+
};
|
|
4166
|
+
}
|
|
4167
|
+
});
|
|
4168
|
+
|
|
3396
4169
|
// src/lib/intercom-queue.ts
|
|
3397
4170
|
var intercom_queue_exports = {};
|
|
3398
4171
|
__export(intercom_queue_exports, {
|
|
@@ -3401,17 +4174,17 @@ __export(intercom_queue_exports, {
|
|
|
3401
4174
|
queueIntercom: () => queueIntercom,
|
|
3402
4175
|
readQueue: () => readQueue
|
|
3403
4176
|
});
|
|
3404
|
-
import { readFileSync as
|
|
3405
|
-
import
|
|
4177
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync4, renameSync as renameSync3, existsSync as existsSync9, mkdirSync as mkdirSync4 } from "fs";
|
|
4178
|
+
import path12 from "path";
|
|
3406
4179
|
import os5 from "os";
|
|
3407
4180
|
function ensureDir() {
|
|
3408
|
-
const dir =
|
|
3409
|
-
if (!
|
|
4181
|
+
const dir = path12.dirname(QUEUE_PATH);
|
|
4182
|
+
if (!existsSync9(dir)) mkdirSync4(dir, { recursive: true });
|
|
3410
4183
|
}
|
|
3411
4184
|
function readQueue() {
|
|
3412
4185
|
try {
|
|
3413
|
-
if (!
|
|
3414
|
-
return JSON.parse(
|
|
4186
|
+
if (!existsSync9(QUEUE_PATH)) return [];
|
|
4187
|
+
return JSON.parse(readFileSync7(QUEUE_PATH, "utf8"));
|
|
3415
4188
|
} catch {
|
|
3416
4189
|
return [];
|
|
3417
4190
|
}
|
|
@@ -3419,7 +4192,7 @@ function readQueue() {
|
|
|
3419
4192
|
function writeQueue(queue) {
|
|
3420
4193
|
ensureDir();
|
|
3421
4194
|
const tmp = `${QUEUE_PATH}.tmp`;
|
|
3422
|
-
|
|
4195
|
+
writeFileSync4(tmp, JSON.stringify(queue, null, 2));
|
|
3423
4196
|
renameSync3(tmp, QUEUE_PATH);
|
|
3424
4197
|
}
|
|
3425
4198
|
function queueIntercom(targetSession, reason) {
|
|
@@ -3502,32 +4275,32 @@ var QUEUE_PATH, MAX_RETRIES2, TTL_MS, INTERCOM_LOG;
|
|
|
3502
4275
|
var init_intercom_queue = __esm({
|
|
3503
4276
|
"src/lib/intercom-queue.ts"() {
|
|
3504
4277
|
"use strict";
|
|
3505
|
-
QUEUE_PATH =
|
|
4278
|
+
QUEUE_PATH = path12.join(os5.homedir(), ".exe-os", "intercom-queue.json");
|
|
3506
4279
|
MAX_RETRIES2 = 5;
|
|
3507
4280
|
TTL_MS = 60 * 60 * 1e3;
|
|
3508
|
-
INTERCOM_LOG =
|
|
4281
|
+
INTERCOM_LOG = path12.join(os5.homedir(), ".exe-os", "intercom.log");
|
|
3509
4282
|
}
|
|
3510
4283
|
});
|
|
3511
4284
|
|
|
3512
4285
|
// src/lib/license.ts
|
|
3513
|
-
import { readFileSync as
|
|
4286
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync5, existsSync as existsSync10, mkdirSync as mkdirSync5 } from "fs";
|
|
3514
4287
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
3515
|
-
import
|
|
4288
|
+
import path13 from "path";
|
|
3516
4289
|
import { jwtVerify, importSPKI } from "jose";
|
|
3517
4290
|
var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH;
|
|
3518
4291
|
var init_license = __esm({
|
|
3519
4292
|
"src/lib/license.ts"() {
|
|
3520
4293
|
"use strict";
|
|
3521
4294
|
init_config();
|
|
3522
|
-
LICENSE_PATH =
|
|
3523
|
-
CACHE_PATH =
|
|
3524
|
-
DEVICE_ID_PATH =
|
|
4295
|
+
LICENSE_PATH = path13.join(EXE_AI_DIR, "license.key");
|
|
4296
|
+
CACHE_PATH = path13.join(EXE_AI_DIR, "license-cache.json");
|
|
4297
|
+
DEVICE_ID_PATH = path13.join(EXE_AI_DIR, "device-id");
|
|
3525
4298
|
}
|
|
3526
4299
|
});
|
|
3527
4300
|
|
|
3528
4301
|
// src/lib/plan-limits.ts
|
|
3529
|
-
import { readFileSync as
|
|
3530
|
-
import
|
|
4302
|
+
import { readFileSync as readFileSync9, existsSync as existsSync11 } from "fs";
|
|
4303
|
+
import path14 from "path";
|
|
3531
4304
|
var CACHE_PATH2;
|
|
3532
4305
|
var init_plan_limits = __esm({
|
|
3533
4306
|
"src/lib/plan-limits.ts"() {
|
|
@@ -3536,13 +4309,13 @@ var init_plan_limits = __esm({
|
|
|
3536
4309
|
init_employees();
|
|
3537
4310
|
init_license();
|
|
3538
4311
|
init_config();
|
|
3539
|
-
CACHE_PATH2 =
|
|
4312
|
+
CACHE_PATH2 = path14.join(EXE_AI_DIR, "license-cache.json");
|
|
3540
4313
|
}
|
|
3541
4314
|
});
|
|
3542
4315
|
|
|
3543
4316
|
// src/lib/tmux-routing.ts
|
|
3544
|
-
import { readFileSync as
|
|
3545
|
-
import
|
|
4317
|
+
import { readFileSync as readFileSync10, writeFileSync as writeFileSync6, mkdirSync as mkdirSync6, existsSync as existsSync12, appendFileSync } from "fs";
|
|
4318
|
+
import path15 from "path";
|
|
3546
4319
|
import os6 from "os";
|
|
3547
4320
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
3548
4321
|
function getMySession() {
|
|
@@ -3583,7 +4356,7 @@ function extractRootExe(name) {
|
|
|
3583
4356
|
}
|
|
3584
4357
|
function getParentExe(sessionKey) {
|
|
3585
4358
|
try {
|
|
3586
|
-
const data = JSON.parse(
|
|
4359
|
+
const data = JSON.parse(readFileSync10(path15.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
3587
4360
|
return data.parentExe || null;
|
|
3588
4361
|
} catch {
|
|
3589
4362
|
return null;
|
|
@@ -3607,32 +4380,50 @@ function isEmployeeAlive(sessionName) {
|
|
|
3607
4380
|
}
|
|
3608
4381
|
function readDebounceState() {
|
|
3609
4382
|
try {
|
|
3610
|
-
if (!
|
|
3611
|
-
|
|
4383
|
+
if (!existsSync12(DEBOUNCE_FILE)) return {};
|
|
4384
|
+
const raw = JSON.parse(readFileSync10(DEBOUNCE_FILE, "utf8"));
|
|
4385
|
+
const state = {};
|
|
4386
|
+
for (const [key, val] of Object.entries(raw)) {
|
|
4387
|
+
if (typeof val === "number") {
|
|
4388
|
+
state[key] = { lastSent: val, pending: 0 };
|
|
4389
|
+
} else if (val && typeof val === "object" && "lastSent" in val) {
|
|
4390
|
+
state[key] = val;
|
|
4391
|
+
}
|
|
4392
|
+
}
|
|
4393
|
+
return state;
|
|
3612
4394
|
} catch {
|
|
3613
4395
|
return {};
|
|
3614
4396
|
}
|
|
3615
4397
|
}
|
|
3616
4398
|
function writeDebounceState(state) {
|
|
3617
4399
|
try {
|
|
3618
|
-
if (!
|
|
3619
|
-
|
|
4400
|
+
if (!existsSync12(SESSION_CACHE)) mkdirSync6(SESSION_CACHE, { recursive: true });
|
|
4401
|
+
writeFileSync6(DEBOUNCE_FILE, JSON.stringify(state));
|
|
3620
4402
|
} catch {
|
|
3621
4403
|
}
|
|
3622
4404
|
}
|
|
3623
4405
|
function isDebounced(targetSession) {
|
|
3624
4406
|
const state = readDebounceState();
|
|
3625
|
-
const
|
|
3626
|
-
|
|
4407
|
+
const entry = state[targetSession];
|
|
4408
|
+
const lastSent = entry?.lastSent ?? 0;
|
|
4409
|
+
if (Date.now() - lastSent < INTERCOM_DEBOUNCE_MS) {
|
|
4410
|
+
if (!state[targetSession]) state[targetSession] = { lastSent, pending: 0 };
|
|
4411
|
+
state[targetSession].pending++;
|
|
4412
|
+
writeDebounceState(state);
|
|
4413
|
+
return true;
|
|
4414
|
+
}
|
|
4415
|
+
return false;
|
|
3627
4416
|
}
|
|
3628
4417
|
function recordDebounce(targetSession) {
|
|
3629
4418
|
const state = readDebounceState();
|
|
3630
|
-
state[targetSession]
|
|
4419
|
+
const batched = state[targetSession]?.pending ?? 0;
|
|
4420
|
+
state[targetSession] = { lastSent: Date.now(), pending: 0 };
|
|
3631
4421
|
const cutoff = Date.now() - DEBOUNCE_CLEANUP_AGE_MS;
|
|
3632
4422
|
for (const key of Object.keys(state)) {
|
|
3633
|
-
if ((state[key] ?? 0) < cutoff) delete state[key];
|
|
4423
|
+
if ((state[key]?.lastSent ?? 0) < cutoff) delete state[key];
|
|
3634
4424
|
}
|
|
3635
4425
|
writeDebounceState(state);
|
|
4426
|
+
return batched;
|
|
3636
4427
|
}
|
|
3637
4428
|
function logIntercom(msg) {
|
|
3638
4429
|
const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${msg}
|
|
@@ -3673,7 +4464,7 @@ function sendIntercom(targetSession) {
|
|
|
3673
4464
|
return "skipped_exe";
|
|
3674
4465
|
}
|
|
3675
4466
|
if (isDebounced(targetSession)) {
|
|
3676
|
-
logIntercom(`DEBOUNCE \u2192 ${targetSession} (
|
|
4467
|
+
logIntercom(`DEBOUNCE \u2192 ${targetSession} (nudge batched, task safe in DB)`);
|
|
3677
4468
|
return "debounced";
|
|
3678
4469
|
}
|
|
3679
4470
|
try {
|
|
@@ -3685,14 +4476,14 @@ function sendIntercom(targetSession) {
|
|
|
3685
4476
|
const sessionState = getSessionState(targetSession);
|
|
3686
4477
|
if (sessionState === "no_claude") {
|
|
3687
4478
|
queueIntercom(targetSession, "claude not running in session");
|
|
3688
|
-
recordDebounce(targetSession);
|
|
3689
|
-
logIntercom(`QUEUED \u2192 ${targetSession} (no claude process
|
|
4479
|
+
const batched2 = recordDebounce(targetSession);
|
|
4480
|
+
logIntercom(`QUEUED \u2192 ${targetSession} (no claude process)${batched2 > 0 ? ` [${batched2} batched]` : ""}`);
|
|
3690
4481
|
return "queued";
|
|
3691
4482
|
}
|
|
3692
4483
|
if (sessionState === "thinking" || sessionState === "tool") {
|
|
3693
4484
|
queueIntercom(targetSession, "session busy at send time");
|
|
3694
|
-
recordDebounce(targetSession);
|
|
3695
|
-
logIntercom(`QUEUED \u2192 ${targetSession} (session busy
|
|
4485
|
+
const batched2 = recordDebounce(targetSession);
|
|
4486
|
+
logIntercom(`QUEUED \u2192 ${targetSession} (session busy)${batched2 > 0 ? ` [${batched2} batched]` : ""}`);
|
|
3696
4487
|
return "queued";
|
|
3697
4488
|
}
|
|
3698
4489
|
if (transport.isPaneInCopyMode(targetSession)) {
|
|
@@ -3700,8 +4491,8 @@ function sendIntercom(targetSession) {
|
|
|
3700
4491
|
transport.sendKeys(targetSession, "q");
|
|
3701
4492
|
}
|
|
3702
4493
|
transport.sendKeys(targetSession, "/exe-intercom");
|
|
3703
|
-
recordDebounce(targetSession);
|
|
3704
|
-
logIntercom(`DELIVERED \u2192 ${targetSession} (fire-and-forget)`);
|
|
4494
|
+
const batched = recordDebounce(targetSession);
|
|
4495
|
+
logIntercom(`DELIVERED \u2192 ${targetSession}${batched > 0 ? ` [${batched} nudges batched during debounce]` : ""} (fire-and-forget)`);
|
|
3705
4496
|
return "delivered";
|
|
3706
4497
|
} catch {
|
|
3707
4498
|
logIntercom(`FAIL \u2192 ${targetSession}`);
|
|
@@ -3718,15 +4509,17 @@ var init_tmux_routing = __esm({
|
|
|
3718
4509
|
init_cc_agent_support();
|
|
3719
4510
|
init_mcp_prefix();
|
|
3720
4511
|
init_provider_table();
|
|
4512
|
+
init_agent_config();
|
|
4513
|
+
init_runtime_table();
|
|
3721
4514
|
init_intercom_queue();
|
|
3722
4515
|
init_plan_limits();
|
|
3723
4516
|
init_employees();
|
|
3724
|
-
SPAWN_LOCK_DIR =
|
|
3725
|
-
SESSION_CACHE =
|
|
4517
|
+
SPAWN_LOCK_DIR = path15.join(os6.homedir(), ".exe-os", "spawn-locks");
|
|
4518
|
+
SESSION_CACHE = path15.join(os6.homedir(), ".exe-os", "session-cache");
|
|
3726
4519
|
VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
|
|
3727
4520
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
3728
|
-
INTERCOM_LOG2 =
|
|
3729
|
-
DEBOUNCE_FILE =
|
|
4521
|
+
INTERCOM_LOG2 = path15.join(os6.homedir(), ".exe-os", "intercom.log");
|
|
4522
|
+
DEBOUNCE_FILE = path15.join(SESSION_CACHE, "intercom-debounce.json");
|
|
3730
4523
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
3731
4524
|
BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
|
|
3732
4525
|
}
|
|
@@ -3985,13 +4778,13 @@ var init_messaging = __esm({
|
|
|
3985
4778
|
|
|
3986
4779
|
// src/lib/notifications.ts
|
|
3987
4780
|
import crypto3 from "crypto";
|
|
3988
|
-
import
|
|
4781
|
+
import path16 from "path";
|
|
3989
4782
|
import os7 from "os";
|
|
3990
4783
|
import {
|
|
3991
|
-
readFileSync as
|
|
4784
|
+
readFileSync as readFileSync11,
|
|
3992
4785
|
readdirSync as readdirSync4,
|
|
3993
4786
|
unlinkSync as unlinkSync4,
|
|
3994
|
-
existsSync as
|
|
4787
|
+
existsSync as existsSync13,
|
|
3995
4788
|
rmdirSync
|
|
3996
4789
|
} from "fs";
|
|
3997
4790
|
async function writeNotification(notification) {
|
|
@@ -4036,8 +4829,8 @@ __export(tasks_review_exports, {
|
|
|
4036
4829
|
getReviewChecklist: () => getReviewChecklist,
|
|
4037
4830
|
listPendingReviews: () => listPendingReviews
|
|
4038
4831
|
});
|
|
4039
|
-
import
|
|
4040
|
-
import { existsSync as
|
|
4832
|
+
import path17 from "path";
|
|
4833
|
+
import { existsSync as existsSync14, readdirSync as readdirSync5, unlinkSync as unlinkSync5 } from "fs";
|
|
4041
4834
|
async function countPendingReviews(sessionScope) {
|
|
4042
4835
|
const client = getClient();
|
|
4043
4836
|
if (sessionScope) {
|
|
@@ -4059,7 +4852,7 @@ async function countNewPendingReviewsSince(sinceIso, sessionScope) {
|
|
|
4059
4852
|
const result2 = await client.execute({
|
|
4060
4853
|
sql: `SELECT COUNT(*) as cnt FROM tasks
|
|
4061
4854
|
WHERE status = 'needs_review' AND updated_at > ?
|
|
4062
|
-
AND
|
|
4855
|
+
AND session_scope = ?`,
|
|
4063
4856
|
args: [sinceIso, sessionScope]
|
|
4064
4857
|
});
|
|
4065
4858
|
return Number(result2.rows[0]?.cnt) || 0;
|
|
@@ -4077,7 +4870,7 @@ async function listPendingReviews(limit, sessionScope) {
|
|
|
4077
4870
|
const result2 = await client.execute({
|
|
4078
4871
|
sql: `SELECT title, assigned_to, project_name FROM tasks
|
|
4079
4872
|
WHERE status = 'needs_review'
|
|
4080
|
-
AND
|
|
4873
|
+
AND session_scope = ?
|
|
4081
4874
|
ORDER BY priority ASC, created_at DESC LIMIT ?`,
|
|
4082
4875
|
args: [sessionScope, limit]
|
|
4083
4876
|
});
|
|
@@ -4178,7 +4971,7 @@ async function createReviewForCompletedTask(row, result, _baseDir, now) {
|
|
|
4178
4971
|
const taskFile = String(row.task_file);
|
|
4179
4972
|
const employees = await loadEmployees();
|
|
4180
4973
|
const coordinatorName = getCoordinatorName(employees);
|
|
4181
|
-
if (
|
|
4974
|
+
if (isCoordinatorName(String(row.assigned_to), employees)) return;
|
|
4182
4975
|
if (String(row.title).startsWith("Review:")) return;
|
|
4183
4976
|
const fileName = taskFile.split("/").pop() ?? "";
|
|
4184
4977
|
if (fileName.startsWith("review-") && String(row.assigned_by) === "system") return;
|
|
@@ -4287,14 +5080,14 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
4287
5080
|
if (parts.length >= 3 && parts[0] === "review") {
|
|
4288
5081
|
const agent = parts[1];
|
|
4289
5082
|
const slug = parts.slice(2).join("-");
|
|
4290
|
-
const
|
|
5083
|
+
const legacyTaskFile = `exe/${agent}/${slug}.md`;
|
|
4291
5084
|
const result = await client.execute({
|
|
4292
|
-
sql: "UPDATE tasks SET status = 'done', updated_at = ? WHERE task_file = ? AND status = 'needs_review'",
|
|
4293
|
-
args: [now,
|
|
5085
|
+
sql: "UPDATE tasks SET status = 'done', updated_at = ? WHERE (task_file = ? OR task_file LIKE ?) AND status = 'needs_review'",
|
|
5086
|
+
args: [now, legacyTaskFile, `tasks/%/${agent}/${slug}.md`]
|
|
4294
5087
|
});
|
|
4295
5088
|
if (result.rowsAffected > 0) {
|
|
4296
5089
|
process.stderr.write(
|
|
4297
|
-
`[review-cleanup] Cascaded original task to done
|
|
5090
|
+
`[review-cleanup] Cascaded original task to done: ${agent}/${slug}.md
|
|
4298
5091
|
`
|
|
4299
5092
|
);
|
|
4300
5093
|
}
|
|
@@ -4307,11 +5100,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
4307
5100
|
);
|
|
4308
5101
|
}
|
|
4309
5102
|
try {
|
|
4310
|
-
const cacheDir =
|
|
4311
|
-
if (
|
|
5103
|
+
const cacheDir = path17.join(EXE_AI_DIR, "session-cache");
|
|
5104
|
+
if (existsSync14(cacheDir)) {
|
|
4312
5105
|
for (const f of readdirSync5(cacheDir)) {
|
|
4313
5106
|
if (f.startsWith("review-notified-")) {
|
|
4314
|
-
unlinkSync5(
|
|
5107
|
+
unlinkSync5(path17.join(cacheDir, f));
|
|
4315
5108
|
}
|
|
4316
5109
|
}
|
|
4317
5110
|
}
|
|
@@ -4336,8 +5129,8 @@ init_config();
|
|
|
4336
5129
|
init_config();
|
|
4337
5130
|
init_store();
|
|
4338
5131
|
import { spawn as spawn2 } from "child_process";
|
|
4339
|
-
import { readFileSync as
|
|
4340
|
-
import
|
|
5132
|
+
import { readFileSync as readFileSync12, writeFileSync as writeFileSync7, mkdirSync as mkdirSync7, existsSync as existsSync15, openSync as openSync2, closeSync as closeSync2 } from "fs";
|
|
5133
|
+
import path18 from "path";
|
|
4341
5134
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
4342
5135
|
|
|
4343
5136
|
// src/lib/hybrid-search.ts
|
|
@@ -4384,8 +5177,14 @@ async function hybridSearch(queryText, agentId, options) {
|
|
|
4384
5177
|
`
|
|
4385
5178
|
);
|
|
4386
5179
|
}
|
|
5180
|
+
let rerankerAvailable = false;
|
|
5181
|
+
try {
|
|
5182
|
+
const { isRerankerAvailable: isRerankerAvailable2 } = await Promise.resolve().then(() => (init_reranker(), reranker_exports));
|
|
5183
|
+
rerankerAvailable = isRerankerAvailable2();
|
|
5184
|
+
} catch {
|
|
5185
|
+
}
|
|
4387
5186
|
const broadFetchTopK = config.scalingRoadmap?.rerankerAutoTrigger?.fetchTopK ?? 150;
|
|
4388
|
-
const fetchLimit = effectiveIsBroad ? Math.max(limit * 5, broadFetchTopK) : Math.max(limit * 3, 30);
|
|
5187
|
+
const fetchLimit = effectiveIsBroad ? Math.max(limit * 5, broadFetchTopK) : rerankerAvailable ? Math.max(limit * 4, 60) : Math.max(limit * 3, 30);
|
|
4389
5188
|
const fetchOptions = { ...effectiveOptions, limit: fetchLimit, includeSource: false };
|
|
4390
5189
|
let queryVector = null;
|
|
4391
5190
|
try {
|
|
@@ -4395,7 +5194,7 @@ async function hybridSearch(queryText, agentId, options) {
|
|
|
4395
5194
|
process.stderr.write("[hybrid-search] Embed daemon unavailable \u2014 FTS-only mode\n");
|
|
4396
5195
|
}
|
|
4397
5196
|
let grepPromise = Promise.resolve([]);
|
|
4398
|
-
if (config.fileGrepEnabled
|
|
5197
|
+
if (config.fileGrepEnabled === true) {
|
|
4399
5198
|
try {
|
|
4400
5199
|
const { getProjectName: getProjectName2 } = await Promise.resolve().then(() => (init_project_name(), project_name_exports));
|
|
4401
5200
|
const projectRoot = process.cwd();
|
|
@@ -4431,7 +5230,17 @@ async function hybridSearch(queryText, agentId, options) {
|
|
|
4431
5230
|
if (lists.length === 0) return [];
|
|
4432
5231
|
if (lists.length === 1 && !effectiveIsBroad) return lists[0].slice(0, limit);
|
|
4433
5232
|
const rrfLimit = effectiveIsBroad ? Math.max(limit * 5, 150) : limit;
|
|
4434
|
-
|
|
5233
|
+
let merged = lists.length === 1 ? lists[0].slice(0, rrfLimit) : rrfMergeMulti(lists, rrfLimit, RRF_K, weights);
|
|
5234
|
+
let graphContextMap = /* @__PURE__ */ new Map();
|
|
5235
|
+
if (merged.length > 0) {
|
|
5236
|
+
try {
|
|
5237
|
+
const { applyEntityBoost: applyEntityBoost2 } = await Promise.resolve().then(() => (init_entity_boost(), entity_boost_exports));
|
|
5238
|
+
const boosted = await applyEntityBoost2(merged, effectiveQuery, getClient());
|
|
5239
|
+
merged = boosted.results;
|
|
5240
|
+
graphContextMap = boosted.graphContext;
|
|
5241
|
+
} catch {
|
|
5242
|
+
}
|
|
5243
|
+
}
|
|
4435
5244
|
const auto = config.scalingRoadmap?.rerankerAutoTrigger ?? {
|
|
4436
5245
|
enabled: config.rerankerEnabled ?? true,
|
|
4437
5246
|
broadQueryMinCardinality: 5e4,
|
|
@@ -4439,20 +5248,29 @@ async function hybridSearch(queryText, agentId, options) {
|
|
|
4439
5248
|
returnTopK: 5
|
|
4440
5249
|
};
|
|
4441
5250
|
let rerankedAndBlended = null;
|
|
4442
|
-
if (effectiveIsBroad && auto.enabled) {
|
|
5251
|
+
if (effectiveIsBroad && auto.enabled && rerankerAvailable) {
|
|
4443
5252
|
const cardinality2 = await estimateCardinality(agentId, effectiveOptions);
|
|
4444
5253
|
if (cardinality2 > auto.broadQueryMinCardinality) {
|
|
4445
5254
|
try {
|
|
4446
|
-
|
|
4447
|
-
if (
|
|
4448
|
-
const
|
|
4449
|
-
|
|
4450
|
-
|
|
4451
|
-
|
|
4452
|
-
|
|
4453
|
-
|
|
4454
|
-
|
|
4455
|
-
|
|
5255
|
+
let rerankedRecords;
|
|
5256
|
+
if (graphContextMap.size > 0) {
|
|
5257
|
+
const { rerankWithContext: rerankWithContext2 } = await Promise.resolve().then(() => (init_reranker(), reranker_exports));
|
|
5258
|
+
const candidates = merged.map((m) => ({
|
|
5259
|
+
text: m.raw_text,
|
|
5260
|
+
context: graphContextMap.get(m.id)
|
|
5261
|
+
}));
|
|
5262
|
+
const scored = await rerankWithContext2(effectiveQuery, candidates, auto.returnTopK);
|
|
5263
|
+
rerankedRecords = scored.map((s) => merged[s.index]);
|
|
5264
|
+
} else {
|
|
5265
|
+
const { rerank: rerank2 } = await Promise.resolve().then(() => (init_reranker(), reranker_exports));
|
|
5266
|
+
rerankedRecords = await rerank2(effectiveQuery, merged, auto.returnTopK);
|
|
5267
|
+
}
|
|
5268
|
+
if (rerankedRecords.length > 0) {
|
|
5269
|
+
rerankedAndBlended = rrfMergeMulti(
|
|
5270
|
+
[rerankedRecords],
|
|
5271
|
+
auto.returnTopK,
|
|
5272
|
+
RRF_K
|
|
5273
|
+
);
|
|
4456
5274
|
}
|
|
4457
5275
|
} catch {
|
|
4458
5276
|
}
|
|
@@ -4472,7 +5290,7 @@ async function hybridSearch(queryText, agentId, options) {
|
|
|
4472
5290
|
try {
|
|
4473
5291
|
const client = getClient();
|
|
4474
5292
|
void client.execute({
|
|
4475
|
-
sql: `UPDATE memories SET last_accessed =
|
|
5293
|
+
sql: `UPDATE memories SET last_accessed = ?, retrieval_count = COALESCE(retrieval_count, 0) + 1 WHERE id IN (${placeholders})`,
|
|
4476
5294
|
args: [now, ...ids]
|
|
4477
5295
|
}).catch(() => {
|
|
4478
5296
|
});
|
|
@@ -4642,7 +5460,7 @@ async function ftsQuery(client, matchExpr, agentId, options, limit) {
|
|
|
4642
5460
|
source_type: row.source_type ?? null
|
|
4643
5461
|
}));
|
|
4644
5462
|
}
|
|
4645
|
-
async function recentRecords(agentId, options, limit) {
|
|
5463
|
+
async function recentRecords(agentId, options, limit, textFilter) {
|
|
4646
5464
|
const client = getClient();
|
|
4647
5465
|
const statusFilter = options?.includeArchived ? "" : `
|
|
4648
5466
|
AND COALESCE(status, 'active') = 'active'`;
|
|
@@ -4682,6 +5500,10 @@ async function recentRecords(agentId, options, limit) {
|
|
|
4682
5500
|
sql += ` AND memory_type = ?`;
|
|
4683
5501
|
args.push(options.memoryType);
|
|
4684
5502
|
}
|
|
5503
|
+
if (textFilter) {
|
|
5504
|
+
sql += ` AND raw_text LIKE '%' || ? || '%'`;
|
|
5505
|
+
args.push(textFilter);
|
|
5506
|
+
}
|
|
4685
5507
|
sql += ` ORDER BY timestamp DESC LIMIT ?`;
|
|
4686
5508
|
args.push(limit);
|
|
4687
5509
|
const result = await client.execute({ sql, args });
|
|
@@ -4904,7 +5726,7 @@ if (!process.env.AGENT_ID) {
|
|
|
4904
5726
|
if (!loadConfigSync().autoRetrieval) {
|
|
4905
5727
|
process.exit(0);
|
|
4906
5728
|
}
|
|
4907
|
-
var WORKER_LOG_PATH =
|
|
5729
|
+
var WORKER_LOG_PATH = path18.join(EXE_AI_DIR, "workers.log");
|
|
4908
5730
|
function openWorkerLog() {
|
|
4909
5731
|
try {
|
|
4910
5732
|
return openSync2(WORKER_LOG_PATH, "a");
|
|
@@ -4912,10 +5734,10 @@ function openWorkerLog() {
|
|
|
4912
5734
|
return "ignore";
|
|
4913
5735
|
}
|
|
4914
5736
|
}
|
|
4915
|
-
var CACHE_DIR2 =
|
|
5737
|
+
var CACHE_DIR2 = path18.join(EXE_AI_DIR, "session-cache");
|
|
4916
5738
|
function loadInjectedIds(sessionId) {
|
|
4917
5739
|
try {
|
|
4918
|
-
const raw =
|
|
5740
|
+
const raw = readFileSync12(path18.join(CACHE_DIR2, `${sessionId}.json`), "utf8");
|
|
4919
5741
|
return new Set(JSON.parse(raw));
|
|
4920
5742
|
} catch {
|
|
4921
5743
|
return /* @__PURE__ */ new Set();
|
|
@@ -4923,9 +5745,9 @@ function loadInjectedIds(sessionId) {
|
|
|
4923
5745
|
}
|
|
4924
5746
|
function saveInjectedIds(sessionId, ids) {
|
|
4925
5747
|
try {
|
|
4926
|
-
|
|
4927
|
-
|
|
4928
|
-
|
|
5748
|
+
mkdirSync7(CACHE_DIR2, { recursive: true });
|
|
5749
|
+
writeFileSync7(
|
|
5750
|
+
path18.join(CACHE_DIR2, `${sessionId}.json`),
|
|
4929
5751
|
JSON.stringify([...ids])
|
|
4930
5752
|
);
|
|
4931
5753
|
} catch {
|
|
@@ -5008,7 +5830,7 @@ ${fresh.map(
|
|
|
5008
5830
|
try {
|
|
5009
5831
|
const { countPendingReviews: countPendingReviews2, countNewPendingReviewsSince: countNewPendingReviewsSince2 } = await Promise.resolve().then(() => (init_tasks_review(), tasks_review_exports));
|
|
5010
5832
|
const sessionKey = getSessionKey();
|
|
5011
|
-
const lastCheckPath =
|
|
5833
|
+
const lastCheckPath = path18.join(CACHE_DIR2, `review-lastcheck-${sessionKey}.json`);
|
|
5012
5834
|
let sessionScope;
|
|
5013
5835
|
try {
|
|
5014
5836
|
const { execSync: execSync7 } = await import("child_process");
|
|
@@ -5018,7 +5840,7 @@ ${fresh.map(
|
|
|
5018
5840
|
}
|
|
5019
5841
|
let lastCheckedAt = "";
|
|
5020
5842
|
try {
|
|
5021
|
-
lastCheckedAt =
|
|
5843
|
+
lastCheckedAt = readFileSync12(lastCheckPath, "utf8").trim();
|
|
5022
5844
|
} catch {
|
|
5023
5845
|
}
|
|
5024
5846
|
const totalCount = await countPendingReviews2(sessionScope);
|
|
@@ -5076,11 +5898,11 @@ IMPORTANT: After completing your current task, you MUST address the pending revi
|
|
|
5076
5898
|
function spawnPromptWorker(prompt, sessionId, agent) {
|
|
5077
5899
|
if (!loadConfigSync().autoIngestion) return;
|
|
5078
5900
|
try {
|
|
5079
|
-
const workerPath =
|
|
5080
|
-
|
|
5901
|
+
const workerPath = path18.resolve(
|
|
5902
|
+
path18.dirname(fileURLToPath3(import.meta.url)),
|
|
5081
5903
|
"prompt-ingest-worker.js"
|
|
5082
5904
|
);
|
|
5083
|
-
if (!
|
|
5905
|
+
if (!existsSync15(workerPath)) {
|
|
5084
5906
|
process.stderr.write(`[prompt-submit] WARN: prompt-ingest-worker not found at ${workerPath}
|
|
5085
5907
|
`);
|
|
5086
5908
|
return;
|