@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
|
@@ -286,6 +286,443 @@ var init_db_retry = __esm({
|
|
|
286
286
|
}
|
|
287
287
|
});
|
|
288
288
|
|
|
289
|
+
// src/lib/exe-daemon-client.ts
|
|
290
|
+
import net from "net";
|
|
291
|
+
import { spawn } from "child_process";
|
|
292
|
+
import { randomUUID } from "crypto";
|
|
293
|
+
import { existsSync as existsSync3, unlinkSync as unlinkSync3, readFileSync as readFileSync4, openSync, closeSync, statSync } from "fs";
|
|
294
|
+
import path4 from "path";
|
|
295
|
+
import { fileURLToPath } from "url";
|
|
296
|
+
function handleData(chunk) {
|
|
297
|
+
_buffer += chunk.toString();
|
|
298
|
+
if (_buffer.length > MAX_BUFFER) {
|
|
299
|
+
_buffer = "";
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
let newlineIdx;
|
|
303
|
+
while ((newlineIdx = _buffer.indexOf("\n")) !== -1) {
|
|
304
|
+
const line = _buffer.slice(0, newlineIdx).trim();
|
|
305
|
+
_buffer = _buffer.slice(newlineIdx + 1);
|
|
306
|
+
if (!line) continue;
|
|
307
|
+
try {
|
|
308
|
+
const response = JSON.parse(line);
|
|
309
|
+
const id = response.id;
|
|
310
|
+
if (!id) continue;
|
|
311
|
+
const entry = _pending.get(id);
|
|
312
|
+
if (entry) {
|
|
313
|
+
clearTimeout(entry.timer);
|
|
314
|
+
_pending.delete(id);
|
|
315
|
+
entry.resolve(response);
|
|
316
|
+
}
|
|
317
|
+
} catch {
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
function cleanupStaleFiles() {
|
|
322
|
+
if (existsSync3(PID_PATH)) {
|
|
323
|
+
try {
|
|
324
|
+
const pid = parseInt(readFileSync4(PID_PATH, "utf8").trim(), 10);
|
|
325
|
+
if (pid > 0) {
|
|
326
|
+
try {
|
|
327
|
+
process.kill(pid, 0);
|
|
328
|
+
return;
|
|
329
|
+
} catch {
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
} catch {
|
|
333
|
+
}
|
|
334
|
+
try {
|
|
335
|
+
unlinkSync3(PID_PATH);
|
|
336
|
+
} catch {
|
|
337
|
+
}
|
|
338
|
+
try {
|
|
339
|
+
unlinkSync3(SOCKET_PATH);
|
|
340
|
+
} catch {
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
function findPackageRoot() {
|
|
345
|
+
let dir = path4.dirname(fileURLToPath(import.meta.url));
|
|
346
|
+
const { root } = path4.parse(dir);
|
|
347
|
+
while (dir !== root) {
|
|
348
|
+
if (existsSync3(path4.join(dir, "package.json"))) return dir;
|
|
349
|
+
dir = path4.dirname(dir);
|
|
350
|
+
}
|
|
351
|
+
return null;
|
|
352
|
+
}
|
|
353
|
+
function spawnDaemon() {
|
|
354
|
+
const pkgRoot = findPackageRoot();
|
|
355
|
+
if (!pkgRoot) {
|
|
356
|
+
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
const daemonPath = path4.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
360
|
+
if (!existsSync3(daemonPath)) {
|
|
361
|
+
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
362
|
+
`);
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
const resolvedPath = daemonPath;
|
|
366
|
+
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
367
|
+
`);
|
|
368
|
+
const logPath = path4.join(path4.dirname(SOCKET_PATH), "exed.log");
|
|
369
|
+
let stderrFd = "ignore";
|
|
370
|
+
try {
|
|
371
|
+
stderrFd = openSync(logPath, "a");
|
|
372
|
+
} catch {
|
|
373
|
+
}
|
|
374
|
+
const child = spawn(process.execPath, [resolvedPath], {
|
|
375
|
+
detached: true,
|
|
376
|
+
stdio: ["ignore", "ignore", stderrFd],
|
|
377
|
+
env: {
|
|
378
|
+
...process.env,
|
|
379
|
+
TMUX: void 0,
|
|
380
|
+
// Daemon is global — must not inherit session scope
|
|
381
|
+
TMUX_PANE: void 0,
|
|
382
|
+
// Prevents resolveExeSession() from scoping to one session
|
|
383
|
+
EXE_DAEMON_SOCK: SOCKET_PATH,
|
|
384
|
+
EXE_DAEMON_PID: PID_PATH
|
|
385
|
+
}
|
|
386
|
+
});
|
|
387
|
+
child.unref();
|
|
388
|
+
if (typeof stderrFd === "number") {
|
|
389
|
+
try {
|
|
390
|
+
closeSync(stderrFd);
|
|
391
|
+
} catch {
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
function acquireSpawnLock() {
|
|
396
|
+
try {
|
|
397
|
+
const fd = openSync(SPAWN_LOCK_PATH, "wx");
|
|
398
|
+
closeSync(fd);
|
|
399
|
+
return true;
|
|
400
|
+
} catch {
|
|
401
|
+
try {
|
|
402
|
+
const stat = statSync(SPAWN_LOCK_PATH);
|
|
403
|
+
if (Date.now() - stat.mtimeMs > SPAWN_LOCK_STALE_MS) {
|
|
404
|
+
try {
|
|
405
|
+
unlinkSync3(SPAWN_LOCK_PATH);
|
|
406
|
+
} catch {
|
|
407
|
+
}
|
|
408
|
+
try {
|
|
409
|
+
const fd = openSync(SPAWN_LOCK_PATH, "wx");
|
|
410
|
+
closeSync(fd);
|
|
411
|
+
return true;
|
|
412
|
+
} catch {
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
} catch {
|
|
416
|
+
}
|
|
417
|
+
return false;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
function releaseSpawnLock() {
|
|
421
|
+
try {
|
|
422
|
+
unlinkSync3(SPAWN_LOCK_PATH);
|
|
423
|
+
} catch {
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
function connectToSocket() {
|
|
427
|
+
return new Promise((resolve) => {
|
|
428
|
+
if (_socket && _connected) {
|
|
429
|
+
resolve(true);
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
const socket = net.createConnection({ path: SOCKET_PATH });
|
|
433
|
+
const connectTimeout = setTimeout(() => {
|
|
434
|
+
socket.destroy();
|
|
435
|
+
resolve(false);
|
|
436
|
+
}, 2e3);
|
|
437
|
+
socket.on("connect", () => {
|
|
438
|
+
clearTimeout(connectTimeout);
|
|
439
|
+
_socket = socket;
|
|
440
|
+
_connected = true;
|
|
441
|
+
_buffer = "";
|
|
442
|
+
socket.on("data", handleData);
|
|
443
|
+
socket.on("close", () => {
|
|
444
|
+
_connected = false;
|
|
445
|
+
_socket = null;
|
|
446
|
+
for (const [id, entry] of _pending) {
|
|
447
|
+
clearTimeout(entry.timer);
|
|
448
|
+
_pending.delete(id);
|
|
449
|
+
entry.resolve({ error: "Connection closed" });
|
|
450
|
+
}
|
|
451
|
+
});
|
|
452
|
+
socket.on("error", () => {
|
|
453
|
+
_connected = false;
|
|
454
|
+
_socket = null;
|
|
455
|
+
});
|
|
456
|
+
resolve(true);
|
|
457
|
+
});
|
|
458
|
+
socket.on("error", () => {
|
|
459
|
+
clearTimeout(connectTimeout);
|
|
460
|
+
resolve(false);
|
|
461
|
+
});
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
async function connectEmbedDaemon() {
|
|
465
|
+
if (_socket && _connected) return true;
|
|
466
|
+
if (await connectToSocket()) return true;
|
|
467
|
+
if (acquireSpawnLock()) {
|
|
468
|
+
try {
|
|
469
|
+
cleanupStaleFiles();
|
|
470
|
+
spawnDaemon();
|
|
471
|
+
} finally {
|
|
472
|
+
releaseSpawnLock();
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
const start = Date.now();
|
|
476
|
+
let delay2 = 100;
|
|
477
|
+
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
478
|
+
await new Promise((r) => setTimeout(r, delay2));
|
|
479
|
+
if (await connectToSocket()) return true;
|
|
480
|
+
delay2 = Math.min(delay2 * 2, 3e3);
|
|
481
|
+
}
|
|
482
|
+
return false;
|
|
483
|
+
}
|
|
484
|
+
function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
485
|
+
return new Promise((resolve) => {
|
|
486
|
+
if (!_socket || !_connected) {
|
|
487
|
+
resolve({ error: "Not connected" });
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
const id = randomUUID();
|
|
491
|
+
const timer = setTimeout(() => {
|
|
492
|
+
_pending.delete(id);
|
|
493
|
+
resolve({ error: "Request timeout" });
|
|
494
|
+
}, timeoutMs);
|
|
495
|
+
_pending.set(id, { resolve, timer });
|
|
496
|
+
try {
|
|
497
|
+
_socket.write(JSON.stringify({ id, ...payload }) + "\n");
|
|
498
|
+
} catch {
|
|
499
|
+
clearTimeout(timer);
|
|
500
|
+
_pending.delete(id);
|
|
501
|
+
resolve({ error: "Write failed" });
|
|
502
|
+
}
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
function isClientConnected() {
|
|
506
|
+
return _connected;
|
|
507
|
+
}
|
|
508
|
+
var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _pending, MAX_BUFFER;
|
|
509
|
+
var init_exe_daemon_client = __esm({
|
|
510
|
+
"src/lib/exe-daemon-client.ts"() {
|
|
511
|
+
"use strict";
|
|
512
|
+
init_config();
|
|
513
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path4.join(EXE_AI_DIR, "exed.sock");
|
|
514
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path4.join(EXE_AI_DIR, "exed.pid");
|
|
515
|
+
SPAWN_LOCK_PATH = path4.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
516
|
+
SPAWN_LOCK_STALE_MS = 3e4;
|
|
517
|
+
CONNECT_TIMEOUT_MS = 15e3;
|
|
518
|
+
REQUEST_TIMEOUT_MS = 3e4;
|
|
519
|
+
_socket = null;
|
|
520
|
+
_connected = false;
|
|
521
|
+
_buffer = "";
|
|
522
|
+
_pending = /* @__PURE__ */ new Map();
|
|
523
|
+
MAX_BUFFER = 1e7;
|
|
524
|
+
}
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
// src/lib/daemon-protocol.ts
|
|
528
|
+
function serializeValue(v) {
|
|
529
|
+
if (v === null || v === void 0) return null;
|
|
530
|
+
if (typeof v === "bigint") return Number(v);
|
|
531
|
+
if (typeof v === "boolean") return v ? 1 : 0;
|
|
532
|
+
if (v instanceof Uint8Array) {
|
|
533
|
+
return { __blob: Buffer.from(v).toString("base64") };
|
|
534
|
+
}
|
|
535
|
+
if (ArrayBuffer.isView(v)) {
|
|
536
|
+
return { __blob: Buffer.from(v.buffer, v.byteOffset, v.byteLength).toString("base64") };
|
|
537
|
+
}
|
|
538
|
+
if (v instanceof ArrayBuffer) {
|
|
539
|
+
return { __blob: Buffer.from(v).toString("base64") };
|
|
540
|
+
}
|
|
541
|
+
if (typeof v === "string" || typeof v === "number") return v;
|
|
542
|
+
return String(v);
|
|
543
|
+
}
|
|
544
|
+
function deserializeValue(v) {
|
|
545
|
+
if (v === null) return null;
|
|
546
|
+
if (typeof v === "object" && v !== null && "__blob" in v) {
|
|
547
|
+
const buf = Buffer.from(v.__blob, "base64");
|
|
548
|
+
return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
|
|
549
|
+
}
|
|
550
|
+
return v;
|
|
551
|
+
}
|
|
552
|
+
function deserializeResultSet(srs) {
|
|
553
|
+
const rows = srs.rows.map((obj) => {
|
|
554
|
+
const values = srs.columns.map(
|
|
555
|
+
(col) => deserializeValue(obj[col] ?? null)
|
|
556
|
+
);
|
|
557
|
+
const row = values;
|
|
558
|
+
for (let i = 0; i < srs.columns.length; i++) {
|
|
559
|
+
const col = srs.columns[i];
|
|
560
|
+
if (col !== void 0) {
|
|
561
|
+
row[col] = values[i] ?? null;
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
Object.defineProperty(row, "length", {
|
|
565
|
+
value: values.length,
|
|
566
|
+
enumerable: false
|
|
567
|
+
});
|
|
568
|
+
return row;
|
|
569
|
+
});
|
|
570
|
+
return {
|
|
571
|
+
columns: srs.columns,
|
|
572
|
+
columnTypes: srs.columnTypes ?? [],
|
|
573
|
+
rows,
|
|
574
|
+
rowsAffected: srs.rowsAffected,
|
|
575
|
+
lastInsertRowid: srs.lastInsertRowid != null ? BigInt(srs.lastInsertRowid) : void 0,
|
|
576
|
+
toJSON: () => ({
|
|
577
|
+
columns: srs.columns,
|
|
578
|
+
columnTypes: srs.columnTypes ?? [],
|
|
579
|
+
rows: srs.rows,
|
|
580
|
+
rowsAffected: srs.rowsAffected,
|
|
581
|
+
lastInsertRowid: srs.lastInsertRowid
|
|
582
|
+
})
|
|
583
|
+
};
|
|
584
|
+
}
|
|
585
|
+
var init_daemon_protocol = __esm({
|
|
586
|
+
"src/lib/daemon-protocol.ts"() {
|
|
587
|
+
"use strict";
|
|
588
|
+
}
|
|
589
|
+
});
|
|
590
|
+
|
|
591
|
+
// src/lib/db-daemon-client.ts
|
|
592
|
+
var db_daemon_client_exports = {};
|
|
593
|
+
__export(db_daemon_client_exports, {
|
|
594
|
+
createDaemonDbClient: () => createDaemonDbClient,
|
|
595
|
+
initDaemonDbClient: () => initDaemonDbClient
|
|
596
|
+
});
|
|
597
|
+
function normalizeStatement(stmt) {
|
|
598
|
+
if (typeof stmt === "string") {
|
|
599
|
+
return { sql: stmt, args: [] };
|
|
600
|
+
}
|
|
601
|
+
const sql = stmt.sql;
|
|
602
|
+
let args = [];
|
|
603
|
+
if (Array.isArray(stmt.args)) {
|
|
604
|
+
args = stmt.args.map((v) => serializeValue(v));
|
|
605
|
+
} else if (stmt.args && typeof stmt.args === "object") {
|
|
606
|
+
const named = {};
|
|
607
|
+
for (const [key, val] of Object.entries(stmt.args)) {
|
|
608
|
+
named[key] = serializeValue(val);
|
|
609
|
+
}
|
|
610
|
+
return { sql, args: named };
|
|
611
|
+
}
|
|
612
|
+
return { sql, args };
|
|
613
|
+
}
|
|
614
|
+
function createDaemonDbClient(fallbackClient) {
|
|
615
|
+
let _useDaemon = false;
|
|
616
|
+
const client = {
|
|
617
|
+
async execute(stmt) {
|
|
618
|
+
if (!_useDaemon || !isClientConnected()) {
|
|
619
|
+
return fallbackClient.execute(stmt);
|
|
620
|
+
}
|
|
621
|
+
const { sql, args } = normalizeStatement(stmt);
|
|
622
|
+
const response = await sendDaemonRequest({
|
|
623
|
+
type: "db-execute",
|
|
624
|
+
sql,
|
|
625
|
+
args
|
|
626
|
+
});
|
|
627
|
+
if (response.error) {
|
|
628
|
+
const errMsg = String(response.error);
|
|
629
|
+
if (errMsg === "Not connected" || errMsg === "Request timeout" || errMsg === "Write failed") {
|
|
630
|
+
process.stderr.write(`[db-daemon] Transport error (${errMsg}), falling back to direct
|
|
631
|
+
`);
|
|
632
|
+
return fallbackClient.execute(stmt);
|
|
633
|
+
}
|
|
634
|
+
throw new Error(errMsg);
|
|
635
|
+
}
|
|
636
|
+
if (response.db) {
|
|
637
|
+
return deserializeResultSet(response.db);
|
|
638
|
+
}
|
|
639
|
+
process.stderr.write("[db-daemon] Unexpected response shape, falling back to direct\n");
|
|
640
|
+
return fallbackClient.execute(stmt);
|
|
641
|
+
},
|
|
642
|
+
async batch(stmts, mode) {
|
|
643
|
+
if (!_useDaemon || !isClientConnected()) {
|
|
644
|
+
return fallbackClient.batch(stmts, mode);
|
|
645
|
+
}
|
|
646
|
+
const statements = stmts.map(normalizeStatement);
|
|
647
|
+
const response = await sendDaemonRequest({
|
|
648
|
+
type: "db-batch",
|
|
649
|
+
statements,
|
|
650
|
+
mode: mode ?? "deferred"
|
|
651
|
+
});
|
|
652
|
+
if (response.error) {
|
|
653
|
+
const errMsg = String(response.error);
|
|
654
|
+
if (errMsg === "Not connected" || errMsg === "Request timeout" || errMsg === "Write failed") {
|
|
655
|
+
process.stderr.write(`[db-daemon] Batch transport error (${errMsg}), falling back to direct
|
|
656
|
+
`);
|
|
657
|
+
return fallbackClient.batch(stmts, mode);
|
|
658
|
+
}
|
|
659
|
+
throw new Error(errMsg);
|
|
660
|
+
}
|
|
661
|
+
const batchResults = response["db-batch"];
|
|
662
|
+
if (batchResults) {
|
|
663
|
+
return batchResults.map(deserializeResultSet);
|
|
664
|
+
}
|
|
665
|
+
process.stderr.write("[db-daemon] Unexpected batch response shape, falling back to direct\n");
|
|
666
|
+
return fallbackClient.batch(stmts, mode);
|
|
667
|
+
},
|
|
668
|
+
// Transaction support — delegate to fallback (transactions need direct connection)
|
|
669
|
+
async transaction(mode) {
|
|
670
|
+
return fallbackClient.transaction(mode);
|
|
671
|
+
},
|
|
672
|
+
// executeMultiple — delegate to fallback (used only for schema migrations)
|
|
673
|
+
async executeMultiple(sql) {
|
|
674
|
+
return fallbackClient.executeMultiple(sql);
|
|
675
|
+
},
|
|
676
|
+
// migrate — delegate to fallback
|
|
677
|
+
async migrate(stmts) {
|
|
678
|
+
return fallbackClient.migrate(stmts);
|
|
679
|
+
},
|
|
680
|
+
// Sync mode — delegate to fallback
|
|
681
|
+
sync() {
|
|
682
|
+
return fallbackClient.sync();
|
|
683
|
+
},
|
|
684
|
+
close() {
|
|
685
|
+
_useDaemon = false;
|
|
686
|
+
},
|
|
687
|
+
get closed() {
|
|
688
|
+
return fallbackClient.closed;
|
|
689
|
+
},
|
|
690
|
+
get protocol() {
|
|
691
|
+
return fallbackClient.protocol;
|
|
692
|
+
}
|
|
693
|
+
};
|
|
694
|
+
return {
|
|
695
|
+
...client,
|
|
696
|
+
/** Enable daemon routing (call after confirming daemon is connected) */
|
|
697
|
+
_enableDaemon() {
|
|
698
|
+
_useDaemon = true;
|
|
699
|
+
},
|
|
700
|
+
/** Check if daemon routing is active */
|
|
701
|
+
_isDaemonActive() {
|
|
702
|
+
return _useDaemon && isClientConnected();
|
|
703
|
+
}
|
|
704
|
+
};
|
|
705
|
+
}
|
|
706
|
+
async function initDaemonDbClient(fallbackClient) {
|
|
707
|
+
if (process.env.EXE_IS_DAEMON === "1") return null;
|
|
708
|
+
const connected = await connectEmbedDaemon();
|
|
709
|
+
if (!connected) {
|
|
710
|
+
process.stderr.write("[db-daemon] Daemon unavailable \u2014 using direct SQLite\n");
|
|
711
|
+
return null;
|
|
712
|
+
}
|
|
713
|
+
const client = createDaemonDbClient(fallbackClient);
|
|
714
|
+
client._enableDaemon();
|
|
715
|
+
process.stderr.write("[db-daemon] DB routing through daemon (single-writer)\n");
|
|
716
|
+
return client;
|
|
717
|
+
}
|
|
718
|
+
var init_db_daemon_client = __esm({
|
|
719
|
+
"src/lib/db-daemon-client.ts"() {
|
|
720
|
+
"use strict";
|
|
721
|
+
init_exe_daemon_client();
|
|
722
|
+
init_daemon_protocol();
|
|
723
|
+
}
|
|
724
|
+
});
|
|
725
|
+
|
|
289
726
|
// src/lib/database.ts
|
|
290
727
|
var database_exports = {};
|
|
291
728
|
__export(database_exports, {
|
|
@@ -294,6 +731,7 @@ __export(database_exports, {
|
|
|
294
731
|
ensureSchema: () => ensureSchema,
|
|
295
732
|
getClient: () => getClient,
|
|
296
733
|
getRawClient: () => getRawClient,
|
|
734
|
+
initDaemonClient: () => initDaemonClient,
|
|
297
735
|
initDatabase: () => initDatabase,
|
|
298
736
|
initTurso: () => initTurso,
|
|
299
737
|
isInitialized: () => isInitialized
|
|
@@ -321,8 +759,27 @@ function getClient() {
|
|
|
321
759
|
if (!_resilientClient) {
|
|
322
760
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
323
761
|
}
|
|
762
|
+
if (process.env.EXE_IS_DAEMON === "1") {
|
|
763
|
+
return _resilientClient;
|
|
764
|
+
}
|
|
765
|
+
if (_daemonClient && _daemonClient._isDaemonActive()) {
|
|
766
|
+
return _daemonClient;
|
|
767
|
+
}
|
|
324
768
|
return _resilientClient;
|
|
325
769
|
}
|
|
770
|
+
async function initDaemonClient() {
|
|
771
|
+
if (process.env.EXE_IS_DAEMON === "1") return;
|
|
772
|
+
if (!_resilientClient) return;
|
|
773
|
+
try {
|
|
774
|
+
const { initDaemonDbClient: initDaemonDbClient2 } = await Promise.resolve().then(() => (init_db_daemon_client(), db_daemon_client_exports));
|
|
775
|
+
_daemonClient = await initDaemonDbClient2(_resilientClient);
|
|
776
|
+
} catch (err) {
|
|
777
|
+
process.stderr.write(
|
|
778
|
+
`[database] Daemon client init failed (non-fatal): ${err instanceof Error ? err.message : String(err)}
|
|
779
|
+
`
|
|
780
|
+
);
|
|
781
|
+
}
|
|
782
|
+
}
|
|
326
783
|
function getRawClient() {
|
|
327
784
|
if (!_client) {
|
|
328
785
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
@@ -809,6 +1266,12 @@ async function ensureSchema() {
|
|
|
809
1266
|
} catch {
|
|
810
1267
|
}
|
|
811
1268
|
}
|
|
1269
|
+
try {
|
|
1270
|
+
await client.execute(
|
|
1271
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_content_hash ON memories(content_hash, agent_id)`
|
|
1272
|
+
);
|
|
1273
|
+
} catch {
|
|
1274
|
+
}
|
|
812
1275
|
await client.executeMultiple(`
|
|
813
1276
|
CREATE TABLE IF NOT EXISTS entities (
|
|
814
1277
|
id TEXT PRIMARY KEY,
|
|
@@ -861,7 +1324,30 @@ async function ensureSchema() {
|
|
|
861
1324
|
entity_id TEXT NOT NULL,
|
|
862
1325
|
PRIMARY KEY (hyperedge_id, entity_id)
|
|
863
1326
|
);
|
|
1327
|
+
|
|
1328
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS entities_fts USING fts5(
|
|
1329
|
+
name,
|
|
1330
|
+
content=entities,
|
|
1331
|
+
content_rowid=rowid
|
|
1332
|
+
);
|
|
1333
|
+
|
|
1334
|
+
CREATE TRIGGER IF NOT EXISTS entities_fts_ai AFTER INSERT ON entities BEGIN
|
|
1335
|
+
INSERT INTO entities_fts(rowid, name) VALUES (new.rowid, new.name);
|
|
1336
|
+
END;
|
|
1337
|
+
|
|
1338
|
+
CREATE TRIGGER IF NOT EXISTS entities_fts_ad AFTER DELETE ON entities BEGIN
|
|
1339
|
+
INSERT INTO entities_fts(entities_fts, rowid, name) VALUES('delete', old.rowid, old.name);
|
|
1340
|
+
END;
|
|
1341
|
+
|
|
1342
|
+
CREATE TRIGGER IF NOT EXISTS entities_fts_au AFTER UPDATE ON entities BEGIN
|
|
1343
|
+
INSERT INTO entities_fts(entities_fts, rowid, name) VALUES('delete', old.rowid, old.name);
|
|
1344
|
+
INSERT INTO entities_fts(rowid, name) VALUES (new.rowid, new.name);
|
|
1345
|
+
END;
|
|
864
1346
|
`);
|
|
1347
|
+
try {
|
|
1348
|
+
await client.execute("INSERT INTO entities_fts(entities_fts) VALUES('rebuild')");
|
|
1349
|
+
} catch {
|
|
1350
|
+
}
|
|
865
1351
|
await client.executeMultiple(`
|
|
866
1352
|
CREATE TABLE IF NOT EXISTS entity_aliases (
|
|
867
1353
|
alias TEXT NOT NULL PRIMARY KEY,
|
|
@@ -1042,6 +1528,33 @@ async function ensureSchema() {
|
|
|
1042
1528
|
CREATE INDEX IF NOT EXISTS idx_conversations_channel
|
|
1043
1529
|
ON conversations(channel_id);
|
|
1044
1530
|
`);
|
|
1531
|
+
await client.executeMultiple(`
|
|
1532
|
+
CREATE TABLE IF NOT EXISTS session_agent_map (
|
|
1533
|
+
session_uuid TEXT PRIMARY KEY,
|
|
1534
|
+
agent_id TEXT NOT NULL,
|
|
1535
|
+
session_name TEXT,
|
|
1536
|
+
task_id TEXT,
|
|
1537
|
+
project_name TEXT,
|
|
1538
|
+
started_at TEXT NOT NULL
|
|
1539
|
+
);
|
|
1540
|
+
|
|
1541
|
+
CREATE INDEX IF NOT EXISTS idx_session_agent_map_agent
|
|
1542
|
+
ON session_agent_map(agent_id);
|
|
1543
|
+
`);
|
|
1544
|
+
try {
|
|
1545
|
+
const mapCount = await client.execute({ sql: `SELECT COUNT(*) as cnt FROM session_agent_map`, args: [] });
|
|
1546
|
+
if (Number(mapCount.rows[0]?.cnt ?? 0) === 0) {
|
|
1547
|
+
await client.execute({
|
|
1548
|
+
sql: `INSERT OR IGNORE INTO session_agent_map (session_uuid, agent_id, session_name, started_at)
|
|
1549
|
+
SELECT session_id, agent_id, '', MIN(timestamp)
|
|
1550
|
+
FROM memories
|
|
1551
|
+
WHERE session_id IS NOT NULL AND session_id != '' AND agent_id IS NOT NULL AND agent_id != ''
|
|
1552
|
+
GROUP BY session_id, agent_id`,
|
|
1553
|
+
args: []
|
|
1554
|
+
});
|
|
1555
|
+
}
|
|
1556
|
+
} catch {
|
|
1557
|
+
}
|
|
1045
1558
|
try {
|
|
1046
1559
|
await client.execute({
|
|
1047
1560
|
sql: `ALTER TABLE tasks ADD COLUMN budget_tokens INTEGER`,
|
|
@@ -1175,15 +1688,41 @@ async function ensureSchema() {
|
|
|
1175
1688
|
});
|
|
1176
1689
|
} catch {
|
|
1177
1690
|
}
|
|
1691
|
+
for (const col of [
|
|
1692
|
+
"ALTER TABLE memories ADD COLUMN intent TEXT",
|
|
1693
|
+
"ALTER TABLE memories ADD COLUMN outcome TEXT",
|
|
1694
|
+
"ALTER TABLE memories ADD COLUMN domain TEXT",
|
|
1695
|
+
"ALTER TABLE memories ADD COLUMN referenced_entities TEXT",
|
|
1696
|
+
"ALTER TABLE memories ADD COLUMN retrieval_count INTEGER DEFAULT 0",
|
|
1697
|
+
"ALTER TABLE memories ADD COLUMN chain_position TEXT",
|
|
1698
|
+
"ALTER TABLE memories ADD COLUMN review_status TEXT",
|
|
1699
|
+
"ALTER TABLE memories ADD COLUMN context_window_pct INTEGER",
|
|
1700
|
+
"ALTER TABLE memories ADD COLUMN file_paths TEXT",
|
|
1701
|
+
"ALTER TABLE memories ADD COLUMN commit_hash TEXT",
|
|
1702
|
+
"ALTER TABLE memories ADD COLUMN duration_ms INTEGER",
|
|
1703
|
+
"ALTER TABLE memories ADD COLUMN token_cost REAL",
|
|
1704
|
+
"ALTER TABLE memories ADD COLUMN audience TEXT",
|
|
1705
|
+
"ALTER TABLE memories ADD COLUMN language_type TEXT",
|
|
1706
|
+
"ALTER TABLE memories ADD COLUMN parent_memory_id TEXT"
|
|
1707
|
+
]) {
|
|
1708
|
+
try {
|
|
1709
|
+
await client.execute(col);
|
|
1710
|
+
} catch {
|
|
1711
|
+
}
|
|
1712
|
+
}
|
|
1178
1713
|
}
|
|
1179
1714
|
async function disposeDatabase() {
|
|
1715
|
+
if (_daemonClient) {
|
|
1716
|
+
_daemonClient.close();
|
|
1717
|
+
_daemonClient = null;
|
|
1718
|
+
}
|
|
1180
1719
|
if (_client) {
|
|
1181
1720
|
_client.close();
|
|
1182
1721
|
_client = null;
|
|
1183
1722
|
_resilientClient = null;
|
|
1184
1723
|
}
|
|
1185
1724
|
}
|
|
1186
|
-
var _client, _resilientClient, initTurso, disposeTurso;
|
|
1725
|
+
var _client, _resilientClient, _daemonClient, initTurso, disposeTurso;
|
|
1187
1726
|
var init_database = __esm({
|
|
1188
1727
|
"src/lib/database.ts"() {
|
|
1189
1728
|
"use strict";
|
|
@@ -1191,6 +1730,7 @@ var init_database = __esm({
|
|
|
1191
1730
|
init_employees();
|
|
1192
1731
|
_client = null;
|
|
1193
1732
|
_resilientClient = null;
|
|
1733
|
+
_daemonClient = null;
|
|
1194
1734
|
initTurso = initDatabase;
|
|
1195
1735
|
disposeTurso = disposeDatabase;
|
|
1196
1736
|
}
|
|
@@ -1207,14 +1747,14 @@ var init_memory = __esm({
|
|
|
1207
1747
|
|
|
1208
1748
|
// src/lib/keychain.ts
|
|
1209
1749
|
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
1210
|
-
import { existsSync as
|
|
1211
|
-
import
|
|
1750
|
+
import { existsSync as existsSync5 } from "fs";
|
|
1751
|
+
import path6 from "path";
|
|
1212
1752
|
import os3 from "os";
|
|
1213
1753
|
function getKeyDir() {
|
|
1214
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
1754
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path6.join(os3.homedir(), ".exe-os");
|
|
1215
1755
|
}
|
|
1216
1756
|
function getKeyPath() {
|
|
1217
|
-
return
|
|
1757
|
+
return path6.join(getKeyDir(), "master.key");
|
|
1218
1758
|
}
|
|
1219
1759
|
async function tryKeytar() {
|
|
1220
1760
|
try {
|
|
@@ -1235,13 +1775,21 @@ async function getMasterKey() {
|
|
|
1235
1775
|
}
|
|
1236
1776
|
}
|
|
1237
1777
|
const keyPath = getKeyPath();
|
|
1238
|
-
if (!
|
|
1778
|
+
if (!existsSync5(keyPath)) {
|
|
1779
|
+
process.stderr.write(
|
|
1780
|
+
`[keychain] Key not found at ${keyPath} (HOME=${os3.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
1781
|
+
`
|
|
1782
|
+
);
|
|
1239
1783
|
return null;
|
|
1240
1784
|
}
|
|
1241
1785
|
try {
|
|
1242
1786
|
const content = await readFile3(keyPath, "utf-8");
|
|
1243
1787
|
return Buffer.from(content.trim(), "base64");
|
|
1244
|
-
} catch {
|
|
1788
|
+
} catch (err) {
|
|
1789
|
+
process.stderr.write(
|
|
1790
|
+
`[keychain] Key read failed at ${keyPath}: ${err instanceof Error ? err.message : String(err)}
|
|
1791
|
+
`
|
|
1792
|
+
);
|
|
1245
1793
|
return null;
|
|
1246
1794
|
}
|
|
1247
1795
|
}
|
|
@@ -1322,12 +1870,12 @@ __export(shard_manager_exports, {
|
|
|
1322
1870
|
listShards: () => listShards,
|
|
1323
1871
|
shardExists: () => shardExists
|
|
1324
1872
|
});
|
|
1325
|
-
import
|
|
1326
|
-
import { existsSync as
|
|
1873
|
+
import path7 from "path";
|
|
1874
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync3, readdirSync as readdirSync3 } from "fs";
|
|
1327
1875
|
import { createClient as createClient2 } from "@libsql/client";
|
|
1328
1876
|
function initShardManager(encryptionKey) {
|
|
1329
1877
|
_encryptionKey = encryptionKey;
|
|
1330
|
-
if (!
|
|
1878
|
+
if (!existsSync6(SHARDS_DIR)) {
|
|
1331
1879
|
mkdirSync3(SHARDS_DIR, { recursive: true });
|
|
1332
1880
|
}
|
|
1333
1881
|
_shardingEnabled = true;
|
|
@@ -1348,7 +1896,7 @@ function getShardClient(projectName) {
|
|
|
1348
1896
|
}
|
|
1349
1897
|
const cached = _shards.get(safeName);
|
|
1350
1898
|
if (cached) return cached;
|
|
1351
|
-
const dbPath =
|
|
1899
|
+
const dbPath = path7.join(SHARDS_DIR, `${safeName}.db`);
|
|
1352
1900
|
const client = createClient2({
|
|
1353
1901
|
url: `file:${dbPath}`,
|
|
1354
1902
|
encryptionKey: _encryptionKey
|
|
@@ -1358,10 +1906,10 @@ function getShardClient(projectName) {
|
|
|
1358
1906
|
}
|
|
1359
1907
|
function shardExists(projectName) {
|
|
1360
1908
|
const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
1361
|
-
return
|
|
1909
|
+
return existsSync6(path7.join(SHARDS_DIR, `${safeName}.db`));
|
|
1362
1910
|
}
|
|
1363
1911
|
function listShards() {
|
|
1364
|
-
if (!
|
|
1912
|
+
if (!existsSync6(SHARDS_DIR)) return [];
|
|
1365
1913
|
return readdirSync3(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
1366
1914
|
}
|
|
1367
1915
|
async function ensureShardSchema(client) {
|
|
@@ -1547,7 +2095,7 @@ var init_shard_manager = __esm({
|
|
|
1547
2095
|
"src/lib/shard-manager.ts"() {
|
|
1548
2096
|
"use strict";
|
|
1549
2097
|
init_config();
|
|
1550
|
-
SHARDS_DIR =
|
|
2098
|
+
SHARDS_DIR = path7.join(EXE_AI_DIR, "shards");
|
|
1551
2099
|
_shards = /* @__PURE__ */ new Map();
|
|
1552
2100
|
_encryptionKey = null;
|
|
1553
2101
|
_shardingEnabled = false;
|
|
@@ -1672,7 +2220,7 @@ __export(global_procedures_exports, {
|
|
|
1672
2220
|
loadGlobalProcedures: () => loadGlobalProcedures,
|
|
1673
2221
|
storeGlobalProcedure: () => storeGlobalProcedure
|
|
1674
2222
|
});
|
|
1675
|
-
import { randomUUID } from "crypto";
|
|
2223
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
1676
2224
|
async function loadGlobalProcedures() {
|
|
1677
2225
|
const client = getClient();
|
|
1678
2226
|
const result = await client.execute({
|
|
@@ -1701,7 +2249,7 @@ ${sections.join("\n\n")}
|
|
|
1701
2249
|
`;
|
|
1702
2250
|
}
|
|
1703
2251
|
async function storeGlobalProcedure(input2) {
|
|
1704
|
-
const id =
|
|
2252
|
+
const id = randomUUID2();
|
|
1705
2253
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1706
2254
|
const client = getClient();
|
|
1707
2255
|
await client.execute({
|
|
@@ -1752,6 +2300,7 @@ __export(store_exports, {
|
|
|
1752
2300
|
vectorToBlob: () => vectorToBlob,
|
|
1753
2301
|
writeMemory: () => writeMemory
|
|
1754
2302
|
});
|
|
2303
|
+
import { createHash as createHash2 } from "crypto";
|
|
1755
2304
|
function isBusyError2(err) {
|
|
1756
2305
|
if (err instanceof Error) {
|
|
1757
2306
|
const msg = err.message.toLowerCase();
|
|
@@ -1825,12 +2374,52 @@ function classifyTier(record) {
|
|
|
1825
2374
|
if (["store_memory", "manual"].includes(record.tool_name ?? "") && (record.importance ?? 0) >= 5) return 2;
|
|
1826
2375
|
return 3;
|
|
1827
2376
|
}
|
|
2377
|
+
function inferFilePaths(record) {
|
|
2378
|
+
if (!["Read", "Write", "Edit"].includes(record.tool_name)) return null;
|
|
2379
|
+
const firstLine = record.raw_text.split("\n")[0] ?? "";
|
|
2380
|
+
const match = firstLine.match(/(\/[\w./-]+\.\w+)/);
|
|
2381
|
+
return match ? JSON.stringify([match[1]]) : null;
|
|
2382
|
+
}
|
|
2383
|
+
function inferCommitHash(record) {
|
|
2384
|
+
if (record.tool_name !== "Bash") return null;
|
|
2385
|
+
const match = record.raw_text.match(/\b([a-f0-9]{7,40})\b/);
|
|
2386
|
+
return match ? match[1] : null;
|
|
2387
|
+
}
|
|
2388
|
+
function inferLanguageType(record) {
|
|
2389
|
+
const text = record.raw_text;
|
|
2390
|
+
if (!text || text.length < 10) return null;
|
|
2391
|
+
const trimmed = text.trimStart();
|
|
2392
|
+
if (trimmed.startsWith("{") || trimmed.startsWith("[")) return "json";
|
|
2393
|
+
if (/\b(SELECT|INSERT|UPDATE|DELETE|CREATE TABLE|ALTER TABLE)\b/i.test(text)) return "sql";
|
|
2394
|
+
if (/\b(function |const |import |export |class |def |async |=>)\b/.test(text)) return "code";
|
|
2395
|
+
if (trimmed.startsWith("#") || trimmed.startsWith("*")) return "prose";
|
|
2396
|
+
return "mixed";
|
|
2397
|
+
}
|
|
2398
|
+
function inferDomain(record) {
|
|
2399
|
+
const proj = (record.project_name ?? "").toLowerCase();
|
|
2400
|
+
if (proj.includes("marketing") || proj.includes("content")) return "marketing";
|
|
2401
|
+
if (proj.includes("crm") || proj.includes("customer")) return "customer";
|
|
2402
|
+
return null;
|
|
2403
|
+
}
|
|
1828
2404
|
async function writeMemory(record) {
|
|
1829
2405
|
if (record.vector !== null && record.vector.length !== EMBEDDING_DIM) {
|
|
1830
2406
|
throw new Error(
|
|
1831
2407
|
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
1832
2408
|
);
|
|
1833
2409
|
}
|
|
2410
|
+
const contentHash2 = createHash2("md5").update(record.raw_text).digest("hex");
|
|
2411
|
+
if (_pendingRecords.some((r) => r.content_hash === contentHash2 && r.agent_id === record.agent_id)) {
|
|
2412
|
+
return;
|
|
2413
|
+
}
|
|
2414
|
+
try {
|
|
2415
|
+
const client = getClient();
|
|
2416
|
+
const existing = await client.execute({
|
|
2417
|
+
sql: "SELECT id FROM memories WHERE content_hash = ? AND agent_id = ? LIMIT 1",
|
|
2418
|
+
args: [contentHash2, record.agent_id]
|
|
2419
|
+
});
|
|
2420
|
+
if (existing.rows.length > 0) return;
|
|
2421
|
+
} catch {
|
|
2422
|
+
}
|
|
1834
2423
|
const dbRow = {
|
|
1835
2424
|
id: record.id,
|
|
1836
2425
|
agent_id: record.agent_id,
|
|
@@ -1860,7 +2449,23 @@ async function writeMemory(record) {
|
|
|
1860
2449
|
supersedes_id: record.supersedes_id ?? null,
|
|
1861
2450
|
draft: record.draft ? 1 : 0,
|
|
1862
2451
|
memory_type: record.memory_type ?? "raw",
|
|
1863
|
-
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null
|
|
2452
|
+
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
2453
|
+
content_hash: contentHash2,
|
|
2454
|
+
intent: record.intent ?? null,
|
|
2455
|
+
outcome: record.outcome ?? null,
|
|
2456
|
+
domain: record.domain ?? inferDomain(record),
|
|
2457
|
+
referenced_entities: record.referenced_entities ?? null,
|
|
2458
|
+
retrieval_count: record.retrieval_count ?? 0,
|
|
2459
|
+
chain_position: record.chain_position ?? null,
|
|
2460
|
+
review_status: record.review_status ?? null,
|
|
2461
|
+
context_window_pct: record.context_window_pct ?? null,
|
|
2462
|
+
file_paths: record.file_paths ?? inferFilePaths(record),
|
|
2463
|
+
commit_hash: record.commit_hash ?? inferCommitHash(record),
|
|
2464
|
+
duration_ms: record.duration_ms ?? null,
|
|
2465
|
+
token_cost: record.token_cost ?? null,
|
|
2466
|
+
audience: record.audience ?? null,
|
|
2467
|
+
language_type: record.language_type ?? inferLanguageType(record),
|
|
2468
|
+
parent_memory_id: record.parent_memory_id ?? null
|
|
1864
2469
|
};
|
|
1865
2470
|
_pendingRecords.push(dbRow);
|
|
1866
2471
|
orgBus.emit({
|
|
@@ -1918,80 +2523,85 @@ async function flushBatch() {
|
|
|
1918
2523
|
const draft = row.draft ? 1 : 0;
|
|
1919
2524
|
const memoryType = row.memory_type ?? "raw";
|
|
1920
2525
|
const trajectory = row.trajectory ?? null;
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
2526
|
+
const contentHash2 = row.content_hash ?? null;
|
|
2527
|
+
const intent = row.intent ?? null;
|
|
2528
|
+
const outcome = row.outcome ?? null;
|
|
2529
|
+
const domain = row.domain ?? null;
|
|
2530
|
+
const referencedEntities = row.referenced_entities ?? null;
|
|
2531
|
+
const retrievalCount = row.retrieval_count ?? 0;
|
|
2532
|
+
const chainPosition = row.chain_position ?? null;
|
|
2533
|
+
const reviewStatus = row.review_status ?? null;
|
|
2534
|
+
const contextWindowPct = row.context_window_pct ?? null;
|
|
2535
|
+
const filePaths = row.file_paths ?? null;
|
|
2536
|
+
const commitHash = row.commit_hash ?? null;
|
|
2537
|
+
const durationMs = row.duration_ms ?? null;
|
|
2538
|
+
const tokenCost = row.token_cost ?? null;
|
|
2539
|
+
const audience = row.audience ?? null;
|
|
2540
|
+
const languageType = row.language_type ?? null;
|
|
2541
|
+
const parentMemoryId = row.parent_memory_id ?? null;
|
|
2542
|
+
const cols = `id, agent_id, agent_role, session_id, timestamp,
|
|
1931
2543
|
tool_name, project_name,
|
|
1932
2544
|
has_error, raw_text, vector, version, task_id, importance, status,
|
|
1933
2545
|
confidence, last_accessed,
|
|
1934
2546
|
workspace_id, document_id, user_id, char_offset, page_number,
|
|
1935
|
-
source_path, source_type, tier, supersedes_id, draft, memory_type, trajectory
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
trajectory
|
|
1994
|
-
]
|
|
2547
|
+
source_path, source_type, tier, supersedes_id, draft, memory_type, trajectory, content_hash,
|
|
2548
|
+
intent, outcome, domain, referenced_entities, retrieval_count,
|
|
2549
|
+
chain_position, review_status, context_window_pct, file_paths, commit_hash,
|
|
2550
|
+
duration_ms, token_cost, audience, language_type, parent_memory_id`;
|
|
2551
|
+
const metaArgs = [
|
|
2552
|
+
intent,
|
|
2553
|
+
outcome,
|
|
2554
|
+
domain,
|
|
2555
|
+
referencedEntities,
|
|
2556
|
+
retrievalCount,
|
|
2557
|
+
chainPosition,
|
|
2558
|
+
reviewStatus,
|
|
2559
|
+
contextWindowPct,
|
|
2560
|
+
filePaths,
|
|
2561
|
+
commitHash,
|
|
2562
|
+
durationMs,
|
|
2563
|
+
tokenCost,
|
|
2564
|
+
audience,
|
|
2565
|
+
languageType,
|
|
2566
|
+
parentMemoryId
|
|
2567
|
+
];
|
|
2568
|
+
const baseArgs = [
|
|
2569
|
+
row.id,
|
|
2570
|
+
row.agent_id,
|
|
2571
|
+
row.agent_role,
|
|
2572
|
+
row.session_id,
|
|
2573
|
+
row.timestamp,
|
|
2574
|
+
row.tool_name,
|
|
2575
|
+
row.project_name,
|
|
2576
|
+
row.has_error,
|
|
2577
|
+
row.raw_text
|
|
2578
|
+
];
|
|
2579
|
+
const sharedArgs = [
|
|
2580
|
+
row.version,
|
|
2581
|
+
taskId,
|
|
2582
|
+
importance,
|
|
2583
|
+
status,
|
|
2584
|
+
confidence,
|
|
2585
|
+
lastAccessed,
|
|
2586
|
+
workspaceId,
|
|
2587
|
+
documentId,
|
|
2588
|
+
userId,
|
|
2589
|
+
charOffset,
|
|
2590
|
+
pageNumber,
|
|
2591
|
+
sourcePath,
|
|
2592
|
+
sourceType,
|
|
2593
|
+
tier,
|
|
2594
|
+
supersedesId,
|
|
2595
|
+
draft,
|
|
2596
|
+
memoryType,
|
|
2597
|
+
trajectory,
|
|
2598
|
+
contentHash2
|
|
2599
|
+
];
|
|
2600
|
+
return {
|
|
2601
|
+
sql: hasVector ? `INSERT OR IGNORE INTO memories (${cols})
|
|
2602
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories (${cols})
|
|
2603
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
2604
|
+
args: hasVector ? [...baseArgs, vectorToBlob(row.vector), ...sharedArgs, ...metaArgs] : [...baseArgs, ...sharedArgs, ...metaArgs]
|
|
1995
2605
|
};
|
|
1996
2606
|
};
|
|
1997
2607
|
const globalClient = getClient();
|
|
@@ -2371,18 +2981,18 @@ function getActiveAgent() {
|
|
|
2371
2981
|
// src/lib/identity.ts
|
|
2372
2982
|
init_config();
|
|
2373
2983
|
init_database();
|
|
2374
|
-
import { existsSync as
|
|
2984
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "fs";
|
|
2375
2985
|
import { readdirSync as readdirSync2 } from "fs";
|
|
2376
|
-
import
|
|
2986
|
+
import path5 from "path";
|
|
2377
2987
|
import { createHash } from "crypto";
|
|
2378
|
-
var IDENTITY_DIR =
|
|
2988
|
+
var IDENTITY_DIR = path5.join(EXE_AI_DIR, "identity");
|
|
2379
2989
|
function ensureDir() {
|
|
2380
|
-
if (!
|
|
2990
|
+
if (!existsSync4(IDENTITY_DIR)) {
|
|
2381
2991
|
mkdirSync2(IDENTITY_DIR, { recursive: true });
|
|
2382
2992
|
}
|
|
2383
2993
|
}
|
|
2384
2994
|
function identityPath(agentId) {
|
|
2385
|
-
return
|
|
2995
|
+
return path5.join(IDENTITY_DIR, `${agentId}.md`);
|
|
2386
2996
|
}
|
|
2387
2997
|
function parseFrontmatter(raw) {
|
|
2388
2998
|
const match = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
@@ -2423,8 +3033,8 @@ function contentHash(content) {
|
|
|
2423
3033
|
}
|
|
2424
3034
|
function getIdentity(agentId) {
|
|
2425
3035
|
const filePath = identityPath(agentId);
|
|
2426
|
-
if (!
|
|
2427
|
-
const raw =
|
|
3036
|
+
if (!existsSync4(filePath)) return null;
|
|
3037
|
+
const raw = readFileSync5(filePath, "utf-8");
|
|
2428
3038
|
const { frontmatter, body } = parseFrontmatter(raw);
|
|
2429
3039
|
return {
|
|
2430
3040
|
agentId,
|
|
@@ -2524,12 +3134,12 @@ process.stdin.on("end", async () => {
|
|
|
2524
3134
|
} catch {
|
|
2525
3135
|
}
|
|
2526
3136
|
try {
|
|
2527
|
-
const { readFileSync:
|
|
3137
|
+
const { readFileSync: readFileSync6, existsSync: existsSync7 } = await import("fs");
|
|
2528
3138
|
const { join } = await import("path");
|
|
2529
3139
|
const os4 = await import("os");
|
|
2530
3140
|
const claudeMd = join(os4.homedir(), ".claude", "CLAUDE.md");
|
|
2531
|
-
if (
|
|
2532
|
-
const content =
|
|
3141
|
+
if (existsSync7(claudeMd)) {
|
|
3142
|
+
const content = readFileSync6(claudeMd, "utf8");
|
|
2533
3143
|
const hasOrchRules = content.includes("exe-os:orchestration-start");
|
|
2534
3144
|
if (hasOrchRules) {
|
|
2535
3145
|
checks.push("PASS: orchestration rules intact in CLAUDE.md");
|