@askexenow/exe-os 0.9.7 → 0.9.8
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 +754 -79
- package/dist/bin/backfill-responses.js +752 -77
- package/dist/bin/backfill-vectors.js +752 -77
- package/dist/bin/cleanup-stale-review-tasks.js +657 -35
- package/dist/bin/cli.js +1388 -605
- package/dist/bin/exe-agent-config.js +123 -95
- package/dist/bin/exe-agent.js +41 -25
- package/dist/bin/exe-assign.js +732 -57
- package/dist/bin/exe-boot.js +784 -153
- package/dist/bin/exe-call.js +209 -138
- package/dist/bin/exe-cloud.js +35 -12
- package/dist/bin/exe-dispatch.js +692 -70
- package/dist/bin/exe-doctor.js +648 -26
- package/dist/bin/exe-export-behaviors.js +650 -20
- package/dist/bin/exe-forget.js +635 -13
- package/dist/bin/exe-gateway.js +1053 -271
- package/dist/bin/exe-heartbeat.js +665 -43
- package/dist/bin/exe-kill.js +646 -16
- package/dist/bin/exe-launch-agent.js +887 -97
- package/dist/bin/exe-link.js +658 -43
- package/dist/bin/exe-new-employee.js +378 -177
- package/dist/bin/exe-pending-messages.js +656 -34
- package/dist/bin/exe-pending-notifications.js +635 -13
- package/dist/bin/exe-pending-reviews.js +659 -37
- package/dist/bin/exe-rename.js +645 -30
- package/dist/bin/exe-review.js +635 -13
- package/dist/bin/exe-search.js +771 -88
- package/dist/bin/exe-session-cleanup.js +834 -150
- package/dist/bin/exe-settings.js +127 -91
- package/dist/bin/exe-start-codex.js +729 -94
- package/dist/bin/exe-start-opencode.js +717 -82
- package/dist/bin/exe-status.js +657 -35
- package/dist/bin/exe-team.js +635 -13
- package/dist/bin/git-sweep.js +720 -89
- package/dist/bin/graph-backfill.js +643 -13
- package/dist/bin/graph-export.js +646 -16
- package/dist/bin/install.js +596 -193
- package/dist/bin/scan-tasks.js +724 -93
- package/dist/bin/setup.js +1038 -210
- package/dist/bin/shard-migrate.js +645 -15
- package/dist/bin/wiki-sync.js +646 -16
- package/dist/gateway/index.js +1027 -245
- package/dist/hooks/bug-report-worker.js +891 -170
- package/dist/hooks/commit-complete.js +718 -87
- package/dist/hooks/error-recall.js +776 -93
- package/dist/hooks/exe-heartbeat-hook.js +85 -71
- package/dist/hooks/ingest-worker.js +840 -156
- package/dist/hooks/ingest.js +90 -73
- package/dist/hooks/instructions-loaded.js +669 -38
- package/dist/hooks/notification.js +661 -30
- package/dist/hooks/post-compact.js +674 -43
- package/dist/hooks/pre-compact.js +718 -87
- package/dist/hooks/pre-tool-use.js +872 -125
- package/dist/hooks/prompt-ingest-worker.js +758 -83
- package/dist/hooks/prompt-submit.js +1060 -319
- package/dist/hooks/response-ingest-worker.js +758 -83
- package/dist/hooks/session-end.js +721 -90
- package/dist/hooks/session-start.js +1031 -207
- package/dist/hooks/stop.js +680 -49
- package/dist/hooks/subagent-stop.js +674 -43
- package/dist/hooks/summary-worker.js +816 -132
- package/dist/index.js +1015 -232
- package/dist/lib/cloud-sync.js +663 -48
- package/dist/lib/consolidation.js +26 -3
- package/dist/lib/database.js +626 -18
- package/dist/lib/db.js +2261 -0
- package/dist/lib/device-registry.js +640 -25
- package/dist/lib/embedder.js +96 -43
- package/dist/lib/employee-templates.js +16 -0
- package/dist/lib/employees.js +259 -83
- package/dist/lib/exe-daemon-client.js +101 -63
- package/dist/lib/exe-daemon.js +894 -162
- package/dist/lib/hybrid-search.js +771 -88
- package/dist/lib/identity.js +27 -7
- package/dist/lib/messaging.js +55 -28
- package/dist/lib/reminders.js +21 -1
- package/dist/lib/schedules.js +636 -14
- package/dist/lib/skill-learning.js +21 -1
- package/dist/lib/store.js +643 -13
- package/dist/lib/task-router.js +82 -71
- package/dist/lib/tasks.js +98 -71
- package/dist/lib/tmux-routing.js +87 -60
- package/dist/lib/token-spend.js +26 -6
- package/dist/mcp/server.js +1784 -458
- package/dist/mcp/tools/complete-reminder.js +21 -1
- package/dist/mcp/tools/create-reminder.js +21 -1
- package/dist/mcp/tools/create-task.js +290 -164
- package/dist/mcp/tools/deactivate-behavior.js +24 -4
- package/dist/mcp/tools/list-reminders.js +21 -1
- package/dist/mcp/tools/list-tasks.js +195 -38
- package/dist/mcp/tools/send-message.js +58 -31
- package/dist/mcp/tools/update-task.js +75 -48
- package/dist/runtime/index.js +720 -89
- package/dist/tui/App.js +853 -123
- package/package.json +3 -2
|
@@ -284,7 +284,7 @@ function baseAgentName(name, employees) {
|
|
|
284
284
|
if (getEmployee(roster, base)) return base;
|
|
285
285
|
return name;
|
|
286
286
|
}
|
|
287
|
-
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE;
|
|
287
|
+
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, IDENTITY_DIR;
|
|
288
288
|
var init_employees = __esm({
|
|
289
289
|
"src/lib/employees.ts"() {
|
|
290
290
|
"use strict";
|
|
@@ -292,12 +292,609 @@ var init_employees = __esm({
|
|
|
292
292
|
EMPLOYEES_PATH = path2.join(EXE_AI_DIR, "exe-employees.json");
|
|
293
293
|
DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
|
|
294
294
|
COORDINATOR_ROLE = "COO";
|
|
295
|
+
IDENTITY_DIR = path2.join(EXE_AI_DIR, "identity");
|
|
296
|
+
}
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
// src/lib/database-adapter.ts
|
|
300
|
+
import os3 from "os";
|
|
301
|
+
import path3 from "path";
|
|
302
|
+
import { createRequire } from "module";
|
|
303
|
+
import { pathToFileURL } from "url";
|
|
304
|
+
function quotedIdentifier(identifier) {
|
|
305
|
+
return `"${identifier.replace(/"/g, '""')}"`;
|
|
306
|
+
}
|
|
307
|
+
function unqualifiedTableName(name) {
|
|
308
|
+
const raw = name.trim().replace(/^"|"$/g, "");
|
|
309
|
+
const parts = raw.split(".");
|
|
310
|
+
return parts[parts.length - 1].replace(/^"|"$/g, "").toLowerCase();
|
|
311
|
+
}
|
|
312
|
+
function stripTrailingSemicolon(sql) {
|
|
313
|
+
return sql.trim().replace(/;+\s*$/u, "");
|
|
314
|
+
}
|
|
315
|
+
function appendClause(sql, clause) {
|
|
316
|
+
const trimmed = stripTrailingSemicolon(sql);
|
|
317
|
+
const returningMatch = /\sRETURNING\b[\s\S]*$/iu.exec(trimmed);
|
|
318
|
+
if (!returningMatch) {
|
|
319
|
+
return `${trimmed}${clause}`;
|
|
320
|
+
}
|
|
321
|
+
const idx = returningMatch.index;
|
|
322
|
+
return `${trimmed.slice(0, idx)}${clause}${trimmed.slice(idx)}`;
|
|
323
|
+
}
|
|
324
|
+
function normalizeStatement(stmt) {
|
|
325
|
+
if (typeof stmt === "string") {
|
|
326
|
+
return { kind: "positional", sql: stmt, args: [] };
|
|
327
|
+
}
|
|
328
|
+
const sql = stmt.sql;
|
|
329
|
+
if (Array.isArray(stmt.args) || stmt.args === void 0) {
|
|
330
|
+
return { kind: "positional", sql, args: stmt.args ?? [] };
|
|
331
|
+
}
|
|
332
|
+
return { kind: "named", sql, args: stmt.args };
|
|
333
|
+
}
|
|
334
|
+
function rewriteBooleanLiterals(sql) {
|
|
335
|
+
let out = sql;
|
|
336
|
+
for (const column of BOOLEAN_COLUMN_NAMES) {
|
|
337
|
+
const scoped = `((?:\\b[a-z_][a-z0-9_]*\\.)?${column})`;
|
|
338
|
+
out = out.replace(new RegExp(`${scoped}\\s*=\\s*0\\b`, "giu"), "$1 = FALSE");
|
|
339
|
+
out = out.replace(new RegExp(`${scoped}\\s*=\\s*1\\b`, "giu"), "$1 = TRUE");
|
|
340
|
+
out = out.replace(new RegExp(`${scoped}\\s*!=\\s*0\\b`, "giu"), "$1 != FALSE");
|
|
341
|
+
out = out.replace(new RegExp(`${scoped}\\s*!=\\s*1\\b`, "giu"), "$1 != TRUE");
|
|
342
|
+
out = out.replace(new RegExp(`${scoped}\\s*<>\\s*0\\b`, "giu"), "$1 <> FALSE");
|
|
343
|
+
out = out.replace(new RegExp(`${scoped}\\s*<>\\s*1\\b`, "giu"), "$1 <> TRUE");
|
|
344
|
+
}
|
|
345
|
+
return out;
|
|
346
|
+
}
|
|
347
|
+
function rewriteInsertOrIgnore(sql) {
|
|
348
|
+
if (!/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu.test(sql)) {
|
|
349
|
+
return sql;
|
|
350
|
+
}
|
|
351
|
+
const replaced = sql.replace(/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu, "INSERT INTO");
|
|
352
|
+
return /\bON\s+CONFLICT\b/iu.test(replaced) ? replaced : appendClause(replaced, " ON CONFLICT DO NOTHING");
|
|
353
|
+
}
|
|
354
|
+
function rewriteInsertOrReplace(sql) {
|
|
355
|
+
const match = /^\s*INSERT\s+OR\s+REPLACE\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)([\s\S]*)$/iu.exec(sql);
|
|
356
|
+
if (!match) {
|
|
357
|
+
return sql;
|
|
358
|
+
}
|
|
359
|
+
const rawTable = match[1];
|
|
360
|
+
const rawColumns = match[2];
|
|
361
|
+
const remainder = match[3];
|
|
362
|
+
const tableName = unqualifiedTableName(rawTable);
|
|
363
|
+
const conflictKeys = UPSERT_KEYS[tableName];
|
|
364
|
+
if (!conflictKeys?.length) {
|
|
365
|
+
return sql;
|
|
366
|
+
}
|
|
367
|
+
const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
|
|
368
|
+
const updateColumns = columns.filter((col) => !conflictKeys.includes(col));
|
|
369
|
+
const conflictTarget = conflictKeys.map(quotedIdentifier).join(", ");
|
|
370
|
+
const updateClause = updateColumns.length === 0 ? " DO NOTHING" : ` DO UPDATE SET ${updateColumns.map((col) => `${quotedIdentifier(col)} = EXCLUDED.${quotedIdentifier(col)}`).join(", ")}`;
|
|
371
|
+
return `INSERT INTO ${rawTable} (${rawColumns})${appendClause(remainder, ` ON CONFLICT (${conflictTarget})${updateClause}`)}`;
|
|
372
|
+
}
|
|
373
|
+
function rewriteSql(sql) {
|
|
374
|
+
let out = sql;
|
|
375
|
+
out = out.replace(/\bdatetime\(\s*['"]now['"]\s*\)/giu, "CURRENT_TIMESTAMP");
|
|
376
|
+
out = out.replace(/\bvector32\s*\(\s*\?\s*\)/giu, "?");
|
|
377
|
+
out = rewriteBooleanLiterals(out);
|
|
378
|
+
out = rewriteInsertOrReplace(out);
|
|
379
|
+
out = rewriteInsertOrIgnore(out);
|
|
380
|
+
return stripTrailingSemicolon(out);
|
|
381
|
+
}
|
|
382
|
+
function toBoolean(value) {
|
|
383
|
+
if (value === null || value === void 0) return value;
|
|
384
|
+
if (typeof value === "boolean") return value;
|
|
385
|
+
if (typeof value === "number") return value !== 0;
|
|
386
|
+
if (typeof value === "bigint") return value !== 0n;
|
|
387
|
+
if (typeof value === "string") {
|
|
388
|
+
const normalized = value.trim().toLowerCase();
|
|
389
|
+
if (normalized === "0" || normalized === "false") return false;
|
|
390
|
+
if (normalized === "1" || normalized === "true") return true;
|
|
391
|
+
}
|
|
392
|
+
return Boolean(value);
|
|
393
|
+
}
|
|
394
|
+
function countQuestionMarks(sql, end) {
|
|
395
|
+
let count = 0;
|
|
396
|
+
let inSingle = false;
|
|
397
|
+
let inDouble = false;
|
|
398
|
+
let inLineComment = false;
|
|
399
|
+
let inBlockComment = false;
|
|
400
|
+
for (let i = 0; i < end; i++) {
|
|
401
|
+
const ch = sql[i];
|
|
402
|
+
const next = sql[i + 1];
|
|
403
|
+
if (inLineComment) {
|
|
404
|
+
if (ch === "\n") inLineComment = false;
|
|
405
|
+
continue;
|
|
406
|
+
}
|
|
407
|
+
if (inBlockComment) {
|
|
408
|
+
if (ch === "*" && next === "/") {
|
|
409
|
+
inBlockComment = false;
|
|
410
|
+
i += 1;
|
|
411
|
+
}
|
|
412
|
+
continue;
|
|
413
|
+
}
|
|
414
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
415
|
+
inLineComment = true;
|
|
416
|
+
i += 1;
|
|
417
|
+
continue;
|
|
418
|
+
}
|
|
419
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
420
|
+
inBlockComment = true;
|
|
421
|
+
i += 1;
|
|
422
|
+
continue;
|
|
423
|
+
}
|
|
424
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
425
|
+
inSingle = !inSingle;
|
|
426
|
+
continue;
|
|
427
|
+
}
|
|
428
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
429
|
+
inDouble = !inDouble;
|
|
430
|
+
continue;
|
|
431
|
+
}
|
|
432
|
+
if (!inSingle && !inDouble && ch === "?") {
|
|
433
|
+
count += 1;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
return count;
|
|
437
|
+
}
|
|
438
|
+
function findBooleanPlaceholderIndexes(sql) {
|
|
439
|
+
const indexes = /* @__PURE__ */ new Set();
|
|
440
|
+
for (const column of BOOLEAN_COLUMN_NAMES) {
|
|
441
|
+
const pattern = new RegExp(`(?:\\b[a-z_][a-z0-9_]*\\.)?${column}\\s*=\\s*\\?`, "giu");
|
|
442
|
+
for (const match of sql.matchAll(pattern)) {
|
|
443
|
+
const matchText = match[0];
|
|
444
|
+
const qIndex = match.index + matchText.lastIndexOf("?");
|
|
445
|
+
indexes.add(countQuestionMarks(sql, qIndex + 1));
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
return indexes;
|
|
449
|
+
}
|
|
450
|
+
function coerceInsertBooleanArgs(sql, args) {
|
|
451
|
+
const match = /^\s*INSERT(?:\s+OR\s+(?:IGNORE|REPLACE))?\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)/iu.exec(sql);
|
|
452
|
+
if (!match) return;
|
|
453
|
+
const rawTable = match[1];
|
|
454
|
+
const rawColumns = match[2];
|
|
455
|
+
const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
|
|
456
|
+
if (!boolColumns?.size) return;
|
|
457
|
+
const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
|
|
458
|
+
for (const [index, column] of columns.entries()) {
|
|
459
|
+
if (boolColumns.has(column) && index < args.length) {
|
|
460
|
+
args[index] = toBoolean(args[index]);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
function coerceUpdateBooleanArgs(sql, args) {
|
|
465
|
+
const match = /^\s*UPDATE\s+([A-Za-z0-9_."]+)\s+SET\s+([\s\S]+?)(?:\s+WHERE\b|$)/iu.exec(sql);
|
|
466
|
+
if (!match) return;
|
|
467
|
+
const rawTable = match[1];
|
|
468
|
+
const setClause = match[2];
|
|
469
|
+
const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
|
|
470
|
+
if (!boolColumns?.size) return;
|
|
471
|
+
const assignments = setClause.split(",");
|
|
472
|
+
let placeholderIndex = 0;
|
|
473
|
+
for (const assignment of assignments) {
|
|
474
|
+
if (!assignment.includes("?")) continue;
|
|
475
|
+
placeholderIndex += 1;
|
|
476
|
+
const colMatch = /^\s*(?:[A-Za-z_][A-Za-z0-9_]*\.)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*\?/iu.exec(assignment);
|
|
477
|
+
if (colMatch && boolColumns.has(colMatch[1])) {
|
|
478
|
+
args[placeholderIndex - 1] = toBoolean(args[placeholderIndex - 1]);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
function coerceBooleanArgs(sql, args) {
|
|
483
|
+
const nextArgs = [...args];
|
|
484
|
+
coerceInsertBooleanArgs(sql, nextArgs);
|
|
485
|
+
coerceUpdateBooleanArgs(sql, nextArgs);
|
|
486
|
+
const placeholderIndexes = findBooleanPlaceholderIndexes(sql);
|
|
487
|
+
for (const index of placeholderIndexes) {
|
|
488
|
+
if (index > 0 && index <= nextArgs.length) {
|
|
489
|
+
nextArgs[index - 1] = toBoolean(nextArgs[index - 1]);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
return nextArgs;
|
|
493
|
+
}
|
|
494
|
+
function convertQuestionMarksToDollarParams(sql) {
|
|
495
|
+
let out = "";
|
|
496
|
+
let placeholder = 0;
|
|
497
|
+
let inSingle = false;
|
|
498
|
+
let inDouble = false;
|
|
499
|
+
let inLineComment = false;
|
|
500
|
+
let inBlockComment = false;
|
|
501
|
+
for (let i = 0; i < sql.length; i++) {
|
|
502
|
+
const ch = sql[i];
|
|
503
|
+
const next = sql[i + 1];
|
|
504
|
+
if (inLineComment) {
|
|
505
|
+
out += ch;
|
|
506
|
+
if (ch === "\n") inLineComment = false;
|
|
507
|
+
continue;
|
|
508
|
+
}
|
|
509
|
+
if (inBlockComment) {
|
|
510
|
+
out += ch;
|
|
511
|
+
if (ch === "*" && next === "/") {
|
|
512
|
+
out += next;
|
|
513
|
+
inBlockComment = false;
|
|
514
|
+
i += 1;
|
|
515
|
+
}
|
|
516
|
+
continue;
|
|
517
|
+
}
|
|
518
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
519
|
+
out += ch + next;
|
|
520
|
+
inLineComment = true;
|
|
521
|
+
i += 1;
|
|
522
|
+
continue;
|
|
523
|
+
}
|
|
524
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
525
|
+
out += ch + next;
|
|
526
|
+
inBlockComment = true;
|
|
527
|
+
i += 1;
|
|
528
|
+
continue;
|
|
529
|
+
}
|
|
530
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
531
|
+
inSingle = !inSingle;
|
|
532
|
+
out += ch;
|
|
533
|
+
continue;
|
|
534
|
+
}
|
|
535
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
536
|
+
inDouble = !inDouble;
|
|
537
|
+
out += ch;
|
|
538
|
+
continue;
|
|
539
|
+
}
|
|
540
|
+
if (!inSingle && !inDouble && ch === "?") {
|
|
541
|
+
placeholder += 1;
|
|
542
|
+
out += `$${placeholder}`;
|
|
543
|
+
continue;
|
|
544
|
+
}
|
|
545
|
+
out += ch;
|
|
546
|
+
}
|
|
547
|
+
return out;
|
|
548
|
+
}
|
|
549
|
+
function translateStatementForPostgres(stmt) {
|
|
550
|
+
const normalized = normalizeStatement(stmt);
|
|
551
|
+
if (normalized.kind === "named") {
|
|
552
|
+
throw new Error("Named SQL parameters are not supported by the Prisma adapter.");
|
|
553
|
+
}
|
|
554
|
+
const rewrittenSql = rewriteSql(normalized.sql);
|
|
555
|
+
const coercedArgs = coerceBooleanArgs(rewrittenSql, normalized.args);
|
|
556
|
+
return {
|
|
557
|
+
sql: convertQuestionMarksToDollarParams(rewrittenSql),
|
|
558
|
+
args: coercedArgs
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
function shouldBypassPostgres(stmt) {
|
|
562
|
+
const normalized = normalizeStatement(stmt);
|
|
563
|
+
if (normalized.kind === "named") {
|
|
564
|
+
return true;
|
|
565
|
+
}
|
|
566
|
+
return IMMEDIATE_FALLBACK_PATTERNS.some((pattern) => pattern.test(normalized.sql));
|
|
567
|
+
}
|
|
568
|
+
function shouldFallbackOnError(error) {
|
|
569
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
570
|
+
return /42P01|42883|42601|does not exist|syntax error|not supported|Named SQL parameters are not supported/iu.test(message);
|
|
571
|
+
}
|
|
572
|
+
function isReadQuery(sql) {
|
|
573
|
+
const trimmed = sql.trimStart();
|
|
574
|
+
return /^(SELECT|WITH|SHOW|EXPLAIN|VALUES)\b/iu.test(trimmed) || /\bRETURNING\b/iu.test(trimmed);
|
|
575
|
+
}
|
|
576
|
+
function buildRow(row, columns) {
|
|
577
|
+
const values = columns.map((column) => row[column]);
|
|
578
|
+
return Object.assign(values, row);
|
|
579
|
+
}
|
|
580
|
+
function buildResultSet(rows, rowsAffected = 0) {
|
|
581
|
+
const columns = rows[0] ? Object.keys(rows[0]) : [];
|
|
582
|
+
const resultRows = rows.map((row) => buildRow(row, columns));
|
|
583
|
+
return {
|
|
584
|
+
columns,
|
|
585
|
+
columnTypes: columns.map(() => ""),
|
|
586
|
+
rows: resultRows,
|
|
587
|
+
rowsAffected,
|
|
588
|
+
lastInsertRowid: void 0,
|
|
589
|
+
toJSON() {
|
|
590
|
+
return {
|
|
591
|
+
columns,
|
|
592
|
+
columnTypes: columns.map(() => ""),
|
|
593
|
+
rows,
|
|
594
|
+
rowsAffected,
|
|
595
|
+
lastInsertRowid: void 0
|
|
596
|
+
};
|
|
597
|
+
}
|
|
598
|
+
};
|
|
599
|
+
}
|
|
600
|
+
async function loadPrismaClient() {
|
|
601
|
+
if (!prismaClientPromise) {
|
|
602
|
+
prismaClientPromise = (async () => {
|
|
603
|
+
const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
|
|
604
|
+
if (explicitPath) {
|
|
605
|
+
const module2 = await import(pathToFileURL(explicitPath).href);
|
|
606
|
+
const PrismaClient2 = module2.PrismaClient ?? module2.default?.PrismaClient;
|
|
607
|
+
if (!PrismaClient2) {
|
|
608
|
+
throw new Error(`No PrismaClient export found at ${explicitPath}`);
|
|
609
|
+
}
|
|
610
|
+
return new PrismaClient2();
|
|
611
|
+
}
|
|
612
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path3.join(os3.homedir(), "exe-db");
|
|
613
|
+
const requireFromExeDb = createRequire(path3.join(exeDbRoot, "package.json"));
|
|
614
|
+
const prismaEntry = requireFromExeDb.resolve("@prisma/client");
|
|
615
|
+
const module = await import(pathToFileURL(prismaEntry).href);
|
|
616
|
+
const PrismaClient = module.PrismaClient ?? module.default?.PrismaClient;
|
|
617
|
+
if (!PrismaClient) {
|
|
618
|
+
throw new Error(`No PrismaClient export found in ${prismaEntry}`);
|
|
619
|
+
}
|
|
620
|
+
return new PrismaClient();
|
|
621
|
+
})();
|
|
622
|
+
}
|
|
623
|
+
return prismaClientPromise;
|
|
624
|
+
}
|
|
625
|
+
async function ensureCompatibilityViews(prisma) {
|
|
626
|
+
if (!compatibilityBootstrapPromise) {
|
|
627
|
+
compatibilityBootstrapPromise = (async () => {
|
|
628
|
+
for (const mapping of VIEW_MAPPINGS) {
|
|
629
|
+
const relation = mapping.source.replace(/"/g, "");
|
|
630
|
+
const rows = await prisma.$queryRawUnsafe(
|
|
631
|
+
"SELECT to_regclass($1) AS regclass",
|
|
632
|
+
relation
|
|
633
|
+
);
|
|
634
|
+
if (!rows[0]?.regclass) {
|
|
635
|
+
continue;
|
|
636
|
+
}
|
|
637
|
+
await prisma.$executeRawUnsafe(
|
|
638
|
+
`CREATE OR REPLACE VIEW public.${quotedIdentifier(mapping.view)} AS SELECT * FROM ${mapping.source}`
|
|
639
|
+
);
|
|
640
|
+
}
|
|
641
|
+
})();
|
|
642
|
+
}
|
|
643
|
+
return compatibilityBootstrapPromise;
|
|
644
|
+
}
|
|
645
|
+
async function executeOnPrisma(executor, stmt) {
|
|
646
|
+
const translated = translateStatementForPostgres(stmt);
|
|
647
|
+
if (isReadQuery(translated.sql)) {
|
|
648
|
+
const rows = await executor.$queryRawUnsafe(
|
|
649
|
+
translated.sql,
|
|
650
|
+
...translated.args
|
|
651
|
+
);
|
|
652
|
+
return buildResultSet(rows, /\bRETURNING\b/iu.test(translated.sql) ? rows.length : 0);
|
|
653
|
+
}
|
|
654
|
+
const rowsAffected = await executor.$executeRawUnsafe(translated.sql, ...translated.args);
|
|
655
|
+
return buildResultSet([], rowsAffected);
|
|
656
|
+
}
|
|
657
|
+
function splitSqlStatements(sql) {
|
|
658
|
+
const parts = [];
|
|
659
|
+
let current = "";
|
|
660
|
+
let inSingle = false;
|
|
661
|
+
let inDouble = false;
|
|
662
|
+
let inLineComment = false;
|
|
663
|
+
let inBlockComment = false;
|
|
664
|
+
for (let i = 0; i < sql.length; i++) {
|
|
665
|
+
const ch = sql[i];
|
|
666
|
+
const next = sql[i + 1];
|
|
667
|
+
if (inLineComment) {
|
|
668
|
+
current += ch;
|
|
669
|
+
if (ch === "\n") inLineComment = false;
|
|
670
|
+
continue;
|
|
671
|
+
}
|
|
672
|
+
if (inBlockComment) {
|
|
673
|
+
current += ch;
|
|
674
|
+
if (ch === "*" && next === "/") {
|
|
675
|
+
current += next;
|
|
676
|
+
inBlockComment = false;
|
|
677
|
+
i += 1;
|
|
678
|
+
}
|
|
679
|
+
continue;
|
|
680
|
+
}
|
|
681
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
682
|
+
current += ch + next;
|
|
683
|
+
inLineComment = true;
|
|
684
|
+
i += 1;
|
|
685
|
+
continue;
|
|
686
|
+
}
|
|
687
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
688
|
+
current += ch + next;
|
|
689
|
+
inBlockComment = true;
|
|
690
|
+
i += 1;
|
|
691
|
+
continue;
|
|
692
|
+
}
|
|
693
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
694
|
+
inSingle = !inSingle;
|
|
695
|
+
current += ch;
|
|
696
|
+
continue;
|
|
697
|
+
}
|
|
698
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
699
|
+
inDouble = !inDouble;
|
|
700
|
+
current += ch;
|
|
701
|
+
continue;
|
|
702
|
+
}
|
|
703
|
+
if (!inSingle && !inDouble && ch === ";") {
|
|
704
|
+
if (current.trim()) {
|
|
705
|
+
parts.push(current.trim());
|
|
706
|
+
}
|
|
707
|
+
current = "";
|
|
708
|
+
continue;
|
|
709
|
+
}
|
|
710
|
+
current += ch;
|
|
711
|
+
}
|
|
712
|
+
if (current.trim()) {
|
|
713
|
+
parts.push(current.trim());
|
|
714
|
+
}
|
|
715
|
+
return parts;
|
|
716
|
+
}
|
|
717
|
+
async function createPrismaDbAdapter(fallbackClient) {
|
|
718
|
+
const prisma = await loadPrismaClient();
|
|
719
|
+
await ensureCompatibilityViews(prisma);
|
|
720
|
+
let closed = false;
|
|
721
|
+
let adapter;
|
|
722
|
+
const fallbackExecute = async (stmt, error) => {
|
|
723
|
+
if (!fallbackClient) {
|
|
724
|
+
if (error) throw error;
|
|
725
|
+
throw new Error("No fallback SQLite client is available for this Prisma-routed query.");
|
|
726
|
+
}
|
|
727
|
+
if (error) {
|
|
728
|
+
process.stderr.write(
|
|
729
|
+
`[database-adapter] Falling back to SQLite: ${error instanceof Error ? error.message : String(error)}
|
|
730
|
+
`
|
|
731
|
+
);
|
|
732
|
+
}
|
|
733
|
+
return fallbackClient.execute(stmt);
|
|
734
|
+
};
|
|
735
|
+
adapter = {
|
|
736
|
+
async execute(stmt) {
|
|
737
|
+
if (shouldBypassPostgres(stmt)) {
|
|
738
|
+
return fallbackExecute(stmt);
|
|
739
|
+
}
|
|
740
|
+
try {
|
|
741
|
+
return await executeOnPrisma(prisma, stmt);
|
|
742
|
+
} catch (error) {
|
|
743
|
+
if (shouldFallbackOnError(error)) {
|
|
744
|
+
return fallbackExecute(stmt, error);
|
|
745
|
+
}
|
|
746
|
+
throw error;
|
|
747
|
+
}
|
|
748
|
+
},
|
|
749
|
+
async batch(stmts, mode) {
|
|
750
|
+
if (stmts.some((stmt) => shouldBypassPostgres(stmt))) {
|
|
751
|
+
if (!fallbackClient) {
|
|
752
|
+
throw new Error("Cannot batch unsupported SQLite-only statements without a fallback client.");
|
|
753
|
+
}
|
|
754
|
+
return fallbackClient.batch(stmts, mode);
|
|
755
|
+
}
|
|
756
|
+
try {
|
|
757
|
+
if (prisma.$transaction) {
|
|
758
|
+
return await prisma.$transaction(async (tx) => {
|
|
759
|
+
const results2 = [];
|
|
760
|
+
for (const stmt of stmts) {
|
|
761
|
+
results2.push(await executeOnPrisma(tx, stmt));
|
|
762
|
+
}
|
|
763
|
+
return results2;
|
|
764
|
+
});
|
|
765
|
+
}
|
|
766
|
+
const results = [];
|
|
767
|
+
for (const stmt of stmts) {
|
|
768
|
+
results.push(await executeOnPrisma(prisma, stmt));
|
|
769
|
+
}
|
|
770
|
+
return results;
|
|
771
|
+
} catch (error) {
|
|
772
|
+
if (fallbackClient && shouldFallbackOnError(error)) {
|
|
773
|
+
process.stderr.write(
|
|
774
|
+
`[database-adapter] Falling back batch to SQLite: ${error instanceof Error ? error.message : String(error)}
|
|
775
|
+
`
|
|
776
|
+
);
|
|
777
|
+
return fallbackClient.batch(stmts, mode);
|
|
778
|
+
}
|
|
779
|
+
throw error;
|
|
780
|
+
}
|
|
781
|
+
},
|
|
782
|
+
async migrate(stmts) {
|
|
783
|
+
if (fallbackClient) {
|
|
784
|
+
return fallbackClient.migrate(stmts);
|
|
785
|
+
}
|
|
786
|
+
return adapter.batch(stmts, "deferred");
|
|
787
|
+
},
|
|
788
|
+
async transaction(mode) {
|
|
789
|
+
if (!fallbackClient) {
|
|
790
|
+
throw new Error("Interactive transactions are only supported on the SQLite fallback client.");
|
|
791
|
+
}
|
|
792
|
+
return fallbackClient.transaction(mode);
|
|
793
|
+
},
|
|
794
|
+
async executeMultiple(sql) {
|
|
795
|
+
if (fallbackClient && shouldBypassPostgres(sql)) {
|
|
796
|
+
return fallbackClient.executeMultiple(sql);
|
|
797
|
+
}
|
|
798
|
+
for (const statement of splitSqlStatements(sql)) {
|
|
799
|
+
await adapter.execute(statement);
|
|
800
|
+
}
|
|
801
|
+
},
|
|
802
|
+
async sync() {
|
|
803
|
+
if (fallbackClient) {
|
|
804
|
+
return fallbackClient.sync();
|
|
805
|
+
}
|
|
806
|
+
return { frame_no: 0, frames_synced: 0 };
|
|
807
|
+
},
|
|
808
|
+
close() {
|
|
809
|
+
closed = true;
|
|
810
|
+
prismaClientPromise = null;
|
|
811
|
+
compatibilityBootstrapPromise = null;
|
|
812
|
+
void prisma.$disconnect?.();
|
|
813
|
+
},
|
|
814
|
+
get closed() {
|
|
815
|
+
return closed;
|
|
816
|
+
},
|
|
817
|
+
get protocol() {
|
|
818
|
+
return "prisma-postgres";
|
|
819
|
+
}
|
|
820
|
+
};
|
|
821
|
+
return adapter;
|
|
822
|
+
}
|
|
823
|
+
var VIEW_MAPPINGS, UPSERT_KEYS, BOOLEAN_COLUMNS_BY_TABLE, BOOLEAN_COLUMN_NAMES, IMMEDIATE_FALLBACK_PATTERNS, prismaClientPromise, compatibilityBootstrapPromise;
|
|
824
|
+
var init_database_adapter = __esm({
|
|
825
|
+
"src/lib/database-adapter.ts"() {
|
|
826
|
+
"use strict";
|
|
827
|
+
VIEW_MAPPINGS = [
|
|
828
|
+
{ view: "memories", source: "memory.memory_records" },
|
|
829
|
+
{ view: "tasks", source: "memory.tasks" },
|
|
830
|
+
{ view: "behaviors", source: "memory.behaviors" },
|
|
831
|
+
{ view: "entities", source: "memory.entities" },
|
|
832
|
+
{ view: "relationships", source: "memory.relationships" },
|
|
833
|
+
{ view: "entity_memories", source: "memory.entity_memories" },
|
|
834
|
+
{ view: "entity_aliases", source: "memory.entity_aliases" },
|
|
835
|
+
{ view: "notifications", source: "memory.notifications" },
|
|
836
|
+
{ view: "messages", source: "memory.messages" },
|
|
837
|
+
{ view: "users", source: "wiki.users" },
|
|
838
|
+
{ view: "workspaces", source: "wiki.workspaces" },
|
|
839
|
+
{ view: "workspace_users", source: "wiki.workspace_users" },
|
|
840
|
+
{ view: "documents", source: "wiki.workspace_documents" },
|
|
841
|
+
{ view: "chats", source: "wiki.workspace_chats" }
|
|
842
|
+
];
|
|
843
|
+
UPSERT_KEYS = {
|
|
844
|
+
memories: ["id"],
|
|
845
|
+
tasks: ["id"],
|
|
846
|
+
behaviors: ["id"],
|
|
847
|
+
entities: ["id"],
|
|
848
|
+
relationships: ["id"],
|
|
849
|
+
entity_aliases: ["alias"],
|
|
850
|
+
notifications: ["id"],
|
|
851
|
+
messages: ["id"],
|
|
852
|
+
users: ["id"],
|
|
853
|
+
workspaces: ["id"],
|
|
854
|
+
workspace_users: ["id"],
|
|
855
|
+
documents: ["id"],
|
|
856
|
+
chats: ["id"]
|
|
857
|
+
};
|
|
858
|
+
BOOLEAN_COLUMNS_BY_TABLE = {
|
|
859
|
+
memories: /* @__PURE__ */ new Set(["has_error", "draft"]),
|
|
860
|
+
behaviors: /* @__PURE__ */ new Set(["active"]),
|
|
861
|
+
notifications: /* @__PURE__ */ new Set(["read"]),
|
|
862
|
+
users: /* @__PURE__ */ new Set(["has_personal_memory"])
|
|
863
|
+
};
|
|
864
|
+
BOOLEAN_COLUMN_NAMES = new Set(
|
|
865
|
+
Object.values(BOOLEAN_COLUMNS_BY_TABLE).flatMap((cols) => [...cols])
|
|
866
|
+
);
|
|
867
|
+
IMMEDIATE_FALLBACK_PATTERNS = [
|
|
868
|
+
/\bPRAGMA\b/i,
|
|
869
|
+
/\bsqlite_master\b/i,
|
|
870
|
+
/(?:^|[.\s])(?:memories|conversations|entities)_fts\b/i,
|
|
871
|
+
/\bMATCH\b/i,
|
|
872
|
+
/\bvector_distance_cos\s*\(/i,
|
|
873
|
+
/\bjson_extract\s*\(/i,
|
|
874
|
+
/\bjulianday\s*\(/i,
|
|
875
|
+
/\bstrftime\s*\(/i,
|
|
876
|
+
/\blast_insert_rowid\s*\(/i
|
|
877
|
+
];
|
|
878
|
+
prismaClientPromise = null;
|
|
879
|
+
compatibilityBootstrapPromise = null;
|
|
295
880
|
}
|
|
296
881
|
});
|
|
297
882
|
|
|
298
883
|
// src/lib/database.ts
|
|
299
884
|
import { createClient } from "@libsql/client";
|
|
300
885
|
async function initDatabase(config) {
|
|
886
|
+
if (_walCheckpointTimer) {
|
|
887
|
+
clearInterval(_walCheckpointTimer);
|
|
888
|
+
_walCheckpointTimer = null;
|
|
889
|
+
}
|
|
890
|
+
if (_daemonClient) {
|
|
891
|
+
_daemonClient.close();
|
|
892
|
+
_daemonClient = null;
|
|
893
|
+
}
|
|
894
|
+
if (_adapterClient && _adapterClient !== _resilientClient) {
|
|
895
|
+
_adapterClient.close();
|
|
896
|
+
}
|
|
897
|
+
_adapterClient = null;
|
|
301
898
|
if (_client) {
|
|
302
899
|
_client.close();
|
|
303
900
|
_client = null;
|
|
@@ -311,6 +908,7 @@ async function initDatabase(config) {
|
|
|
311
908
|
}
|
|
312
909
|
_client = createClient(opts);
|
|
313
910
|
_resilientClient = wrapWithRetry(_client);
|
|
911
|
+
_adapterClient = _resilientClient;
|
|
314
912
|
_client.execute("PRAGMA busy_timeout = 30000").catch(() => {
|
|
315
913
|
});
|
|
316
914
|
_client.execute("PRAGMA journal_mode = WAL").catch(() => {
|
|
@@ -321,11 +919,17 @@ async function initDatabase(config) {
|
|
|
321
919
|
});
|
|
322
920
|
}, 3e4);
|
|
323
921
|
_walCheckpointTimer.unref();
|
|
922
|
+
if (process.env.DATABASE_URL) {
|
|
923
|
+
_adapterClient = await createPrismaDbAdapter(_resilientClient);
|
|
924
|
+
}
|
|
324
925
|
}
|
|
325
926
|
function getClient() {
|
|
326
|
-
if (!
|
|
927
|
+
if (!_adapterClient) {
|
|
327
928
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
328
929
|
}
|
|
930
|
+
if (process.env.DATABASE_URL) {
|
|
931
|
+
return _adapterClient;
|
|
932
|
+
}
|
|
329
933
|
if (process.env.EXE_IS_DAEMON === "1") {
|
|
330
934
|
return _resilientClient;
|
|
331
935
|
}
|
|
@@ -1266,26 +1870,36 @@ async function ensureSchema() {
|
|
|
1266
1870
|
}
|
|
1267
1871
|
}
|
|
1268
1872
|
async function disposeDatabase() {
|
|
1873
|
+
if (_walCheckpointTimer) {
|
|
1874
|
+
clearInterval(_walCheckpointTimer);
|
|
1875
|
+
_walCheckpointTimer = null;
|
|
1876
|
+
}
|
|
1269
1877
|
if (_daemonClient) {
|
|
1270
1878
|
_daemonClient.close();
|
|
1271
1879
|
_daemonClient = null;
|
|
1272
1880
|
}
|
|
1881
|
+
if (_adapterClient && _adapterClient !== _resilientClient) {
|
|
1882
|
+
_adapterClient.close();
|
|
1883
|
+
}
|
|
1884
|
+
_adapterClient = null;
|
|
1273
1885
|
if (_client) {
|
|
1274
1886
|
_client.close();
|
|
1275
1887
|
_client = null;
|
|
1276
1888
|
_resilientClient = null;
|
|
1277
1889
|
}
|
|
1278
1890
|
}
|
|
1279
|
-
var _client, _resilientClient, _walCheckpointTimer, _daemonClient, initTurso, disposeTurso;
|
|
1891
|
+
var _client, _resilientClient, _walCheckpointTimer, _daemonClient, _adapterClient, initTurso, disposeTurso;
|
|
1280
1892
|
var init_database = __esm({
|
|
1281
1893
|
"src/lib/database.ts"() {
|
|
1282
1894
|
"use strict";
|
|
1283
1895
|
init_db_retry();
|
|
1284
1896
|
init_employees();
|
|
1897
|
+
init_database_adapter();
|
|
1285
1898
|
_client = null;
|
|
1286
1899
|
_resilientClient = null;
|
|
1287
1900
|
_walCheckpointTimer = null;
|
|
1288
1901
|
_daemonClient = null;
|
|
1902
|
+
_adapterClient = null;
|
|
1289
1903
|
initTurso = initDatabase;
|
|
1290
1904
|
disposeTurso = disposeDatabase;
|
|
1291
1905
|
}
|
|
@@ -1304,7 +1918,7 @@ __export(shard_manager_exports, {
|
|
|
1304
1918
|
listShards: () => listShards,
|
|
1305
1919
|
shardExists: () => shardExists
|
|
1306
1920
|
});
|
|
1307
|
-
import
|
|
1921
|
+
import path5 from "path";
|
|
1308
1922
|
import { existsSync as existsSync4, mkdirSync, readdirSync } from "fs";
|
|
1309
1923
|
import { createClient as createClient2 } from "@libsql/client";
|
|
1310
1924
|
function initShardManager(encryptionKey) {
|
|
@@ -1330,7 +1944,7 @@ function getShardClient(projectName) {
|
|
|
1330
1944
|
}
|
|
1331
1945
|
const cached = _shards.get(safeName);
|
|
1332
1946
|
if (cached) return cached;
|
|
1333
|
-
const dbPath =
|
|
1947
|
+
const dbPath = path5.join(SHARDS_DIR, `${safeName}.db`);
|
|
1334
1948
|
const client = createClient2({
|
|
1335
1949
|
url: `file:${dbPath}`,
|
|
1336
1950
|
encryptionKey: _encryptionKey
|
|
@@ -1340,7 +1954,7 @@ function getShardClient(projectName) {
|
|
|
1340
1954
|
}
|
|
1341
1955
|
function shardExists(projectName) {
|
|
1342
1956
|
const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
1343
|
-
return existsSync4(
|
|
1957
|
+
return existsSync4(path5.join(SHARDS_DIR, `${safeName}.db`));
|
|
1344
1958
|
}
|
|
1345
1959
|
function listShards() {
|
|
1346
1960
|
if (!existsSync4(SHARDS_DIR)) return [];
|
|
@@ -1417,7 +2031,23 @@ async function ensureShardSchema(client) {
|
|
|
1417
2031
|
// MS-11: draft staging, MS-6a: memory_type, MS-7: trajectory
|
|
1418
2032
|
"ALTER TABLE memories ADD COLUMN draft INTEGER DEFAULT 0",
|
|
1419
2033
|
"ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'",
|
|
1420
|
-
"ALTER TABLE memories ADD COLUMN trajectory TEXT"
|
|
2034
|
+
"ALTER TABLE memories ADD COLUMN trajectory TEXT",
|
|
2035
|
+
// Metadata enrichment columns (must match database.ts)
|
|
2036
|
+
"ALTER TABLE memories ADD COLUMN intent TEXT",
|
|
2037
|
+
"ALTER TABLE memories ADD COLUMN outcome TEXT",
|
|
2038
|
+
"ALTER TABLE memories ADD COLUMN domain TEXT",
|
|
2039
|
+
"ALTER TABLE memories ADD COLUMN referenced_entities TEXT",
|
|
2040
|
+
"ALTER TABLE memories ADD COLUMN retrieval_count INTEGER DEFAULT 0",
|
|
2041
|
+
"ALTER TABLE memories ADD COLUMN chain_position TEXT",
|
|
2042
|
+
"ALTER TABLE memories ADD COLUMN review_status TEXT",
|
|
2043
|
+
"ALTER TABLE memories ADD COLUMN context_window_pct INTEGER",
|
|
2044
|
+
"ALTER TABLE memories ADD COLUMN file_paths TEXT",
|
|
2045
|
+
"ALTER TABLE memories ADD COLUMN commit_hash TEXT",
|
|
2046
|
+
"ALTER TABLE memories ADD COLUMN duration_ms INTEGER",
|
|
2047
|
+
"ALTER TABLE memories ADD COLUMN token_cost REAL",
|
|
2048
|
+
"ALTER TABLE memories ADD COLUMN audience TEXT",
|
|
2049
|
+
"ALTER TABLE memories ADD COLUMN language_type TEXT",
|
|
2050
|
+
"ALTER TABLE memories ADD COLUMN parent_memory_id TEXT"
|
|
1421
2051
|
]) {
|
|
1422
2052
|
try {
|
|
1423
2053
|
await client.execute(col);
|
|
@@ -1529,7 +2159,7 @@ var init_shard_manager = __esm({
|
|
|
1529
2159
|
"src/lib/shard-manager.ts"() {
|
|
1530
2160
|
"use strict";
|
|
1531
2161
|
init_config();
|
|
1532
|
-
SHARDS_DIR =
|
|
2162
|
+
SHARDS_DIR = path5.join(EXE_AI_DIR, "shards");
|
|
1533
2163
|
_shards = /* @__PURE__ */ new Map();
|
|
1534
2164
|
_encryptionKey = null;
|
|
1535
2165
|
_shardingEnabled = false;
|
|
@@ -1723,6 +2353,32 @@ ${p.content}`).join("\n\n");
|
|
|
1723
2353
|
}
|
|
1724
2354
|
});
|
|
1725
2355
|
|
|
2356
|
+
// src/lib/runtime-table.ts
|
|
2357
|
+
var RUNTIME_TABLE;
|
|
2358
|
+
var init_runtime_table = __esm({
|
|
2359
|
+
"src/lib/runtime-table.ts"() {
|
|
2360
|
+
"use strict";
|
|
2361
|
+
RUNTIME_TABLE = {
|
|
2362
|
+
codex: {
|
|
2363
|
+
binary: "codex",
|
|
2364
|
+
launchMode: "interactive",
|
|
2365
|
+
autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
|
|
2366
|
+
inlineFlag: "--no-alt-screen",
|
|
2367
|
+
apiKeyEnv: "OPENAI_API_KEY",
|
|
2368
|
+
defaultModel: "gpt-5.4"
|
|
2369
|
+
},
|
|
2370
|
+
opencode: {
|
|
2371
|
+
binary: "opencode",
|
|
2372
|
+
launchMode: "exec",
|
|
2373
|
+
autoApproveFlag: "--dangerously-skip-permissions",
|
|
2374
|
+
inlineFlag: "",
|
|
2375
|
+
apiKeyEnv: "ANTHROPIC_API_KEY",
|
|
2376
|
+
defaultModel: "anthropic/claude-sonnet-4-6"
|
|
2377
|
+
}
|
|
2378
|
+
};
|
|
2379
|
+
}
|
|
2380
|
+
});
|
|
2381
|
+
|
|
1726
2382
|
// src/lib/session-key.ts
|
|
1727
2383
|
import { execSync as execSync2 } from "child_process";
|
|
1728
2384
|
function normalizeCommand(command) {
|
|
@@ -1803,7 +2459,7 @@ __export(active_agent_exports, {
|
|
|
1803
2459
|
});
|
|
1804
2460
|
import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, unlinkSync as unlinkSync3, readdirSync as readdirSync3 } from "fs";
|
|
1805
2461
|
import { execSync as execSync3 } from "child_process";
|
|
1806
|
-
import
|
|
2462
|
+
import path7 from "path";
|
|
1807
2463
|
function isNameWithOptionalInstance(candidate, baseName) {
|
|
1808
2464
|
if (candidate === baseName) return true;
|
|
1809
2465
|
if (!candidate.startsWith(baseName)) return false;
|
|
@@ -1847,7 +2503,7 @@ function resolveActiveAgentFromTmuxSession(sessionName) {
|
|
|
1847
2503
|
return null;
|
|
1848
2504
|
}
|
|
1849
2505
|
function getMarkerPath() {
|
|
1850
|
-
return
|
|
2506
|
+
return path7.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
|
|
1851
2507
|
}
|
|
1852
2508
|
function writeActiveAgent(agentId, agentRole) {
|
|
1853
2509
|
try {
|
|
@@ -1916,14 +2572,14 @@ function getAllActiveAgents() {
|
|
|
1916
2572
|
const key = file.slice("active-agent-".length, -".json".length);
|
|
1917
2573
|
if (key === "undefined") continue;
|
|
1918
2574
|
try {
|
|
1919
|
-
const raw = readFileSync3(
|
|
2575
|
+
const raw = readFileSync3(path7.join(CACHE_DIR, file), "utf8");
|
|
1920
2576
|
const data = JSON.parse(raw);
|
|
1921
2577
|
if (!data.agentId) continue;
|
|
1922
2578
|
if (data.startedAt) {
|
|
1923
2579
|
const age = Date.now() - new Date(data.startedAt).getTime();
|
|
1924
2580
|
if (age > STALE_MS) {
|
|
1925
2581
|
try {
|
|
1926
|
-
unlinkSync3(
|
|
2582
|
+
unlinkSync3(path7.join(CACHE_DIR, file));
|
|
1927
2583
|
} catch {
|
|
1928
2584
|
}
|
|
1929
2585
|
continue;
|
|
@@ -1946,11 +2602,11 @@ function getAllActiveAgents() {
|
|
|
1946
2602
|
function cleanupSessionMarkers() {
|
|
1947
2603
|
const key = getSessionKey();
|
|
1948
2604
|
try {
|
|
1949
|
-
unlinkSync3(
|
|
2605
|
+
unlinkSync3(path7.join(CACHE_DIR, `active-agent-${key}.json`));
|
|
1950
2606
|
} catch {
|
|
1951
2607
|
}
|
|
1952
2608
|
try {
|
|
1953
|
-
unlinkSync3(
|
|
2609
|
+
unlinkSync3(path7.join(CACHE_DIR, "active-agent-undefined.json"));
|
|
1954
2610
|
} catch {
|
|
1955
2611
|
}
|
|
1956
2612
|
}
|
|
@@ -1961,14 +2617,14 @@ var init_active_agent = __esm({
|
|
|
1961
2617
|
init_config();
|
|
1962
2618
|
init_session_key();
|
|
1963
2619
|
init_employees();
|
|
1964
|
-
CACHE_DIR =
|
|
2620
|
+
CACHE_DIR = path7.join(EXE_AI_DIR, "session-cache");
|
|
1965
2621
|
STALE_MS = 24 * 60 * 60 * 1e3;
|
|
1966
2622
|
}
|
|
1967
2623
|
});
|
|
1968
2624
|
|
|
1969
2625
|
// src/lib/agent-symlinks.ts
|
|
1970
|
-
import
|
|
1971
|
-
import
|
|
2626
|
+
import os6 from "os";
|
|
2627
|
+
import path8 from "path";
|
|
1972
2628
|
import {
|
|
1973
2629
|
existsSync as existsSync6,
|
|
1974
2630
|
lstatSync,
|
|
@@ -1999,8 +2655,8 @@ var init_mcp_prefix = __esm({
|
|
|
1999
2655
|
|
|
2000
2656
|
// src/lib/preferences.ts
|
|
2001
2657
|
import { existsSync as existsSync7, readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync5 } from "fs";
|
|
2002
|
-
import
|
|
2003
|
-
import
|
|
2658
|
+
import path9 from "path";
|
|
2659
|
+
import os7 from "os";
|
|
2004
2660
|
var init_preferences = __esm({
|
|
2005
2661
|
"src/lib/preferences.ts"() {
|
|
2006
2662
|
"use strict";
|
|
@@ -2010,16 +2666,16 @@ var init_preferences = __esm({
|
|
|
2010
2666
|
// src/adapters/claude/installer.ts
|
|
2011
2667
|
import { readFile as readFile4, writeFile as writeFile4, mkdir as mkdir4, readdir } from "fs/promises";
|
|
2012
2668
|
import { existsSync as existsSync8, readFileSync as readFileSync5, writeFileSync as writeFileSync5, copyFileSync, mkdirSync as mkdirSync6 } from "fs";
|
|
2013
|
-
import
|
|
2014
|
-
import
|
|
2669
|
+
import path10 from "path";
|
|
2670
|
+
import os8 from "os";
|
|
2015
2671
|
import { execSync as execSync4 } from "child_process";
|
|
2016
2672
|
import { fileURLToPath } from "url";
|
|
2017
2673
|
function resolvePackageRoot() {
|
|
2018
2674
|
const thisFile = fileURLToPath(import.meta.url);
|
|
2019
|
-
let dir =
|
|
2020
|
-
const root =
|
|
2675
|
+
let dir = path10.dirname(thisFile);
|
|
2676
|
+
const root = path10.parse(dir).root;
|
|
2021
2677
|
while (dir !== root) {
|
|
2022
|
-
const pkgPath =
|
|
2678
|
+
const pkgPath = path10.join(dir, "package.json");
|
|
2023
2679
|
if (existsSync8(pkgPath)) {
|
|
2024
2680
|
try {
|
|
2025
2681
|
const pkg = JSON.parse(readFileSync5(pkgPath, "utf-8"));
|
|
@@ -2027,9 +2683,9 @@ function resolvePackageRoot() {
|
|
|
2027
2683
|
} catch {
|
|
2028
2684
|
}
|
|
2029
2685
|
}
|
|
2030
|
-
dir =
|
|
2686
|
+
dir = path10.dirname(dir);
|
|
2031
2687
|
}
|
|
2032
|
-
return
|
|
2688
|
+
return path10.resolve(path10.dirname(thisFile), "..", "..", "..");
|
|
2033
2689
|
}
|
|
2034
2690
|
var EXE_SECTION_START, EXE_SECTION_END, ORCHESTRATION_RULES;
|
|
2035
2691
|
var init_installer = __esm({
|
|
@@ -2248,11 +2904,11 @@ __export(installer_exports, {
|
|
|
2248
2904
|
});
|
|
2249
2905
|
import { readFile as readFile5, writeFile as writeFile5, mkdir as mkdir5 } from "fs/promises";
|
|
2250
2906
|
import { existsSync as existsSync9, readFileSync as readFileSync6 } from "fs";
|
|
2251
|
-
import
|
|
2252
|
-
import
|
|
2253
|
-
async function registerOpenCodeMcp(packageRoot, homeDir =
|
|
2254
|
-
const configDir =
|
|
2255
|
-
const configPath =
|
|
2907
|
+
import path11 from "path";
|
|
2908
|
+
import os9 from "os";
|
|
2909
|
+
async function registerOpenCodeMcp(packageRoot, homeDir = os9.homedir()) {
|
|
2910
|
+
const configDir = path11.join(homeDir, ".config", "opencode");
|
|
2911
|
+
const configPath = path11.join(configDir, "opencode.json");
|
|
2256
2912
|
await mkdir5(configDir, { recursive: true });
|
|
2257
2913
|
let config = {};
|
|
2258
2914
|
if (existsSync9(configPath)) {
|
|
@@ -2267,7 +2923,7 @@ async function registerOpenCodeMcp(packageRoot, homeDir = os8.homedir()) {
|
|
|
2267
2923
|
}
|
|
2268
2924
|
const newEntry = {
|
|
2269
2925
|
type: "local",
|
|
2270
|
-
command: ["node",
|
|
2926
|
+
command: ["node", path11.join(packageRoot, "dist", "mcp", "server.js")],
|
|
2271
2927
|
enabled: true
|
|
2272
2928
|
};
|
|
2273
2929
|
const current = config.mcp["exe-os"];
|
|
@@ -2281,9 +2937,9 @@ async function registerOpenCodeMcp(packageRoot, homeDir = os8.homedir()) {
|
|
|
2281
2937
|
await writeFile5(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
2282
2938
|
return true;
|
|
2283
2939
|
}
|
|
2284
|
-
async function installOpenCodePlugin(packageRoot, homeDir =
|
|
2285
|
-
const pluginDir =
|
|
2286
|
-
const pluginPath =
|
|
2940
|
+
async function installOpenCodePlugin(packageRoot, homeDir = os9.homedir()) {
|
|
2941
|
+
const pluginDir = path11.join(homeDir, ".config", "opencode", "plugins");
|
|
2942
|
+
const pluginPath = path11.join(pluginDir, "exe-os.mjs");
|
|
2287
2943
|
await mkdir5(pluginDir, { recursive: true });
|
|
2288
2944
|
const pluginContent = PLUGIN_TEMPLATE.replace(
|
|
2289
2945
|
/__PACKAGE_ROOT__/g,
|
|
@@ -2298,9 +2954,9 @@ async function installOpenCodePlugin(packageRoot, homeDir = os8.homedir()) {
|
|
|
2298
2954
|
await writeFile5(pluginPath, pluginContent);
|
|
2299
2955
|
return true;
|
|
2300
2956
|
}
|
|
2301
|
-
function verifyOpenCodeHooks(homeDir =
|
|
2302
|
-
const configPath =
|
|
2303
|
-
const pluginPath =
|
|
2957
|
+
function verifyOpenCodeHooks(homeDir = os9.homedir()) {
|
|
2958
|
+
const configPath = path11.join(homeDir, ".config", "opencode", "opencode.json");
|
|
2959
|
+
const pluginPath = path11.join(homeDir, ".config", "opencode", "plugins", "exe-os.mjs");
|
|
2304
2960
|
if (!existsSync9(configPath)) return false;
|
|
2305
2961
|
try {
|
|
2306
2962
|
const config = JSON.parse(readFileSync6(configPath, "utf-8"));
|
|
@@ -2333,8 +2989,8 @@ var init_installer2 = __esm({
|
|
|
2333
2989
|
});
|
|
2334
2990
|
|
|
2335
2991
|
// src/bin/exe-start-opencode.ts
|
|
2336
|
-
import
|
|
2337
|
-
import
|
|
2992
|
+
import os10 from "os";
|
|
2993
|
+
import path12 from "path";
|
|
2338
2994
|
import {
|
|
2339
2995
|
existsSync as existsSync10,
|
|
2340
2996
|
readFileSync as readFileSync7,
|
|
@@ -2351,15 +3007,15 @@ init_database();
|
|
|
2351
3007
|
// src/lib/keychain.ts
|
|
2352
3008
|
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
2353
3009
|
import { existsSync as existsSync3 } from "fs";
|
|
2354
|
-
import
|
|
2355
|
-
import
|
|
3010
|
+
import path4 from "path";
|
|
3011
|
+
import os4 from "os";
|
|
2356
3012
|
var SERVICE = "exe-mem";
|
|
2357
3013
|
var ACCOUNT = "master-key";
|
|
2358
3014
|
function getKeyDir() {
|
|
2359
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
3015
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path4.join(os4.homedir(), ".exe-os");
|
|
2360
3016
|
}
|
|
2361
3017
|
function getKeyPath() {
|
|
2362
|
-
return
|
|
3018
|
+
return path4.join(getKeyDir(), "master.key");
|
|
2363
3019
|
}
|
|
2364
3020
|
async function tryKeytar() {
|
|
2365
3021
|
try {
|
|
@@ -2382,7 +3038,7 @@ async function getMasterKey() {
|
|
|
2382
3038
|
const keyPath = getKeyPath();
|
|
2383
3039
|
if (!existsSync3(keyPath)) {
|
|
2384
3040
|
process.stderr.write(
|
|
2385
|
-
`[keychain] Key not found at ${keyPath} (HOME=${
|
|
3041
|
+
`[keychain] Key not found at ${keyPath} (HOME=${os4.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
2386
3042
|
`
|
|
2387
3043
|
);
|
|
2388
3044
|
return null;
|
|
@@ -2693,8 +3349,8 @@ function vectorToBlob(vector) {
|
|
|
2693
3349
|
}
|
|
2694
3350
|
|
|
2695
3351
|
// src/lib/behaviors-export.ts
|
|
2696
|
-
import
|
|
2697
|
-
import
|
|
3352
|
+
import os5 from "os";
|
|
3353
|
+
import path6 from "path";
|
|
2698
3354
|
import {
|
|
2699
3355
|
existsSync as existsSync5,
|
|
2700
3356
|
mkdirSync as mkdirSync2,
|
|
@@ -2736,8 +3392,8 @@ async function listBehaviors(agentId, projectName, limit = 30) {
|
|
|
2736
3392
|
}
|
|
2737
3393
|
|
|
2738
3394
|
// src/lib/behaviors-export.ts
|
|
2739
|
-
var BEHAVIORS_EXPORT_DIR =
|
|
2740
|
-
|
|
3395
|
+
var BEHAVIORS_EXPORT_DIR = path6.join(
|
|
3396
|
+
os5.homedir(),
|
|
2741
3397
|
".exe-os",
|
|
2742
3398
|
"behaviors-export"
|
|
2743
3399
|
);
|
|
@@ -2752,7 +3408,7 @@ function sweepStaleBehaviorExports(now = Date.now()) {
|
|
|
2752
3408
|
return;
|
|
2753
3409
|
}
|
|
2754
3410
|
for (const entry of entries) {
|
|
2755
|
-
const filePath =
|
|
3411
|
+
const filePath = path6.join(BEHAVIORS_EXPORT_DIR, entry);
|
|
2756
3412
|
try {
|
|
2757
3413
|
const stat = statSync(filePath);
|
|
2758
3414
|
if (now - stat.mtimeMs > STALE_EXPORT_AGE_MS) {
|
|
@@ -2785,10 +3441,10 @@ function renderBehaviorExport(behaviors) {
|
|
|
2785
3441
|
}
|
|
2786
3442
|
function exportFilePath(agentId, projectName, sessionKey) {
|
|
2787
3443
|
if (!sessionKey) {
|
|
2788
|
-
return
|
|
3444
|
+
return path6.join(BEHAVIORS_EXPORT_DIR, `${agentId}.md`);
|
|
2789
3445
|
}
|
|
2790
3446
|
const safeProject = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
2791
|
-
return
|
|
3447
|
+
return path6.join(
|
|
2792
3448
|
BEHAVIORS_EXPORT_DIR,
|
|
2793
3449
|
`${agentId}-${safeProject}-${sessionKey}.md`
|
|
2794
3450
|
);
|
|
@@ -2806,28 +3462,7 @@ async function exportBehaviorsForAgent(agentId, projectName, sessionKey) {
|
|
|
2806
3462
|
|
|
2807
3463
|
// src/bin/exe-start-opencode.ts
|
|
2808
3464
|
init_employees();
|
|
2809
|
-
|
|
2810
|
-
// src/lib/runtime-table.ts
|
|
2811
|
-
var RUNTIME_TABLE = {
|
|
2812
|
-
codex: {
|
|
2813
|
-
binary: "codex",
|
|
2814
|
-
launchMode: "interactive",
|
|
2815
|
-
autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
|
|
2816
|
-
inlineFlag: "--no-alt-screen",
|
|
2817
|
-
apiKeyEnv: "OPENAI_API_KEY",
|
|
2818
|
-
defaultModel: "gpt-5.4"
|
|
2819
|
-
},
|
|
2820
|
-
opencode: {
|
|
2821
|
-
binary: "opencode",
|
|
2822
|
-
launchMode: "exec",
|
|
2823
|
-
autoApproveFlag: "--dangerously-skip-permissions",
|
|
2824
|
-
inlineFlag: "",
|
|
2825
|
-
apiKeyEnv: "ANTHROPIC_API_KEY",
|
|
2826
|
-
defaultModel: "anthropic/claude-sonnet-4-6"
|
|
2827
|
-
}
|
|
2828
|
-
};
|
|
2829
|
-
|
|
2830
|
-
// src/bin/exe-start-opencode.ts
|
|
3465
|
+
init_runtime_table();
|
|
2831
3466
|
var OC = RUNTIME_TABLE.opencode;
|
|
2832
3467
|
var BOOT_INSTRUCTIONS = `
|
|
2833
3468
|
---
|
|
@@ -2838,7 +3473,7 @@ When done with a task: call update_task with status "done".
|
|
|
2838
3473
|
Always call store_memory to persist important findings.
|
|
2839
3474
|
`;
|
|
2840
3475
|
function resolveAgent(argv) {
|
|
2841
|
-
const invokedAs =
|
|
3476
|
+
const invokedAs = path12.basename(argv[1] ?? "");
|
|
2842
3477
|
if (invokedAs && invokedAs !== "exe-start-opencode" && !invokedAs.endsWith(".js")) {
|
|
2843
3478
|
const agent2 = invokedAs.replace(/-opencode$/, "").toLowerCase();
|
|
2844
3479
|
return { agent: agent2, passthrough: argv.slice(2) };
|
|
@@ -2855,8 +3490,8 @@ function resolveAgent(argv) {
|
|
|
2855
3490
|
return { agent, passthrough };
|
|
2856
3491
|
}
|
|
2857
3492
|
function loadIdentity(agent) {
|
|
2858
|
-
const dir =
|
|
2859
|
-
const exact =
|
|
3493
|
+
const dir = path12.join(os10.homedir(), ".exe-os", "identity");
|
|
3494
|
+
const exact = path12.join(dir, `${agent}.md`);
|
|
2860
3495
|
if (existsSync10(exact)) {
|
|
2861
3496
|
const content = readFileSync7(exact, "utf-8").trim();
|
|
2862
3497
|
if (content) return content;
|
|
@@ -2865,13 +3500,13 @@ function loadIdentity(agent) {
|
|
|
2865
3500
|
const files = readdirSync4(dir);
|
|
2866
3501
|
const match = files.find((f) => f.toLowerCase() === `${agent.toLowerCase()}.md`);
|
|
2867
3502
|
if (match) {
|
|
2868
|
-
const content = readFileSync7(
|
|
3503
|
+
const content = readFileSync7(path12.join(dir, match), "utf-8").trim();
|
|
2869
3504
|
if (content) return content;
|
|
2870
3505
|
}
|
|
2871
3506
|
} catch {
|
|
2872
3507
|
}
|
|
2873
3508
|
try {
|
|
2874
|
-
const rosterPath =
|
|
3509
|
+
const rosterPath = path12.join(os10.homedir(), ".exe-os", "exe-employees.json");
|
|
2875
3510
|
const roster = JSON.parse(readFileSync7(rosterPath, "utf8"));
|
|
2876
3511
|
const emp = roster.find((e) => e.name.toLowerCase() === agent.toLowerCase());
|
|
2877
3512
|
if (emp?.systemPrompt && emp.systemPrompt.trim().length > 20) {
|
|
@@ -2882,7 +3517,7 @@ function loadIdentity(agent) {
|
|
|
2882
3517
|
return null;
|
|
2883
3518
|
}
|
|
2884
3519
|
function writeAgentFile(agent, identity, behaviorsPath) {
|
|
2885
|
-
const agentDir =
|
|
3520
|
+
const agentDir = path12.join(os10.homedir(), ".config", "opencode", "agents");
|
|
2886
3521
|
mkdirSync7(agentDir, { recursive: true });
|
|
2887
3522
|
let content = identity;
|
|
2888
3523
|
if (behaviorsPath && existsSync10(behaviorsPath)) {
|
|
@@ -2892,7 +3527,7 @@ function writeAgentFile(agent, identity, behaviorsPath) {
|
|
|
2892
3527
|
}
|
|
2893
3528
|
}
|
|
2894
3529
|
content += "\n" + BOOT_INSTRUCTIONS;
|
|
2895
|
-
const outPath =
|
|
3530
|
+
const outPath = path12.join(agentDir, `${agent}.md`);
|
|
2896
3531
|
writeFileSync6(outPath, content, "utf-8");
|
|
2897
3532
|
return outPath;
|
|
2898
3533
|
}
|
|
@@ -2955,7 +3590,7 @@ async function main() {
|
|
|
2955
3590
|
const empRole = (() => {
|
|
2956
3591
|
try {
|
|
2957
3592
|
const emps = readFileSync7(
|
|
2958
|
-
|
|
3593
|
+
path12.join(os10.homedir(), ".exe-os", "exe-employees.json"),
|
|
2959
3594
|
"utf-8"
|
|
2960
3595
|
);
|
|
2961
3596
|
const found = JSON.parse(emps).find(
|