@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
package/dist/bin/exe-rename.js
CHANGED
|
@@ -262,6 +262,443 @@ var init_db_retry = __esm({
|
|
|
262
262
|
}
|
|
263
263
|
});
|
|
264
264
|
|
|
265
|
+
// src/lib/exe-daemon-client.ts
|
|
266
|
+
import net from "net";
|
|
267
|
+
import { spawn } from "child_process";
|
|
268
|
+
import { randomUUID } from "crypto";
|
|
269
|
+
import { existsSync as existsSync3, unlinkSync as unlinkSync2, readFileSync as readFileSync3, openSync, closeSync, statSync } from "fs";
|
|
270
|
+
import path3 from "path";
|
|
271
|
+
import { fileURLToPath } from "url";
|
|
272
|
+
function handleData(chunk) {
|
|
273
|
+
_buffer += chunk.toString();
|
|
274
|
+
if (_buffer.length > MAX_BUFFER) {
|
|
275
|
+
_buffer = "";
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
let newlineIdx;
|
|
279
|
+
while ((newlineIdx = _buffer.indexOf("\n")) !== -1) {
|
|
280
|
+
const line = _buffer.slice(0, newlineIdx).trim();
|
|
281
|
+
_buffer = _buffer.slice(newlineIdx + 1);
|
|
282
|
+
if (!line) continue;
|
|
283
|
+
try {
|
|
284
|
+
const response = JSON.parse(line);
|
|
285
|
+
const id = response.id;
|
|
286
|
+
if (!id) continue;
|
|
287
|
+
const entry = _pending.get(id);
|
|
288
|
+
if (entry) {
|
|
289
|
+
clearTimeout(entry.timer);
|
|
290
|
+
_pending.delete(id);
|
|
291
|
+
entry.resolve(response);
|
|
292
|
+
}
|
|
293
|
+
} catch {
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
function cleanupStaleFiles() {
|
|
298
|
+
if (existsSync3(PID_PATH)) {
|
|
299
|
+
try {
|
|
300
|
+
const pid = parseInt(readFileSync3(PID_PATH, "utf8").trim(), 10);
|
|
301
|
+
if (pid > 0) {
|
|
302
|
+
try {
|
|
303
|
+
process.kill(pid, 0);
|
|
304
|
+
return;
|
|
305
|
+
} catch {
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
} catch {
|
|
309
|
+
}
|
|
310
|
+
try {
|
|
311
|
+
unlinkSync2(PID_PATH);
|
|
312
|
+
} catch {
|
|
313
|
+
}
|
|
314
|
+
try {
|
|
315
|
+
unlinkSync2(SOCKET_PATH);
|
|
316
|
+
} catch {
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
function findPackageRoot() {
|
|
321
|
+
let dir = path3.dirname(fileURLToPath(import.meta.url));
|
|
322
|
+
const { root } = path3.parse(dir);
|
|
323
|
+
while (dir !== root) {
|
|
324
|
+
if (existsSync3(path3.join(dir, "package.json"))) return dir;
|
|
325
|
+
dir = path3.dirname(dir);
|
|
326
|
+
}
|
|
327
|
+
return null;
|
|
328
|
+
}
|
|
329
|
+
function spawnDaemon() {
|
|
330
|
+
const pkgRoot = findPackageRoot();
|
|
331
|
+
if (!pkgRoot) {
|
|
332
|
+
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
const daemonPath = path3.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
336
|
+
if (!existsSync3(daemonPath)) {
|
|
337
|
+
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
338
|
+
`);
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
const resolvedPath = daemonPath;
|
|
342
|
+
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
343
|
+
`);
|
|
344
|
+
const logPath = path3.join(path3.dirname(SOCKET_PATH), "exed.log");
|
|
345
|
+
let stderrFd = "ignore";
|
|
346
|
+
try {
|
|
347
|
+
stderrFd = openSync(logPath, "a");
|
|
348
|
+
} catch {
|
|
349
|
+
}
|
|
350
|
+
const child = spawn(process.execPath, [resolvedPath], {
|
|
351
|
+
detached: true,
|
|
352
|
+
stdio: ["ignore", "ignore", stderrFd],
|
|
353
|
+
env: {
|
|
354
|
+
...process.env,
|
|
355
|
+
TMUX: void 0,
|
|
356
|
+
// Daemon is global — must not inherit session scope
|
|
357
|
+
TMUX_PANE: void 0,
|
|
358
|
+
// Prevents resolveExeSession() from scoping to one session
|
|
359
|
+
EXE_DAEMON_SOCK: SOCKET_PATH,
|
|
360
|
+
EXE_DAEMON_PID: PID_PATH
|
|
361
|
+
}
|
|
362
|
+
});
|
|
363
|
+
child.unref();
|
|
364
|
+
if (typeof stderrFd === "number") {
|
|
365
|
+
try {
|
|
366
|
+
closeSync(stderrFd);
|
|
367
|
+
} catch {
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
function acquireSpawnLock() {
|
|
372
|
+
try {
|
|
373
|
+
const fd = openSync(SPAWN_LOCK_PATH, "wx");
|
|
374
|
+
closeSync(fd);
|
|
375
|
+
return true;
|
|
376
|
+
} catch {
|
|
377
|
+
try {
|
|
378
|
+
const stat = statSync(SPAWN_LOCK_PATH);
|
|
379
|
+
if (Date.now() - stat.mtimeMs > SPAWN_LOCK_STALE_MS) {
|
|
380
|
+
try {
|
|
381
|
+
unlinkSync2(SPAWN_LOCK_PATH);
|
|
382
|
+
} catch {
|
|
383
|
+
}
|
|
384
|
+
try {
|
|
385
|
+
const fd = openSync(SPAWN_LOCK_PATH, "wx");
|
|
386
|
+
closeSync(fd);
|
|
387
|
+
return true;
|
|
388
|
+
} catch {
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
} catch {
|
|
392
|
+
}
|
|
393
|
+
return false;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
function releaseSpawnLock() {
|
|
397
|
+
try {
|
|
398
|
+
unlinkSync2(SPAWN_LOCK_PATH);
|
|
399
|
+
} catch {
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
function connectToSocket() {
|
|
403
|
+
return new Promise((resolve) => {
|
|
404
|
+
if (_socket && _connected) {
|
|
405
|
+
resolve(true);
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
const socket = net.createConnection({ path: SOCKET_PATH });
|
|
409
|
+
const connectTimeout = setTimeout(() => {
|
|
410
|
+
socket.destroy();
|
|
411
|
+
resolve(false);
|
|
412
|
+
}, 2e3);
|
|
413
|
+
socket.on("connect", () => {
|
|
414
|
+
clearTimeout(connectTimeout);
|
|
415
|
+
_socket = socket;
|
|
416
|
+
_connected = true;
|
|
417
|
+
_buffer = "";
|
|
418
|
+
socket.on("data", handleData);
|
|
419
|
+
socket.on("close", () => {
|
|
420
|
+
_connected = false;
|
|
421
|
+
_socket = null;
|
|
422
|
+
for (const [id, entry] of _pending) {
|
|
423
|
+
clearTimeout(entry.timer);
|
|
424
|
+
_pending.delete(id);
|
|
425
|
+
entry.resolve({ error: "Connection closed" });
|
|
426
|
+
}
|
|
427
|
+
});
|
|
428
|
+
socket.on("error", () => {
|
|
429
|
+
_connected = false;
|
|
430
|
+
_socket = null;
|
|
431
|
+
});
|
|
432
|
+
resolve(true);
|
|
433
|
+
});
|
|
434
|
+
socket.on("error", () => {
|
|
435
|
+
clearTimeout(connectTimeout);
|
|
436
|
+
resolve(false);
|
|
437
|
+
});
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
async function connectEmbedDaemon() {
|
|
441
|
+
if (_socket && _connected) return true;
|
|
442
|
+
if (await connectToSocket()) return true;
|
|
443
|
+
if (acquireSpawnLock()) {
|
|
444
|
+
try {
|
|
445
|
+
cleanupStaleFiles();
|
|
446
|
+
spawnDaemon();
|
|
447
|
+
} finally {
|
|
448
|
+
releaseSpawnLock();
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
const start = Date.now();
|
|
452
|
+
let delay2 = 100;
|
|
453
|
+
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
454
|
+
await new Promise((r) => setTimeout(r, delay2));
|
|
455
|
+
if (await connectToSocket()) return true;
|
|
456
|
+
delay2 = Math.min(delay2 * 2, 3e3);
|
|
457
|
+
}
|
|
458
|
+
return false;
|
|
459
|
+
}
|
|
460
|
+
function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
461
|
+
return new Promise((resolve) => {
|
|
462
|
+
if (!_socket || !_connected) {
|
|
463
|
+
resolve({ error: "Not connected" });
|
|
464
|
+
return;
|
|
465
|
+
}
|
|
466
|
+
const id = randomUUID();
|
|
467
|
+
const timer = setTimeout(() => {
|
|
468
|
+
_pending.delete(id);
|
|
469
|
+
resolve({ error: "Request timeout" });
|
|
470
|
+
}, timeoutMs);
|
|
471
|
+
_pending.set(id, { resolve, timer });
|
|
472
|
+
try {
|
|
473
|
+
_socket.write(JSON.stringify({ id, ...payload }) + "\n");
|
|
474
|
+
} catch {
|
|
475
|
+
clearTimeout(timer);
|
|
476
|
+
_pending.delete(id);
|
|
477
|
+
resolve({ error: "Write failed" });
|
|
478
|
+
}
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
function isClientConnected() {
|
|
482
|
+
return _connected;
|
|
483
|
+
}
|
|
484
|
+
var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _pending, MAX_BUFFER;
|
|
485
|
+
var init_exe_daemon_client = __esm({
|
|
486
|
+
"src/lib/exe-daemon-client.ts"() {
|
|
487
|
+
"use strict";
|
|
488
|
+
init_config();
|
|
489
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path3.join(EXE_AI_DIR, "exed.sock");
|
|
490
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path3.join(EXE_AI_DIR, "exed.pid");
|
|
491
|
+
SPAWN_LOCK_PATH = path3.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
492
|
+
SPAWN_LOCK_STALE_MS = 3e4;
|
|
493
|
+
CONNECT_TIMEOUT_MS = 15e3;
|
|
494
|
+
REQUEST_TIMEOUT_MS = 3e4;
|
|
495
|
+
_socket = null;
|
|
496
|
+
_connected = false;
|
|
497
|
+
_buffer = "";
|
|
498
|
+
_pending = /* @__PURE__ */ new Map();
|
|
499
|
+
MAX_BUFFER = 1e7;
|
|
500
|
+
}
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
// src/lib/daemon-protocol.ts
|
|
504
|
+
function serializeValue(v) {
|
|
505
|
+
if (v === null || v === void 0) return null;
|
|
506
|
+
if (typeof v === "bigint") return Number(v);
|
|
507
|
+
if (typeof v === "boolean") return v ? 1 : 0;
|
|
508
|
+
if (v instanceof Uint8Array) {
|
|
509
|
+
return { __blob: Buffer.from(v).toString("base64") };
|
|
510
|
+
}
|
|
511
|
+
if (ArrayBuffer.isView(v)) {
|
|
512
|
+
return { __blob: Buffer.from(v.buffer, v.byteOffset, v.byteLength).toString("base64") };
|
|
513
|
+
}
|
|
514
|
+
if (v instanceof ArrayBuffer) {
|
|
515
|
+
return { __blob: Buffer.from(v).toString("base64") };
|
|
516
|
+
}
|
|
517
|
+
if (typeof v === "string" || typeof v === "number") return v;
|
|
518
|
+
return String(v);
|
|
519
|
+
}
|
|
520
|
+
function deserializeValue(v) {
|
|
521
|
+
if (v === null) return null;
|
|
522
|
+
if (typeof v === "object" && v !== null && "__blob" in v) {
|
|
523
|
+
const buf = Buffer.from(v.__blob, "base64");
|
|
524
|
+
return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
|
|
525
|
+
}
|
|
526
|
+
return v;
|
|
527
|
+
}
|
|
528
|
+
function deserializeResultSet(srs) {
|
|
529
|
+
const rows = srs.rows.map((obj) => {
|
|
530
|
+
const values = srs.columns.map(
|
|
531
|
+
(col) => deserializeValue(obj[col] ?? null)
|
|
532
|
+
);
|
|
533
|
+
const row = values;
|
|
534
|
+
for (let i = 0; i < srs.columns.length; i++) {
|
|
535
|
+
const col = srs.columns[i];
|
|
536
|
+
if (col !== void 0) {
|
|
537
|
+
row[col] = values[i] ?? null;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
Object.defineProperty(row, "length", {
|
|
541
|
+
value: values.length,
|
|
542
|
+
enumerable: false
|
|
543
|
+
});
|
|
544
|
+
return row;
|
|
545
|
+
});
|
|
546
|
+
return {
|
|
547
|
+
columns: srs.columns,
|
|
548
|
+
columnTypes: srs.columnTypes ?? [],
|
|
549
|
+
rows,
|
|
550
|
+
rowsAffected: srs.rowsAffected,
|
|
551
|
+
lastInsertRowid: srs.lastInsertRowid != null ? BigInt(srs.lastInsertRowid) : void 0,
|
|
552
|
+
toJSON: () => ({
|
|
553
|
+
columns: srs.columns,
|
|
554
|
+
columnTypes: srs.columnTypes ?? [],
|
|
555
|
+
rows: srs.rows,
|
|
556
|
+
rowsAffected: srs.rowsAffected,
|
|
557
|
+
lastInsertRowid: srs.lastInsertRowid
|
|
558
|
+
})
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
var init_daemon_protocol = __esm({
|
|
562
|
+
"src/lib/daemon-protocol.ts"() {
|
|
563
|
+
"use strict";
|
|
564
|
+
}
|
|
565
|
+
});
|
|
566
|
+
|
|
567
|
+
// src/lib/db-daemon-client.ts
|
|
568
|
+
var db_daemon_client_exports = {};
|
|
569
|
+
__export(db_daemon_client_exports, {
|
|
570
|
+
createDaemonDbClient: () => createDaemonDbClient,
|
|
571
|
+
initDaemonDbClient: () => initDaemonDbClient
|
|
572
|
+
});
|
|
573
|
+
function normalizeStatement(stmt) {
|
|
574
|
+
if (typeof stmt === "string") {
|
|
575
|
+
return { sql: stmt, args: [] };
|
|
576
|
+
}
|
|
577
|
+
const sql = stmt.sql;
|
|
578
|
+
let args = [];
|
|
579
|
+
if (Array.isArray(stmt.args)) {
|
|
580
|
+
args = stmt.args.map((v) => serializeValue(v));
|
|
581
|
+
} else if (stmt.args && typeof stmt.args === "object") {
|
|
582
|
+
const named = {};
|
|
583
|
+
for (const [key, val] of Object.entries(stmt.args)) {
|
|
584
|
+
named[key] = serializeValue(val);
|
|
585
|
+
}
|
|
586
|
+
return { sql, args: named };
|
|
587
|
+
}
|
|
588
|
+
return { sql, args };
|
|
589
|
+
}
|
|
590
|
+
function createDaemonDbClient(fallbackClient) {
|
|
591
|
+
let _useDaemon = false;
|
|
592
|
+
const client = {
|
|
593
|
+
async execute(stmt) {
|
|
594
|
+
if (!_useDaemon || !isClientConnected()) {
|
|
595
|
+
return fallbackClient.execute(stmt);
|
|
596
|
+
}
|
|
597
|
+
const { sql, args } = normalizeStatement(stmt);
|
|
598
|
+
const response = await sendDaemonRequest({
|
|
599
|
+
type: "db-execute",
|
|
600
|
+
sql,
|
|
601
|
+
args
|
|
602
|
+
});
|
|
603
|
+
if (response.error) {
|
|
604
|
+
const errMsg = String(response.error);
|
|
605
|
+
if (errMsg === "Not connected" || errMsg === "Request timeout" || errMsg === "Write failed") {
|
|
606
|
+
process.stderr.write(`[db-daemon] Transport error (${errMsg}), falling back to direct
|
|
607
|
+
`);
|
|
608
|
+
return fallbackClient.execute(stmt);
|
|
609
|
+
}
|
|
610
|
+
throw new Error(errMsg);
|
|
611
|
+
}
|
|
612
|
+
if (response.db) {
|
|
613
|
+
return deserializeResultSet(response.db);
|
|
614
|
+
}
|
|
615
|
+
process.stderr.write("[db-daemon] Unexpected response shape, falling back to direct\n");
|
|
616
|
+
return fallbackClient.execute(stmt);
|
|
617
|
+
},
|
|
618
|
+
async batch(stmts, mode) {
|
|
619
|
+
if (!_useDaemon || !isClientConnected()) {
|
|
620
|
+
return fallbackClient.batch(stmts, mode);
|
|
621
|
+
}
|
|
622
|
+
const statements = stmts.map(normalizeStatement);
|
|
623
|
+
const response = await sendDaemonRequest({
|
|
624
|
+
type: "db-batch",
|
|
625
|
+
statements,
|
|
626
|
+
mode: mode ?? "deferred"
|
|
627
|
+
});
|
|
628
|
+
if (response.error) {
|
|
629
|
+
const errMsg = String(response.error);
|
|
630
|
+
if (errMsg === "Not connected" || errMsg === "Request timeout" || errMsg === "Write failed") {
|
|
631
|
+
process.stderr.write(`[db-daemon] Batch transport error (${errMsg}), falling back to direct
|
|
632
|
+
`);
|
|
633
|
+
return fallbackClient.batch(stmts, mode);
|
|
634
|
+
}
|
|
635
|
+
throw new Error(errMsg);
|
|
636
|
+
}
|
|
637
|
+
const batchResults = response["db-batch"];
|
|
638
|
+
if (batchResults) {
|
|
639
|
+
return batchResults.map(deserializeResultSet);
|
|
640
|
+
}
|
|
641
|
+
process.stderr.write("[db-daemon] Unexpected batch response shape, falling back to direct\n");
|
|
642
|
+
return fallbackClient.batch(stmts, mode);
|
|
643
|
+
},
|
|
644
|
+
// Transaction support — delegate to fallback (transactions need direct connection)
|
|
645
|
+
async transaction(mode) {
|
|
646
|
+
return fallbackClient.transaction(mode);
|
|
647
|
+
},
|
|
648
|
+
// executeMultiple — delegate to fallback (used only for schema migrations)
|
|
649
|
+
async executeMultiple(sql) {
|
|
650
|
+
return fallbackClient.executeMultiple(sql);
|
|
651
|
+
},
|
|
652
|
+
// migrate — delegate to fallback
|
|
653
|
+
async migrate(stmts) {
|
|
654
|
+
return fallbackClient.migrate(stmts);
|
|
655
|
+
},
|
|
656
|
+
// Sync mode — delegate to fallback
|
|
657
|
+
sync() {
|
|
658
|
+
return fallbackClient.sync();
|
|
659
|
+
},
|
|
660
|
+
close() {
|
|
661
|
+
_useDaemon = false;
|
|
662
|
+
},
|
|
663
|
+
get closed() {
|
|
664
|
+
return fallbackClient.closed;
|
|
665
|
+
},
|
|
666
|
+
get protocol() {
|
|
667
|
+
return fallbackClient.protocol;
|
|
668
|
+
}
|
|
669
|
+
};
|
|
670
|
+
return {
|
|
671
|
+
...client,
|
|
672
|
+
/** Enable daemon routing (call after confirming daemon is connected) */
|
|
673
|
+
_enableDaemon() {
|
|
674
|
+
_useDaemon = true;
|
|
675
|
+
},
|
|
676
|
+
/** Check if daemon routing is active */
|
|
677
|
+
_isDaemonActive() {
|
|
678
|
+
return _useDaemon && isClientConnected();
|
|
679
|
+
}
|
|
680
|
+
};
|
|
681
|
+
}
|
|
682
|
+
async function initDaemonDbClient(fallbackClient) {
|
|
683
|
+
if (process.env.EXE_IS_DAEMON === "1") return null;
|
|
684
|
+
const connected = await connectEmbedDaemon();
|
|
685
|
+
if (!connected) {
|
|
686
|
+
process.stderr.write("[db-daemon] Daemon unavailable \u2014 using direct SQLite\n");
|
|
687
|
+
return null;
|
|
688
|
+
}
|
|
689
|
+
const client = createDaemonDbClient(fallbackClient);
|
|
690
|
+
client._enableDaemon();
|
|
691
|
+
process.stderr.write("[db-daemon] DB routing through daemon (single-writer)\n");
|
|
692
|
+
return client;
|
|
693
|
+
}
|
|
694
|
+
var init_db_daemon_client = __esm({
|
|
695
|
+
"src/lib/db-daemon-client.ts"() {
|
|
696
|
+
"use strict";
|
|
697
|
+
init_exe_daemon_client();
|
|
698
|
+
init_daemon_protocol();
|
|
699
|
+
}
|
|
700
|
+
});
|
|
701
|
+
|
|
265
702
|
// src/lib/database.ts
|
|
266
703
|
var database_exports = {};
|
|
267
704
|
__export(database_exports, {
|
|
@@ -270,6 +707,7 @@ __export(database_exports, {
|
|
|
270
707
|
ensureSchema: () => ensureSchema,
|
|
271
708
|
getClient: () => getClient,
|
|
272
709
|
getRawClient: () => getRawClient,
|
|
710
|
+
initDaemonClient: () => initDaemonClient,
|
|
273
711
|
initDatabase: () => initDatabase,
|
|
274
712
|
initTurso: () => initTurso,
|
|
275
713
|
isInitialized: () => isInitialized
|
|
@@ -297,8 +735,27 @@ function getClient() {
|
|
|
297
735
|
if (!_resilientClient) {
|
|
298
736
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
299
737
|
}
|
|
738
|
+
if (process.env.EXE_IS_DAEMON === "1") {
|
|
739
|
+
return _resilientClient;
|
|
740
|
+
}
|
|
741
|
+
if (_daemonClient && _daemonClient._isDaemonActive()) {
|
|
742
|
+
return _daemonClient;
|
|
743
|
+
}
|
|
300
744
|
return _resilientClient;
|
|
301
745
|
}
|
|
746
|
+
async function initDaemonClient() {
|
|
747
|
+
if (process.env.EXE_IS_DAEMON === "1") return;
|
|
748
|
+
if (!_resilientClient) return;
|
|
749
|
+
try {
|
|
750
|
+
const { initDaemonDbClient: initDaemonDbClient2 } = await Promise.resolve().then(() => (init_db_daemon_client(), db_daemon_client_exports));
|
|
751
|
+
_daemonClient = await initDaemonDbClient2(_resilientClient);
|
|
752
|
+
} catch (err) {
|
|
753
|
+
process.stderr.write(
|
|
754
|
+
`[database] Daemon client init failed (non-fatal): ${err instanceof Error ? err.message : String(err)}
|
|
755
|
+
`
|
|
756
|
+
);
|
|
757
|
+
}
|
|
758
|
+
}
|
|
302
759
|
function getRawClient() {
|
|
303
760
|
if (!_client) {
|
|
304
761
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
@@ -785,6 +1242,12 @@ async function ensureSchema() {
|
|
|
785
1242
|
} catch {
|
|
786
1243
|
}
|
|
787
1244
|
}
|
|
1245
|
+
try {
|
|
1246
|
+
await client.execute(
|
|
1247
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_content_hash ON memories(content_hash, agent_id)`
|
|
1248
|
+
);
|
|
1249
|
+
} catch {
|
|
1250
|
+
}
|
|
788
1251
|
await client.executeMultiple(`
|
|
789
1252
|
CREATE TABLE IF NOT EXISTS entities (
|
|
790
1253
|
id TEXT PRIMARY KEY,
|
|
@@ -837,7 +1300,30 @@ async function ensureSchema() {
|
|
|
837
1300
|
entity_id TEXT NOT NULL,
|
|
838
1301
|
PRIMARY KEY (hyperedge_id, entity_id)
|
|
839
1302
|
);
|
|
1303
|
+
|
|
1304
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS entities_fts USING fts5(
|
|
1305
|
+
name,
|
|
1306
|
+
content=entities,
|
|
1307
|
+
content_rowid=rowid
|
|
1308
|
+
);
|
|
1309
|
+
|
|
1310
|
+
CREATE TRIGGER IF NOT EXISTS entities_fts_ai AFTER INSERT ON entities BEGIN
|
|
1311
|
+
INSERT INTO entities_fts(rowid, name) VALUES (new.rowid, new.name);
|
|
1312
|
+
END;
|
|
1313
|
+
|
|
1314
|
+
CREATE TRIGGER IF NOT EXISTS entities_fts_ad AFTER DELETE ON entities BEGIN
|
|
1315
|
+
INSERT INTO entities_fts(entities_fts, rowid, name) VALUES('delete', old.rowid, old.name);
|
|
1316
|
+
END;
|
|
1317
|
+
|
|
1318
|
+
CREATE TRIGGER IF NOT EXISTS entities_fts_au AFTER UPDATE ON entities BEGIN
|
|
1319
|
+
INSERT INTO entities_fts(entities_fts, rowid, name) VALUES('delete', old.rowid, old.name);
|
|
1320
|
+
INSERT INTO entities_fts(rowid, name) VALUES (new.rowid, new.name);
|
|
1321
|
+
END;
|
|
840
1322
|
`);
|
|
1323
|
+
try {
|
|
1324
|
+
await client.execute("INSERT INTO entities_fts(entities_fts) VALUES('rebuild')");
|
|
1325
|
+
} catch {
|
|
1326
|
+
}
|
|
841
1327
|
await client.executeMultiple(`
|
|
842
1328
|
CREATE TABLE IF NOT EXISTS entity_aliases (
|
|
843
1329
|
alias TEXT NOT NULL PRIMARY KEY,
|
|
@@ -1018,6 +1504,33 @@ async function ensureSchema() {
|
|
|
1018
1504
|
CREATE INDEX IF NOT EXISTS idx_conversations_channel
|
|
1019
1505
|
ON conversations(channel_id);
|
|
1020
1506
|
`);
|
|
1507
|
+
await client.executeMultiple(`
|
|
1508
|
+
CREATE TABLE IF NOT EXISTS session_agent_map (
|
|
1509
|
+
session_uuid TEXT PRIMARY KEY,
|
|
1510
|
+
agent_id TEXT NOT NULL,
|
|
1511
|
+
session_name TEXT,
|
|
1512
|
+
task_id TEXT,
|
|
1513
|
+
project_name TEXT,
|
|
1514
|
+
started_at TEXT NOT NULL
|
|
1515
|
+
);
|
|
1516
|
+
|
|
1517
|
+
CREATE INDEX IF NOT EXISTS idx_session_agent_map_agent
|
|
1518
|
+
ON session_agent_map(agent_id);
|
|
1519
|
+
`);
|
|
1520
|
+
try {
|
|
1521
|
+
const mapCount = await client.execute({ sql: `SELECT COUNT(*) as cnt FROM session_agent_map`, args: [] });
|
|
1522
|
+
if (Number(mapCount.rows[0]?.cnt ?? 0) === 0) {
|
|
1523
|
+
await client.execute({
|
|
1524
|
+
sql: `INSERT OR IGNORE INTO session_agent_map (session_uuid, agent_id, session_name, started_at)
|
|
1525
|
+
SELECT session_id, agent_id, '', MIN(timestamp)
|
|
1526
|
+
FROM memories
|
|
1527
|
+
WHERE session_id IS NOT NULL AND session_id != '' AND agent_id IS NOT NULL AND agent_id != ''
|
|
1528
|
+
GROUP BY session_id, agent_id`,
|
|
1529
|
+
args: []
|
|
1530
|
+
});
|
|
1531
|
+
}
|
|
1532
|
+
} catch {
|
|
1533
|
+
}
|
|
1021
1534
|
try {
|
|
1022
1535
|
await client.execute({
|
|
1023
1536
|
sql: `ALTER TABLE tasks ADD COLUMN budget_tokens INTEGER`,
|
|
@@ -1151,15 +1664,41 @@ async function ensureSchema() {
|
|
|
1151
1664
|
});
|
|
1152
1665
|
} catch {
|
|
1153
1666
|
}
|
|
1667
|
+
for (const col of [
|
|
1668
|
+
"ALTER TABLE memories ADD COLUMN intent TEXT",
|
|
1669
|
+
"ALTER TABLE memories ADD COLUMN outcome TEXT",
|
|
1670
|
+
"ALTER TABLE memories ADD COLUMN domain TEXT",
|
|
1671
|
+
"ALTER TABLE memories ADD COLUMN referenced_entities TEXT",
|
|
1672
|
+
"ALTER TABLE memories ADD COLUMN retrieval_count INTEGER DEFAULT 0",
|
|
1673
|
+
"ALTER TABLE memories ADD COLUMN chain_position TEXT",
|
|
1674
|
+
"ALTER TABLE memories ADD COLUMN review_status TEXT",
|
|
1675
|
+
"ALTER TABLE memories ADD COLUMN context_window_pct INTEGER",
|
|
1676
|
+
"ALTER TABLE memories ADD COLUMN file_paths TEXT",
|
|
1677
|
+
"ALTER TABLE memories ADD COLUMN commit_hash TEXT",
|
|
1678
|
+
"ALTER TABLE memories ADD COLUMN duration_ms INTEGER",
|
|
1679
|
+
"ALTER TABLE memories ADD COLUMN token_cost REAL",
|
|
1680
|
+
"ALTER TABLE memories ADD COLUMN audience TEXT",
|
|
1681
|
+
"ALTER TABLE memories ADD COLUMN language_type TEXT",
|
|
1682
|
+
"ALTER TABLE memories ADD COLUMN parent_memory_id TEXT"
|
|
1683
|
+
]) {
|
|
1684
|
+
try {
|
|
1685
|
+
await client.execute(col);
|
|
1686
|
+
} catch {
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1154
1689
|
}
|
|
1155
1690
|
async function disposeDatabase() {
|
|
1691
|
+
if (_daemonClient) {
|
|
1692
|
+
_daemonClient.close();
|
|
1693
|
+
_daemonClient = null;
|
|
1694
|
+
}
|
|
1156
1695
|
if (_client) {
|
|
1157
1696
|
_client.close();
|
|
1158
1697
|
_client = null;
|
|
1159
1698
|
_resilientClient = null;
|
|
1160
1699
|
}
|
|
1161
1700
|
}
|
|
1162
|
-
var _client, _resilientClient, initTurso, disposeTurso;
|
|
1701
|
+
var _client, _resilientClient, _daemonClient, initTurso, disposeTurso;
|
|
1163
1702
|
var init_database = __esm({
|
|
1164
1703
|
"src/lib/database.ts"() {
|
|
1165
1704
|
"use strict";
|
|
@@ -1167,6 +1706,7 @@ var init_database = __esm({
|
|
|
1167
1706
|
init_employees();
|
|
1168
1707
|
_client = null;
|
|
1169
1708
|
_resilientClient = null;
|
|
1709
|
+
_daemonClient = null;
|
|
1170
1710
|
initTurso = initDatabase;
|
|
1171
1711
|
disposeTurso = disposeDatabase;
|
|
1172
1712
|
}
|
|
@@ -1174,14 +1714,14 @@ var init_database = __esm({
|
|
|
1174
1714
|
|
|
1175
1715
|
// src/bin/exe-rename.ts
|
|
1176
1716
|
init_employees();
|
|
1177
|
-
import { readFileSync as
|
|
1717
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, renameSync as renameSync3, unlinkSync as unlinkSync3, existsSync as existsSync4 } from "fs";
|
|
1178
1718
|
import { execSync as execSync2 } from "child_process";
|
|
1179
|
-
import
|
|
1719
|
+
import path4 from "path";
|
|
1180
1720
|
import { homedir } from "os";
|
|
1181
1721
|
|
|
1182
1722
|
// src/lib/global-procedures.ts
|
|
1183
1723
|
init_database();
|
|
1184
|
-
import { randomUUID } from "crypto";
|
|
1724
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
1185
1725
|
|
|
1186
1726
|
// src/lib/platform-procedures.ts
|
|
1187
1727
|
var PLATFORM_PROCEDURES = [
|
|
@@ -1300,12 +1840,13 @@ function personalizePrompt(prompt, templateName, actualName) {
|
|
|
1300
1840
|
|
|
1301
1841
|
// src/lib/is-main.ts
|
|
1302
1842
|
import { realpathSync } from "fs";
|
|
1303
|
-
import { fileURLToPath } from "url";
|
|
1843
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
1304
1844
|
function isMainModule(importMetaUrl) {
|
|
1305
1845
|
if (process.argv[1] == null) return false;
|
|
1846
|
+
if (process.argv[1].includes("mcp/server")) return false;
|
|
1306
1847
|
try {
|
|
1307
1848
|
const scriptPath = realpathSync(process.argv[1]);
|
|
1308
|
-
const modulePath = realpathSync(
|
|
1849
|
+
const modulePath = realpathSync(fileURLToPath2(importMetaUrl));
|
|
1309
1850
|
return scriptPath === modulePath;
|
|
1310
1851
|
} catch {
|
|
1311
1852
|
return importMetaUrl === `file://${process.argv[1]}` || importMetaUrl === new URL(process.argv[1], "file://").href;
|
|
@@ -1314,9 +1855,9 @@ function isMainModule(importMetaUrl) {
|
|
|
1314
1855
|
|
|
1315
1856
|
// src/bin/exe-rename.ts
|
|
1316
1857
|
async function renameEmployee(oldName, newName, opts = {}) {
|
|
1317
|
-
const rosterPath = opts.rosterPath ??
|
|
1318
|
-
const identityDir = opts.identityDir ??
|
|
1319
|
-
const agentsDir = opts.agentsDir ??
|
|
1858
|
+
const rosterPath = opts.rosterPath ?? path4.join(homedir(), ".exe-os", "exe-employees.json");
|
|
1859
|
+
const identityDir = opts.identityDir ?? path4.join(homedir(), ".exe-os", "identity");
|
|
1860
|
+
const agentsDir = opts.agentsDir ?? path4.join(homedir(), ".claude", "agents");
|
|
1320
1861
|
const validation = validateEmployeeName(newName);
|
|
1321
1862
|
if (!validation.valid) {
|
|
1322
1863
|
return { success: false, error: validation.error };
|
|
@@ -1348,10 +1889,10 @@ async function renameEmployee(oldName, newName, opts = {}) {
|
|
|
1348
1889
|
writeFileSync2(rosterPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
|
|
1349
1890
|
}
|
|
1350
1891
|
});
|
|
1351
|
-
const oldIdentityPath =
|
|
1352
|
-
const newIdentityPath =
|
|
1353
|
-
if (
|
|
1354
|
-
const content =
|
|
1892
|
+
const oldIdentityPath = path4.join(identityDir, `${rosterOldName}.md`);
|
|
1893
|
+
const newIdentityPath = path4.join(identityDir, `${newName}.md`);
|
|
1894
|
+
if (existsSync4(oldIdentityPath)) {
|
|
1895
|
+
const content = readFileSync4(oldIdentityPath, "utf-8");
|
|
1355
1896
|
const updatedContent = content.replace(
|
|
1356
1897
|
/^(agent_id:\s*)\S+/m,
|
|
1357
1898
|
`$1${newName}`
|
|
@@ -1361,22 +1902,22 @@ async function renameEmployee(oldName, newName, opts = {}) {
|
|
|
1361
1902
|
rollbackStack.push({
|
|
1362
1903
|
description: "restore identity file",
|
|
1363
1904
|
undo: () => {
|
|
1364
|
-
if (
|
|
1905
|
+
if (existsSync4(newIdentityPath)) {
|
|
1365
1906
|
writeFileSync2(newIdentityPath, content, "utf-8");
|
|
1366
1907
|
renameSync3(newIdentityPath, oldIdentityPath);
|
|
1367
1908
|
}
|
|
1368
1909
|
}
|
|
1369
1910
|
});
|
|
1370
1911
|
}
|
|
1371
|
-
const oldAgentPath =
|
|
1372
|
-
const newAgentPath =
|
|
1373
|
-
if (
|
|
1374
|
-
const agentContent =
|
|
1912
|
+
const oldAgentPath = path4.join(agentsDir, `${rosterOldName}.md`);
|
|
1913
|
+
const newAgentPath = path4.join(agentsDir, `${newName}.md`);
|
|
1914
|
+
if (existsSync4(oldAgentPath)) {
|
|
1915
|
+
const agentContent = readFileSync4(oldAgentPath, "utf-8");
|
|
1375
1916
|
renameSync3(oldAgentPath, newAgentPath);
|
|
1376
1917
|
rollbackStack.push({
|
|
1377
1918
|
description: "restore agent file",
|
|
1378
1919
|
undo: () => {
|
|
1379
|
-
if (
|
|
1920
|
+
if (existsSync4(newAgentPath)) {
|
|
1380
1921
|
renameSync3(newAgentPath, oldAgentPath);
|
|
1381
1922
|
writeFileSync2(oldAgentPath, agentContent, "utf-8");
|
|
1382
1923
|
}
|
|
@@ -1456,12 +1997,12 @@ function removeOldSymlinks(name) {
|
|
|
1456
1997
|
try {
|
|
1457
1998
|
const exeBinPath = findExeBin2();
|
|
1458
1999
|
if (!exeBinPath) return;
|
|
1459
|
-
const binDir =
|
|
2000
|
+
const binDir = path4.dirname(exeBinPath);
|
|
1460
2001
|
for (const suffix of ["", "-opencode"]) {
|
|
1461
|
-
const linkPath =
|
|
1462
|
-
if (
|
|
2002
|
+
const linkPath = path4.join(binDir, `${name}${suffix}`);
|
|
2003
|
+
if (existsSync4(linkPath)) {
|
|
1463
2004
|
try {
|
|
1464
|
-
|
|
2005
|
+
unlinkSync3(linkPath);
|
|
1465
2006
|
} catch {
|
|
1466
2007
|
}
|
|
1467
2008
|
}
|