@askexenow/exe-os 0.9.6 → 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 +668 -37
- package/dist/bin/cli.js +1399 -607
- 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 +795 -155
- package/dist/bin/exe-call.js +209 -138
- package/dist/bin/exe-cloud.js +35 -12
- package/dist/bin/exe-dispatch.js +703 -72
- 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 +1064 -273
- package/dist/bin/exe-heartbeat.js +676 -45
- 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 +845 -152
- 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 +668 -37
- package/dist/bin/exe-team.js +635 -13
- package/dist/bin/git-sweep.js +731 -91
- 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 +735 -95
- 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 +1038 -247
- package/dist/hooks/bug-report-worker.js +902 -172
- package/dist/hooks/commit-complete.js +729 -89
- package/dist/hooks/error-recall.js +776 -93
- package/dist/hooks/exe-heartbeat-hook.js +85 -71
- package/dist/hooks/ingest-worker.js +851 -158
- 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 +685 -45
- package/dist/hooks/pre-compact.js +729 -89
- package/dist/hooks/pre-tool-use.js +883 -127
- package/dist/hooks/prompt-ingest-worker.js +758 -83
- package/dist/hooks/prompt-submit.js +1071 -321
- package/dist/hooks/response-ingest-worker.js +758 -83
- package/dist/hooks/session-end.js +732 -92
- package/dist/hooks/session-start.js +1042 -209
- package/dist/hooks/stop.js +691 -51
- package/dist/hooks/subagent-stop.js +685 -45
- package/dist/hooks/summary-worker.js +827 -134
- package/dist/index.js +1026 -234
- 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 +905 -164
- package/dist/lib/hybrid-search.js +771 -88
- package/dist/lib/identity.js +27 -7
- package/dist/lib/messaging.js +66 -30
- 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 +109 -73
- package/dist/lib/tmux-routing.js +98 -62
- package/dist/lib/token-spend.js +26 -6
- package/dist/mcp/server.js +1807 -472
- 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 +301 -166
- 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 +206 -40
- package/dist/mcp/tools/send-message.js +69 -33
- package/dist/mcp/tools/update-task.js +86 -50
- package/dist/runtime/index.js +731 -91
- package/dist/tui/App.js +864 -125
- package/package.json +3 -2
|
@@ -270,6 +270,118 @@ var init_session_key = __esm({
|
|
|
270
270
|
}
|
|
271
271
|
});
|
|
272
272
|
|
|
273
|
+
// src/lib/runtime-table.ts
|
|
274
|
+
var RUNTIME_TABLE, DEFAULT_RUNTIME;
|
|
275
|
+
var init_runtime_table = __esm({
|
|
276
|
+
"src/lib/runtime-table.ts"() {
|
|
277
|
+
"use strict";
|
|
278
|
+
RUNTIME_TABLE = {
|
|
279
|
+
codex: {
|
|
280
|
+
binary: "codex",
|
|
281
|
+
launchMode: "interactive",
|
|
282
|
+
autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
|
|
283
|
+
inlineFlag: "--no-alt-screen",
|
|
284
|
+
apiKeyEnv: "OPENAI_API_KEY",
|
|
285
|
+
defaultModel: "gpt-5.4"
|
|
286
|
+
},
|
|
287
|
+
opencode: {
|
|
288
|
+
binary: "opencode",
|
|
289
|
+
launchMode: "exec",
|
|
290
|
+
autoApproveFlag: "--dangerously-skip-permissions",
|
|
291
|
+
inlineFlag: "",
|
|
292
|
+
apiKeyEnv: "ANTHROPIC_API_KEY",
|
|
293
|
+
defaultModel: "anthropic/claude-sonnet-4-6"
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
DEFAULT_RUNTIME = "claude";
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
// src/lib/agent-config.ts
|
|
301
|
+
var agent_config_exports = {};
|
|
302
|
+
__export(agent_config_exports, {
|
|
303
|
+
AGENT_CONFIG_PATH: () => AGENT_CONFIG_PATH,
|
|
304
|
+
DEFAULT_MODELS: () => DEFAULT_MODELS,
|
|
305
|
+
KNOWN_RUNTIMES: () => KNOWN_RUNTIMES,
|
|
306
|
+
RUNTIME_LABELS: () => RUNTIME_LABELS,
|
|
307
|
+
clearAgentRuntime: () => clearAgentRuntime,
|
|
308
|
+
getAgentRuntime: () => getAgentRuntime,
|
|
309
|
+
loadAgentConfig: () => loadAgentConfig,
|
|
310
|
+
saveAgentConfig: () => saveAgentConfig,
|
|
311
|
+
setAgentRuntime: () => setAgentRuntime
|
|
312
|
+
});
|
|
313
|
+
import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync2, mkdirSync } from "fs";
|
|
314
|
+
import path2 from "path";
|
|
315
|
+
function loadAgentConfig() {
|
|
316
|
+
if (!existsSync2(AGENT_CONFIG_PATH)) return {};
|
|
317
|
+
try {
|
|
318
|
+
return JSON.parse(readFileSync2(AGENT_CONFIG_PATH, "utf-8"));
|
|
319
|
+
} catch {
|
|
320
|
+
return {};
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
function saveAgentConfig(config) {
|
|
324
|
+
const dir = path2.dirname(AGENT_CONFIG_PATH);
|
|
325
|
+
if (!existsSync2(dir)) mkdirSync(dir, { recursive: true });
|
|
326
|
+
writeFileSync(AGENT_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
327
|
+
}
|
|
328
|
+
function getAgentRuntime(agentId) {
|
|
329
|
+
const config = loadAgentConfig();
|
|
330
|
+
const entry = config[agentId];
|
|
331
|
+
if (entry) return entry;
|
|
332
|
+
const orgDefault = config["default"];
|
|
333
|
+
if (orgDefault) return orgDefault;
|
|
334
|
+
return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
|
|
335
|
+
}
|
|
336
|
+
function setAgentRuntime(agentId, runtime, model) {
|
|
337
|
+
const knownModels = KNOWN_RUNTIMES[runtime];
|
|
338
|
+
if (!knownModels) {
|
|
339
|
+
return {
|
|
340
|
+
ok: false,
|
|
341
|
+
error: `Unknown runtime "${runtime}". Valid: ${Object.keys(KNOWN_RUNTIMES).join(", ")}`
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
if (!knownModels.includes(model)) {
|
|
345
|
+
return {
|
|
346
|
+
ok: false,
|
|
347
|
+
error: `Unknown model "${model}" for runtime "${runtime}". Valid: ${knownModels.join(", ")}`
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
const config = loadAgentConfig();
|
|
351
|
+
config[agentId] = { runtime, model };
|
|
352
|
+
saveAgentConfig(config);
|
|
353
|
+
return { ok: true };
|
|
354
|
+
}
|
|
355
|
+
function clearAgentRuntime(agentId) {
|
|
356
|
+
const config = loadAgentConfig();
|
|
357
|
+
delete config[agentId];
|
|
358
|
+
saveAgentConfig(config);
|
|
359
|
+
}
|
|
360
|
+
var AGENT_CONFIG_PATH, KNOWN_RUNTIMES, RUNTIME_LABELS, DEFAULT_MODELS;
|
|
361
|
+
var init_agent_config = __esm({
|
|
362
|
+
"src/lib/agent-config.ts"() {
|
|
363
|
+
"use strict";
|
|
364
|
+
init_config();
|
|
365
|
+
init_runtime_table();
|
|
366
|
+
AGENT_CONFIG_PATH = path2.join(EXE_AI_DIR, "agent-config.json");
|
|
367
|
+
KNOWN_RUNTIMES = {
|
|
368
|
+
claude: ["claude-opus-4", "claude-sonnet-4", "claude-haiku-4.5"],
|
|
369
|
+
codex: ["gpt-5.4", "gpt-5.5", "gpt-5.3-codex-spark", "o3", "o4-mini"],
|
|
370
|
+
opencode: ["anthropic/claude-sonnet-4-6", "openai/gpt-5.4", "google/gemini-2.5-pro", "deepseek/deepseek-r3", "minimax/minimax-m2.5"]
|
|
371
|
+
};
|
|
372
|
+
RUNTIME_LABELS = {
|
|
373
|
+
claude: "Claude Code (Anthropic)",
|
|
374
|
+
codex: "Codex (OpenAI)",
|
|
375
|
+
opencode: "OpenCode (open source)"
|
|
376
|
+
};
|
|
377
|
+
DEFAULT_MODELS = {
|
|
378
|
+
claude: "claude-opus-4",
|
|
379
|
+
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
380
|
+
opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
});
|
|
384
|
+
|
|
273
385
|
// src/lib/employees.ts
|
|
274
386
|
var employees_exports = {};
|
|
275
387
|
__export(employees_exports, {
|
|
@@ -285,6 +397,7 @@ __export(employees_exports, {
|
|
|
285
397
|
getEmployeeByRole: () => getEmployeeByRole,
|
|
286
398
|
getEmployeeNamesByRole: () => getEmployeeNamesByRole,
|
|
287
399
|
hasRole: () => hasRole,
|
|
400
|
+
hireEmployee: () => hireEmployee,
|
|
288
401
|
isCoordinatorName: () => isCoordinatorName,
|
|
289
402
|
isCoordinatorRole: () => isCoordinatorRole,
|
|
290
403
|
isMultiInstance: () => isMultiInstance,
|
|
@@ -297,9 +410,9 @@ __export(employees_exports, {
|
|
|
297
410
|
validateEmployeeName: () => validateEmployeeName
|
|
298
411
|
});
|
|
299
412
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
300
|
-
import { existsSync as
|
|
413
|
+
import { existsSync as existsSync3, symlinkSync, readlinkSync, readFileSync as readFileSync3, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
|
|
301
414
|
import { execSync as execSync2 } from "child_process";
|
|
302
|
-
import
|
|
415
|
+
import path3 from "path";
|
|
303
416
|
import os2 from "os";
|
|
304
417
|
function normalizeRole(role) {
|
|
305
418
|
return (role ?? "").trim().toLowerCase();
|
|
@@ -336,7 +449,7 @@ function validateEmployeeName(name) {
|
|
|
336
449
|
return { valid: true };
|
|
337
450
|
}
|
|
338
451
|
async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
339
|
-
if (!
|
|
452
|
+
if (!existsSync3(employeesPath)) {
|
|
340
453
|
return [];
|
|
341
454
|
}
|
|
342
455
|
const raw = await readFile2(employeesPath, "utf-8");
|
|
@@ -347,13 +460,13 @@ async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
|
347
460
|
}
|
|
348
461
|
}
|
|
349
462
|
async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
|
|
350
|
-
await mkdir2(
|
|
463
|
+
await mkdir2(path3.dirname(employeesPath), { recursive: true });
|
|
351
464
|
await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
|
|
352
465
|
}
|
|
353
466
|
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
354
|
-
if (!
|
|
467
|
+
if (!existsSync3(employeesPath)) return [];
|
|
355
468
|
try {
|
|
356
|
-
return JSON.parse(
|
|
469
|
+
return JSON.parse(readFileSync3(employeesPath, "utf-8"));
|
|
357
470
|
} catch {
|
|
358
471
|
return [];
|
|
359
472
|
}
|
|
@@ -395,6 +508,52 @@ function addEmployee(employees, employee) {
|
|
|
395
508
|
}
|
|
396
509
|
return [...employees, normalized];
|
|
397
510
|
}
|
|
511
|
+
function appendToCoordinatorTeam(employee) {
|
|
512
|
+
const coordinator = getCoordinatorEmployee(loadEmployeesSync());
|
|
513
|
+
if (!coordinator) return;
|
|
514
|
+
const idPath = path3.join(IDENTITY_DIR, `${coordinator.name}.md`);
|
|
515
|
+
if (!existsSync3(idPath)) return;
|
|
516
|
+
const content = readFileSync3(idPath, "utf-8");
|
|
517
|
+
if (content.includes(`**${capitalize(employee.name)}`)) return;
|
|
518
|
+
const teamMatch = content.match(TEAM_SECTION_RE);
|
|
519
|
+
if (!teamMatch || teamMatch.index === void 0) return;
|
|
520
|
+
const afterTeam = content.slice(teamMatch.index + teamMatch[0].length);
|
|
521
|
+
const nextHeading = afterTeam.match(/\n## /);
|
|
522
|
+
const entry = `
|
|
523
|
+
**${capitalize(employee.name)} (${employee.role}):** Newly hired. Update this description as the role develops.
|
|
524
|
+
`;
|
|
525
|
+
let updated;
|
|
526
|
+
if (nextHeading && nextHeading.index !== void 0) {
|
|
527
|
+
const insertAt = teamMatch.index + teamMatch[0].length + nextHeading.index;
|
|
528
|
+
updated = content.slice(0, insertAt) + entry + content.slice(insertAt);
|
|
529
|
+
} else {
|
|
530
|
+
updated = content.trimEnd() + "\n" + entry;
|
|
531
|
+
}
|
|
532
|
+
writeFileSync2(idPath, updated, "utf-8");
|
|
533
|
+
}
|
|
534
|
+
function capitalize(s) {
|
|
535
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
536
|
+
}
|
|
537
|
+
async function hireEmployee(employee) {
|
|
538
|
+
const employees = await loadEmployees();
|
|
539
|
+
const updated = addEmployee(employees, employee);
|
|
540
|
+
await saveEmployees(updated);
|
|
541
|
+
try {
|
|
542
|
+
appendToCoordinatorTeam(employee);
|
|
543
|
+
} catch {
|
|
544
|
+
}
|
|
545
|
+
try {
|
|
546
|
+
const { loadAgentConfig: loadAgentConfig2, saveAgentConfig: saveAgentConfig2 } = await Promise.resolve().then(() => (init_agent_config(), agent_config_exports));
|
|
547
|
+
const config = loadAgentConfig2();
|
|
548
|
+
const name = employee.name.toLowerCase();
|
|
549
|
+
if (!config[name] && config["default"]) {
|
|
550
|
+
config[name] = { ...config["default"] };
|
|
551
|
+
saveAgentConfig2(config);
|
|
552
|
+
}
|
|
553
|
+
} catch {
|
|
554
|
+
}
|
|
555
|
+
return updated;
|
|
556
|
+
}
|
|
398
557
|
async function normalizeRosterCase(rosterPath) {
|
|
399
558
|
const employees = await loadEmployees(rosterPath);
|
|
400
559
|
let changed = false;
|
|
@@ -404,14 +563,14 @@ async function normalizeRosterCase(rosterPath) {
|
|
|
404
563
|
emp.name = emp.name.toLowerCase();
|
|
405
564
|
changed = true;
|
|
406
565
|
try {
|
|
407
|
-
const identityDir =
|
|
408
|
-
const oldPath =
|
|
409
|
-
const newPath =
|
|
410
|
-
if (
|
|
566
|
+
const identityDir = path3.join(os2.homedir(), ".exe-os", "identity");
|
|
567
|
+
const oldPath = path3.join(identityDir, `${oldName}.md`);
|
|
568
|
+
const newPath = path3.join(identityDir, `${emp.name}.md`);
|
|
569
|
+
if (existsSync3(oldPath) && !existsSync3(newPath)) {
|
|
411
570
|
renameSync2(oldPath, newPath);
|
|
412
|
-
} else if (
|
|
413
|
-
const content =
|
|
414
|
-
|
|
571
|
+
} else if (existsSync3(oldPath) && oldPath !== newPath) {
|
|
572
|
+
const content = readFileSync3(oldPath, "utf-8");
|
|
573
|
+
writeFileSync2(newPath, content, "utf-8");
|
|
415
574
|
if (oldPath.toLowerCase() !== newPath.toLowerCase()) {
|
|
416
575
|
unlinkSync(oldPath);
|
|
417
576
|
}
|
|
@@ -441,7 +600,7 @@ function registerBinSymlinks(name) {
|
|
|
441
600
|
errors.push("Could not find 'exe-os' in PATH");
|
|
442
601
|
return { created, skipped, errors };
|
|
443
602
|
}
|
|
444
|
-
const binDir =
|
|
603
|
+
const binDir = path3.dirname(exeBinPath);
|
|
445
604
|
let target;
|
|
446
605
|
try {
|
|
447
606
|
target = readlinkSync(exeBinPath);
|
|
@@ -451,8 +610,8 @@ function registerBinSymlinks(name) {
|
|
|
451
610
|
}
|
|
452
611
|
for (const suffix of ["", "-opencode"]) {
|
|
453
612
|
const linkName = `${name}${suffix}`;
|
|
454
|
-
const linkPath =
|
|
455
|
-
if (
|
|
613
|
+
const linkPath = path3.join(binDir, linkName);
|
|
614
|
+
if (existsSync3(linkPath)) {
|
|
456
615
|
skipped.push(linkName);
|
|
457
616
|
continue;
|
|
458
617
|
}
|
|
@@ -465,26 +624,28 @@ function registerBinSymlinks(name) {
|
|
|
465
624
|
}
|
|
466
625
|
return { created, skipped, errors };
|
|
467
626
|
}
|
|
468
|
-
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES;
|
|
627
|
+
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES, IDENTITY_DIR, TEAM_SECTION_RE;
|
|
469
628
|
var init_employees = __esm({
|
|
470
629
|
"src/lib/employees.ts"() {
|
|
471
630
|
"use strict";
|
|
472
631
|
init_config();
|
|
473
|
-
EMPLOYEES_PATH =
|
|
632
|
+
EMPLOYEES_PATH = path3.join(EXE_AI_DIR, "exe-employees.json");
|
|
474
633
|
DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
|
|
475
634
|
COORDINATOR_ROLE = "COO";
|
|
476
635
|
MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
|
|
636
|
+
IDENTITY_DIR = path3.join(EXE_AI_DIR, "identity");
|
|
637
|
+
TEAM_SECTION_RE = /^## Team\b.*$/m;
|
|
477
638
|
}
|
|
478
639
|
});
|
|
479
640
|
|
|
480
641
|
// src/lib/session-registry.ts
|
|
481
|
-
import
|
|
642
|
+
import path5 from "path";
|
|
482
643
|
import os3 from "os";
|
|
483
644
|
var REGISTRY_PATH;
|
|
484
645
|
var init_session_registry = __esm({
|
|
485
646
|
"src/lib/session-registry.ts"() {
|
|
486
647
|
"use strict";
|
|
487
|
-
REGISTRY_PATH =
|
|
648
|
+
REGISTRY_PATH = path5.join(os3.homedir(), ".exe-os", "session-registry.json");
|
|
488
649
|
}
|
|
489
650
|
});
|
|
490
651
|
|
|
@@ -624,50 +785,6 @@ var init_provider_table = __esm({
|
|
|
624
785
|
}
|
|
625
786
|
});
|
|
626
787
|
|
|
627
|
-
// src/lib/runtime-table.ts
|
|
628
|
-
var RUNTIME_TABLE;
|
|
629
|
-
var init_runtime_table = __esm({
|
|
630
|
-
"src/lib/runtime-table.ts"() {
|
|
631
|
-
"use strict";
|
|
632
|
-
RUNTIME_TABLE = {
|
|
633
|
-
codex: {
|
|
634
|
-
binary: "codex",
|
|
635
|
-
launchMode: "interactive",
|
|
636
|
-
autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
|
|
637
|
-
inlineFlag: "--no-alt-screen",
|
|
638
|
-
apiKeyEnv: "OPENAI_API_KEY",
|
|
639
|
-
defaultModel: "gpt-5.4"
|
|
640
|
-
},
|
|
641
|
-
opencode: {
|
|
642
|
-
binary: "opencode",
|
|
643
|
-
launchMode: "exec",
|
|
644
|
-
autoApproveFlag: "--dangerously-skip-permissions",
|
|
645
|
-
inlineFlag: "",
|
|
646
|
-
apiKeyEnv: "ANTHROPIC_API_KEY",
|
|
647
|
-
defaultModel: "anthropic/claude-sonnet-4-6"
|
|
648
|
-
}
|
|
649
|
-
};
|
|
650
|
-
}
|
|
651
|
-
});
|
|
652
|
-
|
|
653
|
-
// src/lib/agent-config.ts
|
|
654
|
-
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync3, mkdirSync as mkdirSync2 } from "fs";
|
|
655
|
-
import path5 from "path";
|
|
656
|
-
var AGENT_CONFIG_PATH, DEFAULT_MODELS;
|
|
657
|
-
var init_agent_config = __esm({
|
|
658
|
-
"src/lib/agent-config.ts"() {
|
|
659
|
-
"use strict";
|
|
660
|
-
init_config();
|
|
661
|
-
init_runtime_table();
|
|
662
|
-
AGENT_CONFIG_PATH = path5.join(EXE_AI_DIR, "agent-config.json");
|
|
663
|
-
DEFAULT_MODELS = {
|
|
664
|
-
claude: "claude-opus-4",
|
|
665
|
-
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
666
|
-
opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
|
|
667
|
-
};
|
|
668
|
-
}
|
|
669
|
-
});
|
|
670
|
-
|
|
671
788
|
// src/lib/intercom-queue.ts
|
|
672
789
|
import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, renameSync as renameSync3, existsSync as existsSync4, mkdirSync as mkdirSync3 } from "fs";
|
|
673
790
|
import path6 from "path";
|
|
@@ -737,13 +854,597 @@ var init_db_retry = __esm({
|
|
|
737
854
|
}
|
|
738
855
|
});
|
|
739
856
|
|
|
857
|
+
// src/lib/database-adapter.ts
|
|
858
|
+
import os5 from "os";
|
|
859
|
+
import path7 from "path";
|
|
860
|
+
import { createRequire } from "module";
|
|
861
|
+
import { pathToFileURL } from "url";
|
|
862
|
+
function quotedIdentifier(identifier) {
|
|
863
|
+
return `"${identifier.replace(/"/g, '""')}"`;
|
|
864
|
+
}
|
|
865
|
+
function unqualifiedTableName(name) {
|
|
866
|
+
const raw = name.trim().replace(/^"|"$/g, "");
|
|
867
|
+
const parts = raw.split(".");
|
|
868
|
+
return parts[parts.length - 1].replace(/^"|"$/g, "").toLowerCase();
|
|
869
|
+
}
|
|
870
|
+
function stripTrailingSemicolon(sql) {
|
|
871
|
+
return sql.trim().replace(/;+\s*$/u, "");
|
|
872
|
+
}
|
|
873
|
+
function appendClause(sql, clause) {
|
|
874
|
+
const trimmed = stripTrailingSemicolon(sql);
|
|
875
|
+
const returningMatch = /\sRETURNING\b[\s\S]*$/iu.exec(trimmed);
|
|
876
|
+
if (!returningMatch) {
|
|
877
|
+
return `${trimmed}${clause}`;
|
|
878
|
+
}
|
|
879
|
+
const idx = returningMatch.index;
|
|
880
|
+
return `${trimmed.slice(0, idx)}${clause}${trimmed.slice(idx)}`;
|
|
881
|
+
}
|
|
882
|
+
function normalizeStatement(stmt) {
|
|
883
|
+
if (typeof stmt === "string") {
|
|
884
|
+
return { kind: "positional", sql: stmt, args: [] };
|
|
885
|
+
}
|
|
886
|
+
const sql = stmt.sql;
|
|
887
|
+
if (Array.isArray(stmt.args) || stmt.args === void 0) {
|
|
888
|
+
return { kind: "positional", sql, args: stmt.args ?? [] };
|
|
889
|
+
}
|
|
890
|
+
return { kind: "named", sql, args: stmt.args };
|
|
891
|
+
}
|
|
892
|
+
function rewriteBooleanLiterals(sql) {
|
|
893
|
+
let out = sql;
|
|
894
|
+
for (const column of BOOLEAN_COLUMN_NAMES) {
|
|
895
|
+
const scoped = `((?:\\b[a-z_][a-z0-9_]*\\.)?${column})`;
|
|
896
|
+
out = out.replace(new RegExp(`${scoped}\\s*=\\s*0\\b`, "giu"), "$1 = FALSE");
|
|
897
|
+
out = out.replace(new RegExp(`${scoped}\\s*=\\s*1\\b`, "giu"), "$1 = TRUE");
|
|
898
|
+
out = out.replace(new RegExp(`${scoped}\\s*!=\\s*0\\b`, "giu"), "$1 != FALSE");
|
|
899
|
+
out = out.replace(new RegExp(`${scoped}\\s*!=\\s*1\\b`, "giu"), "$1 != TRUE");
|
|
900
|
+
out = out.replace(new RegExp(`${scoped}\\s*<>\\s*0\\b`, "giu"), "$1 <> FALSE");
|
|
901
|
+
out = out.replace(new RegExp(`${scoped}\\s*<>\\s*1\\b`, "giu"), "$1 <> TRUE");
|
|
902
|
+
}
|
|
903
|
+
return out;
|
|
904
|
+
}
|
|
905
|
+
function rewriteInsertOrIgnore(sql) {
|
|
906
|
+
if (!/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu.test(sql)) {
|
|
907
|
+
return sql;
|
|
908
|
+
}
|
|
909
|
+
const replaced = sql.replace(/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu, "INSERT INTO");
|
|
910
|
+
return /\bON\s+CONFLICT\b/iu.test(replaced) ? replaced : appendClause(replaced, " ON CONFLICT DO NOTHING");
|
|
911
|
+
}
|
|
912
|
+
function rewriteInsertOrReplace(sql) {
|
|
913
|
+
const match = /^\s*INSERT\s+OR\s+REPLACE\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)([\s\S]*)$/iu.exec(sql);
|
|
914
|
+
if (!match) {
|
|
915
|
+
return sql;
|
|
916
|
+
}
|
|
917
|
+
const rawTable = match[1];
|
|
918
|
+
const rawColumns = match[2];
|
|
919
|
+
const remainder = match[3];
|
|
920
|
+
const tableName = unqualifiedTableName(rawTable);
|
|
921
|
+
const conflictKeys = UPSERT_KEYS[tableName];
|
|
922
|
+
if (!conflictKeys?.length) {
|
|
923
|
+
return sql;
|
|
924
|
+
}
|
|
925
|
+
const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
|
|
926
|
+
const updateColumns = columns.filter((col) => !conflictKeys.includes(col));
|
|
927
|
+
const conflictTarget = conflictKeys.map(quotedIdentifier).join(", ");
|
|
928
|
+
const updateClause = updateColumns.length === 0 ? " DO NOTHING" : ` DO UPDATE SET ${updateColumns.map((col) => `${quotedIdentifier(col)} = EXCLUDED.${quotedIdentifier(col)}`).join(", ")}`;
|
|
929
|
+
return `INSERT INTO ${rawTable} (${rawColumns})${appendClause(remainder, ` ON CONFLICT (${conflictTarget})${updateClause}`)}`;
|
|
930
|
+
}
|
|
931
|
+
function rewriteSql(sql) {
|
|
932
|
+
let out = sql;
|
|
933
|
+
out = out.replace(/\bdatetime\(\s*['"]now['"]\s*\)/giu, "CURRENT_TIMESTAMP");
|
|
934
|
+
out = out.replace(/\bvector32\s*\(\s*\?\s*\)/giu, "?");
|
|
935
|
+
out = rewriteBooleanLiterals(out);
|
|
936
|
+
out = rewriteInsertOrReplace(out);
|
|
937
|
+
out = rewriteInsertOrIgnore(out);
|
|
938
|
+
return stripTrailingSemicolon(out);
|
|
939
|
+
}
|
|
940
|
+
function toBoolean(value) {
|
|
941
|
+
if (value === null || value === void 0) return value;
|
|
942
|
+
if (typeof value === "boolean") return value;
|
|
943
|
+
if (typeof value === "number") return value !== 0;
|
|
944
|
+
if (typeof value === "bigint") return value !== 0n;
|
|
945
|
+
if (typeof value === "string") {
|
|
946
|
+
const normalized = value.trim().toLowerCase();
|
|
947
|
+
if (normalized === "0" || normalized === "false") return false;
|
|
948
|
+
if (normalized === "1" || normalized === "true") return true;
|
|
949
|
+
}
|
|
950
|
+
return Boolean(value);
|
|
951
|
+
}
|
|
952
|
+
function countQuestionMarks(sql, end) {
|
|
953
|
+
let count = 0;
|
|
954
|
+
let inSingle = false;
|
|
955
|
+
let inDouble = false;
|
|
956
|
+
let inLineComment = false;
|
|
957
|
+
let inBlockComment = false;
|
|
958
|
+
for (let i = 0; i < end; i++) {
|
|
959
|
+
const ch = sql[i];
|
|
960
|
+
const next = sql[i + 1];
|
|
961
|
+
if (inLineComment) {
|
|
962
|
+
if (ch === "\n") inLineComment = false;
|
|
963
|
+
continue;
|
|
964
|
+
}
|
|
965
|
+
if (inBlockComment) {
|
|
966
|
+
if (ch === "*" && next === "/") {
|
|
967
|
+
inBlockComment = false;
|
|
968
|
+
i += 1;
|
|
969
|
+
}
|
|
970
|
+
continue;
|
|
971
|
+
}
|
|
972
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
973
|
+
inLineComment = true;
|
|
974
|
+
i += 1;
|
|
975
|
+
continue;
|
|
976
|
+
}
|
|
977
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
978
|
+
inBlockComment = true;
|
|
979
|
+
i += 1;
|
|
980
|
+
continue;
|
|
981
|
+
}
|
|
982
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
983
|
+
inSingle = !inSingle;
|
|
984
|
+
continue;
|
|
985
|
+
}
|
|
986
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
987
|
+
inDouble = !inDouble;
|
|
988
|
+
continue;
|
|
989
|
+
}
|
|
990
|
+
if (!inSingle && !inDouble && ch === "?") {
|
|
991
|
+
count += 1;
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
return count;
|
|
995
|
+
}
|
|
996
|
+
function findBooleanPlaceholderIndexes(sql) {
|
|
997
|
+
const indexes = /* @__PURE__ */ new Set();
|
|
998
|
+
for (const column of BOOLEAN_COLUMN_NAMES) {
|
|
999
|
+
const pattern = new RegExp(`(?:\\b[a-z_][a-z0-9_]*\\.)?${column}\\s*=\\s*\\?`, "giu");
|
|
1000
|
+
for (const match of sql.matchAll(pattern)) {
|
|
1001
|
+
const matchText = match[0];
|
|
1002
|
+
const qIndex = match.index + matchText.lastIndexOf("?");
|
|
1003
|
+
indexes.add(countQuestionMarks(sql, qIndex + 1));
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
return indexes;
|
|
1007
|
+
}
|
|
1008
|
+
function coerceInsertBooleanArgs(sql, args) {
|
|
1009
|
+
const match = /^\s*INSERT(?:\s+OR\s+(?:IGNORE|REPLACE))?\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)/iu.exec(sql);
|
|
1010
|
+
if (!match) return;
|
|
1011
|
+
const rawTable = match[1];
|
|
1012
|
+
const rawColumns = match[2];
|
|
1013
|
+
const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
|
|
1014
|
+
if (!boolColumns?.size) return;
|
|
1015
|
+
const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
|
|
1016
|
+
for (const [index, column] of columns.entries()) {
|
|
1017
|
+
if (boolColumns.has(column) && index < args.length) {
|
|
1018
|
+
args[index] = toBoolean(args[index]);
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
function coerceUpdateBooleanArgs(sql, args) {
|
|
1023
|
+
const match = /^\s*UPDATE\s+([A-Za-z0-9_."]+)\s+SET\s+([\s\S]+?)(?:\s+WHERE\b|$)/iu.exec(sql);
|
|
1024
|
+
if (!match) return;
|
|
1025
|
+
const rawTable = match[1];
|
|
1026
|
+
const setClause = match[2];
|
|
1027
|
+
const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
|
|
1028
|
+
if (!boolColumns?.size) return;
|
|
1029
|
+
const assignments = setClause.split(",");
|
|
1030
|
+
let placeholderIndex = 0;
|
|
1031
|
+
for (const assignment of assignments) {
|
|
1032
|
+
if (!assignment.includes("?")) continue;
|
|
1033
|
+
placeholderIndex += 1;
|
|
1034
|
+
const colMatch = /^\s*(?:[A-Za-z_][A-Za-z0-9_]*\.)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*\?/iu.exec(assignment);
|
|
1035
|
+
if (colMatch && boolColumns.has(colMatch[1])) {
|
|
1036
|
+
args[placeholderIndex - 1] = toBoolean(args[placeholderIndex - 1]);
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
function coerceBooleanArgs(sql, args) {
|
|
1041
|
+
const nextArgs = [...args];
|
|
1042
|
+
coerceInsertBooleanArgs(sql, nextArgs);
|
|
1043
|
+
coerceUpdateBooleanArgs(sql, nextArgs);
|
|
1044
|
+
const placeholderIndexes = findBooleanPlaceholderIndexes(sql);
|
|
1045
|
+
for (const index of placeholderIndexes) {
|
|
1046
|
+
if (index > 0 && index <= nextArgs.length) {
|
|
1047
|
+
nextArgs[index - 1] = toBoolean(nextArgs[index - 1]);
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
return nextArgs;
|
|
1051
|
+
}
|
|
1052
|
+
function convertQuestionMarksToDollarParams(sql) {
|
|
1053
|
+
let out = "";
|
|
1054
|
+
let placeholder = 0;
|
|
1055
|
+
let inSingle = false;
|
|
1056
|
+
let inDouble = false;
|
|
1057
|
+
let inLineComment = false;
|
|
1058
|
+
let inBlockComment = false;
|
|
1059
|
+
for (let i = 0; i < sql.length; i++) {
|
|
1060
|
+
const ch = sql[i];
|
|
1061
|
+
const next = sql[i + 1];
|
|
1062
|
+
if (inLineComment) {
|
|
1063
|
+
out += ch;
|
|
1064
|
+
if (ch === "\n") inLineComment = false;
|
|
1065
|
+
continue;
|
|
1066
|
+
}
|
|
1067
|
+
if (inBlockComment) {
|
|
1068
|
+
out += ch;
|
|
1069
|
+
if (ch === "*" && next === "/") {
|
|
1070
|
+
out += next;
|
|
1071
|
+
inBlockComment = false;
|
|
1072
|
+
i += 1;
|
|
1073
|
+
}
|
|
1074
|
+
continue;
|
|
1075
|
+
}
|
|
1076
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
1077
|
+
out += ch + next;
|
|
1078
|
+
inLineComment = true;
|
|
1079
|
+
i += 1;
|
|
1080
|
+
continue;
|
|
1081
|
+
}
|
|
1082
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
1083
|
+
out += ch + next;
|
|
1084
|
+
inBlockComment = true;
|
|
1085
|
+
i += 1;
|
|
1086
|
+
continue;
|
|
1087
|
+
}
|
|
1088
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
1089
|
+
inSingle = !inSingle;
|
|
1090
|
+
out += ch;
|
|
1091
|
+
continue;
|
|
1092
|
+
}
|
|
1093
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
1094
|
+
inDouble = !inDouble;
|
|
1095
|
+
out += ch;
|
|
1096
|
+
continue;
|
|
1097
|
+
}
|
|
1098
|
+
if (!inSingle && !inDouble && ch === "?") {
|
|
1099
|
+
placeholder += 1;
|
|
1100
|
+
out += `$${placeholder}`;
|
|
1101
|
+
continue;
|
|
1102
|
+
}
|
|
1103
|
+
out += ch;
|
|
1104
|
+
}
|
|
1105
|
+
return out;
|
|
1106
|
+
}
|
|
1107
|
+
function translateStatementForPostgres(stmt) {
|
|
1108
|
+
const normalized = normalizeStatement(stmt);
|
|
1109
|
+
if (normalized.kind === "named") {
|
|
1110
|
+
throw new Error("Named SQL parameters are not supported by the Prisma adapter.");
|
|
1111
|
+
}
|
|
1112
|
+
const rewrittenSql = rewriteSql(normalized.sql);
|
|
1113
|
+
const coercedArgs = coerceBooleanArgs(rewrittenSql, normalized.args);
|
|
1114
|
+
return {
|
|
1115
|
+
sql: convertQuestionMarksToDollarParams(rewrittenSql),
|
|
1116
|
+
args: coercedArgs
|
|
1117
|
+
};
|
|
1118
|
+
}
|
|
1119
|
+
function shouldBypassPostgres(stmt) {
|
|
1120
|
+
const normalized = normalizeStatement(stmt);
|
|
1121
|
+
if (normalized.kind === "named") {
|
|
1122
|
+
return true;
|
|
1123
|
+
}
|
|
1124
|
+
return IMMEDIATE_FALLBACK_PATTERNS.some((pattern) => pattern.test(normalized.sql));
|
|
1125
|
+
}
|
|
1126
|
+
function shouldFallbackOnError(error) {
|
|
1127
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1128
|
+
return /42P01|42883|42601|does not exist|syntax error|not supported|Named SQL parameters are not supported/iu.test(message);
|
|
1129
|
+
}
|
|
1130
|
+
function isReadQuery(sql) {
|
|
1131
|
+
const trimmed = sql.trimStart();
|
|
1132
|
+
return /^(SELECT|WITH|SHOW|EXPLAIN|VALUES)\b/iu.test(trimmed) || /\bRETURNING\b/iu.test(trimmed);
|
|
1133
|
+
}
|
|
1134
|
+
function buildRow(row, columns) {
|
|
1135
|
+
const values = columns.map((column) => row[column]);
|
|
1136
|
+
return Object.assign(values, row);
|
|
1137
|
+
}
|
|
1138
|
+
function buildResultSet(rows, rowsAffected = 0) {
|
|
1139
|
+
const columns = rows[0] ? Object.keys(rows[0]) : [];
|
|
1140
|
+
const resultRows = rows.map((row) => buildRow(row, columns));
|
|
1141
|
+
return {
|
|
1142
|
+
columns,
|
|
1143
|
+
columnTypes: columns.map(() => ""),
|
|
1144
|
+
rows: resultRows,
|
|
1145
|
+
rowsAffected,
|
|
1146
|
+
lastInsertRowid: void 0,
|
|
1147
|
+
toJSON() {
|
|
1148
|
+
return {
|
|
1149
|
+
columns,
|
|
1150
|
+
columnTypes: columns.map(() => ""),
|
|
1151
|
+
rows,
|
|
1152
|
+
rowsAffected,
|
|
1153
|
+
lastInsertRowid: void 0
|
|
1154
|
+
};
|
|
1155
|
+
}
|
|
1156
|
+
};
|
|
1157
|
+
}
|
|
1158
|
+
async function loadPrismaClient() {
|
|
1159
|
+
if (!prismaClientPromise) {
|
|
1160
|
+
prismaClientPromise = (async () => {
|
|
1161
|
+
const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
|
|
1162
|
+
if (explicitPath) {
|
|
1163
|
+
const module2 = await import(pathToFileURL(explicitPath).href);
|
|
1164
|
+
const PrismaClient2 = module2.PrismaClient ?? module2.default?.PrismaClient;
|
|
1165
|
+
if (!PrismaClient2) {
|
|
1166
|
+
throw new Error(`No PrismaClient export found at ${explicitPath}`);
|
|
1167
|
+
}
|
|
1168
|
+
return new PrismaClient2();
|
|
1169
|
+
}
|
|
1170
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path7.join(os5.homedir(), "exe-db");
|
|
1171
|
+
const requireFromExeDb = createRequire(path7.join(exeDbRoot, "package.json"));
|
|
1172
|
+
const prismaEntry = requireFromExeDb.resolve("@prisma/client");
|
|
1173
|
+
const module = await import(pathToFileURL(prismaEntry).href);
|
|
1174
|
+
const PrismaClient = module.PrismaClient ?? module.default?.PrismaClient;
|
|
1175
|
+
if (!PrismaClient) {
|
|
1176
|
+
throw new Error(`No PrismaClient export found in ${prismaEntry}`);
|
|
1177
|
+
}
|
|
1178
|
+
return new PrismaClient();
|
|
1179
|
+
})();
|
|
1180
|
+
}
|
|
1181
|
+
return prismaClientPromise;
|
|
1182
|
+
}
|
|
1183
|
+
async function ensureCompatibilityViews(prisma) {
|
|
1184
|
+
if (!compatibilityBootstrapPromise) {
|
|
1185
|
+
compatibilityBootstrapPromise = (async () => {
|
|
1186
|
+
for (const mapping of VIEW_MAPPINGS) {
|
|
1187
|
+
const relation = mapping.source.replace(/"/g, "");
|
|
1188
|
+
const rows = await prisma.$queryRawUnsafe(
|
|
1189
|
+
"SELECT to_regclass($1) AS regclass",
|
|
1190
|
+
relation
|
|
1191
|
+
);
|
|
1192
|
+
if (!rows[0]?.regclass) {
|
|
1193
|
+
continue;
|
|
1194
|
+
}
|
|
1195
|
+
await prisma.$executeRawUnsafe(
|
|
1196
|
+
`CREATE OR REPLACE VIEW public.${quotedIdentifier(mapping.view)} AS SELECT * FROM ${mapping.source}`
|
|
1197
|
+
);
|
|
1198
|
+
}
|
|
1199
|
+
})();
|
|
1200
|
+
}
|
|
1201
|
+
return compatibilityBootstrapPromise;
|
|
1202
|
+
}
|
|
1203
|
+
async function executeOnPrisma(executor, stmt) {
|
|
1204
|
+
const translated = translateStatementForPostgres(stmt);
|
|
1205
|
+
if (isReadQuery(translated.sql)) {
|
|
1206
|
+
const rows = await executor.$queryRawUnsafe(
|
|
1207
|
+
translated.sql,
|
|
1208
|
+
...translated.args
|
|
1209
|
+
);
|
|
1210
|
+
return buildResultSet(rows, /\bRETURNING\b/iu.test(translated.sql) ? rows.length : 0);
|
|
1211
|
+
}
|
|
1212
|
+
const rowsAffected = await executor.$executeRawUnsafe(translated.sql, ...translated.args);
|
|
1213
|
+
return buildResultSet([], rowsAffected);
|
|
1214
|
+
}
|
|
1215
|
+
function splitSqlStatements(sql) {
|
|
1216
|
+
const parts = [];
|
|
1217
|
+
let current = "";
|
|
1218
|
+
let inSingle = false;
|
|
1219
|
+
let inDouble = false;
|
|
1220
|
+
let inLineComment = false;
|
|
1221
|
+
let inBlockComment = false;
|
|
1222
|
+
for (let i = 0; i < sql.length; i++) {
|
|
1223
|
+
const ch = sql[i];
|
|
1224
|
+
const next = sql[i + 1];
|
|
1225
|
+
if (inLineComment) {
|
|
1226
|
+
current += ch;
|
|
1227
|
+
if (ch === "\n") inLineComment = false;
|
|
1228
|
+
continue;
|
|
1229
|
+
}
|
|
1230
|
+
if (inBlockComment) {
|
|
1231
|
+
current += ch;
|
|
1232
|
+
if (ch === "*" && next === "/") {
|
|
1233
|
+
current += next;
|
|
1234
|
+
inBlockComment = false;
|
|
1235
|
+
i += 1;
|
|
1236
|
+
}
|
|
1237
|
+
continue;
|
|
1238
|
+
}
|
|
1239
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
1240
|
+
current += ch + next;
|
|
1241
|
+
inLineComment = true;
|
|
1242
|
+
i += 1;
|
|
1243
|
+
continue;
|
|
1244
|
+
}
|
|
1245
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
1246
|
+
current += ch + next;
|
|
1247
|
+
inBlockComment = true;
|
|
1248
|
+
i += 1;
|
|
1249
|
+
continue;
|
|
1250
|
+
}
|
|
1251
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
1252
|
+
inSingle = !inSingle;
|
|
1253
|
+
current += ch;
|
|
1254
|
+
continue;
|
|
1255
|
+
}
|
|
1256
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
1257
|
+
inDouble = !inDouble;
|
|
1258
|
+
current += ch;
|
|
1259
|
+
continue;
|
|
1260
|
+
}
|
|
1261
|
+
if (!inSingle && !inDouble && ch === ";") {
|
|
1262
|
+
if (current.trim()) {
|
|
1263
|
+
parts.push(current.trim());
|
|
1264
|
+
}
|
|
1265
|
+
current = "";
|
|
1266
|
+
continue;
|
|
1267
|
+
}
|
|
1268
|
+
current += ch;
|
|
1269
|
+
}
|
|
1270
|
+
if (current.trim()) {
|
|
1271
|
+
parts.push(current.trim());
|
|
1272
|
+
}
|
|
1273
|
+
return parts;
|
|
1274
|
+
}
|
|
1275
|
+
async function createPrismaDbAdapter(fallbackClient) {
|
|
1276
|
+
const prisma = await loadPrismaClient();
|
|
1277
|
+
await ensureCompatibilityViews(prisma);
|
|
1278
|
+
let closed = false;
|
|
1279
|
+
let adapter;
|
|
1280
|
+
const fallbackExecute = async (stmt, error) => {
|
|
1281
|
+
if (!fallbackClient) {
|
|
1282
|
+
if (error) throw error;
|
|
1283
|
+
throw new Error("No fallback SQLite client is available for this Prisma-routed query.");
|
|
1284
|
+
}
|
|
1285
|
+
if (error) {
|
|
1286
|
+
process.stderr.write(
|
|
1287
|
+
`[database-adapter] Falling back to SQLite: ${error instanceof Error ? error.message : String(error)}
|
|
1288
|
+
`
|
|
1289
|
+
);
|
|
1290
|
+
}
|
|
1291
|
+
return fallbackClient.execute(stmt);
|
|
1292
|
+
};
|
|
1293
|
+
adapter = {
|
|
1294
|
+
async execute(stmt) {
|
|
1295
|
+
if (shouldBypassPostgres(stmt)) {
|
|
1296
|
+
return fallbackExecute(stmt);
|
|
1297
|
+
}
|
|
1298
|
+
try {
|
|
1299
|
+
return await executeOnPrisma(prisma, stmt);
|
|
1300
|
+
} catch (error) {
|
|
1301
|
+
if (shouldFallbackOnError(error)) {
|
|
1302
|
+
return fallbackExecute(stmt, error);
|
|
1303
|
+
}
|
|
1304
|
+
throw error;
|
|
1305
|
+
}
|
|
1306
|
+
},
|
|
1307
|
+
async batch(stmts, mode) {
|
|
1308
|
+
if (stmts.some((stmt) => shouldBypassPostgres(stmt))) {
|
|
1309
|
+
if (!fallbackClient) {
|
|
1310
|
+
throw new Error("Cannot batch unsupported SQLite-only statements without a fallback client.");
|
|
1311
|
+
}
|
|
1312
|
+
return fallbackClient.batch(stmts, mode);
|
|
1313
|
+
}
|
|
1314
|
+
try {
|
|
1315
|
+
if (prisma.$transaction) {
|
|
1316
|
+
return await prisma.$transaction(async (tx) => {
|
|
1317
|
+
const results2 = [];
|
|
1318
|
+
for (const stmt of stmts) {
|
|
1319
|
+
results2.push(await executeOnPrisma(tx, stmt));
|
|
1320
|
+
}
|
|
1321
|
+
return results2;
|
|
1322
|
+
});
|
|
1323
|
+
}
|
|
1324
|
+
const results = [];
|
|
1325
|
+
for (const stmt of stmts) {
|
|
1326
|
+
results.push(await executeOnPrisma(prisma, stmt));
|
|
1327
|
+
}
|
|
1328
|
+
return results;
|
|
1329
|
+
} catch (error) {
|
|
1330
|
+
if (fallbackClient && shouldFallbackOnError(error)) {
|
|
1331
|
+
process.stderr.write(
|
|
1332
|
+
`[database-adapter] Falling back batch to SQLite: ${error instanceof Error ? error.message : String(error)}
|
|
1333
|
+
`
|
|
1334
|
+
);
|
|
1335
|
+
return fallbackClient.batch(stmts, mode);
|
|
1336
|
+
}
|
|
1337
|
+
throw error;
|
|
1338
|
+
}
|
|
1339
|
+
},
|
|
1340
|
+
async migrate(stmts) {
|
|
1341
|
+
if (fallbackClient) {
|
|
1342
|
+
return fallbackClient.migrate(stmts);
|
|
1343
|
+
}
|
|
1344
|
+
return adapter.batch(stmts, "deferred");
|
|
1345
|
+
},
|
|
1346
|
+
async transaction(mode) {
|
|
1347
|
+
if (!fallbackClient) {
|
|
1348
|
+
throw new Error("Interactive transactions are only supported on the SQLite fallback client.");
|
|
1349
|
+
}
|
|
1350
|
+
return fallbackClient.transaction(mode);
|
|
1351
|
+
},
|
|
1352
|
+
async executeMultiple(sql) {
|
|
1353
|
+
if (fallbackClient && shouldBypassPostgres(sql)) {
|
|
1354
|
+
return fallbackClient.executeMultiple(sql);
|
|
1355
|
+
}
|
|
1356
|
+
for (const statement of splitSqlStatements(sql)) {
|
|
1357
|
+
await adapter.execute(statement);
|
|
1358
|
+
}
|
|
1359
|
+
},
|
|
1360
|
+
async sync() {
|
|
1361
|
+
if (fallbackClient) {
|
|
1362
|
+
return fallbackClient.sync();
|
|
1363
|
+
}
|
|
1364
|
+
return { frame_no: 0, frames_synced: 0 };
|
|
1365
|
+
},
|
|
1366
|
+
close() {
|
|
1367
|
+
closed = true;
|
|
1368
|
+
prismaClientPromise = null;
|
|
1369
|
+
compatibilityBootstrapPromise = null;
|
|
1370
|
+
void prisma.$disconnect?.();
|
|
1371
|
+
},
|
|
1372
|
+
get closed() {
|
|
1373
|
+
return closed;
|
|
1374
|
+
},
|
|
1375
|
+
get protocol() {
|
|
1376
|
+
return "prisma-postgres";
|
|
1377
|
+
}
|
|
1378
|
+
};
|
|
1379
|
+
return adapter;
|
|
1380
|
+
}
|
|
1381
|
+
var VIEW_MAPPINGS, UPSERT_KEYS, BOOLEAN_COLUMNS_BY_TABLE, BOOLEAN_COLUMN_NAMES, IMMEDIATE_FALLBACK_PATTERNS, prismaClientPromise, compatibilityBootstrapPromise;
|
|
1382
|
+
var init_database_adapter = __esm({
|
|
1383
|
+
"src/lib/database-adapter.ts"() {
|
|
1384
|
+
"use strict";
|
|
1385
|
+
VIEW_MAPPINGS = [
|
|
1386
|
+
{ view: "memories", source: "memory.memory_records" },
|
|
1387
|
+
{ view: "tasks", source: "memory.tasks" },
|
|
1388
|
+
{ view: "behaviors", source: "memory.behaviors" },
|
|
1389
|
+
{ view: "entities", source: "memory.entities" },
|
|
1390
|
+
{ view: "relationships", source: "memory.relationships" },
|
|
1391
|
+
{ view: "entity_memories", source: "memory.entity_memories" },
|
|
1392
|
+
{ view: "entity_aliases", source: "memory.entity_aliases" },
|
|
1393
|
+
{ view: "notifications", source: "memory.notifications" },
|
|
1394
|
+
{ view: "messages", source: "memory.messages" },
|
|
1395
|
+
{ view: "users", source: "wiki.users" },
|
|
1396
|
+
{ view: "workspaces", source: "wiki.workspaces" },
|
|
1397
|
+
{ view: "workspace_users", source: "wiki.workspace_users" },
|
|
1398
|
+
{ view: "documents", source: "wiki.workspace_documents" },
|
|
1399
|
+
{ view: "chats", source: "wiki.workspace_chats" }
|
|
1400
|
+
];
|
|
1401
|
+
UPSERT_KEYS = {
|
|
1402
|
+
memories: ["id"],
|
|
1403
|
+
tasks: ["id"],
|
|
1404
|
+
behaviors: ["id"],
|
|
1405
|
+
entities: ["id"],
|
|
1406
|
+
relationships: ["id"],
|
|
1407
|
+
entity_aliases: ["alias"],
|
|
1408
|
+
notifications: ["id"],
|
|
1409
|
+
messages: ["id"],
|
|
1410
|
+
users: ["id"],
|
|
1411
|
+
workspaces: ["id"],
|
|
1412
|
+
workspace_users: ["id"],
|
|
1413
|
+
documents: ["id"],
|
|
1414
|
+
chats: ["id"]
|
|
1415
|
+
};
|
|
1416
|
+
BOOLEAN_COLUMNS_BY_TABLE = {
|
|
1417
|
+
memories: /* @__PURE__ */ new Set(["has_error", "draft"]),
|
|
1418
|
+
behaviors: /* @__PURE__ */ new Set(["active"]),
|
|
1419
|
+
notifications: /* @__PURE__ */ new Set(["read"]),
|
|
1420
|
+
users: /* @__PURE__ */ new Set(["has_personal_memory"])
|
|
1421
|
+
};
|
|
1422
|
+
BOOLEAN_COLUMN_NAMES = new Set(
|
|
1423
|
+
Object.values(BOOLEAN_COLUMNS_BY_TABLE).flatMap((cols) => [...cols])
|
|
1424
|
+
);
|
|
1425
|
+
IMMEDIATE_FALLBACK_PATTERNS = [
|
|
1426
|
+
/\bPRAGMA\b/i,
|
|
1427
|
+
/\bsqlite_master\b/i,
|
|
1428
|
+
/(?:^|[.\s])(?:memories|conversations|entities)_fts\b/i,
|
|
1429
|
+
/\bMATCH\b/i,
|
|
1430
|
+
/\bvector_distance_cos\s*\(/i,
|
|
1431
|
+
/\bjson_extract\s*\(/i,
|
|
1432
|
+
/\bjulianday\s*\(/i,
|
|
1433
|
+
/\bstrftime\s*\(/i,
|
|
1434
|
+
/\blast_insert_rowid\s*\(/i
|
|
1435
|
+
];
|
|
1436
|
+
prismaClientPromise = null;
|
|
1437
|
+
compatibilityBootstrapPromise = null;
|
|
1438
|
+
}
|
|
1439
|
+
});
|
|
1440
|
+
|
|
740
1441
|
// src/lib/exe-daemon-client.ts
|
|
741
1442
|
import net from "net";
|
|
742
|
-
import
|
|
1443
|
+
import os6 from "os";
|
|
743
1444
|
import { spawn } from "child_process";
|
|
744
1445
|
import { randomUUID } from "crypto";
|
|
745
1446
|
import { existsSync as existsSync5, unlinkSync as unlinkSync3, readFileSync as readFileSync6, openSync, closeSync, statSync } from "fs";
|
|
746
|
-
import
|
|
1447
|
+
import path8 from "path";
|
|
747
1448
|
import { fileURLToPath } from "url";
|
|
748
1449
|
function handleData(chunk) {
|
|
749
1450
|
_buffer += chunk.toString();
|
|
@@ -794,17 +1495,17 @@ function cleanupStaleFiles() {
|
|
|
794
1495
|
}
|
|
795
1496
|
}
|
|
796
1497
|
function findPackageRoot() {
|
|
797
|
-
let dir =
|
|
798
|
-
const { root } =
|
|
1498
|
+
let dir = path8.dirname(fileURLToPath(import.meta.url));
|
|
1499
|
+
const { root } = path8.parse(dir);
|
|
799
1500
|
while (dir !== root) {
|
|
800
|
-
if (existsSync5(
|
|
801
|
-
dir =
|
|
1501
|
+
if (existsSync5(path8.join(dir, "package.json"))) return dir;
|
|
1502
|
+
dir = path8.dirname(dir);
|
|
802
1503
|
}
|
|
803
1504
|
return null;
|
|
804
1505
|
}
|
|
805
1506
|
function spawnDaemon() {
|
|
806
|
-
const freeGB =
|
|
807
|
-
const totalGB =
|
|
1507
|
+
const freeGB = os6.freemem() / (1024 * 1024 * 1024);
|
|
1508
|
+
const totalGB = os6.totalmem() / (1024 * 1024 * 1024);
|
|
808
1509
|
if (totalGB <= 8) {
|
|
809
1510
|
process.stderr.write(
|
|
810
1511
|
`[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
|
|
@@ -824,7 +1525,7 @@ function spawnDaemon() {
|
|
|
824
1525
|
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
825
1526
|
return;
|
|
826
1527
|
}
|
|
827
|
-
const daemonPath =
|
|
1528
|
+
const daemonPath = path8.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
828
1529
|
if (!existsSync5(daemonPath)) {
|
|
829
1530
|
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
830
1531
|
`);
|
|
@@ -833,7 +1534,7 @@ function spawnDaemon() {
|
|
|
833
1534
|
const resolvedPath = daemonPath;
|
|
834
1535
|
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
835
1536
|
`);
|
|
836
|
-
const logPath =
|
|
1537
|
+
const logPath = path8.join(path8.dirname(SOCKET_PATH), "exed.log");
|
|
837
1538
|
let stderrFd = "ignore";
|
|
838
1539
|
try {
|
|
839
1540
|
stderrFd = openSync(logPath, "a");
|
|
@@ -980,9 +1681,9 @@ var init_exe_daemon_client = __esm({
|
|
|
980
1681
|
"src/lib/exe-daemon-client.ts"() {
|
|
981
1682
|
"use strict";
|
|
982
1683
|
init_config();
|
|
983
|
-
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ??
|
|
984
|
-
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ??
|
|
985
|
-
SPAWN_LOCK_PATH =
|
|
1684
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path8.join(EXE_AI_DIR, "exed.sock");
|
|
1685
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path8.join(EXE_AI_DIR, "exed.pid");
|
|
1686
|
+
SPAWN_LOCK_PATH = path8.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
986
1687
|
SPAWN_LOCK_STALE_MS = 3e4;
|
|
987
1688
|
CONNECT_TIMEOUT_MS = 15e3;
|
|
988
1689
|
REQUEST_TIMEOUT_MS = 3e4;
|
|
@@ -1064,7 +1765,7 @@ __export(db_daemon_client_exports, {
|
|
|
1064
1765
|
createDaemonDbClient: () => createDaemonDbClient,
|
|
1065
1766
|
initDaemonDbClient: () => initDaemonDbClient
|
|
1066
1767
|
});
|
|
1067
|
-
function
|
|
1768
|
+
function normalizeStatement2(stmt) {
|
|
1068
1769
|
if (typeof stmt === "string") {
|
|
1069
1770
|
return { sql: stmt, args: [] };
|
|
1070
1771
|
}
|
|
@@ -1088,7 +1789,7 @@ function createDaemonDbClient(fallbackClient) {
|
|
|
1088
1789
|
if (!_useDaemon || !isClientConnected()) {
|
|
1089
1790
|
return fallbackClient.execute(stmt);
|
|
1090
1791
|
}
|
|
1091
|
-
const { sql, args } =
|
|
1792
|
+
const { sql, args } = normalizeStatement2(stmt);
|
|
1092
1793
|
const response = await sendDaemonRequest({
|
|
1093
1794
|
type: "db-execute",
|
|
1094
1795
|
sql,
|
|
@@ -1113,7 +1814,7 @@ function createDaemonDbClient(fallbackClient) {
|
|
|
1113
1814
|
if (!_useDaemon || !isClientConnected()) {
|
|
1114
1815
|
return fallbackClient.batch(stmts, mode);
|
|
1115
1816
|
}
|
|
1116
|
-
const statements = stmts.map(
|
|
1817
|
+
const statements = stmts.map(normalizeStatement2);
|
|
1117
1818
|
const response = await sendDaemonRequest({
|
|
1118
1819
|
type: "db-batch",
|
|
1119
1820
|
statements,
|
|
@@ -1208,6 +1909,18 @@ __export(database_exports, {
|
|
|
1208
1909
|
});
|
|
1209
1910
|
import { createClient } from "@libsql/client";
|
|
1210
1911
|
async function initDatabase(config) {
|
|
1912
|
+
if (_walCheckpointTimer) {
|
|
1913
|
+
clearInterval(_walCheckpointTimer);
|
|
1914
|
+
_walCheckpointTimer = null;
|
|
1915
|
+
}
|
|
1916
|
+
if (_daemonClient) {
|
|
1917
|
+
_daemonClient.close();
|
|
1918
|
+
_daemonClient = null;
|
|
1919
|
+
}
|
|
1920
|
+
if (_adapterClient && _adapterClient !== _resilientClient) {
|
|
1921
|
+
_adapterClient.close();
|
|
1922
|
+
}
|
|
1923
|
+
_adapterClient = null;
|
|
1211
1924
|
if (_client) {
|
|
1212
1925
|
_client.close();
|
|
1213
1926
|
_client = null;
|
|
@@ -1221,6 +1934,7 @@ async function initDatabase(config) {
|
|
|
1221
1934
|
}
|
|
1222
1935
|
_client = createClient(opts);
|
|
1223
1936
|
_resilientClient = wrapWithRetry(_client);
|
|
1937
|
+
_adapterClient = _resilientClient;
|
|
1224
1938
|
_client.execute("PRAGMA busy_timeout = 30000").catch(() => {
|
|
1225
1939
|
});
|
|
1226
1940
|
_client.execute("PRAGMA journal_mode = WAL").catch(() => {
|
|
@@ -1231,14 +1945,20 @@ async function initDatabase(config) {
|
|
|
1231
1945
|
});
|
|
1232
1946
|
}, 3e4);
|
|
1233
1947
|
_walCheckpointTimer.unref();
|
|
1948
|
+
if (process.env.DATABASE_URL) {
|
|
1949
|
+
_adapterClient = await createPrismaDbAdapter(_resilientClient);
|
|
1950
|
+
}
|
|
1234
1951
|
}
|
|
1235
1952
|
function isInitialized() {
|
|
1236
|
-
return _client !== null;
|
|
1953
|
+
return _adapterClient !== null || _client !== null;
|
|
1237
1954
|
}
|
|
1238
1955
|
function getClient() {
|
|
1239
|
-
if (!
|
|
1956
|
+
if (!_adapterClient) {
|
|
1240
1957
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
1241
1958
|
}
|
|
1959
|
+
if (process.env.DATABASE_URL) {
|
|
1960
|
+
return _adapterClient;
|
|
1961
|
+
}
|
|
1242
1962
|
if (process.env.EXE_IS_DAEMON === "1") {
|
|
1243
1963
|
return _resilientClient;
|
|
1244
1964
|
}
|
|
@@ -1248,6 +1968,7 @@ function getClient() {
|
|
|
1248
1968
|
return _resilientClient;
|
|
1249
1969
|
}
|
|
1250
1970
|
async function initDaemonClient() {
|
|
1971
|
+
if (process.env.DATABASE_URL) return;
|
|
1251
1972
|
if (process.env.EXE_IS_DAEMON === "1") return;
|
|
1252
1973
|
if (!_resilientClient) return;
|
|
1253
1974
|
try {
|
|
@@ -2192,26 +2913,36 @@ async function ensureSchema() {
|
|
|
2192
2913
|
}
|
|
2193
2914
|
}
|
|
2194
2915
|
async function disposeDatabase() {
|
|
2916
|
+
if (_walCheckpointTimer) {
|
|
2917
|
+
clearInterval(_walCheckpointTimer);
|
|
2918
|
+
_walCheckpointTimer = null;
|
|
2919
|
+
}
|
|
2195
2920
|
if (_daemonClient) {
|
|
2196
2921
|
_daemonClient.close();
|
|
2197
2922
|
_daemonClient = null;
|
|
2198
2923
|
}
|
|
2924
|
+
if (_adapterClient && _adapterClient !== _resilientClient) {
|
|
2925
|
+
_adapterClient.close();
|
|
2926
|
+
}
|
|
2927
|
+
_adapterClient = null;
|
|
2199
2928
|
if (_client) {
|
|
2200
2929
|
_client.close();
|
|
2201
2930
|
_client = null;
|
|
2202
2931
|
_resilientClient = null;
|
|
2203
2932
|
}
|
|
2204
2933
|
}
|
|
2205
|
-
var _client, _resilientClient, _walCheckpointTimer, _daemonClient, initTurso, disposeTurso;
|
|
2934
|
+
var _client, _resilientClient, _walCheckpointTimer, _daemonClient, _adapterClient, initTurso, disposeTurso;
|
|
2206
2935
|
var init_database = __esm({
|
|
2207
2936
|
"src/lib/database.ts"() {
|
|
2208
2937
|
"use strict";
|
|
2209
2938
|
init_db_retry();
|
|
2210
2939
|
init_employees();
|
|
2940
|
+
init_database_adapter();
|
|
2211
2941
|
_client = null;
|
|
2212
2942
|
_resilientClient = null;
|
|
2213
2943
|
_walCheckpointTimer = null;
|
|
2214
2944
|
_daemonClient = null;
|
|
2945
|
+
_adapterClient = null;
|
|
2215
2946
|
initTurso = initDatabase;
|
|
2216
2947
|
disposeTurso = disposeDatabase;
|
|
2217
2948
|
}
|
|
@@ -2220,22 +2951,22 @@ var init_database = __esm({
|
|
|
2220
2951
|
// src/lib/license.ts
|
|
2221
2952
|
import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, existsSync as existsSync6, mkdirSync as mkdirSync4 } from "fs";
|
|
2222
2953
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
2223
|
-
import
|
|
2954
|
+
import path9 from "path";
|
|
2224
2955
|
import { jwtVerify, importSPKI } from "jose";
|
|
2225
2956
|
var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH;
|
|
2226
2957
|
var init_license = __esm({
|
|
2227
2958
|
"src/lib/license.ts"() {
|
|
2228
2959
|
"use strict";
|
|
2229
2960
|
init_config();
|
|
2230
|
-
LICENSE_PATH =
|
|
2231
|
-
CACHE_PATH =
|
|
2232
|
-
DEVICE_ID_PATH =
|
|
2961
|
+
LICENSE_PATH = path9.join(EXE_AI_DIR, "license.key");
|
|
2962
|
+
CACHE_PATH = path9.join(EXE_AI_DIR, "license-cache.json");
|
|
2963
|
+
DEVICE_ID_PATH = path9.join(EXE_AI_DIR, "device-id");
|
|
2233
2964
|
}
|
|
2234
2965
|
});
|
|
2235
2966
|
|
|
2236
2967
|
// src/lib/plan-limits.ts
|
|
2237
2968
|
import { readFileSync as readFileSync8, existsSync as existsSync7 } from "fs";
|
|
2238
|
-
import
|
|
2969
|
+
import path10 from "path";
|
|
2239
2970
|
var CACHE_PATH2;
|
|
2240
2971
|
var init_plan_limits = __esm({
|
|
2241
2972
|
"src/lib/plan-limits.ts"() {
|
|
@@ -2244,14 +2975,14 @@ var init_plan_limits = __esm({
|
|
|
2244
2975
|
init_employees();
|
|
2245
2976
|
init_license();
|
|
2246
2977
|
init_config();
|
|
2247
|
-
CACHE_PATH2 =
|
|
2978
|
+
CACHE_PATH2 = path10.join(EXE_AI_DIR, "license-cache.json");
|
|
2248
2979
|
}
|
|
2249
2980
|
});
|
|
2250
2981
|
|
|
2251
2982
|
// src/lib/tmux-routing.ts
|
|
2252
2983
|
import { readFileSync as readFileSync9, writeFileSync as writeFileSync6, mkdirSync as mkdirSync5, existsSync as existsSync8, appendFileSync, readdirSync as readdirSync2 } from "fs";
|
|
2253
|
-
import
|
|
2254
|
-
import
|
|
2984
|
+
import path11 from "path";
|
|
2985
|
+
import os7 from "os";
|
|
2255
2986
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
2256
2987
|
function getMySession() {
|
|
2257
2988
|
return getTransport().getMySession();
|
|
@@ -2264,7 +2995,7 @@ function extractRootExe(name) {
|
|
|
2264
2995
|
}
|
|
2265
2996
|
function getParentExe(sessionKey) {
|
|
2266
2997
|
try {
|
|
2267
|
-
const data = JSON.parse(readFileSync9(
|
|
2998
|
+
const data = JSON.parse(readFileSync9(path11.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
2268
2999
|
return data.parentExe || null;
|
|
2269
3000
|
} catch {
|
|
2270
3001
|
return null;
|
|
@@ -2273,15 +3004,24 @@ function getParentExe(sessionKey) {
|
|
|
2273
3004
|
function resolveExeSession() {
|
|
2274
3005
|
const mySession = getMySession();
|
|
2275
3006
|
if (!mySession) return null;
|
|
3007
|
+
const fromSessionName = extractRootExe(mySession);
|
|
2276
3008
|
try {
|
|
2277
3009
|
const key = getSessionKey();
|
|
2278
3010
|
const parentExe = getParentExe(key);
|
|
2279
3011
|
if (parentExe) {
|
|
2280
|
-
|
|
3012
|
+
const fromCache = extractRootExe(parentExe) ?? parentExe;
|
|
3013
|
+
if (fromSessionName && fromCache !== fromSessionName) {
|
|
3014
|
+
process.stderr.write(
|
|
3015
|
+
`[tmux-routing] WARN: cache says "${fromCache}" but session name says "${fromSessionName}". Trusting session name.
|
|
3016
|
+
`
|
|
3017
|
+
);
|
|
3018
|
+
return fromSessionName;
|
|
3019
|
+
}
|
|
3020
|
+
return fromCache;
|
|
2281
3021
|
}
|
|
2282
3022
|
} catch {
|
|
2283
3023
|
}
|
|
2284
|
-
return
|
|
3024
|
+
return fromSessionName ?? mySession;
|
|
2285
3025
|
}
|
|
2286
3026
|
var SPAWN_LOCK_DIR, SESSION_CACHE, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS;
|
|
2287
3027
|
var init_tmux_routing = __esm({
|
|
@@ -2298,10 +3038,10 @@ var init_tmux_routing = __esm({
|
|
|
2298
3038
|
init_intercom_queue();
|
|
2299
3039
|
init_plan_limits();
|
|
2300
3040
|
init_employees();
|
|
2301
|
-
SPAWN_LOCK_DIR =
|
|
2302
|
-
SESSION_CACHE =
|
|
2303
|
-
INTERCOM_LOG2 =
|
|
2304
|
-
DEBOUNCE_FILE =
|
|
3041
|
+
SPAWN_LOCK_DIR = path11.join(os7.homedir(), ".exe-os", "spawn-locks");
|
|
3042
|
+
SESSION_CACHE = path11.join(os7.homedir(), ".exe-os", "session-cache");
|
|
3043
|
+
INTERCOM_LOG2 = path11.join(os7.homedir(), ".exe-os", "intercom.log");
|
|
3044
|
+
DEBOUNCE_FILE = path11.join(SESSION_CACHE, "intercom-debounce.json");
|
|
2305
3045
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
2306
3046
|
}
|
|
2307
3047
|
});
|
|
@@ -2343,8 +3083,8 @@ __export(cto_delegation_gate_exports, {
|
|
|
2343
3083
|
hasValidScratchpadEscape: () => hasValidScratchpadEscape,
|
|
2344
3084
|
scratchpadPath: () => scratchpadPath
|
|
2345
3085
|
});
|
|
2346
|
-
import
|
|
2347
|
-
import
|
|
3086
|
+
import os8 from "os";
|
|
3087
|
+
import path12 from "path";
|
|
2348
3088
|
import { existsSync as existsSync9, readFileSync as readFileSync10, statSync as statSync2 } from "fs";
|
|
2349
3089
|
function resolveGatedAgent() {
|
|
2350
3090
|
try {
|
|
@@ -2359,12 +3099,12 @@ function getGatedAgent() {
|
|
|
2359
3099
|
return resolveGatedAgent() || GATED_AGENT;
|
|
2360
3100
|
}
|
|
2361
3101
|
function toWorkspaceRelative(filePath, cwd = process.cwd()) {
|
|
2362
|
-
const normalized =
|
|
2363
|
-
if (normalized.startsWith(cwd +
|
|
3102
|
+
const normalized = path12.normalize(filePath);
|
|
3103
|
+
if (normalized.startsWith(cwd + path12.sep)) {
|
|
2364
3104
|
return normalized.slice(cwd.length + 1);
|
|
2365
3105
|
}
|
|
2366
|
-
if (
|
|
2367
|
-
return
|
|
3106
|
+
if (path12.isAbsolute(normalized)) {
|
|
3107
|
+
return path12.basename(normalized);
|
|
2368
3108
|
}
|
|
2369
3109
|
return normalized;
|
|
2370
3110
|
}
|
|
@@ -2373,8 +3113,8 @@ function isDockerfile(basename) {
|
|
|
2373
3113
|
}
|
|
2374
3114
|
function classifyPath(filePath, cwd) {
|
|
2375
3115
|
const rel = toWorkspaceRelative(filePath, cwd);
|
|
2376
|
-
const basename =
|
|
2377
|
-
const ext =
|
|
3116
|
+
const basename = path12.basename(rel);
|
|
3117
|
+
const ext = path12.extname(rel).toLowerCase();
|
|
2378
3118
|
if (ext === ".md") {
|
|
2379
3119
|
for (const prefix of EXEMPT_MD_DIR_PREFIXES) {
|
|
2380
3120
|
if (rel.startsWith(prefix)) return "exempt";
|
|
@@ -2416,15 +3156,15 @@ async function hasRecentEngineerDispatch(now = Date.now()) {
|
|
|
2416
3156
|
return false;
|
|
2417
3157
|
}
|
|
2418
3158
|
}
|
|
2419
|
-
function scratchpadPath(sessionId, homeDir =
|
|
2420
|
-
return
|
|
3159
|
+
function scratchpadPath(sessionId, homeDir = os8.homedir()) {
|
|
3160
|
+
return path12.join(
|
|
2421
3161
|
homeDir,
|
|
2422
3162
|
".exe-os",
|
|
2423
3163
|
"session-cache",
|
|
2424
3164
|
`cto-scratchpad-${sessionId}.txt`
|
|
2425
3165
|
);
|
|
2426
3166
|
}
|
|
2427
|
-
function hasValidScratchpadEscape(sessionId, now = Date.now(), homeDir =
|
|
3167
|
+
function hasValidScratchpadEscape(sessionId, now = Date.now(), homeDir = os8.homedir()) {
|
|
2428
3168
|
const paths = [scratchpadPath(sessionId, homeDir)];
|
|
2429
3169
|
for (const filePath of paths) {
|
|
2430
3170
|
if (!existsSync9(filePath)) continue;
|
|
@@ -2523,13 +3263,13 @@ var init_memory = __esm({
|
|
|
2523
3263
|
// src/lib/keychain.ts
|
|
2524
3264
|
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
2525
3265
|
import { existsSync as existsSync10 } from "fs";
|
|
2526
|
-
import
|
|
2527
|
-
import
|
|
3266
|
+
import path13 from "path";
|
|
3267
|
+
import os9 from "os";
|
|
2528
3268
|
function getKeyDir() {
|
|
2529
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
3269
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path13.join(os9.homedir(), ".exe-os");
|
|
2530
3270
|
}
|
|
2531
3271
|
function getKeyPath() {
|
|
2532
|
-
return
|
|
3272
|
+
return path13.join(getKeyDir(), "master.key");
|
|
2533
3273
|
}
|
|
2534
3274
|
async function tryKeytar() {
|
|
2535
3275
|
try {
|
|
@@ -2552,7 +3292,7 @@ async function getMasterKey() {
|
|
|
2552
3292
|
const keyPath = getKeyPath();
|
|
2553
3293
|
if (!existsSync10(keyPath)) {
|
|
2554
3294
|
process.stderr.write(
|
|
2555
|
-
`[keychain] Key not found at ${keyPath} (HOME=${
|
|
3295
|
+
`[keychain] Key not found at ${keyPath} (HOME=${os9.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
2556
3296
|
`
|
|
2557
3297
|
);
|
|
2558
3298
|
return null;
|
|
@@ -2645,7 +3385,7 @@ __export(shard_manager_exports, {
|
|
|
2645
3385
|
listShards: () => listShards,
|
|
2646
3386
|
shardExists: () => shardExists
|
|
2647
3387
|
});
|
|
2648
|
-
import
|
|
3388
|
+
import path14 from "path";
|
|
2649
3389
|
import { existsSync as existsSync11, mkdirSync as mkdirSync6, readdirSync as readdirSync3 } from "fs";
|
|
2650
3390
|
import { createClient as createClient2 } from "@libsql/client";
|
|
2651
3391
|
function initShardManager(encryptionKey) {
|
|
@@ -2671,7 +3411,7 @@ function getShardClient(projectName) {
|
|
|
2671
3411
|
}
|
|
2672
3412
|
const cached = _shards.get(safeName);
|
|
2673
3413
|
if (cached) return cached;
|
|
2674
|
-
const dbPath =
|
|
3414
|
+
const dbPath = path14.join(SHARDS_DIR, `${safeName}.db`);
|
|
2675
3415
|
const client = createClient2({
|
|
2676
3416
|
url: `file:${dbPath}`,
|
|
2677
3417
|
encryptionKey: _encryptionKey
|
|
@@ -2681,7 +3421,7 @@ function getShardClient(projectName) {
|
|
|
2681
3421
|
}
|
|
2682
3422
|
function shardExists(projectName) {
|
|
2683
3423
|
const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
2684
|
-
return existsSync11(
|
|
3424
|
+
return existsSync11(path14.join(SHARDS_DIR, `${safeName}.db`));
|
|
2685
3425
|
}
|
|
2686
3426
|
function listShards() {
|
|
2687
3427
|
if (!existsSync11(SHARDS_DIR)) return [];
|
|
@@ -2758,7 +3498,23 @@ async function ensureShardSchema(client) {
|
|
|
2758
3498
|
// MS-11: draft staging, MS-6a: memory_type, MS-7: trajectory
|
|
2759
3499
|
"ALTER TABLE memories ADD COLUMN draft INTEGER DEFAULT 0",
|
|
2760
3500
|
"ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'",
|
|
2761
|
-
"ALTER TABLE memories ADD COLUMN trajectory TEXT"
|
|
3501
|
+
"ALTER TABLE memories ADD COLUMN trajectory TEXT",
|
|
3502
|
+
// Metadata enrichment columns (must match database.ts)
|
|
3503
|
+
"ALTER TABLE memories ADD COLUMN intent TEXT",
|
|
3504
|
+
"ALTER TABLE memories ADD COLUMN outcome TEXT",
|
|
3505
|
+
"ALTER TABLE memories ADD COLUMN domain TEXT",
|
|
3506
|
+
"ALTER TABLE memories ADD COLUMN referenced_entities TEXT",
|
|
3507
|
+
"ALTER TABLE memories ADD COLUMN retrieval_count INTEGER DEFAULT 0",
|
|
3508
|
+
"ALTER TABLE memories ADD COLUMN chain_position TEXT",
|
|
3509
|
+
"ALTER TABLE memories ADD COLUMN review_status TEXT",
|
|
3510
|
+
"ALTER TABLE memories ADD COLUMN context_window_pct INTEGER",
|
|
3511
|
+
"ALTER TABLE memories ADD COLUMN file_paths TEXT",
|
|
3512
|
+
"ALTER TABLE memories ADD COLUMN commit_hash TEXT",
|
|
3513
|
+
"ALTER TABLE memories ADD COLUMN duration_ms INTEGER",
|
|
3514
|
+
"ALTER TABLE memories ADD COLUMN token_cost REAL",
|
|
3515
|
+
"ALTER TABLE memories ADD COLUMN audience TEXT",
|
|
3516
|
+
"ALTER TABLE memories ADD COLUMN language_type TEXT",
|
|
3517
|
+
"ALTER TABLE memories ADD COLUMN parent_memory_id TEXT"
|
|
2762
3518
|
]) {
|
|
2763
3519
|
try {
|
|
2764
3520
|
await client.execute(col);
|
|
@@ -2870,7 +3626,7 @@ var init_shard_manager = __esm({
|
|
|
2870
3626
|
"src/lib/shard-manager.ts"() {
|
|
2871
3627
|
"use strict";
|
|
2872
3628
|
init_config();
|
|
2873
|
-
SHARDS_DIR =
|
|
3629
|
+
SHARDS_DIR = path14.join(EXE_AI_DIR, "shards");
|
|
2874
3630
|
_shards = /* @__PURE__ */ new Map();
|
|
2875
3631
|
_encryptionKey = null;
|
|
2876
3632
|
_shardingEnabled = false;
|
|
@@ -3763,16 +4519,16 @@ var init_review_gate = __esm({
|
|
|
3763
4519
|
// src/adapters/claude/hooks/pre-tool-use.ts
|
|
3764
4520
|
import { existsSync as existsSync13, writeFileSync as writeFileSync7, mkdirSync as mkdirSync7 } from "fs";
|
|
3765
4521
|
import { execSync as execSync6 } from "child_process";
|
|
3766
|
-
import
|
|
4522
|
+
import path15 from "path";
|
|
3767
4523
|
|
|
3768
4524
|
// src/lib/active-agent.ts
|
|
3769
4525
|
init_config();
|
|
3770
4526
|
init_session_key();
|
|
3771
4527
|
init_employees();
|
|
3772
|
-
import { readFileSync as
|
|
4528
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, mkdirSync as mkdirSync2, unlinkSync as unlinkSync2, readdirSync } from "fs";
|
|
3773
4529
|
import { execSync as execSync3 } from "child_process";
|
|
3774
|
-
import
|
|
3775
|
-
var CACHE_DIR =
|
|
4530
|
+
import path4 from "path";
|
|
4531
|
+
var CACHE_DIR = path4.join(EXE_AI_DIR, "session-cache");
|
|
3776
4532
|
var STALE_MS = 24 * 60 * 60 * 1e3;
|
|
3777
4533
|
function isNameWithOptionalInstance(candidate, baseName) {
|
|
3778
4534
|
if (candidate === baseName) return true;
|
|
@@ -3817,12 +4573,12 @@ function resolveActiveAgentFromTmuxSession(sessionName) {
|
|
|
3817
4573
|
return null;
|
|
3818
4574
|
}
|
|
3819
4575
|
function getMarkerPath() {
|
|
3820
|
-
return
|
|
4576
|
+
return path4.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
|
|
3821
4577
|
}
|
|
3822
4578
|
function getActiveAgent() {
|
|
3823
4579
|
try {
|
|
3824
4580
|
const markerPath = getMarkerPath();
|
|
3825
|
-
const raw =
|
|
4581
|
+
const raw = readFileSync4(markerPath, "utf8");
|
|
3826
4582
|
const data = JSON.parse(raw);
|
|
3827
4583
|
if (data.agentId) {
|
|
3828
4584
|
if (data.startedAt) {
|
|
@@ -3877,13 +4633,13 @@ if (!process.env.AGENT_ID) {
|
|
|
3877
4633
|
}
|
|
3878
4634
|
var DELEGATION_TASK_THRESHOLD = 3;
|
|
3879
4635
|
var CTO_ROLES = ["CTO", "executive"];
|
|
3880
|
-
var CACHE_DIR2 =
|
|
4636
|
+
var CACHE_DIR2 = path15.join(EXE_AI_DIR, "session-cache");
|
|
3881
4637
|
var timeout = setTimeout(() => {
|
|
3882
4638
|
process.exit(0);
|
|
3883
4639
|
}, 5e3);
|
|
3884
4640
|
timeout.unref();
|
|
3885
4641
|
function getDelegationFlagPath() {
|
|
3886
|
-
return
|
|
4642
|
+
return path15.join(CACHE_DIR2, `delegation-checkpoint-${getSessionKey()}.json`);
|
|
3887
4643
|
}
|
|
3888
4644
|
function hasDelegationFired() {
|
|
3889
4645
|
try {
|