@askexenow/exe-os 0.8.83 → 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 +14350 -12518
- package/dist/bin/exe-agent.js +97 -88
- package/dist/bin/exe-assign.js +1003 -854
- package/dist/bin/exe-boot.js +1257 -320
- package/dist/bin/exe-call.js +10 -0
- package/dist/bin/exe-cloud.js +29 -6
- package/dist/bin/exe-dispatch.js +210 -34
- 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 +550 -171
- 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 +38 -8
- 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 +564 -23
- 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 +899 -207
- 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 +904 -211
- package/dist/bin/setup.js +867 -268
- 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 +548 -166
- package/dist/hooks/bug-report-worker.js +208 -23
- package/dist/hooks/commit-complete.js +897 -205
- package/dist/hooks/error-recall.js +988 -226
- package/dist/hooks/ingest-worker.js +1638 -1194
- 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 +897 -205
- package/dist/hooks/pre-tool-use.js +742 -123
- package/dist/hooks/prompt-ingest-worker.js +242 -101
- package/dist/hooks/prompt-submit.js +995 -233
- package/dist/hooks/response-ingest-worker.js +242 -101
- 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 +1964 -1330
- package/dist/index.js +1651 -1053
- 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 +1955 -922
- 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/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 +111 -22
- package/dist/lib/tmux-routing.js +120 -31
- package/dist/lib/token-spend.js +273 -0
- package/dist/lib/ws-client.js +11 -0
- package/dist/mcp/server.js +5222 -475
- 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 +120 -22
- 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 +911 -219
- package/dist/tui/App.js +997 -295
- package/package.json +6 -1
|
@@ -191,6 +191,443 @@ var init_employees = __esm({
|
|
|
191
191
|
}
|
|
192
192
|
});
|
|
193
193
|
|
|
194
|
+
// src/lib/exe-daemon-client.ts
|
|
195
|
+
import net from "net";
|
|
196
|
+
import { spawn } from "child_process";
|
|
197
|
+
import { randomUUID } from "crypto";
|
|
198
|
+
import { existsSync as existsSync3, unlinkSync as unlinkSync2, readFileSync as readFileSync3, openSync, closeSync, statSync } from "fs";
|
|
199
|
+
import path3 from "path";
|
|
200
|
+
import { fileURLToPath } from "url";
|
|
201
|
+
function handleData(chunk) {
|
|
202
|
+
_buffer += chunk.toString();
|
|
203
|
+
if (_buffer.length > MAX_BUFFER) {
|
|
204
|
+
_buffer = "";
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
let newlineIdx;
|
|
208
|
+
while ((newlineIdx = _buffer.indexOf("\n")) !== -1) {
|
|
209
|
+
const line = _buffer.slice(0, newlineIdx).trim();
|
|
210
|
+
_buffer = _buffer.slice(newlineIdx + 1);
|
|
211
|
+
if (!line) continue;
|
|
212
|
+
try {
|
|
213
|
+
const response = JSON.parse(line);
|
|
214
|
+
const id = response.id;
|
|
215
|
+
if (!id) continue;
|
|
216
|
+
const entry = _pending.get(id);
|
|
217
|
+
if (entry) {
|
|
218
|
+
clearTimeout(entry.timer);
|
|
219
|
+
_pending.delete(id);
|
|
220
|
+
entry.resolve(response);
|
|
221
|
+
}
|
|
222
|
+
} catch {
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
function cleanupStaleFiles() {
|
|
227
|
+
if (existsSync3(PID_PATH)) {
|
|
228
|
+
try {
|
|
229
|
+
const pid = parseInt(readFileSync3(PID_PATH, "utf8").trim(), 10);
|
|
230
|
+
if (pid > 0) {
|
|
231
|
+
try {
|
|
232
|
+
process.kill(pid, 0);
|
|
233
|
+
return;
|
|
234
|
+
} catch {
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
} catch {
|
|
238
|
+
}
|
|
239
|
+
try {
|
|
240
|
+
unlinkSync2(PID_PATH);
|
|
241
|
+
} catch {
|
|
242
|
+
}
|
|
243
|
+
try {
|
|
244
|
+
unlinkSync2(SOCKET_PATH);
|
|
245
|
+
} catch {
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
function findPackageRoot() {
|
|
250
|
+
let dir = path3.dirname(fileURLToPath(import.meta.url));
|
|
251
|
+
const { root } = path3.parse(dir);
|
|
252
|
+
while (dir !== root) {
|
|
253
|
+
if (existsSync3(path3.join(dir, "package.json"))) return dir;
|
|
254
|
+
dir = path3.dirname(dir);
|
|
255
|
+
}
|
|
256
|
+
return null;
|
|
257
|
+
}
|
|
258
|
+
function spawnDaemon() {
|
|
259
|
+
const pkgRoot = findPackageRoot();
|
|
260
|
+
if (!pkgRoot) {
|
|
261
|
+
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
const daemonPath = path3.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
265
|
+
if (!existsSync3(daemonPath)) {
|
|
266
|
+
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
267
|
+
`);
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
const resolvedPath = daemonPath;
|
|
271
|
+
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
272
|
+
`);
|
|
273
|
+
const logPath = path3.join(path3.dirname(SOCKET_PATH), "exed.log");
|
|
274
|
+
let stderrFd = "ignore";
|
|
275
|
+
try {
|
|
276
|
+
stderrFd = openSync(logPath, "a");
|
|
277
|
+
} catch {
|
|
278
|
+
}
|
|
279
|
+
const child = spawn(process.execPath, [resolvedPath], {
|
|
280
|
+
detached: true,
|
|
281
|
+
stdio: ["ignore", "ignore", stderrFd],
|
|
282
|
+
env: {
|
|
283
|
+
...process.env,
|
|
284
|
+
TMUX: void 0,
|
|
285
|
+
// Daemon is global — must not inherit session scope
|
|
286
|
+
TMUX_PANE: void 0,
|
|
287
|
+
// Prevents resolveExeSession() from scoping to one session
|
|
288
|
+
EXE_DAEMON_SOCK: SOCKET_PATH,
|
|
289
|
+
EXE_DAEMON_PID: PID_PATH
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
child.unref();
|
|
293
|
+
if (typeof stderrFd === "number") {
|
|
294
|
+
try {
|
|
295
|
+
closeSync(stderrFd);
|
|
296
|
+
} catch {
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
function acquireSpawnLock() {
|
|
301
|
+
try {
|
|
302
|
+
const fd = openSync(SPAWN_LOCK_PATH, "wx");
|
|
303
|
+
closeSync(fd);
|
|
304
|
+
return true;
|
|
305
|
+
} catch {
|
|
306
|
+
try {
|
|
307
|
+
const stat = statSync(SPAWN_LOCK_PATH);
|
|
308
|
+
if (Date.now() - stat.mtimeMs > SPAWN_LOCK_STALE_MS) {
|
|
309
|
+
try {
|
|
310
|
+
unlinkSync2(SPAWN_LOCK_PATH);
|
|
311
|
+
} catch {
|
|
312
|
+
}
|
|
313
|
+
try {
|
|
314
|
+
const fd = openSync(SPAWN_LOCK_PATH, "wx");
|
|
315
|
+
closeSync(fd);
|
|
316
|
+
return true;
|
|
317
|
+
} catch {
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
} catch {
|
|
321
|
+
}
|
|
322
|
+
return false;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
function releaseSpawnLock() {
|
|
326
|
+
try {
|
|
327
|
+
unlinkSync2(SPAWN_LOCK_PATH);
|
|
328
|
+
} catch {
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
function connectToSocket() {
|
|
332
|
+
return new Promise((resolve) => {
|
|
333
|
+
if (_socket && _connected) {
|
|
334
|
+
resolve(true);
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
const socket = net.createConnection({ path: SOCKET_PATH });
|
|
338
|
+
const connectTimeout = setTimeout(() => {
|
|
339
|
+
socket.destroy();
|
|
340
|
+
resolve(false);
|
|
341
|
+
}, 2e3);
|
|
342
|
+
socket.on("connect", () => {
|
|
343
|
+
clearTimeout(connectTimeout);
|
|
344
|
+
_socket = socket;
|
|
345
|
+
_connected = true;
|
|
346
|
+
_buffer = "";
|
|
347
|
+
socket.on("data", handleData);
|
|
348
|
+
socket.on("close", () => {
|
|
349
|
+
_connected = false;
|
|
350
|
+
_socket = null;
|
|
351
|
+
for (const [id, entry] of _pending) {
|
|
352
|
+
clearTimeout(entry.timer);
|
|
353
|
+
_pending.delete(id);
|
|
354
|
+
entry.resolve({ error: "Connection closed" });
|
|
355
|
+
}
|
|
356
|
+
});
|
|
357
|
+
socket.on("error", () => {
|
|
358
|
+
_connected = false;
|
|
359
|
+
_socket = null;
|
|
360
|
+
});
|
|
361
|
+
resolve(true);
|
|
362
|
+
});
|
|
363
|
+
socket.on("error", () => {
|
|
364
|
+
clearTimeout(connectTimeout);
|
|
365
|
+
resolve(false);
|
|
366
|
+
});
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
async function connectEmbedDaemon() {
|
|
370
|
+
if (_socket && _connected) return true;
|
|
371
|
+
if (await connectToSocket()) return true;
|
|
372
|
+
if (acquireSpawnLock()) {
|
|
373
|
+
try {
|
|
374
|
+
cleanupStaleFiles();
|
|
375
|
+
spawnDaemon();
|
|
376
|
+
} finally {
|
|
377
|
+
releaseSpawnLock();
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
const start = Date.now();
|
|
381
|
+
let delay2 = 100;
|
|
382
|
+
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
383
|
+
await new Promise((r) => setTimeout(r, delay2));
|
|
384
|
+
if (await connectToSocket()) return true;
|
|
385
|
+
delay2 = Math.min(delay2 * 2, 3e3);
|
|
386
|
+
}
|
|
387
|
+
return false;
|
|
388
|
+
}
|
|
389
|
+
function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
390
|
+
return new Promise((resolve) => {
|
|
391
|
+
if (!_socket || !_connected) {
|
|
392
|
+
resolve({ error: "Not connected" });
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
const id = randomUUID();
|
|
396
|
+
const timer = setTimeout(() => {
|
|
397
|
+
_pending.delete(id);
|
|
398
|
+
resolve({ error: "Request timeout" });
|
|
399
|
+
}, timeoutMs);
|
|
400
|
+
_pending.set(id, { resolve, timer });
|
|
401
|
+
try {
|
|
402
|
+
_socket.write(JSON.stringify({ id, ...payload }) + "\n");
|
|
403
|
+
} catch {
|
|
404
|
+
clearTimeout(timer);
|
|
405
|
+
_pending.delete(id);
|
|
406
|
+
resolve({ error: "Write failed" });
|
|
407
|
+
}
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
function isClientConnected() {
|
|
411
|
+
return _connected;
|
|
412
|
+
}
|
|
413
|
+
var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _pending, MAX_BUFFER;
|
|
414
|
+
var init_exe_daemon_client = __esm({
|
|
415
|
+
"src/lib/exe-daemon-client.ts"() {
|
|
416
|
+
"use strict";
|
|
417
|
+
init_config();
|
|
418
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path3.join(EXE_AI_DIR, "exed.sock");
|
|
419
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path3.join(EXE_AI_DIR, "exed.pid");
|
|
420
|
+
SPAWN_LOCK_PATH = path3.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
421
|
+
SPAWN_LOCK_STALE_MS = 3e4;
|
|
422
|
+
CONNECT_TIMEOUT_MS = 15e3;
|
|
423
|
+
REQUEST_TIMEOUT_MS = 3e4;
|
|
424
|
+
_socket = null;
|
|
425
|
+
_connected = false;
|
|
426
|
+
_buffer = "";
|
|
427
|
+
_pending = /* @__PURE__ */ new Map();
|
|
428
|
+
MAX_BUFFER = 1e7;
|
|
429
|
+
}
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
// src/lib/daemon-protocol.ts
|
|
433
|
+
function serializeValue(v) {
|
|
434
|
+
if (v === null || v === void 0) return null;
|
|
435
|
+
if (typeof v === "bigint") return Number(v);
|
|
436
|
+
if (typeof v === "boolean") return v ? 1 : 0;
|
|
437
|
+
if (v instanceof Uint8Array) {
|
|
438
|
+
return { __blob: Buffer.from(v).toString("base64") };
|
|
439
|
+
}
|
|
440
|
+
if (ArrayBuffer.isView(v)) {
|
|
441
|
+
return { __blob: Buffer.from(v.buffer, v.byteOffset, v.byteLength).toString("base64") };
|
|
442
|
+
}
|
|
443
|
+
if (v instanceof ArrayBuffer) {
|
|
444
|
+
return { __blob: Buffer.from(v).toString("base64") };
|
|
445
|
+
}
|
|
446
|
+
if (typeof v === "string" || typeof v === "number") return v;
|
|
447
|
+
return String(v);
|
|
448
|
+
}
|
|
449
|
+
function deserializeValue(v) {
|
|
450
|
+
if (v === null) return null;
|
|
451
|
+
if (typeof v === "object" && v !== null && "__blob" in v) {
|
|
452
|
+
const buf = Buffer.from(v.__blob, "base64");
|
|
453
|
+
return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
|
|
454
|
+
}
|
|
455
|
+
return v;
|
|
456
|
+
}
|
|
457
|
+
function deserializeResultSet(srs) {
|
|
458
|
+
const rows = srs.rows.map((obj) => {
|
|
459
|
+
const values = srs.columns.map(
|
|
460
|
+
(col) => deserializeValue(obj[col] ?? null)
|
|
461
|
+
);
|
|
462
|
+
const row = values;
|
|
463
|
+
for (let i = 0; i < srs.columns.length; i++) {
|
|
464
|
+
const col = srs.columns[i];
|
|
465
|
+
if (col !== void 0) {
|
|
466
|
+
row[col] = values[i] ?? null;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
Object.defineProperty(row, "length", {
|
|
470
|
+
value: values.length,
|
|
471
|
+
enumerable: false
|
|
472
|
+
});
|
|
473
|
+
return row;
|
|
474
|
+
});
|
|
475
|
+
return {
|
|
476
|
+
columns: srs.columns,
|
|
477
|
+
columnTypes: srs.columnTypes ?? [],
|
|
478
|
+
rows,
|
|
479
|
+
rowsAffected: srs.rowsAffected,
|
|
480
|
+
lastInsertRowid: srs.lastInsertRowid != null ? BigInt(srs.lastInsertRowid) : void 0,
|
|
481
|
+
toJSON: () => ({
|
|
482
|
+
columns: srs.columns,
|
|
483
|
+
columnTypes: srs.columnTypes ?? [],
|
|
484
|
+
rows: srs.rows,
|
|
485
|
+
rowsAffected: srs.rowsAffected,
|
|
486
|
+
lastInsertRowid: srs.lastInsertRowid
|
|
487
|
+
})
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
var init_daemon_protocol = __esm({
|
|
491
|
+
"src/lib/daemon-protocol.ts"() {
|
|
492
|
+
"use strict";
|
|
493
|
+
}
|
|
494
|
+
});
|
|
495
|
+
|
|
496
|
+
// src/lib/db-daemon-client.ts
|
|
497
|
+
var db_daemon_client_exports = {};
|
|
498
|
+
__export(db_daemon_client_exports, {
|
|
499
|
+
createDaemonDbClient: () => createDaemonDbClient,
|
|
500
|
+
initDaemonDbClient: () => initDaemonDbClient
|
|
501
|
+
});
|
|
502
|
+
function normalizeStatement(stmt) {
|
|
503
|
+
if (typeof stmt === "string") {
|
|
504
|
+
return { sql: stmt, args: [] };
|
|
505
|
+
}
|
|
506
|
+
const sql = stmt.sql;
|
|
507
|
+
let args = [];
|
|
508
|
+
if (Array.isArray(stmt.args)) {
|
|
509
|
+
args = stmt.args.map((v) => serializeValue(v));
|
|
510
|
+
} else if (stmt.args && typeof stmt.args === "object") {
|
|
511
|
+
const named = {};
|
|
512
|
+
for (const [key, val] of Object.entries(stmt.args)) {
|
|
513
|
+
named[key] = serializeValue(val);
|
|
514
|
+
}
|
|
515
|
+
return { sql, args: named };
|
|
516
|
+
}
|
|
517
|
+
return { sql, args };
|
|
518
|
+
}
|
|
519
|
+
function createDaemonDbClient(fallbackClient) {
|
|
520
|
+
let _useDaemon = false;
|
|
521
|
+
const client = {
|
|
522
|
+
async execute(stmt) {
|
|
523
|
+
if (!_useDaemon || !isClientConnected()) {
|
|
524
|
+
return fallbackClient.execute(stmt);
|
|
525
|
+
}
|
|
526
|
+
const { sql, args } = normalizeStatement(stmt);
|
|
527
|
+
const response = await sendDaemonRequest({
|
|
528
|
+
type: "db-execute",
|
|
529
|
+
sql,
|
|
530
|
+
args
|
|
531
|
+
});
|
|
532
|
+
if (response.error) {
|
|
533
|
+
const errMsg = String(response.error);
|
|
534
|
+
if (errMsg === "Not connected" || errMsg === "Request timeout" || errMsg === "Write failed") {
|
|
535
|
+
process.stderr.write(`[db-daemon] Transport error (${errMsg}), falling back to direct
|
|
536
|
+
`);
|
|
537
|
+
return fallbackClient.execute(stmt);
|
|
538
|
+
}
|
|
539
|
+
throw new Error(errMsg);
|
|
540
|
+
}
|
|
541
|
+
if (response.db) {
|
|
542
|
+
return deserializeResultSet(response.db);
|
|
543
|
+
}
|
|
544
|
+
process.stderr.write("[db-daemon] Unexpected response shape, falling back to direct\n");
|
|
545
|
+
return fallbackClient.execute(stmt);
|
|
546
|
+
},
|
|
547
|
+
async batch(stmts, mode) {
|
|
548
|
+
if (!_useDaemon || !isClientConnected()) {
|
|
549
|
+
return fallbackClient.batch(stmts, mode);
|
|
550
|
+
}
|
|
551
|
+
const statements = stmts.map(normalizeStatement);
|
|
552
|
+
const response = await sendDaemonRequest({
|
|
553
|
+
type: "db-batch",
|
|
554
|
+
statements,
|
|
555
|
+
mode: mode ?? "deferred"
|
|
556
|
+
});
|
|
557
|
+
if (response.error) {
|
|
558
|
+
const errMsg = String(response.error);
|
|
559
|
+
if (errMsg === "Not connected" || errMsg === "Request timeout" || errMsg === "Write failed") {
|
|
560
|
+
process.stderr.write(`[db-daemon] Batch transport error (${errMsg}), falling back to direct
|
|
561
|
+
`);
|
|
562
|
+
return fallbackClient.batch(stmts, mode);
|
|
563
|
+
}
|
|
564
|
+
throw new Error(errMsg);
|
|
565
|
+
}
|
|
566
|
+
const batchResults = response["db-batch"];
|
|
567
|
+
if (batchResults) {
|
|
568
|
+
return batchResults.map(deserializeResultSet);
|
|
569
|
+
}
|
|
570
|
+
process.stderr.write("[db-daemon] Unexpected batch response shape, falling back to direct\n");
|
|
571
|
+
return fallbackClient.batch(stmts, mode);
|
|
572
|
+
},
|
|
573
|
+
// Transaction support — delegate to fallback (transactions need direct connection)
|
|
574
|
+
async transaction(mode) {
|
|
575
|
+
return fallbackClient.transaction(mode);
|
|
576
|
+
},
|
|
577
|
+
// executeMultiple — delegate to fallback (used only for schema migrations)
|
|
578
|
+
async executeMultiple(sql) {
|
|
579
|
+
return fallbackClient.executeMultiple(sql);
|
|
580
|
+
},
|
|
581
|
+
// migrate — delegate to fallback
|
|
582
|
+
async migrate(stmts) {
|
|
583
|
+
return fallbackClient.migrate(stmts);
|
|
584
|
+
},
|
|
585
|
+
// Sync mode — delegate to fallback
|
|
586
|
+
sync() {
|
|
587
|
+
return fallbackClient.sync();
|
|
588
|
+
},
|
|
589
|
+
close() {
|
|
590
|
+
_useDaemon = false;
|
|
591
|
+
},
|
|
592
|
+
get closed() {
|
|
593
|
+
return fallbackClient.closed;
|
|
594
|
+
},
|
|
595
|
+
get protocol() {
|
|
596
|
+
return fallbackClient.protocol;
|
|
597
|
+
}
|
|
598
|
+
};
|
|
599
|
+
return {
|
|
600
|
+
...client,
|
|
601
|
+
/** Enable daemon routing (call after confirming daemon is connected) */
|
|
602
|
+
_enableDaemon() {
|
|
603
|
+
_useDaemon = true;
|
|
604
|
+
},
|
|
605
|
+
/** Check if daemon routing is active */
|
|
606
|
+
_isDaemonActive() {
|
|
607
|
+
return _useDaemon && isClientConnected();
|
|
608
|
+
}
|
|
609
|
+
};
|
|
610
|
+
}
|
|
611
|
+
async function initDaemonDbClient(fallbackClient) {
|
|
612
|
+
if (process.env.EXE_IS_DAEMON === "1") return null;
|
|
613
|
+
const connected = await connectEmbedDaemon();
|
|
614
|
+
if (!connected) {
|
|
615
|
+
process.stderr.write("[db-daemon] Daemon unavailable \u2014 using direct SQLite\n");
|
|
616
|
+
return null;
|
|
617
|
+
}
|
|
618
|
+
const client = createDaemonDbClient(fallbackClient);
|
|
619
|
+
client._enableDaemon();
|
|
620
|
+
process.stderr.write("[db-daemon] DB routing through daemon (single-writer)\n");
|
|
621
|
+
return client;
|
|
622
|
+
}
|
|
623
|
+
var init_db_daemon_client = __esm({
|
|
624
|
+
"src/lib/db-daemon-client.ts"() {
|
|
625
|
+
"use strict";
|
|
626
|
+
init_exe_daemon_client();
|
|
627
|
+
init_daemon_protocol();
|
|
628
|
+
}
|
|
629
|
+
});
|
|
630
|
+
|
|
194
631
|
// src/lib/database.ts
|
|
195
632
|
var database_exports = {};
|
|
196
633
|
__export(database_exports, {
|
|
@@ -199,6 +636,7 @@ __export(database_exports, {
|
|
|
199
636
|
ensureSchema: () => ensureSchema,
|
|
200
637
|
getClient: () => getClient,
|
|
201
638
|
getRawClient: () => getRawClient,
|
|
639
|
+
initDaemonClient: () => initDaemonClient,
|
|
202
640
|
initDatabase: () => initDatabase,
|
|
203
641
|
initTurso: () => initTurso,
|
|
204
642
|
isInitialized: () => isInitialized
|
|
@@ -226,8 +664,27 @@ function getClient() {
|
|
|
226
664
|
if (!_resilientClient) {
|
|
227
665
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
228
666
|
}
|
|
667
|
+
if (process.env.EXE_IS_DAEMON === "1") {
|
|
668
|
+
return _resilientClient;
|
|
669
|
+
}
|
|
670
|
+
if (_daemonClient && _daemonClient._isDaemonActive()) {
|
|
671
|
+
return _daemonClient;
|
|
672
|
+
}
|
|
229
673
|
return _resilientClient;
|
|
230
674
|
}
|
|
675
|
+
async function initDaemonClient() {
|
|
676
|
+
if (process.env.EXE_IS_DAEMON === "1") return;
|
|
677
|
+
if (!_resilientClient) return;
|
|
678
|
+
try {
|
|
679
|
+
const { initDaemonDbClient: initDaemonDbClient2 } = await Promise.resolve().then(() => (init_db_daemon_client(), db_daemon_client_exports));
|
|
680
|
+
_daemonClient = await initDaemonDbClient2(_resilientClient);
|
|
681
|
+
} catch (err) {
|
|
682
|
+
process.stderr.write(
|
|
683
|
+
`[database] Daemon client init failed (non-fatal): ${err instanceof Error ? err.message : String(err)}
|
|
684
|
+
`
|
|
685
|
+
);
|
|
686
|
+
}
|
|
687
|
+
}
|
|
231
688
|
function getRawClient() {
|
|
232
689
|
if (!_client) {
|
|
233
690
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
@@ -714,6 +1171,12 @@ async function ensureSchema() {
|
|
|
714
1171
|
} catch {
|
|
715
1172
|
}
|
|
716
1173
|
}
|
|
1174
|
+
try {
|
|
1175
|
+
await client.execute(
|
|
1176
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_content_hash ON memories(content_hash, agent_id)`
|
|
1177
|
+
);
|
|
1178
|
+
} catch {
|
|
1179
|
+
}
|
|
717
1180
|
await client.executeMultiple(`
|
|
718
1181
|
CREATE TABLE IF NOT EXISTS entities (
|
|
719
1182
|
id TEXT PRIMARY KEY,
|
|
@@ -766,7 +1229,30 @@ async function ensureSchema() {
|
|
|
766
1229
|
entity_id TEXT NOT NULL,
|
|
767
1230
|
PRIMARY KEY (hyperedge_id, entity_id)
|
|
768
1231
|
);
|
|
1232
|
+
|
|
1233
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS entities_fts USING fts5(
|
|
1234
|
+
name,
|
|
1235
|
+
content=entities,
|
|
1236
|
+
content_rowid=rowid
|
|
1237
|
+
);
|
|
1238
|
+
|
|
1239
|
+
CREATE TRIGGER IF NOT EXISTS entities_fts_ai AFTER INSERT ON entities BEGIN
|
|
1240
|
+
INSERT INTO entities_fts(rowid, name) VALUES (new.rowid, new.name);
|
|
1241
|
+
END;
|
|
1242
|
+
|
|
1243
|
+
CREATE TRIGGER IF NOT EXISTS entities_fts_ad AFTER DELETE ON entities BEGIN
|
|
1244
|
+
INSERT INTO entities_fts(entities_fts, rowid, name) VALUES('delete', old.rowid, old.name);
|
|
1245
|
+
END;
|
|
1246
|
+
|
|
1247
|
+
CREATE TRIGGER IF NOT EXISTS entities_fts_au AFTER UPDATE ON entities BEGIN
|
|
1248
|
+
INSERT INTO entities_fts(entities_fts, rowid, name) VALUES('delete', old.rowid, old.name);
|
|
1249
|
+
INSERT INTO entities_fts(rowid, name) VALUES (new.rowid, new.name);
|
|
1250
|
+
END;
|
|
769
1251
|
`);
|
|
1252
|
+
try {
|
|
1253
|
+
await client.execute("INSERT INTO entities_fts(entities_fts) VALUES('rebuild')");
|
|
1254
|
+
} catch {
|
|
1255
|
+
}
|
|
770
1256
|
await client.executeMultiple(`
|
|
771
1257
|
CREATE TABLE IF NOT EXISTS entity_aliases (
|
|
772
1258
|
alias TEXT NOT NULL PRIMARY KEY,
|
|
@@ -947,6 +1433,33 @@ async function ensureSchema() {
|
|
|
947
1433
|
CREATE INDEX IF NOT EXISTS idx_conversations_channel
|
|
948
1434
|
ON conversations(channel_id);
|
|
949
1435
|
`);
|
|
1436
|
+
await client.executeMultiple(`
|
|
1437
|
+
CREATE TABLE IF NOT EXISTS session_agent_map (
|
|
1438
|
+
session_uuid TEXT PRIMARY KEY,
|
|
1439
|
+
agent_id TEXT NOT NULL,
|
|
1440
|
+
session_name TEXT,
|
|
1441
|
+
task_id TEXT,
|
|
1442
|
+
project_name TEXT,
|
|
1443
|
+
started_at TEXT NOT NULL
|
|
1444
|
+
);
|
|
1445
|
+
|
|
1446
|
+
CREATE INDEX IF NOT EXISTS idx_session_agent_map_agent
|
|
1447
|
+
ON session_agent_map(agent_id);
|
|
1448
|
+
`);
|
|
1449
|
+
try {
|
|
1450
|
+
const mapCount = await client.execute({ sql: `SELECT COUNT(*) as cnt FROM session_agent_map`, args: [] });
|
|
1451
|
+
if (Number(mapCount.rows[0]?.cnt ?? 0) === 0) {
|
|
1452
|
+
await client.execute({
|
|
1453
|
+
sql: `INSERT OR IGNORE INTO session_agent_map (session_uuid, agent_id, session_name, started_at)
|
|
1454
|
+
SELECT session_id, agent_id, '', MIN(timestamp)
|
|
1455
|
+
FROM memories
|
|
1456
|
+
WHERE session_id IS NOT NULL AND session_id != '' AND agent_id IS NOT NULL AND agent_id != ''
|
|
1457
|
+
GROUP BY session_id, agent_id`,
|
|
1458
|
+
args: []
|
|
1459
|
+
});
|
|
1460
|
+
}
|
|
1461
|
+
} catch {
|
|
1462
|
+
}
|
|
950
1463
|
try {
|
|
951
1464
|
await client.execute({
|
|
952
1465
|
sql: `ALTER TABLE tasks ADD COLUMN budget_tokens INTEGER`,
|
|
@@ -1080,15 +1593,41 @@ async function ensureSchema() {
|
|
|
1080
1593
|
});
|
|
1081
1594
|
} catch {
|
|
1082
1595
|
}
|
|
1596
|
+
for (const col of [
|
|
1597
|
+
"ALTER TABLE memories ADD COLUMN intent TEXT",
|
|
1598
|
+
"ALTER TABLE memories ADD COLUMN outcome TEXT",
|
|
1599
|
+
"ALTER TABLE memories ADD COLUMN domain TEXT",
|
|
1600
|
+
"ALTER TABLE memories ADD COLUMN referenced_entities TEXT",
|
|
1601
|
+
"ALTER TABLE memories ADD COLUMN retrieval_count INTEGER DEFAULT 0",
|
|
1602
|
+
"ALTER TABLE memories ADD COLUMN chain_position TEXT",
|
|
1603
|
+
"ALTER TABLE memories ADD COLUMN review_status TEXT",
|
|
1604
|
+
"ALTER TABLE memories ADD COLUMN context_window_pct INTEGER",
|
|
1605
|
+
"ALTER TABLE memories ADD COLUMN file_paths TEXT",
|
|
1606
|
+
"ALTER TABLE memories ADD COLUMN commit_hash TEXT",
|
|
1607
|
+
"ALTER TABLE memories ADD COLUMN duration_ms INTEGER",
|
|
1608
|
+
"ALTER TABLE memories ADD COLUMN token_cost REAL",
|
|
1609
|
+
"ALTER TABLE memories ADD COLUMN audience TEXT",
|
|
1610
|
+
"ALTER TABLE memories ADD COLUMN language_type TEXT",
|
|
1611
|
+
"ALTER TABLE memories ADD COLUMN parent_memory_id TEXT"
|
|
1612
|
+
]) {
|
|
1613
|
+
try {
|
|
1614
|
+
await client.execute(col);
|
|
1615
|
+
} catch {
|
|
1616
|
+
}
|
|
1617
|
+
}
|
|
1083
1618
|
}
|
|
1084
1619
|
async function disposeDatabase() {
|
|
1620
|
+
if (_daemonClient) {
|
|
1621
|
+
_daemonClient.close();
|
|
1622
|
+
_daemonClient = null;
|
|
1623
|
+
}
|
|
1085
1624
|
if (_client) {
|
|
1086
1625
|
_client.close();
|
|
1087
1626
|
_client = null;
|
|
1088
1627
|
_resilientClient = null;
|
|
1089
1628
|
}
|
|
1090
1629
|
}
|
|
1091
|
-
var _client, _resilientClient, initTurso, disposeTurso;
|
|
1630
|
+
var _client, _resilientClient, _daemonClient, initTurso, disposeTurso;
|
|
1092
1631
|
var init_database = __esm({
|
|
1093
1632
|
"src/lib/database.ts"() {
|
|
1094
1633
|
"use strict";
|
|
@@ -1096,6 +1635,7 @@ var init_database = __esm({
|
|
|
1096
1635
|
init_employees();
|
|
1097
1636
|
_client = null;
|
|
1098
1637
|
_resilientClient = null;
|
|
1638
|
+
_daemonClient = null;
|
|
1099
1639
|
initTurso = initDatabase;
|
|
1100
1640
|
disposeTurso = disposeDatabase;
|
|
1101
1641
|
}
|
|
@@ -1105,13 +1645,13 @@ var init_database = __esm({
|
|
|
1105
1645
|
init_config();
|
|
1106
1646
|
import crypto from "crypto";
|
|
1107
1647
|
import os3 from "os";
|
|
1108
|
-
import { readFileSync as
|
|
1109
|
-
import
|
|
1110
|
-
var DEVICE_JSON_PATH =
|
|
1648
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, mkdirSync, existsSync as existsSync4 } from "fs";
|
|
1649
|
+
import path4 from "path";
|
|
1650
|
+
var DEVICE_JSON_PATH = path4.join(EXE_AI_DIR, "device.json");
|
|
1111
1651
|
function getDeviceInfo() {
|
|
1112
|
-
if (
|
|
1652
|
+
if (existsSync4(DEVICE_JSON_PATH)) {
|
|
1113
1653
|
try {
|
|
1114
|
-
const raw =
|
|
1654
|
+
const raw = readFileSync4(DEVICE_JSON_PATH, "utf8");
|
|
1115
1655
|
const data = JSON.parse(raw);
|
|
1116
1656
|
if (data.deviceId && data.friendlyName && data.hostname) {
|
|
1117
1657
|
return data;
|
|
@@ -1125,7 +1665,7 @@ function getDeviceInfo() {
|
|
|
1125
1665
|
friendlyName: hostname.replace(/\./g, "-").toLowerCase(),
|
|
1126
1666
|
hostname
|
|
1127
1667
|
};
|
|
1128
|
-
mkdirSync(
|
|
1668
|
+
mkdirSync(path4.dirname(DEVICE_JSON_PATH), { recursive: true });
|
|
1129
1669
|
writeFileSync2(DEVICE_JSON_PATH, JSON.stringify(info, null, 2));
|
|
1130
1670
|
return info;
|
|
1131
1671
|
}
|
package/dist/lib/embedder.js
CHANGED
|
@@ -285,10 +285,12 @@ function handleData(chunk) {
|
|
|
285
285
|
if (!line) continue;
|
|
286
286
|
try {
|
|
287
287
|
const response = JSON.parse(line);
|
|
288
|
-
const
|
|
288
|
+
const id = response.id;
|
|
289
|
+
if (!id) continue;
|
|
290
|
+
const entry = _pending.get(id);
|
|
289
291
|
if (entry) {
|
|
290
292
|
clearTimeout(entry.timer);
|
|
291
|
-
_pending.delete(
|
|
293
|
+
_pending.delete(id);
|
|
292
294
|
entry.resolve(response);
|
|
293
295
|
}
|
|
294
296
|
} catch {
|
|
@@ -459,6 +461,9 @@ async function connectEmbedDaemon() {
|
|
|
459
461
|
return false;
|
|
460
462
|
}
|
|
461
463
|
function sendRequest(texts, priority) {
|
|
464
|
+
return sendDaemonRequest({ texts, priority });
|
|
465
|
+
}
|
|
466
|
+
function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
462
467
|
return new Promise((resolve) => {
|
|
463
468
|
if (!_socket || !_connected) {
|
|
464
469
|
resolve({ error: "Not connected" });
|
|
@@ -468,10 +473,10 @@ function sendRequest(texts, priority) {
|
|
|
468
473
|
const timer = setTimeout(() => {
|
|
469
474
|
_pending.delete(id);
|
|
470
475
|
resolve({ error: "Request timeout" });
|
|
471
|
-
},
|
|
476
|
+
}, timeoutMs);
|
|
472
477
|
_pending.set(id, { resolve, timer });
|
|
473
478
|
try {
|
|
474
|
-
_socket.write(JSON.stringify({ id,
|
|
479
|
+
_socket.write(JSON.stringify({ id, ...payload }) + "\n");
|
|
475
480
|
} catch {
|
|
476
481
|
clearTimeout(timer);
|
|
477
482
|
_pending.delete(id);
|
|
@@ -481,30 +486,11 @@ function sendRequest(texts, priority) {
|
|
|
481
486
|
}
|
|
482
487
|
async function pingDaemon() {
|
|
483
488
|
if (!_socket || !_connected) return null;
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
}, 5e3);
|
|
490
|
-
_pending.set(id, {
|
|
491
|
-
resolve: (data) => {
|
|
492
|
-
if (data.health) {
|
|
493
|
-
resolve(data.health);
|
|
494
|
-
} else {
|
|
495
|
-
resolve(null);
|
|
496
|
-
}
|
|
497
|
-
},
|
|
498
|
-
timer
|
|
499
|
-
});
|
|
500
|
-
try {
|
|
501
|
-
_socket.write(JSON.stringify({ id, type: "health" }) + "\n");
|
|
502
|
-
} catch {
|
|
503
|
-
clearTimeout(timer);
|
|
504
|
-
_pending.delete(id);
|
|
505
|
-
resolve(null);
|
|
506
|
-
}
|
|
507
|
-
});
|
|
489
|
+
const response = await sendDaemonRequest({ type: "health" }, 5e3);
|
|
490
|
+
if (response.health) {
|
|
491
|
+
return response.health;
|
|
492
|
+
}
|
|
493
|
+
return null;
|
|
508
494
|
}
|
|
509
495
|
function killAndRespawnDaemon() {
|
|
510
496
|
process.stderr.write("[exed-client] Killing daemon for restart...\n");
|