@askexenow/exe-os 0.9.7 → 0.9.9
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 +953 -105
- package/dist/bin/backfill-responses.js +952 -104
- package/dist/bin/backfill-vectors.js +956 -108
- package/dist/bin/cleanup-stale-review-tasks.js +802 -58
- package/dist/bin/cli.js +2292 -1070
- package/dist/bin/exe-agent-config.js +157 -101
- package/dist/bin/exe-agent.js +55 -29
- package/dist/bin/exe-assign.js +940 -92
- package/dist/bin/exe-boot.js +1424 -442
- package/dist/bin/exe-call.js +240 -141
- package/dist/bin/exe-cloud.js +198 -70
- package/dist/bin/exe-dispatch.js +951 -192
- package/dist/bin/exe-doctor.js +791 -51
- package/dist/bin/exe-export-behaviors.js +790 -42
- package/dist/bin/exe-forget.js +771 -31
- package/dist/bin/exe-gateway.js +1592 -521
- package/dist/bin/exe-heartbeat.js +850 -109
- package/dist/bin/exe-kill.js +783 -35
- package/dist/bin/exe-launch-agent.js +1030 -107
- package/dist/bin/exe-link.js +916 -110
- package/dist/bin/exe-new-employee.js +526 -217
- package/dist/bin/exe-pending-messages.js +1046 -62
- package/dist/bin/exe-pending-notifications.js +1318 -111
- package/dist/bin/exe-pending-reviews.js +1040 -72
- package/dist/bin/exe-rename.js +772 -59
- package/dist/bin/exe-review.js +772 -32
- package/dist/bin/exe-search.js +982 -128
- package/dist/bin/exe-session-cleanup.js +1180 -306
- package/dist/bin/exe-settings.js +185 -105
- package/dist/bin/exe-start-codex.js +886 -132
- package/dist/bin/exe-start-opencode.js +873 -119
- package/dist/bin/exe-status.js +803 -59
- package/dist/bin/exe-team.js +772 -32
- package/dist/bin/git-sweep.js +1046 -223
- package/dist/bin/graph-backfill.js +779 -31
- package/dist/bin/graph-export.js +785 -37
- package/dist/bin/install.js +632 -200
- package/dist/bin/scan-tasks.js +1055 -232
- package/dist/bin/setup.js +1419 -320
- package/dist/bin/shard-migrate.js +783 -35
- package/dist/bin/update.js +138 -49
- package/dist/bin/wiki-sync.js +782 -34
- package/dist/gateway/index.js +1444 -449
- package/dist/hooks/bug-report-worker.js +1141 -269
- package/dist/hooks/codex-stop-task-finalizer.js +4678 -0
- package/dist/hooks/commit-complete.js +1044 -221
- package/dist/hooks/error-recall.js +989 -135
- package/dist/hooks/exe-heartbeat-hook.js +99 -75
- package/dist/hooks/ingest-worker.js +4176 -3226
- package/dist/hooks/ingest.js +920 -168
- package/dist/hooks/instructions-loaded.js +874 -70
- package/dist/hooks/notification.js +860 -56
- package/dist/hooks/post-compact.js +881 -73
- package/dist/hooks/pre-compact.js +1050 -227
- package/dist/hooks/pre-tool-use.js +1084 -159
- package/dist/hooks/prompt-ingest-worker.js +1089 -164
- package/dist/hooks/prompt-submit.js +1469 -515
- package/dist/hooks/response-ingest-worker.js +1104 -179
- package/dist/hooks/session-end.js +1085 -251
- package/dist/hooks/session-start.js +1241 -231
- package/dist/hooks/stop.js +935 -109
- package/dist/hooks/subagent-stop.js +881 -73
- package/dist/hooks/summary-worker.js +1323 -307
- package/dist/index.js +1449 -452
- package/dist/lib/agent-config.js +28 -6
- package/dist/lib/cloud-sync.js +909 -115
- package/dist/lib/config.js +30 -10
- package/dist/lib/consolidation.js +42 -9
- package/dist/lib/database.js +739 -33
- package/dist/lib/db-daemon-client.js +73 -19
- package/dist/lib/db.js +2359 -0
- package/dist/lib/device-registry.js +760 -47
- package/dist/lib/embedder.js +201 -73
- package/dist/lib/employee-templates.js +30 -4
- package/dist/lib/employees.js +290 -86
- package/dist/lib/exe-daemon-client.js +187 -83
- package/dist/lib/exe-daemon.js +1696 -616
- package/dist/lib/hybrid-search.js +982 -128
- package/dist/lib/identity.js +43 -13
- package/dist/lib/license.js +133 -48
- package/dist/lib/messaging.js +167 -80
- package/dist/lib/reminders.js +35 -5
- package/dist/lib/schedules.js +772 -32
- package/dist/lib/skill-learning.js +54 -7
- package/dist/lib/store.js +779 -31
- package/dist/lib/task-router.js +94 -73
- package/dist/lib/tasks.js +298 -225
- package/dist/lib/tmux-routing.js +246 -172
- package/dist/lib/token-spend.js +52 -14
- package/dist/mcp/server.js +2893 -850
- package/dist/mcp/tools/complete-reminder.js +35 -5
- package/dist/mcp/tools/create-reminder.js +35 -5
- package/dist/mcp/tools/create-task.js +507 -323
- package/dist/mcp/tools/deactivate-behavior.js +40 -10
- package/dist/mcp/tools/list-reminders.js +35 -5
- package/dist/mcp/tools/list-tasks.js +277 -104
- package/dist/mcp/tools/send-message.js +129 -56
- package/dist/mcp/tools/update-task.js +1864 -188
- package/dist/runtime/index.js +1083 -259
- package/dist/tui/App.js +1501 -434
- package/package.json +3 -2
|
@@ -70,9 +70,47 @@ var init_db_retry = __esm({
|
|
|
70
70
|
}
|
|
71
71
|
});
|
|
72
72
|
|
|
73
|
+
// src/lib/secure-files.ts
|
|
74
|
+
import { chmodSync, existsSync, mkdirSync } from "fs";
|
|
75
|
+
import { chmod, mkdir } from "fs/promises";
|
|
76
|
+
async function ensurePrivateDir(dirPath) {
|
|
77
|
+
await mkdir(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
78
|
+
try {
|
|
79
|
+
await chmod(dirPath, PRIVATE_DIR_MODE);
|
|
80
|
+
} catch {
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
function ensurePrivateDirSync(dirPath) {
|
|
84
|
+
mkdirSync(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
85
|
+
try {
|
|
86
|
+
chmodSync(dirPath, PRIVATE_DIR_MODE);
|
|
87
|
+
} catch {
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
async function enforcePrivateFile(filePath) {
|
|
91
|
+
try {
|
|
92
|
+
await chmod(filePath, PRIVATE_FILE_MODE);
|
|
93
|
+
} catch {
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
function enforcePrivateFileSync(filePath) {
|
|
97
|
+
try {
|
|
98
|
+
if (existsSync(filePath)) chmodSync(filePath, PRIVATE_FILE_MODE);
|
|
99
|
+
} catch {
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
var PRIVATE_DIR_MODE, PRIVATE_FILE_MODE;
|
|
103
|
+
var init_secure_files = __esm({
|
|
104
|
+
"src/lib/secure-files.ts"() {
|
|
105
|
+
"use strict";
|
|
106
|
+
PRIVATE_DIR_MODE = 448;
|
|
107
|
+
PRIVATE_FILE_MODE = 384;
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
|
|
73
111
|
// src/lib/config.ts
|
|
74
|
-
import { readFile, writeFile
|
|
75
|
-
import { readFileSync, existsSync, renameSync } from "fs";
|
|
112
|
+
import { readFile, writeFile } from "fs/promises";
|
|
113
|
+
import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
|
|
76
114
|
import path from "path";
|
|
77
115
|
import os from "os";
|
|
78
116
|
function resolveDataDir() {
|
|
@@ -80,7 +118,7 @@ function resolveDataDir() {
|
|
|
80
118
|
if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
|
|
81
119
|
const newDir = path.join(os.homedir(), ".exe-os");
|
|
82
120
|
const legacyDir = path.join(os.homedir(), ".exe-mem");
|
|
83
|
-
if (!
|
|
121
|
+
if (!existsSync2(newDir) && existsSync2(legacyDir)) {
|
|
84
122
|
try {
|
|
85
123
|
renameSync(legacyDir, newDir);
|
|
86
124
|
process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
|
|
@@ -143,9 +181,9 @@ function normalizeAutoUpdate(raw) {
|
|
|
143
181
|
}
|
|
144
182
|
async function loadConfig() {
|
|
145
183
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
146
|
-
await
|
|
184
|
+
await ensurePrivateDir(dir);
|
|
147
185
|
const configPath = path.join(dir, "config.json");
|
|
148
|
-
if (!
|
|
186
|
+
if (!existsSync2(configPath)) {
|
|
149
187
|
return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
|
|
150
188
|
}
|
|
151
189
|
const raw = await readFile(configPath, "utf-8");
|
|
@@ -158,6 +196,7 @@ async function loadConfig() {
|
|
|
158
196
|
`);
|
|
159
197
|
try {
|
|
160
198
|
await writeFile(configPath, JSON.stringify(migratedCfg, null, 2) + "\n");
|
|
199
|
+
await enforcePrivateFile(configPath);
|
|
161
200
|
} catch {
|
|
162
201
|
}
|
|
163
202
|
}
|
|
@@ -177,6 +216,7 @@ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CON
|
|
|
177
216
|
var init_config = __esm({
|
|
178
217
|
"src/lib/config.ts"() {
|
|
179
218
|
"use strict";
|
|
219
|
+
init_secure_files();
|
|
180
220
|
EXE_AI_DIR = resolveDataDir();
|
|
181
221
|
DB_PATH = path.join(EXE_AI_DIR, "memories.db");
|
|
182
222
|
MODELS_DIR = path.join(EXE_AI_DIR, "models");
|
|
@@ -253,6 +293,120 @@ var init_config = __esm({
|
|
|
253
293
|
}
|
|
254
294
|
});
|
|
255
295
|
|
|
296
|
+
// src/lib/runtime-table.ts
|
|
297
|
+
var RUNTIME_TABLE, DEFAULT_RUNTIME;
|
|
298
|
+
var init_runtime_table = __esm({
|
|
299
|
+
"src/lib/runtime-table.ts"() {
|
|
300
|
+
"use strict";
|
|
301
|
+
RUNTIME_TABLE = {
|
|
302
|
+
codex: {
|
|
303
|
+
binary: "codex",
|
|
304
|
+
launchMode: "interactive",
|
|
305
|
+
autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
|
|
306
|
+
inlineFlag: "--no-alt-screen",
|
|
307
|
+
apiKeyEnv: "OPENAI_API_KEY",
|
|
308
|
+
defaultModel: "gpt-5.4"
|
|
309
|
+
},
|
|
310
|
+
opencode: {
|
|
311
|
+
binary: "opencode",
|
|
312
|
+
launchMode: "exec",
|
|
313
|
+
autoApproveFlag: "--dangerously-skip-permissions",
|
|
314
|
+
inlineFlag: "",
|
|
315
|
+
apiKeyEnv: "ANTHROPIC_API_KEY",
|
|
316
|
+
defaultModel: "anthropic/claude-sonnet-4-6"
|
|
317
|
+
}
|
|
318
|
+
};
|
|
319
|
+
DEFAULT_RUNTIME = "claude";
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
// src/lib/agent-config.ts
|
|
324
|
+
var agent_config_exports = {};
|
|
325
|
+
__export(agent_config_exports, {
|
|
326
|
+
AGENT_CONFIG_PATH: () => AGENT_CONFIG_PATH,
|
|
327
|
+
DEFAULT_MODELS: () => DEFAULT_MODELS,
|
|
328
|
+
KNOWN_RUNTIMES: () => KNOWN_RUNTIMES,
|
|
329
|
+
RUNTIME_LABELS: () => RUNTIME_LABELS,
|
|
330
|
+
clearAgentRuntime: () => clearAgentRuntime,
|
|
331
|
+
getAgentRuntime: () => getAgentRuntime,
|
|
332
|
+
loadAgentConfig: () => loadAgentConfig,
|
|
333
|
+
saveAgentConfig: () => saveAgentConfig,
|
|
334
|
+
setAgentRuntime: () => setAgentRuntime
|
|
335
|
+
});
|
|
336
|
+
import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync3 } from "fs";
|
|
337
|
+
import path2 from "path";
|
|
338
|
+
function loadAgentConfig() {
|
|
339
|
+
if (!existsSync3(AGENT_CONFIG_PATH)) return {};
|
|
340
|
+
try {
|
|
341
|
+
return JSON.parse(readFileSync2(AGENT_CONFIG_PATH, "utf-8"));
|
|
342
|
+
} catch {
|
|
343
|
+
return {};
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
function saveAgentConfig(config) {
|
|
347
|
+
const dir = path2.dirname(AGENT_CONFIG_PATH);
|
|
348
|
+
ensurePrivateDirSync(dir);
|
|
349
|
+
writeFileSync(AGENT_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
350
|
+
enforcePrivateFileSync(AGENT_CONFIG_PATH);
|
|
351
|
+
}
|
|
352
|
+
function getAgentRuntime(agentId) {
|
|
353
|
+
const config = loadAgentConfig();
|
|
354
|
+
const entry = config[agentId];
|
|
355
|
+
if (entry) return entry;
|
|
356
|
+
const orgDefault = config["default"];
|
|
357
|
+
if (orgDefault) return orgDefault;
|
|
358
|
+
return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
|
|
359
|
+
}
|
|
360
|
+
function setAgentRuntime(agentId, runtime, model) {
|
|
361
|
+
const knownModels = KNOWN_RUNTIMES[runtime];
|
|
362
|
+
if (!knownModels) {
|
|
363
|
+
return {
|
|
364
|
+
ok: false,
|
|
365
|
+
error: `Unknown runtime "${runtime}". Valid: ${Object.keys(KNOWN_RUNTIMES).join(", ")}`
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
if (!knownModels.includes(model)) {
|
|
369
|
+
return {
|
|
370
|
+
ok: false,
|
|
371
|
+
error: `Unknown model "${model}" for runtime "${runtime}". Valid: ${knownModels.join(", ")}`
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
const config = loadAgentConfig();
|
|
375
|
+
config[agentId] = { runtime, model };
|
|
376
|
+
saveAgentConfig(config);
|
|
377
|
+
return { ok: true };
|
|
378
|
+
}
|
|
379
|
+
function clearAgentRuntime(agentId) {
|
|
380
|
+
const config = loadAgentConfig();
|
|
381
|
+
delete config[agentId];
|
|
382
|
+
saveAgentConfig(config);
|
|
383
|
+
}
|
|
384
|
+
var AGENT_CONFIG_PATH, KNOWN_RUNTIMES, RUNTIME_LABELS, DEFAULT_MODELS;
|
|
385
|
+
var init_agent_config = __esm({
|
|
386
|
+
"src/lib/agent-config.ts"() {
|
|
387
|
+
"use strict";
|
|
388
|
+
init_config();
|
|
389
|
+
init_runtime_table();
|
|
390
|
+
init_secure_files();
|
|
391
|
+
AGENT_CONFIG_PATH = path2.join(EXE_AI_DIR, "agent-config.json");
|
|
392
|
+
KNOWN_RUNTIMES = {
|
|
393
|
+
claude: ["claude-opus-4", "claude-sonnet-4", "claude-haiku-4.5"],
|
|
394
|
+
codex: ["gpt-5.4", "gpt-5.5", "gpt-5.3-codex-spark", "o3", "o4-mini"],
|
|
395
|
+
opencode: ["anthropic/claude-sonnet-4-6", "openai/gpt-5.4", "google/gemini-2.5-pro", "deepseek/deepseek-r3", "minimax/minimax-m2.5"]
|
|
396
|
+
};
|
|
397
|
+
RUNTIME_LABELS = {
|
|
398
|
+
claude: "Claude Code (Anthropic)",
|
|
399
|
+
codex: "Codex (OpenAI)",
|
|
400
|
+
opencode: "OpenCode (open source)"
|
|
401
|
+
};
|
|
402
|
+
DEFAULT_MODELS = {
|
|
403
|
+
claude: "claude-opus-4",
|
|
404
|
+
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
405
|
+
opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
});
|
|
409
|
+
|
|
256
410
|
// src/lib/employees.ts
|
|
257
411
|
var employees_exports = {};
|
|
258
412
|
__export(employees_exports, {
|
|
@@ -268,6 +422,7 @@ __export(employees_exports, {
|
|
|
268
422
|
getEmployeeByRole: () => getEmployeeByRole,
|
|
269
423
|
getEmployeeNamesByRole: () => getEmployeeNamesByRole,
|
|
270
424
|
hasRole: () => hasRole,
|
|
425
|
+
hireEmployee: () => hireEmployee,
|
|
271
426
|
isCoordinatorName: () => isCoordinatorName,
|
|
272
427
|
isCoordinatorRole: () => isCoordinatorRole,
|
|
273
428
|
isMultiInstance: () => isMultiInstance,
|
|
@@ -280,9 +435,9 @@ __export(employees_exports, {
|
|
|
280
435
|
validateEmployeeName: () => validateEmployeeName
|
|
281
436
|
});
|
|
282
437
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
283
|
-
import { existsSync as
|
|
438
|
+
import { existsSync as existsSync4, symlinkSync, readlinkSync, readFileSync as readFileSync3, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
|
|
284
439
|
import { execSync } from "child_process";
|
|
285
|
-
import
|
|
440
|
+
import path3 from "path";
|
|
286
441
|
import os2 from "os";
|
|
287
442
|
function normalizeRole(role) {
|
|
288
443
|
return (role ?? "").trim().toLowerCase();
|
|
@@ -319,7 +474,7 @@ function validateEmployeeName(name) {
|
|
|
319
474
|
return { valid: true };
|
|
320
475
|
}
|
|
321
476
|
async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
322
|
-
if (!
|
|
477
|
+
if (!existsSync4(employeesPath)) {
|
|
323
478
|
return [];
|
|
324
479
|
}
|
|
325
480
|
const raw = await readFile2(employeesPath, "utf-8");
|
|
@@ -330,13 +485,13 @@ async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
|
330
485
|
}
|
|
331
486
|
}
|
|
332
487
|
async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
|
|
333
|
-
await mkdir2(
|
|
488
|
+
await mkdir2(path3.dirname(employeesPath), { recursive: true });
|
|
334
489
|
await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
|
|
335
490
|
}
|
|
336
491
|
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
337
|
-
if (!
|
|
492
|
+
if (!existsSync4(employeesPath)) return [];
|
|
338
493
|
try {
|
|
339
|
-
return JSON.parse(
|
|
494
|
+
return JSON.parse(readFileSync3(employeesPath, "utf-8"));
|
|
340
495
|
} catch {
|
|
341
496
|
return [];
|
|
342
497
|
}
|
|
@@ -378,6 +533,52 @@ function addEmployee(employees, employee) {
|
|
|
378
533
|
}
|
|
379
534
|
return [...employees, normalized];
|
|
380
535
|
}
|
|
536
|
+
function appendToCoordinatorTeam(employee) {
|
|
537
|
+
const coordinator = getCoordinatorEmployee(loadEmployeesSync());
|
|
538
|
+
if (!coordinator) return;
|
|
539
|
+
const idPath = path3.join(IDENTITY_DIR, `${coordinator.name}.md`);
|
|
540
|
+
if (!existsSync4(idPath)) return;
|
|
541
|
+
const content = readFileSync3(idPath, "utf-8");
|
|
542
|
+
if (content.includes(`**${capitalize(employee.name)}`)) return;
|
|
543
|
+
const teamMatch = content.match(TEAM_SECTION_RE);
|
|
544
|
+
if (!teamMatch || teamMatch.index === void 0) return;
|
|
545
|
+
const afterTeam = content.slice(teamMatch.index + teamMatch[0].length);
|
|
546
|
+
const nextHeading = afterTeam.match(/\n## /);
|
|
547
|
+
const entry = `
|
|
548
|
+
**${capitalize(employee.name)} (${employee.role}):** Newly hired. Update this description as the role develops.
|
|
549
|
+
`;
|
|
550
|
+
let updated;
|
|
551
|
+
if (nextHeading && nextHeading.index !== void 0) {
|
|
552
|
+
const insertAt = teamMatch.index + teamMatch[0].length + nextHeading.index;
|
|
553
|
+
updated = content.slice(0, insertAt) + entry + content.slice(insertAt);
|
|
554
|
+
} else {
|
|
555
|
+
updated = content.trimEnd() + "\n" + entry;
|
|
556
|
+
}
|
|
557
|
+
writeFileSync2(idPath, updated, "utf-8");
|
|
558
|
+
}
|
|
559
|
+
function capitalize(s) {
|
|
560
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
561
|
+
}
|
|
562
|
+
async function hireEmployee(employee) {
|
|
563
|
+
const employees = await loadEmployees();
|
|
564
|
+
const updated = addEmployee(employees, employee);
|
|
565
|
+
await saveEmployees(updated);
|
|
566
|
+
try {
|
|
567
|
+
appendToCoordinatorTeam(employee);
|
|
568
|
+
} catch {
|
|
569
|
+
}
|
|
570
|
+
try {
|
|
571
|
+
const { loadAgentConfig: loadAgentConfig2, saveAgentConfig: saveAgentConfig2 } = await Promise.resolve().then(() => (init_agent_config(), agent_config_exports));
|
|
572
|
+
const config = loadAgentConfig2();
|
|
573
|
+
const name = employee.name.toLowerCase();
|
|
574
|
+
if (!config[name] && config["default"]) {
|
|
575
|
+
config[name] = { ...config["default"] };
|
|
576
|
+
saveAgentConfig2(config);
|
|
577
|
+
}
|
|
578
|
+
} catch {
|
|
579
|
+
}
|
|
580
|
+
return updated;
|
|
581
|
+
}
|
|
381
582
|
async function normalizeRosterCase(rosterPath) {
|
|
382
583
|
const employees = await loadEmployees(rosterPath);
|
|
383
584
|
let changed = false;
|
|
@@ -387,14 +588,14 @@ async function normalizeRosterCase(rosterPath) {
|
|
|
387
588
|
emp.name = emp.name.toLowerCase();
|
|
388
589
|
changed = true;
|
|
389
590
|
try {
|
|
390
|
-
const identityDir =
|
|
391
|
-
const oldPath =
|
|
392
|
-
const newPath =
|
|
393
|
-
if (
|
|
591
|
+
const identityDir = path3.join(os2.homedir(), ".exe-os", "identity");
|
|
592
|
+
const oldPath = path3.join(identityDir, `${oldName}.md`);
|
|
593
|
+
const newPath = path3.join(identityDir, `${emp.name}.md`);
|
|
594
|
+
if (existsSync4(oldPath) && !existsSync4(newPath)) {
|
|
394
595
|
renameSync2(oldPath, newPath);
|
|
395
|
-
} else if (
|
|
396
|
-
const content =
|
|
397
|
-
|
|
596
|
+
} else if (existsSync4(oldPath) && oldPath !== newPath) {
|
|
597
|
+
const content = readFileSync3(oldPath, "utf-8");
|
|
598
|
+
writeFileSync2(newPath, content, "utf-8");
|
|
398
599
|
if (oldPath.toLowerCase() !== newPath.toLowerCase()) {
|
|
399
600
|
unlinkSync(oldPath);
|
|
400
601
|
}
|
|
@@ -424,7 +625,7 @@ function registerBinSymlinks(name) {
|
|
|
424
625
|
errors.push("Could not find 'exe-os' in PATH");
|
|
425
626
|
return { created, skipped, errors };
|
|
426
627
|
}
|
|
427
|
-
const binDir =
|
|
628
|
+
const binDir = path3.dirname(exeBinPath);
|
|
428
629
|
let target;
|
|
429
630
|
try {
|
|
430
631
|
target = readlinkSync(exeBinPath);
|
|
@@ -434,8 +635,8 @@ function registerBinSymlinks(name) {
|
|
|
434
635
|
}
|
|
435
636
|
for (const suffix of ["", "-opencode"]) {
|
|
436
637
|
const linkName = `${name}${suffix}`;
|
|
437
|
-
const linkPath =
|
|
438
|
-
if (
|
|
638
|
+
const linkPath = path3.join(binDir, linkName);
|
|
639
|
+
if (existsSync4(linkPath)) {
|
|
439
640
|
skipped.push(linkName);
|
|
440
641
|
continue;
|
|
441
642
|
}
|
|
@@ -448,21 +649,619 @@ function registerBinSymlinks(name) {
|
|
|
448
649
|
}
|
|
449
650
|
return { created, skipped, errors };
|
|
450
651
|
}
|
|
451
|
-
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES;
|
|
652
|
+
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES, IDENTITY_DIR, TEAM_SECTION_RE;
|
|
452
653
|
var init_employees = __esm({
|
|
453
654
|
"src/lib/employees.ts"() {
|
|
454
655
|
"use strict";
|
|
455
656
|
init_config();
|
|
456
|
-
EMPLOYEES_PATH =
|
|
657
|
+
EMPLOYEES_PATH = path3.join(EXE_AI_DIR, "exe-employees.json");
|
|
457
658
|
DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
|
|
458
659
|
COORDINATOR_ROLE = "COO";
|
|
459
660
|
MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
|
|
661
|
+
IDENTITY_DIR = path3.join(EXE_AI_DIR, "identity");
|
|
662
|
+
TEAM_SECTION_RE = /^## Team\b.*$/m;
|
|
663
|
+
}
|
|
664
|
+
});
|
|
665
|
+
|
|
666
|
+
// src/lib/database-adapter.ts
|
|
667
|
+
import os3 from "os";
|
|
668
|
+
import path4 from "path";
|
|
669
|
+
import { createRequire } from "module";
|
|
670
|
+
import { pathToFileURL } from "url";
|
|
671
|
+
function quotedIdentifier(identifier) {
|
|
672
|
+
return `"${identifier.replace(/"/g, '""')}"`;
|
|
673
|
+
}
|
|
674
|
+
function unqualifiedTableName(name) {
|
|
675
|
+
const raw = name.trim().replace(/^"|"$/g, "");
|
|
676
|
+
const parts = raw.split(".");
|
|
677
|
+
return parts[parts.length - 1].replace(/^"|"$/g, "").toLowerCase();
|
|
678
|
+
}
|
|
679
|
+
function stripTrailingSemicolon(sql) {
|
|
680
|
+
return sql.trim().replace(/;+\s*$/u, "");
|
|
681
|
+
}
|
|
682
|
+
function appendClause(sql, clause) {
|
|
683
|
+
const trimmed = stripTrailingSemicolon(sql);
|
|
684
|
+
const returningMatch = /\sRETURNING\b[\s\S]*$/iu.exec(trimmed);
|
|
685
|
+
if (!returningMatch) {
|
|
686
|
+
return `${trimmed}${clause}`;
|
|
687
|
+
}
|
|
688
|
+
const idx = returningMatch.index;
|
|
689
|
+
return `${trimmed.slice(0, idx)}${clause}${trimmed.slice(idx)}`;
|
|
690
|
+
}
|
|
691
|
+
function normalizeStatement(stmt) {
|
|
692
|
+
if (typeof stmt === "string") {
|
|
693
|
+
return { kind: "positional", sql: stmt, args: [] };
|
|
694
|
+
}
|
|
695
|
+
const sql = stmt.sql;
|
|
696
|
+
if (Array.isArray(stmt.args) || stmt.args === void 0) {
|
|
697
|
+
return { kind: "positional", sql, args: stmt.args ?? [] };
|
|
698
|
+
}
|
|
699
|
+
return { kind: "named", sql, args: stmt.args };
|
|
700
|
+
}
|
|
701
|
+
function rewriteBooleanLiterals(sql) {
|
|
702
|
+
let out = sql;
|
|
703
|
+
for (const column of BOOLEAN_COLUMN_NAMES) {
|
|
704
|
+
const scoped = `((?:\\b[a-z_][a-z0-9_]*\\.)?${column})`;
|
|
705
|
+
out = out.replace(new RegExp(`${scoped}\\s*=\\s*0\\b`, "giu"), "$1 = FALSE");
|
|
706
|
+
out = out.replace(new RegExp(`${scoped}\\s*=\\s*1\\b`, "giu"), "$1 = TRUE");
|
|
707
|
+
out = out.replace(new RegExp(`${scoped}\\s*!=\\s*0\\b`, "giu"), "$1 != FALSE");
|
|
708
|
+
out = out.replace(new RegExp(`${scoped}\\s*!=\\s*1\\b`, "giu"), "$1 != TRUE");
|
|
709
|
+
out = out.replace(new RegExp(`${scoped}\\s*<>\\s*0\\b`, "giu"), "$1 <> FALSE");
|
|
710
|
+
out = out.replace(new RegExp(`${scoped}\\s*<>\\s*1\\b`, "giu"), "$1 <> TRUE");
|
|
711
|
+
}
|
|
712
|
+
return out;
|
|
713
|
+
}
|
|
714
|
+
function rewriteInsertOrIgnore(sql) {
|
|
715
|
+
if (!/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu.test(sql)) {
|
|
716
|
+
return sql;
|
|
717
|
+
}
|
|
718
|
+
const replaced = sql.replace(/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu, "INSERT INTO");
|
|
719
|
+
return /\bON\s+CONFLICT\b/iu.test(replaced) ? replaced : appendClause(replaced, " ON CONFLICT DO NOTHING");
|
|
720
|
+
}
|
|
721
|
+
function rewriteInsertOrReplace(sql) {
|
|
722
|
+
const match = /^\s*INSERT\s+OR\s+REPLACE\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)([\s\S]*)$/iu.exec(sql);
|
|
723
|
+
if (!match) {
|
|
724
|
+
return sql;
|
|
725
|
+
}
|
|
726
|
+
const rawTable = match[1];
|
|
727
|
+
const rawColumns = match[2];
|
|
728
|
+
const remainder = match[3];
|
|
729
|
+
const tableName = unqualifiedTableName(rawTable);
|
|
730
|
+
const conflictKeys = UPSERT_KEYS[tableName];
|
|
731
|
+
if (!conflictKeys?.length) {
|
|
732
|
+
return sql;
|
|
733
|
+
}
|
|
734
|
+
const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
|
|
735
|
+
const updateColumns = columns.filter((col) => !conflictKeys.includes(col));
|
|
736
|
+
const conflictTarget = conflictKeys.map(quotedIdentifier).join(", ");
|
|
737
|
+
const updateClause = updateColumns.length === 0 ? " DO NOTHING" : ` DO UPDATE SET ${updateColumns.map((col) => `${quotedIdentifier(col)} = EXCLUDED.${quotedIdentifier(col)}`).join(", ")}`;
|
|
738
|
+
return `INSERT INTO ${rawTable} (${rawColumns})${appendClause(remainder, ` ON CONFLICT (${conflictTarget})${updateClause}`)}`;
|
|
739
|
+
}
|
|
740
|
+
function rewriteSql(sql) {
|
|
741
|
+
let out = sql;
|
|
742
|
+
out = out.replace(/\bdatetime\(\s*['"]now['"]\s*\)/giu, "CURRENT_TIMESTAMP");
|
|
743
|
+
out = out.replace(/\bvector32\s*\(\s*\?\s*\)/giu, "?");
|
|
744
|
+
out = rewriteBooleanLiterals(out);
|
|
745
|
+
out = rewriteInsertOrReplace(out);
|
|
746
|
+
out = rewriteInsertOrIgnore(out);
|
|
747
|
+
return stripTrailingSemicolon(out);
|
|
748
|
+
}
|
|
749
|
+
function toBoolean(value) {
|
|
750
|
+
if (value === null || value === void 0) return value;
|
|
751
|
+
if (typeof value === "boolean") return value;
|
|
752
|
+
if (typeof value === "number") return value !== 0;
|
|
753
|
+
if (typeof value === "bigint") return value !== 0n;
|
|
754
|
+
if (typeof value === "string") {
|
|
755
|
+
const normalized = value.trim().toLowerCase();
|
|
756
|
+
if (normalized === "0" || normalized === "false") return false;
|
|
757
|
+
if (normalized === "1" || normalized === "true") return true;
|
|
758
|
+
}
|
|
759
|
+
return Boolean(value);
|
|
760
|
+
}
|
|
761
|
+
function countQuestionMarks(sql, end) {
|
|
762
|
+
let count = 0;
|
|
763
|
+
let inSingle = false;
|
|
764
|
+
let inDouble = false;
|
|
765
|
+
let inLineComment = false;
|
|
766
|
+
let inBlockComment = false;
|
|
767
|
+
for (let i = 0; i < end; i++) {
|
|
768
|
+
const ch = sql[i];
|
|
769
|
+
const next = sql[i + 1];
|
|
770
|
+
if (inLineComment) {
|
|
771
|
+
if (ch === "\n") inLineComment = false;
|
|
772
|
+
continue;
|
|
773
|
+
}
|
|
774
|
+
if (inBlockComment) {
|
|
775
|
+
if (ch === "*" && next === "/") {
|
|
776
|
+
inBlockComment = false;
|
|
777
|
+
i += 1;
|
|
778
|
+
}
|
|
779
|
+
continue;
|
|
780
|
+
}
|
|
781
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
782
|
+
inLineComment = true;
|
|
783
|
+
i += 1;
|
|
784
|
+
continue;
|
|
785
|
+
}
|
|
786
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
787
|
+
inBlockComment = true;
|
|
788
|
+
i += 1;
|
|
789
|
+
continue;
|
|
790
|
+
}
|
|
791
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
792
|
+
inSingle = !inSingle;
|
|
793
|
+
continue;
|
|
794
|
+
}
|
|
795
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
796
|
+
inDouble = !inDouble;
|
|
797
|
+
continue;
|
|
798
|
+
}
|
|
799
|
+
if (!inSingle && !inDouble && ch === "?") {
|
|
800
|
+
count += 1;
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
return count;
|
|
804
|
+
}
|
|
805
|
+
function findBooleanPlaceholderIndexes(sql) {
|
|
806
|
+
const indexes = /* @__PURE__ */ new Set();
|
|
807
|
+
for (const column of BOOLEAN_COLUMN_NAMES) {
|
|
808
|
+
const pattern = new RegExp(`(?:\\b[a-z_][a-z0-9_]*\\.)?${column}\\s*=\\s*\\?`, "giu");
|
|
809
|
+
for (const match of sql.matchAll(pattern)) {
|
|
810
|
+
const matchText = match[0];
|
|
811
|
+
const qIndex = match.index + matchText.lastIndexOf("?");
|
|
812
|
+
indexes.add(countQuestionMarks(sql, qIndex + 1));
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
return indexes;
|
|
816
|
+
}
|
|
817
|
+
function coerceInsertBooleanArgs(sql, args) {
|
|
818
|
+
const match = /^\s*INSERT(?:\s+OR\s+(?:IGNORE|REPLACE))?\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)/iu.exec(sql);
|
|
819
|
+
if (!match) return;
|
|
820
|
+
const rawTable = match[1];
|
|
821
|
+
const rawColumns = match[2];
|
|
822
|
+
const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
|
|
823
|
+
if (!boolColumns?.size) return;
|
|
824
|
+
const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
|
|
825
|
+
for (const [index, column] of columns.entries()) {
|
|
826
|
+
if (boolColumns.has(column) && index < args.length) {
|
|
827
|
+
args[index] = toBoolean(args[index]);
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
function coerceUpdateBooleanArgs(sql, args) {
|
|
832
|
+
const match = /^\s*UPDATE\s+([A-Za-z0-9_."]+)\s+SET\s+([\s\S]+?)(?:\s+WHERE\b|$)/iu.exec(sql);
|
|
833
|
+
if (!match) return;
|
|
834
|
+
const rawTable = match[1];
|
|
835
|
+
const setClause = match[2];
|
|
836
|
+
const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
|
|
837
|
+
if (!boolColumns?.size) return;
|
|
838
|
+
const assignments = setClause.split(",");
|
|
839
|
+
let placeholderIndex = 0;
|
|
840
|
+
for (const assignment of assignments) {
|
|
841
|
+
if (!assignment.includes("?")) continue;
|
|
842
|
+
placeholderIndex += 1;
|
|
843
|
+
const colMatch = /^\s*(?:[A-Za-z_][A-Za-z0-9_]*\.)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*\?/iu.exec(assignment);
|
|
844
|
+
if (colMatch && boolColumns.has(colMatch[1])) {
|
|
845
|
+
args[placeholderIndex - 1] = toBoolean(args[placeholderIndex - 1]);
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
function coerceBooleanArgs(sql, args) {
|
|
850
|
+
const nextArgs = [...args];
|
|
851
|
+
coerceInsertBooleanArgs(sql, nextArgs);
|
|
852
|
+
coerceUpdateBooleanArgs(sql, nextArgs);
|
|
853
|
+
const placeholderIndexes = findBooleanPlaceholderIndexes(sql);
|
|
854
|
+
for (const index of placeholderIndexes) {
|
|
855
|
+
if (index > 0 && index <= nextArgs.length) {
|
|
856
|
+
nextArgs[index - 1] = toBoolean(nextArgs[index - 1]);
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
return nextArgs;
|
|
860
|
+
}
|
|
861
|
+
function convertQuestionMarksToDollarParams(sql) {
|
|
862
|
+
let out = "";
|
|
863
|
+
let placeholder = 0;
|
|
864
|
+
let inSingle = false;
|
|
865
|
+
let inDouble = false;
|
|
866
|
+
let inLineComment = false;
|
|
867
|
+
let inBlockComment = false;
|
|
868
|
+
for (let i = 0; i < sql.length; i++) {
|
|
869
|
+
const ch = sql[i];
|
|
870
|
+
const next = sql[i + 1];
|
|
871
|
+
if (inLineComment) {
|
|
872
|
+
out += ch;
|
|
873
|
+
if (ch === "\n") inLineComment = false;
|
|
874
|
+
continue;
|
|
875
|
+
}
|
|
876
|
+
if (inBlockComment) {
|
|
877
|
+
out += ch;
|
|
878
|
+
if (ch === "*" && next === "/") {
|
|
879
|
+
out += next;
|
|
880
|
+
inBlockComment = false;
|
|
881
|
+
i += 1;
|
|
882
|
+
}
|
|
883
|
+
continue;
|
|
884
|
+
}
|
|
885
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
886
|
+
out += ch + next;
|
|
887
|
+
inLineComment = true;
|
|
888
|
+
i += 1;
|
|
889
|
+
continue;
|
|
890
|
+
}
|
|
891
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
892
|
+
out += ch + next;
|
|
893
|
+
inBlockComment = true;
|
|
894
|
+
i += 1;
|
|
895
|
+
continue;
|
|
896
|
+
}
|
|
897
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
898
|
+
inSingle = !inSingle;
|
|
899
|
+
out += ch;
|
|
900
|
+
continue;
|
|
901
|
+
}
|
|
902
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
903
|
+
inDouble = !inDouble;
|
|
904
|
+
out += ch;
|
|
905
|
+
continue;
|
|
906
|
+
}
|
|
907
|
+
if (!inSingle && !inDouble && ch === "?") {
|
|
908
|
+
placeholder += 1;
|
|
909
|
+
out += `$${placeholder}`;
|
|
910
|
+
continue;
|
|
911
|
+
}
|
|
912
|
+
out += ch;
|
|
913
|
+
}
|
|
914
|
+
return out;
|
|
915
|
+
}
|
|
916
|
+
function translateStatementForPostgres(stmt) {
|
|
917
|
+
const normalized = normalizeStatement(stmt);
|
|
918
|
+
if (normalized.kind === "named") {
|
|
919
|
+
throw new Error("Named SQL parameters are not supported by the Prisma adapter.");
|
|
920
|
+
}
|
|
921
|
+
const rewrittenSql = rewriteSql(normalized.sql);
|
|
922
|
+
const coercedArgs = coerceBooleanArgs(rewrittenSql, normalized.args);
|
|
923
|
+
return {
|
|
924
|
+
sql: convertQuestionMarksToDollarParams(rewrittenSql),
|
|
925
|
+
args: coercedArgs
|
|
926
|
+
};
|
|
927
|
+
}
|
|
928
|
+
function shouldBypassPostgres(stmt) {
|
|
929
|
+
const normalized = normalizeStatement(stmt);
|
|
930
|
+
if (normalized.kind === "named") {
|
|
931
|
+
return true;
|
|
932
|
+
}
|
|
933
|
+
return IMMEDIATE_FALLBACK_PATTERNS.some((pattern) => pattern.test(normalized.sql));
|
|
934
|
+
}
|
|
935
|
+
function shouldFallbackOnError(error) {
|
|
936
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
937
|
+
return /42P01|42883|42601|does not exist|syntax error|not supported|Named SQL parameters are not supported/iu.test(message);
|
|
938
|
+
}
|
|
939
|
+
function isReadQuery(sql) {
|
|
940
|
+
const trimmed = sql.trimStart();
|
|
941
|
+
return /^(SELECT|WITH|SHOW|EXPLAIN|VALUES)\b/iu.test(trimmed) || /\bRETURNING\b/iu.test(trimmed);
|
|
942
|
+
}
|
|
943
|
+
function buildRow(row, columns) {
|
|
944
|
+
const values = columns.map((column) => row[column]);
|
|
945
|
+
return Object.assign(values, row);
|
|
946
|
+
}
|
|
947
|
+
function buildResultSet(rows, rowsAffected = 0) {
|
|
948
|
+
const columns = rows[0] ? Object.keys(rows[0]) : [];
|
|
949
|
+
const resultRows = rows.map((row) => buildRow(row, columns));
|
|
950
|
+
return {
|
|
951
|
+
columns,
|
|
952
|
+
columnTypes: columns.map(() => ""),
|
|
953
|
+
rows: resultRows,
|
|
954
|
+
rowsAffected,
|
|
955
|
+
lastInsertRowid: void 0,
|
|
956
|
+
toJSON() {
|
|
957
|
+
return {
|
|
958
|
+
columns,
|
|
959
|
+
columnTypes: columns.map(() => ""),
|
|
960
|
+
rows,
|
|
961
|
+
rowsAffected,
|
|
962
|
+
lastInsertRowid: void 0
|
|
963
|
+
};
|
|
964
|
+
}
|
|
965
|
+
};
|
|
966
|
+
}
|
|
967
|
+
async function loadPrismaClient() {
|
|
968
|
+
if (!prismaClientPromise) {
|
|
969
|
+
prismaClientPromise = (async () => {
|
|
970
|
+
const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
|
|
971
|
+
if (explicitPath) {
|
|
972
|
+
const module2 = await import(pathToFileURL(explicitPath).href);
|
|
973
|
+
const PrismaClient2 = module2.PrismaClient ?? module2.default?.PrismaClient;
|
|
974
|
+
if (!PrismaClient2) {
|
|
975
|
+
throw new Error(`No PrismaClient export found at ${explicitPath}`);
|
|
976
|
+
}
|
|
977
|
+
return new PrismaClient2();
|
|
978
|
+
}
|
|
979
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path4.join(os3.homedir(), "exe-db");
|
|
980
|
+
const requireFromExeDb = createRequire(path4.join(exeDbRoot, "package.json"));
|
|
981
|
+
const prismaEntry = requireFromExeDb.resolve("@prisma/client");
|
|
982
|
+
const module = await import(pathToFileURL(prismaEntry).href);
|
|
983
|
+
const PrismaClient = module.PrismaClient ?? module.default?.PrismaClient;
|
|
984
|
+
if (!PrismaClient) {
|
|
985
|
+
throw new Error(`No PrismaClient export found in ${prismaEntry}`);
|
|
986
|
+
}
|
|
987
|
+
return new PrismaClient();
|
|
988
|
+
})();
|
|
989
|
+
}
|
|
990
|
+
return prismaClientPromise;
|
|
991
|
+
}
|
|
992
|
+
async function ensureCompatibilityViews(prisma) {
|
|
993
|
+
if (!compatibilityBootstrapPromise) {
|
|
994
|
+
compatibilityBootstrapPromise = (async () => {
|
|
995
|
+
for (const mapping of VIEW_MAPPINGS) {
|
|
996
|
+
const relation = mapping.source.replace(/"/g, "");
|
|
997
|
+
const rows = await prisma.$queryRawUnsafe(
|
|
998
|
+
"SELECT to_regclass($1) AS regclass",
|
|
999
|
+
relation
|
|
1000
|
+
);
|
|
1001
|
+
if (!rows[0]?.regclass) {
|
|
1002
|
+
continue;
|
|
1003
|
+
}
|
|
1004
|
+
await prisma.$executeRawUnsafe(
|
|
1005
|
+
`CREATE OR REPLACE VIEW public.${quotedIdentifier(mapping.view)} AS SELECT * FROM ${mapping.source}`
|
|
1006
|
+
);
|
|
1007
|
+
}
|
|
1008
|
+
})();
|
|
1009
|
+
}
|
|
1010
|
+
return compatibilityBootstrapPromise;
|
|
1011
|
+
}
|
|
1012
|
+
async function executeOnPrisma(executor, stmt) {
|
|
1013
|
+
const translated = translateStatementForPostgres(stmt);
|
|
1014
|
+
if (isReadQuery(translated.sql)) {
|
|
1015
|
+
const rows = await executor.$queryRawUnsafe(
|
|
1016
|
+
translated.sql,
|
|
1017
|
+
...translated.args
|
|
1018
|
+
);
|
|
1019
|
+
return buildResultSet(rows, /\bRETURNING\b/iu.test(translated.sql) ? rows.length : 0);
|
|
1020
|
+
}
|
|
1021
|
+
const rowsAffected = await executor.$executeRawUnsafe(translated.sql, ...translated.args);
|
|
1022
|
+
return buildResultSet([], rowsAffected);
|
|
1023
|
+
}
|
|
1024
|
+
function splitSqlStatements(sql) {
|
|
1025
|
+
const parts = [];
|
|
1026
|
+
let current = "";
|
|
1027
|
+
let inSingle = false;
|
|
1028
|
+
let inDouble = false;
|
|
1029
|
+
let inLineComment = false;
|
|
1030
|
+
let inBlockComment = false;
|
|
1031
|
+
for (let i = 0; i < sql.length; i++) {
|
|
1032
|
+
const ch = sql[i];
|
|
1033
|
+
const next = sql[i + 1];
|
|
1034
|
+
if (inLineComment) {
|
|
1035
|
+
current += ch;
|
|
1036
|
+
if (ch === "\n") inLineComment = false;
|
|
1037
|
+
continue;
|
|
1038
|
+
}
|
|
1039
|
+
if (inBlockComment) {
|
|
1040
|
+
current += ch;
|
|
1041
|
+
if (ch === "*" && next === "/") {
|
|
1042
|
+
current += next;
|
|
1043
|
+
inBlockComment = false;
|
|
1044
|
+
i += 1;
|
|
1045
|
+
}
|
|
1046
|
+
continue;
|
|
1047
|
+
}
|
|
1048
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
1049
|
+
current += ch + next;
|
|
1050
|
+
inLineComment = true;
|
|
1051
|
+
i += 1;
|
|
1052
|
+
continue;
|
|
1053
|
+
}
|
|
1054
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
1055
|
+
current += ch + next;
|
|
1056
|
+
inBlockComment = true;
|
|
1057
|
+
i += 1;
|
|
1058
|
+
continue;
|
|
1059
|
+
}
|
|
1060
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
1061
|
+
inSingle = !inSingle;
|
|
1062
|
+
current += ch;
|
|
1063
|
+
continue;
|
|
1064
|
+
}
|
|
1065
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
1066
|
+
inDouble = !inDouble;
|
|
1067
|
+
current += ch;
|
|
1068
|
+
continue;
|
|
1069
|
+
}
|
|
1070
|
+
if (!inSingle && !inDouble && ch === ";") {
|
|
1071
|
+
if (current.trim()) {
|
|
1072
|
+
parts.push(current.trim());
|
|
1073
|
+
}
|
|
1074
|
+
current = "";
|
|
1075
|
+
continue;
|
|
1076
|
+
}
|
|
1077
|
+
current += ch;
|
|
1078
|
+
}
|
|
1079
|
+
if (current.trim()) {
|
|
1080
|
+
parts.push(current.trim());
|
|
1081
|
+
}
|
|
1082
|
+
return parts;
|
|
1083
|
+
}
|
|
1084
|
+
async function createPrismaDbAdapter(fallbackClient) {
|
|
1085
|
+
const prisma = await loadPrismaClient();
|
|
1086
|
+
await ensureCompatibilityViews(prisma);
|
|
1087
|
+
let closed = false;
|
|
1088
|
+
let adapter;
|
|
1089
|
+
const fallbackExecute = async (stmt, error) => {
|
|
1090
|
+
if (!fallbackClient) {
|
|
1091
|
+
if (error) throw error;
|
|
1092
|
+
throw new Error("No fallback SQLite client is available for this Prisma-routed query.");
|
|
1093
|
+
}
|
|
1094
|
+
if (error) {
|
|
1095
|
+
process.stderr.write(
|
|
1096
|
+
`[database-adapter] Falling back to SQLite: ${error instanceof Error ? error.message : String(error)}
|
|
1097
|
+
`
|
|
1098
|
+
);
|
|
1099
|
+
}
|
|
1100
|
+
return fallbackClient.execute(stmt);
|
|
1101
|
+
};
|
|
1102
|
+
adapter = {
|
|
1103
|
+
async execute(stmt) {
|
|
1104
|
+
if (shouldBypassPostgres(stmt)) {
|
|
1105
|
+
return fallbackExecute(stmt);
|
|
1106
|
+
}
|
|
1107
|
+
try {
|
|
1108
|
+
return await executeOnPrisma(prisma, stmt);
|
|
1109
|
+
} catch (error) {
|
|
1110
|
+
if (shouldFallbackOnError(error)) {
|
|
1111
|
+
return fallbackExecute(stmt, error);
|
|
1112
|
+
}
|
|
1113
|
+
throw error;
|
|
1114
|
+
}
|
|
1115
|
+
},
|
|
1116
|
+
async batch(stmts, mode) {
|
|
1117
|
+
if (stmts.some((stmt) => shouldBypassPostgres(stmt))) {
|
|
1118
|
+
if (!fallbackClient) {
|
|
1119
|
+
throw new Error("Cannot batch unsupported SQLite-only statements without a fallback client.");
|
|
1120
|
+
}
|
|
1121
|
+
return fallbackClient.batch(stmts, mode);
|
|
1122
|
+
}
|
|
1123
|
+
try {
|
|
1124
|
+
if (prisma.$transaction) {
|
|
1125
|
+
return await prisma.$transaction(async (tx) => {
|
|
1126
|
+
const results2 = [];
|
|
1127
|
+
for (const stmt of stmts) {
|
|
1128
|
+
results2.push(await executeOnPrisma(tx, stmt));
|
|
1129
|
+
}
|
|
1130
|
+
return results2;
|
|
1131
|
+
});
|
|
1132
|
+
}
|
|
1133
|
+
const results = [];
|
|
1134
|
+
for (const stmt of stmts) {
|
|
1135
|
+
results.push(await executeOnPrisma(prisma, stmt));
|
|
1136
|
+
}
|
|
1137
|
+
return results;
|
|
1138
|
+
} catch (error) {
|
|
1139
|
+
if (fallbackClient && shouldFallbackOnError(error)) {
|
|
1140
|
+
process.stderr.write(
|
|
1141
|
+
`[database-adapter] Falling back batch to SQLite: ${error instanceof Error ? error.message : String(error)}
|
|
1142
|
+
`
|
|
1143
|
+
);
|
|
1144
|
+
return fallbackClient.batch(stmts, mode);
|
|
1145
|
+
}
|
|
1146
|
+
throw error;
|
|
1147
|
+
}
|
|
1148
|
+
},
|
|
1149
|
+
async migrate(stmts) {
|
|
1150
|
+
if (fallbackClient) {
|
|
1151
|
+
return fallbackClient.migrate(stmts);
|
|
1152
|
+
}
|
|
1153
|
+
return adapter.batch(stmts, "deferred");
|
|
1154
|
+
},
|
|
1155
|
+
async transaction(mode) {
|
|
1156
|
+
if (!fallbackClient) {
|
|
1157
|
+
throw new Error("Interactive transactions are only supported on the SQLite fallback client.");
|
|
1158
|
+
}
|
|
1159
|
+
return fallbackClient.transaction(mode);
|
|
1160
|
+
},
|
|
1161
|
+
async executeMultiple(sql) {
|
|
1162
|
+
if (fallbackClient && shouldBypassPostgres(sql)) {
|
|
1163
|
+
return fallbackClient.executeMultiple(sql);
|
|
1164
|
+
}
|
|
1165
|
+
for (const statement of splitSqlStatements(sql)) {
|
|
1166
|
+
await adapter.execute(statement);
|
|
1167
|
+
}
|
|
1168
|
+
},
|
|
1169
|
+
async sync() {
|
|
1170
|
+
if (fallbackClient) {
|
|
1171
|
+
return fallbackClient.sync();
|
|
1172
|
+
}
|
|
1173
|
+
return { frame_no: 0, frames_synced: 0 };
|
|
1174
|
+
},
|
|
1175
|
+
close() {
|
|
1176
|
+
closed = true;
|
|
1177
|
+
prismaClientPromise = null;
|
|
1178
|
+
compatibilityBootstrapPromise = null;
|
|
1179
|
+
void prisma.$disconnect?.();
|
|
1180
|
+
},
|
|
1181
|
+
get closed() {
|
|
1182
|
+
return closed;
|
|
1183
|
+
},
|
|
1184
|
+
get protocol() {
|
|
1185
|
+
return "prisma-postgres";
|
|
1186
|
+
}
|
|
1187
|
+
};
|
|
1188
|
+
return adapter;
|
|
1189
|
+
}
|
|
1190
|
+
var VIEW_MAPPINGS, UPSERT_KEYS, BOOLEAN_COLUMNS_BY_TABLE, BOOLEAN_COLUMN_NAMES, IMMEDIATE_FALLBACK_PATTERNS, prismaClientPromise, compatibilityBootstrapPromise;
|
|
1191
|
+
var init_database_adapter = __esm({
|
|
1192
|
+
"src/lib/database-adapter.ts"() {
|
|
1193
|
+
"use strict";
|
|
1194
|
+
VIEW_MAPPINGS = [
|
|
1195
|
+
{ view: "memories", source: "memory.memory_records" },
|
|
1196
|
+
{ view: "tasks", source: "memory.tasks" },
|
|
1197
|
+
{ view: "behaviors", source: "memory.behaviors" },
|
|
1198
|
+
{ view: "entities", source: "memory.entities" },
|
|
1199
|
+
{ view: "relationships", source: "memory.relationships" },
|
|
1200
|
+
{ view: "entity_memories", source: "memory.entity_memories" },
|
|
1201
|
+
{ view: "entity_aliases", source: "memory.entity_aliases" },
|
|
1202
|
+
{ view: "notifications", source: "memory.notifications" },
|
|
1203
|
+
{ view: "messages", source: "memory.messages" },
|
|
1204
|
+
{ view: "users", source: "wiki.users" },
|
|
1205
|
+
{ view: "workspaces", source: "wiki.workspaces" },
|
|
1206
|
+
{ view: "workspace_users", source: "wiki.workspace_users" },
|
|
1207
|
+
{ view: "documents", source: "wiki.workspace_documents" },
|
|
1208
|
+
{ view: "chats", source: "wiki.workspace_chats" }
|
|
1209
|
+
];
|
|
1210
|
+
UPSERT_KEYS = {
|
|
1211
|
+
memories: ["id"],
|
|
1212
|
+
tasks: ["id"],
|
|
1213
|
+
behaviors: ["id"],
|
|
1214
|
+
entities: ["id"],
|
|
1215
|
+
relationships: ["id"],
|
|
1216
|
+
entity_aliases: ["alias"],
|
|
1217
|
+
notifications: ["id"],
|
|
1218
|
+
messages: ["id"],
|
|
1219
|
+
users: ["id"],
|
|
1220
|
+
workspaces: ["id"],
|
|
1221
|
+
workspace_users: ["id"],
|
|
1222
|
+
documents: ["id"],
|
|
1223
|
+
chats: ["id"]
|
|
1224
|
+
};
|
|
1225
|
+
BOOLEAN_COLUMNS_BY_TABLE = {
|
|
1226
|
+
memories: /* @__PURE__ */ new Set(["has_error", "draft"]),
|
|
1227
|
+
behaviors: /* @__PURE__ */ new Set(["active"]),
|
|
1228
|
+
notifications: /* @__PURE__ */ new Set(["read"]),
|
|
1229
|
+
users: /* @__PURE__ */ new Set(["has_personal_memory"])
|
|
1230
|
+
};
|
|
1231
|
+
BOOLEAN_COLUMN_NAMES = new Set(
|
|
1232
|
+
Object.values(BOOLEAN_COLUMNS_BY_TABLE).flatMap((cols) => [...cols])
|
|
1233
|
+
);
|
|
1234
|
+
IMMEDIATE_FALLBACK_PATTERNS = [
|
|
1235
|
+
/\bPRAGMA\b/i,
|
|
1236
|
+
/\bsqlite_master\b/i,
|
|
1237
|
+
/(?:^|[.\s])(?:memories|conversations|entities)_fts\b/i,
|
|
1238
|
+
/\bMATCH\b/i,
|
|
1239
|
+
/\bvector_distance_cos\s*\(/i,
|
|
1240
|
+
/\bjson_extract\s*\(/i,
|
|
1241
|
+
/\bjulianday\s*\(/i,
|
|
1242
|
+
/\bstrftime\s*\(/i,
|
|
1243
|
+
/\blast_insert_rowid\s*\(/i
|
|
1244
|
+
];
|
|
1245
|
+
prismaClientPromise = null;
|
|
1246
|
+
compatibilityBootstrapPromise = null;
|
|
460
1247
|
}
|
|
461
1248
|
});
|
|
462
1249
|
|
|
463
1250
|
// src/lib/database.ts
|
|
464
1251
|
import { createClient } from "@libsql/client";
|
|
465
1252
|
async function initDatabase(config) {
|
|
1253
|
+
if (_walCheckpointTimer) {
|
|
1254
|
+
clearInterval(_walCheckpointTimer);
|
|
1255
|
+
_walCheckpointTimer = null;
|
|
1256
|
+
}
|
|
1257
|
+
if (_daemonClient) {
|
|
1258
|
+
_daemonClient.close();
|
|
1259
|
+
_daemonClient = null;
|
|
1260
|
+
}
|
|
1261
|
+
if (_adapterClient && _adapterClient !== _resilientClient) {
|
|
1262
|
+
_adapterClient.close();
|
|
1263
|
+
}
|
|
1264
|
+
_adapterClient = null;
|
|
466
1265
|
if (_client) {
|
|
467
1266
|
_client.close();
|
|
468
1267
|
_client = null;
|
|
@@ -476,6 +1275,7 @@ async function initDatabase(config) {
|
|
|
476
1275
|
}
|
|
477
1276
|
_client = createClient(opts);
|
|
478
1277
|
_resilientClient = wrapWithRetry(_client);
|
|
1278
|
+
_adapterClient = _resilientClient;
|
|
479
1279
|
_client.execute("PRAGMA busy_timeout = 30000").catch(() => {
|
|
480
1280
|
});
|
|
481
1281
|
_client.execute("PRAGMA journal_mode = WAL").catch(() => {
|
|
@@ -486,11 +1286,17 @@ async function initDatabase(config) {
|
|
|
486
1286
|
});
|
|
487
1287
|
}, 3e4);
|
|
488
1288
|
_walCheckpointTimer.unref();
|
|
1289
|
+
if (process.env.DATABASE_URL) {
|
|
1290
|
+
_adapterClient = await createPrismaDbAdapter(_resilientClient);
|
|
1291
|
+
}
|
|
489
1292
|
}
|
|
490
1293
|
function getClient() {
|
|
491
|
-
if (!
|
|
1294
|
+
if (!_adapterClient) {
|
|
492
1295
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
493
1296
|
}
|
|
1297
|
+
if (process.env.DATABASE_URL) {
|
|
1298
|
+
return _adapterClient;
|
|
1299
|
+
}
|
|
494
1300
|
if (process.env.EXE_IS_DAEMON === "1") {
|
|
495
1301
|
return _resilientClient;
|
|
496
1302
|
}
|
|
@@ -783,6 +1589,7 @@ async function ensureSchema() {
|
|
|
783
1589
|
project TEXT NOT NULL,
|
|
784
1590
|
summary TEXT NOT NULL,
|
|
785
1591
|
task_file TEXT,
|
|
1592
|
+
session_scope TEXT,
|
|
786
1593
|
read INTEGER NOT NULL DEFAULT 0,
|
|
787
1594
|
created_at TEXT NOT NULL
|
|
788
1595
|
);
|
|
@@ -791,7 +1598,7 @@ async function ensureSchema() {
|
|
|
791
1598
|
ON notifications(read);
|
|
792
1599
|
|
|
793
1600
|
CREATE INDEX IF NOT EXISTS idx_notifications_agent
|
|
794
|
-
ON notifications(agent_id);
|
|
1601
|
+
ON notifications(agent_id, session_scope);
|
|
795
1602
|
|
|
796
1603
|
CREATE INDEX IF NOT EXISTS idx_notifications_task_file
|
|
797
1604
|
ON notifications(task_file);
|
|
@@ -829,6 +1636,7 @@ async function ensureSchema() {
|
|
|
829
1636
|
target_agent TEXT NOT NULL,
|
|
830
1637
|
target_project TEXT,
|
|
831
1638
|
target_device TEXT NOT NULL DEFAULT 'local',
|
|
1639
|
+
session_scope TEXT,
|
|
832
1640
|
content TEXT NOT NULL,
|
|
833
1641
|
priority TEXT DEFAULT 'normal',
|
|
834
1642
|
status TEXT DEFAULT 'pending',
|
|
@@ -842,10 +1650,31 @@ async function ensureSchema() {
|
|
|
842
1650
|
);
|
|
843
1651
|
|
|
844
1652
|
CREATE INDEX IF NOT EXISTS idx_messages_target
|
|
845
|
-
ON messages(target_agent, status);
|
|
1653
|
+
ON messages(target_agent, session_scope, status);
|
|
846
1654
|
|
|
847
1655
|
CREATE INDEX IF NOT EXISTS idx_messages_conversation_order
|
|
848
|
-
ON messages(target_agent, from_agent, server_seq);
|
|
1656
|
+
ON messages(target_agent, session_scope, from_agent, server_seq);
|
|
1657
|
+
`);
|
|
1658
|
+
try {
|
|
1659
|
+
await client.execute({
|
|
1660
|
+
sql: `ALTER TABLE notifications ADD COLUMN session_scope TEXT`,
|
|
1661
|
+
args: []
|
|
1662
|
+
});
|
|
1663
|
+
} catch {
|
|
1664
|
+
}
|
|
1665
|
+
try {
|
|
1666
|
+
await client.execute({
|
|
1667
|
+
sql: `ALTER TABLE messages ADD COLUMN session_scope TEXT`,
|
|
1668
|
+
args: []
|
|
1669
|
+
});
|
|
1670
|
+
} catch {
|
|
1671
|
+
}
|
|
1672
|
+
await client.executeMultiple(`
|
|
1673
|
+
CREATE INDEX IF NOT EXISTS idx_notifications_agent_scope_read
|
|
1674
|
+
ON notifications(agent_id, session_scope, read, created_at);
|
|
1675
|
+
|
|
1676
|
+
CREATE INDEX IF NOT EXISTS idx_messages_target_scope_status
|
|
1677
|
+
ON messages(target_agent, session_scope, status, created_at);
|
|
849
1678
|
`);
|
|
850
1679
|
try {
|
|
851
1680
|
await client.execute({
|
|
@@ -1429,28 +2258,45 @@ async function ensureSchema() {
|
|
|
1429
2258
|
} catch {
|
|
1430
2259
|
}
|
|
1431
2260
|
}
|
|
2261
|
+
try {
|
|
2262
|
+
await client.execute({
|
|
2263
|
+
sql: `UPDATE tasks SET status = 'closed' WHERE status = 'done' AND result IS NOT NULL`,
|
|
2264
|
+
args: []
|
|
2265
|
+
});
|
|
2266
|
+
} catch {
|
|
2267
|
+
}
|
|
1432
2268
|
}
|
|
1433
2269
|
async function disposeDatabase() {
|
|
2270
|
+
if (_walCheckpointTimer) {
|
|
2271
|
+
clearInterval(_walCheckpointTimer);
|
|
2272
|
+
_walCheckpointTimer = null;
|
|
2273
|
+
}
|
|
1434
2274
|
if (_daemonClient) {
|
|
1435
2275
|
_daemonClient.close();
|
|
1436
2276
|
_daemonClient = null;
|
|
1437
2277
|
}
|
|
2278
|
+
if (_adapterClient && _adapterClient !== _resilientClient) {
|
|
2279
|
+
_adapterClient.close();
|
|
2280
|
+
}
|
|
2281
|
+
_adapterClient = null;
|
|
1438
2282
|
if (_client) {
|
|
1439
2283
|
_client.close();
|
|
1440
2284
|
_client = null;
|
|
1441
2285
|
_resilientClient = null;
|
|
1442
2286
|
}
|
|
1443
2287
|
}
|
|
1444
|
-
var _client, _resilientClient, _walCheckpointTimer, _daemonClient, initTurso, disposeTurso;
|
|
2288
|
+
var _client, _resilientClient, _walCheckpointTimer, _daemonClient, _adapterClient, initTurso, disposeTurso;
|
|
1445
2289
|
var init_database = __esm({
|
|
1446
2290
|
"src/lib/database.ts"() {
|
|
1447
2291
|
"use strict";
|
|
1448
2292
|
init_db_retry();
|
|
1449
2293
|
init_employees();
|
|
2294
|
+
init_database_adapter();
|
|
1450
2295
|
_client = null;
|
|
1451
2296
|
_resilientClient = null;
|
|
1452
2297
|
_walCheckpointTimer = null;
|
|
1453
2298
|
_daemonClient = null;
|
|
2299
|
+
_adapterClient = null;
|
|
1454
2300
|
initTurso = initDatabase;
|
|
1455
2301
|
disposeTurso = disposeDatabase;
|
|
1456
2302
|
}
|
|
@@ -1461,6 +2307,7 @@ var shard_manager_exports = {};
|
|
|
1461
2307
|
__export(shard_manager_exports, {
|
|
1462
2308
|
disposeShards: () => disposeShards,
|
|
1463
2309
|
ensureShardSchema: () => ensureShardSchema,
|
|
2310
|
+
getOpenShardCount: () => getOpenShardCount,
|
|
1464
2311
|
getReadyShardClient: () => getReadyShardClient,
|
|
1465
2312
|
getShardClient: () => getShardClient,
|
|
1466
2313
|
getShardsDir: () => getShardsDir,
|
|
@@ -1469,15 +2316,18 @@ __export(shard_manager_exports, {
|
|
|
1469
2316
|
listShards: () => listShards,
|
|
1470
2317
|
shardExists: () => shardExists
|
|
1471
2318
|
});
|
|
1472
|
-
import
|
|
1473
|
-
import { existsSync as
|
|
2319
|
+
import path6 from "path";
|
|
2320
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync2, readdirSync } from "fs";
|
|
1474
2321
|
import { createClient as createClient2 } from "@libsql/client";
|
|
1475
2322
|
function initShardManager(encryptionKey) {
|
|
1476
2323
|
_encryptionKey = encryptionKey;
|
|
1477
|
-
if (!
|
|
1478
|
-
|
|
2324
|
+
if (!existsSync6(SHARDS_DIR)) {
|
|
2325
|
+
mkdirSync2(SHARDS_DIR, { recursive: true });
|
|
1479
2326
|
}
|
|
1480
2327
|
_shardingEnabled = true;
|
|
2328
|
+
if (_evictionTimer) clearInterval(_evictionTimer);
|
|
2329
|
+
_evictionTimer = setInterval(evictIdleShards, EVICTION_INTERVAL_MS);
|
|
2330
|
+
_evictionTimer.unref();
|
|
1481
2331
|
}
|
|
1482
2332
|
function isShardingEnabled() {
|
|
1483
2333
|
return _shardingEnabled;
|
|
@@ -1494,21 +2344,28 @@ function getShardClient(projectName) {
|
|
|
1494
2344
|
throw new Error(`Invalid project name for shard: "${projectName}"`);
|
|
1495
2345
|
}
|
|
1496
2346
|
const cached = _shards.get(safeName);
|
|
1497
|
-
if (cached)
|
|
1498
|
-
|
|
2347
|
+
if (cached) {
|
|
2348
|
+
_shardLastAccess.set(safeName, Date.now());
|
|
2349
|
+
return cached;
|
|
2350
|
+
}
|
|
2351
|
+
while (_shards.size >= MAX_OPEN_SHARDS) {
|
|
2352
|
+
evictLRU();
|
|
2353
|
+
}
|
|
2354
|
+
const dbPath = path6.join(SHARDS_DIR, `${safeName}.db`);
|
|
1499
2355
|
const client = createClient2({
|
|
1500
2356
|
url: `file:${dbPath}`,
|
|
1501
2357
|
encryptionKey: _encryptionKey
|
|
1502
2358
|
});
|
|
1503
2359
|
_shards.set(safeName, client);
|
|
2360
|
+
_shardLastAccess.set(safeName, Date.now());
|
|
1504
2361
|
return client;
|
|
1505
2362
|
}
|
|
1506
2363
|
function shardExists(projectName) {
|
|
1507
2364
|
const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
1508
|
-
return
|
|
2365
|
+
return existsSync6(path6.join(SHARDS_DIR, `${safeName}.db`));
|
|
1509
2366
|
}
|
|
1510
2367
|
function listShards() {
|
|
1511
|
-
if (!
|
|
2368
|
+
if (!existsSync6(SHARDS_DIR)) return [];
|
|
1512
2369
|
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
1513
2370
|
}
|
|
1514
2371
|
async function ensureShardSchema(client) {
|
|
@@ -1560,6 +2417,8 @@ async function ensureShardSchema(client) {
|
|
|
1560
2417
|
for (const col of [
|
|
1561
2418
|
"ALTER TABLE memories ADD COLUMN task_id TEXT",
|
|
1562
2419
|
"ALTER TABLE memories ADD COLUMN consolidated INTEGER NOT NULL DEFAULT 0",
|
|
2420
|
+
"ALTER TABLE memories ADD COLUMN author_device_id TEXT",
|
|
2421
|
+
"ALTER TABLE memories ADD COLUMN scope TEXT NOT NULL DEFAULT 'business'",
|
|
1563
2422
|
"ALTER TABLE memories ADD COLUMN importance INTEGER DEFAULT 5",
|
|
1564
2423
|
"ALTER TABLE memories ADD COLUMN status TEXT DEFAULT 'active'",
|
|
1565
2424
|
"ALTER TABLE memories ADD COLUMN wiki_synced INTEGER DEFAULT 0",
|
|
@@ -1582,7 +2441,23 @@ async function ensureShardSchema(client) {
|
|
|
1582
2441
|
// MS-11: draft staging, MS-6a: memory_type, MS-7: trajectory
|
|
1583
2442
|
"ALTER TABLE memories ADD COLUMN draft INTEGER DEFAULT 0",
|
|
1584
2443
|
"ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'",
|
|
1585
|
-
"ALTER TABLE memories ADD COLUMN trajectory TEXT"
|
|
2444
|
+
"ALTER TABLE memories ADD COLUMN trajectory TEXT",
|
|
2445
|
+
// Metadata enrichment columns (must match database.ts)
|
|
2446
|
+
"ALTER TABLE memories ADD COLUMN intent TEXT",
|
|
2447
|
+
"ALTER TABLE memories ADD COLUMN outcome TEXT",
|
|
2448
|
+
"ALTER TABLE memories ADD COLUMN domain TEXT",
|
|
2449
|
+
"ALTER TABLE memories ADD COLUMN referenced_entities TEXT",
|
|
2450
|
+
"ALTER TABLE memories ADD COLUMN retrieval_count INTEGER DEFAULT 0",
|
|
2451
|
+
"ALTER TABLE memories ADD COLUMN chain_position TEXT",
|
|
2452
|
+
"ALTER TABLE memories ADD COLUMN review_status TEXT",
|
|
2453
|
+
"ALTER TABLE memories ADD COLUMN context_window_pct INTEGER",
|
|
2454
|
+
"ALTER TABLE memories ADD COLUMN file_paths TEXT",
|
|
2455
|
+
"ALTER TABLE memories ADD COLUMN commit_hash TEXT",
|
|
2456
|
+
"ALTER TABLE memories ADD COLUMN duration_ms INTEGER",
|
|
2457
|
+
"ALTER TABLE memories ADD COLUMN token_cost REAL",
|
|
2458
|
+
"ALTER TABLE memories ADD COLUMN audience TEXT",
|
|
2459
|
+
"ALTER TABLE memories ADD COLUMN language_type TEXT",
|
|
2460
|
+
"ALTER TABLE memories ADD COLUMN parent_memory_id TEXT"
|
|
1586
2461
|
]) {
|
|
1587
2462
|
try {
|
|
1588
2463
|
await client.execute(col);
|
|
@@ -1681,21 +2556,69 @@ async function getReadyShardClient(projectName) {
|
|
|
1681
2556
|
await ensureShardSchema(client);
|
|
1682
2557
|
return client;
|
|
1683
2558
|
}
|
|
2559
|
+
function evictLRU() {
|
|
2560
|
+
let oldest = null;
|
|
2561
|
+
let oldestTime = Infinity;
|
|
2562
|
+
for (const [name, time] of _shardLastAccess) {
|
|
2563
|
+
if (time < oldestTime) {
|
|
2564
|
+
oldestTime = time;
|
|
2565
|
+
oldest = name;
|
|
2566
|
+
}
|
|
2567
|
+
}
|
|
2568
|
+
if (oldest) {
|
|
2569
|
+
const client = _shards.get(oldest);
|
|
2570
|
+
if (client) {
|
|
2571
|
+
client.close();
|
|
2572
|
+
}
|
|
2573
|
+
_shards.delete(oldest);
|
|
2574
|
+
_shardLastAccess.delete(oldest);
|
|
2575
|
+
}
|
|
2576
|
+
}
|
|
2577
|
+
function evictIdleShards() {
|
|
2578
|
+
const now = Date.now();
|
|
2579
|
+
const toEvict = [];
|
|
2580
|
+
for (const [name, lastAccess] of _shardLastAccess) {
|
|
2581
|
+
if (now - lastAccess > SHARD_IDLE_MS) {
|
|
2582
|
+
toEvict.push(name);
|
|
2583
|
+
}
|
|
2584
|
+
}
|
|
2585
|
+
for (const name of toEvict) {
|
|
2586
|
+
const client = _shards.get(name);
|
|
2587
|
+
if (client) {
|
|
2588
|
+
client.close();
|
|
2589
|
+
}
|
|
2590
|
+
_shards.delete(name);
|
|
2591
|
+
_shardLastAccess.delete(name);
|
|
2592
|
+
}
|
|
2593
|
+
}
|
|
2594
|
+
function getOpenShardCount() {
|
|
2595
|
+
return _shards.size;
|
|
2596
|
+
}
|
|
1684
2597
|
function disposeShards() {
|
|
2598
|
+
if (_evictionTimer) {
|
|
2599
|
+
clearInterval(_evictionTimer);
|
|
2600
|
+
_evictionTimer = null;
|
|
2601
|
+
}
|
|
1685
2602
|
for (const [, client] of _shards) {
|
|
1686
2603
|
client.close();
|
|
1687
2604
|
}
|
|
1688
2605
|
_shards.clear();
|
|
2606
|
+
_shardLastAccess.clear();
|
|
1689
2607
|
_shardingEnabled = false;
|
|
1690
2608
|
_encryptionKey = null;
|
|
1691
2609
|
}
|
|
1692
|
-
var SHARDS_DIR, _shards, _encryptionKey, _shardingEnabled;
|
|
2610
|
+
var SHARDS_DIR, SHARD_IDLE_MS, MAX_OPEN_SHARDS, EVICTION_INTERVAL_MS, _shards, _shardLastAccess, _evictionTimer, _encryptionKey, _shardingEnabled;
|
|
1693
2611
|
var init_shard_manager = __esm({
|
|
1694
2612
|
"src/lib/shard-manager.ts"() {
|
|
1695
2613
|
"use strict";
|
|
1696
2614
|
init_config();
|
|
1697
|
-
SHARDS_DIR =
|
|
2615
|
+
SHARDS_DIR = path6.join(EXE_AI_DIR, "shards");
|
|
2616
|
+
SHARD_IDLE_MS = 5 * 60 * 1e3;
|
|
2617
|
+
MAX_OPEN_SHARDS = 10;
|
|
2618
|
+
EVICTION_INTERVAL_MS = 60 * 1e3;
|
|
1698
2619
|
_shards = /* @__PURE__ */ new Map();
|
|
2620
|
+
_shardLastAccess = /* @__PURE__ */ new Map();
|
|
2621
|
+
_evictionTimer = null;
|
|
1699
2622
|
_encryptionKey = null;
|
|
1700
2623
|
_shardingEnabled = false;
|
|
1701
2624
|
}
|
|
@@ -2583,9 +3506,9 @@ __export(active_agent_exports, {
|
|
|
2583
3506
|
resolveActiveAgentFromTmuxSession: () => resolveActiveAgentFromTmuxSession,
|
|
2584
3507
|
writeActiveAgent: () => writeActiveAgent
|
|
2585
3508
|
});
|
|
2586
|
-
import { readFileSync as
|
|
3509
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4, unlinkSync as unlinkSync3, readdirSync as readdirSync3 } from "fs";
|
|
2587
3510
|
import { execSync as execSync4 } from "child_process";
|
|
2588
|
-
import
|
|
3511
|
+
import path8 from "path";
|
|
2589
3512
|
function isNameWithOptionalInstance(candidate, baseName) {
|
|
2590
3513
|
if (candidate === baseName) return true;
|
|
2591
3514
|
if (!candidate.startsWith(baseName)) return false;
|
|
@@ -2629,12 +3552,12 @@ function resolveActiveAgentFromTmuxSession(sessionName) {
|
|
|
2629
3552
|
return null;
|
|
2630
3553
|
}
|
|
2631
3554
|
function getMarkerPath() {
|
|
2632
|
-
return
|
|
3555
|
+
return path8.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
|
|
2633
3556
|
}
|
|
2634
3557
|
function writeActiveAgent(agentId, agentRole) {
|
|
2635
3558
|
try {
|
|
2636
|
-
|
|
2637
|
-
|
|
3559
|
+
mkdirSync4(CACHE_DIR, { recursive: true });
|
|
3560
|
+
writeFileSync4(
|
|
2638
3561
|
getMarkerPath(),
|
|
2639
3562
|
JSON.stringify({ agentId, agentRole, startedAt: (/* @__PURE__ */ new Date()).toISOString() })
|
|
2640
3563
|
);
|
|
@@ -2650,7 +3573,7 @@ function clearActiveAgent() {
|
|
|
2650
3573
|
function getActiveAgent() {
|
|
2651
3574
|
try {
|
|
2652
3575
|
const markerPath = getMarkerPath();
|
|
2653
|
-
const raw =
|
|
3576
|
+
const raw = readFileSync4(markerPath, "utf8");
|
|
2654
3577
|
const data = JSON.parse(raw);
|
|
2655
3578
|
if (data.agentId) {
|
|
2656
3579
|
if (data.startedAt) {
|
|
@@ -2698,14 +3621,14 @@ function getAllActiveAgents() {
|
|
|
2698
3621
|
const key = file.slice("active-agent-".length, -".json".length);
|
|
2699
3622
|
if (key === "undefined") continue;
|
|
2700
3623
|
try {
|
|
2701
|
-
const raw =
|
|
3624
|
+
const raw = readFileSync4(path8.join(CACHE_DIR, file), "utf8");
|
|
2702
3625
|
const data = JSON.parse(raw);
|
|
2703
3626
|
if (!data.agentId) continue;
|
|
2704
3627
|
if (data.startedAt) {
|
|
2705
3628
|
const age = Date.now() - new Date(data.startedAt).getTime();
|
|
2706
3629
|
if (age > STALE_MS) {
|
|
2707
3630
|
try {
|
|
2708
|
-
unlinkSync3(
|
|
3631
|
+
unlinkSync3(path8.join(CACHE_DIR, file));
|
|
2709
3632
|
} catch {
|
|
2710
3633
|
}
|
|
2711
3634
|
continue;
|
|
@@ -2728,11 +3651,11 @@ function getAllActiveAgents() {
|
|
|
2728
3651
|
function cleanupSessionMarkers() {
|
|
2729
3652
|
const key = getSessionKey();
|
|
2730
3653
|
try {
|
|
2731
|
-
unlinkSync3(
|
|
3654
|
+
unlinkSync3(path8.join(CACHE_DIR, `active-agent-${key}.json`));
|
|
2732
3655
|
} catch {
|
|
2733
3656
|
}
|
|
2734
3657
|
try {
|
|
2735
|
-
unlinkSync3(
|
|
3658
|
+
unlinkSync3(path8.join(CACHE_DIR, "active-agent-undefined.json"));
|
|
2736
3659
|
} catch {
|
|
2737
3660
|
}
|
|
2738
3661
|
}
|
|
@@ -2743,15 +3666,15 @@ var init_active_agent = __esm({
|
|
|
2743
3666
|
init_config();
|
|
2744
3667
|
init_session_key();
|
|
2745
3668
|
init_employees();
|
|
2746
|
-
CACHE_DIR =
|
|
3669
|
+
CACHE_DIR = path8.join(EXE_AI_DIR, "session-cache");
|
|
2747
3670
|
STALE_MS = 24 * 60 * 60 * 1e3;
|
|
2748
3671
|
}
|
|
2749
3672
|
});
|
|
2750
3673
|
|
|
2751
3674
|
// src/bin/exe-launch-agent.ts
|
|
2752
|
-
import
|
|
2753
|
-
import
|
|
2754
|
-
import { existsSync as
|
|
3675
|
+
import os6 from "os";
|
|
3676
|
+
import path9 from "path";
|
|
3677
|
+
import { existsSync as existsSync8, readFileSync as readFileSync5, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, readdirSync as readdirSync4 } from "fs";
|
|
2755
3678
|
import { spawnSync } from "child_process";
|
|
2756
3679
|
|
|
2757
3680
|
// src/lib/store.ts
|
|
@@ -2760,16 +3683,16 @@ init_database();
|
|
|
2760
3683
|
|
|
2761
3684
|
// src/lib/keychain.ts
|
|
2762
3685
|
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
2763
|
-
import { existsSync as
|
|
2764
|
-
import
|
|
2765
|
-
import
|
|
3686
|
+
import { existsSync as existsSync5 } from "fs";
|
|
3687
|
+
import path5 from "path";
|
|
3688
|
+
import os4 from "os";
|
|
2766
3689
|
var SERVICE = "exe-mem";
|
|
2767
3690
|
var ACCOUNT = "master-key";
|
|
2768
3691
|
function getKeyDir() {
|
|
2769
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
3692
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path5.join(os4.homedir(), ".exe-os");
|
|
2770
3693
|
}
|
|
2771
3694
|
function getKeyPath() {
|
|
2772
|
-
return
|
|
3695
|
+
return path5.join(getKeyDir(), "master.key");
|
|
2773
3696
|
}
|
|
2774
3697
|
async function tryKeytar() {
|
|
2775
3698
|
try {
|
|
@@ -2790,9 +3713,9 @@ async function getMasterKey() {
|
|
|
2790
3713
|
}
|
|
2791
3714
|
}
|
|
2792
3715
|
const keyPath = getKeyPath();
|
|
2793
|
-
if (!
|
|
3716
|
+
if (!existsSync5(keyPath)) {
|
|
2794
3717
|
process.stderr.write(
|
|
2795
|
-
`[keychain] Key not found at ${keyPath} (HOME=${
|
|
3718
|
+
`[keychain] Key not found at ${keyPath} (HOME=${os4.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
2796
3719
|
`
|
|
2797
3720
|
);
|
|
2798
3721
|
return null;
|
|
@@ -3103,15 +4026,15 @@ function vectorToBlob(vector) {
|
|
|
3103
4026
|
}
|
|
3104
4027
|
|
|
3105
4028
|
// src/lib/behaviors-export.ts
|
|
3106
|
-
import
|
|
3107
|
-
import
|
|
4029
|
+
import os5 from "os";
|
|
4030
|
+
import path7 from "path";
|
|
3108
4031
|
import {
|
|
3109
|
-
existsSync as
|
|
3110
|
-
mkdirSync as
|
|
4032
|
+
existsSync as existsSync7,
|
|
4033
|
+
mkdirSync as mkdirSync3,
|
|
3111
4034
|
readdirSync as readdirSync2,
|
|
3112
4035
|
statSync,
|
|
3113
4036
|
unlinkSync as unlinkSync2,
|
|
3114
|
-
writeFileSync as
|
|
4037
|
+
writeFileSync as writeFileSync3
|
|
3115
4038
|
} from "fs";
|
|
3116
4039
|
|
|
3117
4040
|
// src/lib/behaviors.ts
|
|
@@ -3146,15 +4069,15 @@ async function listBehaviors(agentId, projectName, limit = 30) {
|
|
|
3146
4069
|
}
|
|
3147
4070
|
|
|
3148
4071
|
// src/lib/behaviors-export.ts
|
|
3149
|
-
var BEHAVIORS_EXPORT_DIR =
|
|
3150
|
-
|
|
4072
|
+
var BEHAVIORS_EXPORT_DIR = path7.join(
|
|
4073
|
+
os5.homedir(),
|
|
3151
4074
|
".exe-os",
|
|
3152
4075
|
"behaviors-export"
|
|
3153
4076
|
);
|
|
3154
4077
|
var STALE_EXPORT_AGE_MS = 60 * 60 * 1e3;
|
|
3155
4078
|
var EXPORT_BEHAVIOR_LIMIT = 30;
|
|
3156
4079
|
function sweepStaleBehaviorExports(now = Date.now()) {
|
|
3157
|
-
if (!
|
|
4080
|
+
if (!existsSync7(BEHAVIORS_EXPORT_DIR)) return;
|
|
3158
4081
|
let entries;
|
|
3159
4082
|
try {
|
|
3160
4083
|
entries = readdirSync2(BEHAVIORS_EXPORT_DIR);
|
|
@@ -3162,7 +4085,7 @@ function sweepStaleBehaviorExports(now = Date.now()) {
|
|
|
3162
4085
|
return;
|
|
3163
4086
|
}
|
|
3164
4087
|
for (const entry of entries) {
|
|
3165
|
-
const filePath =
|
|
4088
|
+
const filePath = path7.join(BEHAVIORS_EXPORT_DIR, entry);
|
|
3166
4089
|
try {
|
|
3167
4090
|
const stat = statSync(filePath);
|
|
3168
4091
|
if (now - stat.mtimeMs > STALE_EXPORT_AGE_MS) {
|
|
@@ -3195,22 +4118,22 @@ function renderBehaviorExport(behaviors) {
|
|
|
3195
4118
|
}
|
|
3196
4119
|
function exportFilePath(agentId, projectName, sessionKey) {
|
|
3197
4120
|
if (!sessionKey) {
|
|
3198
|
-
return
|
|
4121
|
+
return path7.join(BEHAVIORS_EXPORT_DIR, `${agentId}.md`);
|
|
3199
4122
|
}
|
|
3200
4123
|
const safeProject = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
3201
|
-
return
|
|
4124
|
+
return path7.join(
|
|
3202
4125
|
BEHAVIORS_EXPORT_DIR,
|
|
3203
4126
|
`${agentId}-${safeProject}-${sessionKey}.md`
|
|
3204
4127
|
);
|
|
3205
4128
|
}
|
|
3206
4129
|
async function exportBehaviorsForAgent(agentId, projectName, sessionKey) {
|
|
3207
|
-
|
|
4130
|
+
mkdirSync3(BEHAVIORS_EXPORT_DIR, { recursive: true });
|
|
3208
4131
|
sweepStaleBehaviorExports();
|
|
3209
4132
|
const behaviors = await listBehaviors(agentId, projectName, EXPORT_BEHAVIOR_LIMIT);
|
|
3210
4133
|
if (behaviors.length === 0) return null;
|
|
3211
4134
|
const body = renderBehaviorExport(behaviors);
|
|
3212
4135
|
const target = exportFilePath(agentId, projectName, sessionKey);
|
|
3213
|
-
|
|
4136
|
+
writeFileSync3(target, body, "utf-8");
|
|
3214
4137
|
return target;
|
|
3215
4138
|
}
|
|
3216
4139
|
|
|
@@ -3269,7 +4192,7 @@ function parseBasename(basename) {
|
|
|
3269
4192
|
return { agent, provider };
|
|
3270
4193
|
}
|
|
3271
4194
|
function resolveAgent(argv) {
|
|
3272
|
-
const invokedAs =
|
|
4195
|
+
const invokedAs = path9.basename(argv[1] ?? "");
|
|
3273
4196
|
if (invokedAs && invokedAs !== "exe-launch-agent" && !invokedAs.endsWith(".js")) {
|
|
3274
4197
|
const { agent: agent2, provider } = parseBasename(invokedAs.toLowerCase());
|
|
3275
4198
|
return { agent: agent2, provider, passthrough: argv.slice(2) };
|
|
@@ -3298,20 +4221,20 @@ async function isKnownAgent(agent) {
|
|
|
3298
4221
|
}
|
|
3299
4222
|
}
|
|
3300
4223
|
function identityPathFor(agent) {
|
|
3301
|
-
const dir =
|
|
3302
|
-
const exactPath =
|
|
3303
|
-
if (
|
|
4224
|
+
const dir = path9.join(os6.homedir(), ".exe-os", "identity");
|
|
4225
|
+
const exactPath = path9.join(dir, `${agent}.md`);
|
|
4226
|
+
if (existsSync8(exactPath)) return exactPath;
|
|
3304
4227
|
try {
|
|
3305
4228
|
const files = readdirSync4(dir);
|
|
3306
4229
|
const match = files.find((f) => f.toLowerCase() === `${agent.toLowerCase()}.md`);
|
|
3307
|
-
if (match) return
|
|
4230
|
+
if (match) return path9.join(dir, match);
|
|
3308
4231
|
} catch {
|
|
3309
4232
|
}
|
|
3310
4233
|
return exactPath;
|
|
3311
4234
|
}
|
|
3312
4235
|
function leanMcpConfigFor(agent) {
|
|
3313
|
-
const p =
|
|
3314
|
-
return
|
|
4236
|
+
const p = path9.join(os6.homedir(), ".exe-os", "mcp-configs", `${agent}-lean.json`);
|
|
4237
|
+
return existsSync8(p) ? p : null;
|
|
3315
4238
|
}
|
|
3316
4239
|
var _ccHelpOutput = null;
|
|
3317
4240
|
function getCcHelpOutput() {
|
|
@@ -3334,39 +4257,39 @@ function _resetCcHelpCache() {
|
|
|
3334
4257
|
function buildLaunchPlan(agent, behaviorsPath, passthrough, _hasAgentFlag, _provider) {
|
|
3335
4258
|
const args = ["--dangerously-skip-permissions"];
|
|
3336
4259
|
const idPath = identityPathFor(agent);
|
|
3337
|
-
const ccAgentPath =
|
|
4260
|
+
const ccAgentPath = path9.join(os6.homedir(), ".claude", "agents", `${agent}.md`);
|
|
3338
4261
|
let effectiveCcPath = null;
|
|
3339
|
-
if (
|
|
4262
|
+
if (existsSync8(ccAgentPath)) {
|
|
3340
4263
|
effectiveCcPath = ccAgentPath;
|
|
3341
4264
|
} else {
|
|
3342
4265
|
try {
|
|
3343
|
-
const ccAgentDir =
|
|
4266
|
+
const ccAgentDir = path9.join(os6.homedir(), ".claude", "agents");
|
|
3344
4267
|
const ccFiles = readdirSync4(ccAgentDir);
|
|
3345
4268
|
const ccMatch = ccFiles.find((f) => f.toLowerCase() === `${agent.toLowerCase()}.md`);
|
|
3346
|
-
if (ccMatch) effectiveCcPath =
|
|
4269
|
+
if (ccMatch) effectiveCcPath = path9.join(ccAgentDir, ccMatch);
|
|
3347
4270
|
} catch {
|
|
3348
4271
|
}
|
|
3349
4272
|
}
|
|
3350
|
-
const effectiveIdPath =
|
|
4273
|
+
const effectiveIdPath = existsSync8(idPath) ? idPath : effectiveCcPath;
|
|
3351
4274
|
let identityContent = null;
|
|
3352
|
-
if (effectiveIdPath &&
|
|
4275
|
+
if (effectiveIdPath && existsSync8(effectiveIdPath)) {
|
|
3353
4276
|
try {
|
|
3354
|
-
const content =
|
|
4277
|
+
const content = readFileSync5(effectiveIdPath, "utf-8");
|
|
3355
4278
|
if (content.trim().length > 0) identityContent = content;
|
|
3356
4279
|
} catch {
|
|
3357
4280
|
}
|
|
3358
4281
|
}
|
|
3359
4282
|
if (!identityContent) {
|
|
3360
4283
|
try {
|
|
3361
|
-
const rosterPath =
|
|
3362
|
-
if (
|
|
3363
|
-
const roster = JSON.parse(
|
|
4284
|
+
const rosterPath = path9.join(os6.homedir(), ".exe-os", "exe-employees.json");
|
|
4285
|
+
if (existsSync8(rosterPath)) {
|
|
4286
|
+
const roster = JSON.parse(readFileSync5(rosterPath, "utf8"));
|
|
3364
4287
|
const emp = roster.find((e) => e.name.toLowerCase() === agent.toLowerCase());
|
|
3365
4288
|
if (emp?.systemPrompt && emp.systemPrompt.trim().length > 20) {
|
|
3366
4289
|
identityContent = emp.systemPrompt;
|
|
3367
4290
|
try {
|
|
3368
|
-
const dir =
|
|
3369
|
-
if (!
|
|
4291
|
+
const dir = path9.dirname(idPath);
|
|
4292
|
+
if (!existsSync8(dir)) mkdirSync5(dir, { recursive: true });
|
|
3370
4293
|
const hasFrontmatter = identityContent.trimStart().startsWith("---");
|
|
3371
4294
|
const fileContent = hasFrontmatter ? identityContent : `---
|
|
3372
4295
|
role: ${(emp.role ?? "employee").toLowerCase()}
|
|
@@ -3378,7 +4301,7 @@ updated_at: ${(/* @__PURE__ */ new Date()).toISOString()}
|
|
|
3378
4301
|
---
|
|
3379
4302
|
|
|
3380
4303
|
${identityContent}`;
|
|
3381
|
-
|
|
4304
|
+
writeFileSync5(idPath, fileContent, "utf-8");
|
|
3382
4305
|
identityContent = fileContent;
|
|
3383
4306
|
process.stderr.write(`[exe-launch-agent] self-healed missing identity file: ${idPath}
|
|
3384
4307
|
`);
|
|
@@ -3406,15 +4329,15 @@ ${identityContent}`;
|
|
|
3406
4329
|
args.push("--system-prompt", getSessionPrompt(identityContent));
|
|
3407
4330
|
} else {
|
|
3408
4331
|
try {
|
|
3409
|
-
const tmpPath =
|
|
3410
|
-
|
|
3411
|
-
|
|
4332
|
+
const tmpPath = path9.join(os6.homedir(), ".exe-os", "session-cache", `${agent}-identity.md`);
|
|
4333
|
+
mkdirSync5(path9.dirname(tmpPath), { recursive: true });
|
|
4334
|
+
writeFileSync5(tmpPath, identityContent, "utf-8");
|
|
3412
4335
|
args.push("--append-system-prompt-file", tmpPath);
|
|
3413
4336
|
} catch {
|
|
3414
4337
|
}
|
|
3415
4338
|
}
|
|
3416
4339
|
}
|
|
3417
|
-
if (behaviorsPath &&
|
|
4340
|
+
if (behaviorsPath && existsSync8(behaviorsPath)) {
|
|
3418
4341
|
args.push("--append-system-prompt-file", behaviorsPath);
|
|
3419
4342
|
}
|
|
3420
4343
|
const leanMcp = leanMcpConfigFor(agent);
|
|
@@ -3535,28 +4458,28 @@ async function main() {
|
|
|
3535
4458
|
_resetCcAgentSupportCache();
|
|
3536
4459
|
const hasAgentFlag = claudeSupportsAgentFlag();
|
|
3537
4460
|
if (hasAgentFlag) {
|
|
3538
|
-
const ccAgentDir =
|
|
3539
|
-
const ccAgentFile =
|
|
3540
|
-
if (!
|
|
4461
|
+
const ccAgentDir = path9.join(os6.homedir(), ".claude", "agents");
|
|
4462
|
+
const ccAgentFile = path9.join(ccAgentDir, `${agent}.md`);
|
|
4463
|
+
if (!existsSync8(ccAgentFile)) {
|
|
3541
4464
|
const exeIdentity = identityPathFor(agent);
|
|
3542
4465
|
let sourceFile = null;
|
|
3543
|
-
if (
|
|
4466
|
+
if (existsSync8(exeIdentity)) {
|
|
3544
4467
|
sourceFile = exeIdentity;
|
|
3545
4468
|
} else {
|
|
3546
4469
|
try {
|
|
3547
|
-
const identityDir =
|
|
4470
|
+
const identityDir = path9.dirname(exeIdentity);
|
|
3548
4471
|
const files = readdirSync4(identityDir);
|
|
3549
4472
|
const match = files.find((f) => f.toLowerCase() === `${agent.toLowerCase()}.md`);
|
|
3550
|
-
if (match) sourceFile =
|
|
4473
|
+
if (match) sourceFile = path9.join(identityDir, match);
|
|
3551
4474
|
} catch {
|
|
3552
4475
|
}
|
|
3553
4476
|
}
|
|
3554
4477
|
if (sourceFile) {
|
|
3555
4478
|
try {
|
|
3556
|
-
|
|
3557
|
-
let content =
|
|
4479
|
+
mkdirSync5(ccAgentDir, { recursive: true });
|
|
4480
|
+
let content = readFileSync5(sourceFile, "utf-8");
|
|
3558
4481
|
content = content.replace(/\$\{agent_id\}/g, baseAgentName(agent));
|
|
3559
|
-
|
|
4482
|
+
writeFileSync5(ccAgentFile, content, "utf-8");
|
|
3560
4483
|
process.stderr.write(
|
|
3561
4484
|
`[exe-launch-agent] auto-provisioned ${ccAgentFile} from ${sourceFile}
|
|
3562
4485
|
`
|
|
@@ -3576,7 +4499,7 @@ async function main() {
|
|
|
3576
4499
|
const empRole = (() => {
|
|
3577
4500
|
try {
|
|
3578
4501
|
const emps = __require("fs").readFileSync(
|
|
3579
|
-
|
|
4502
|
+
path9.join(os6.homedir(), ".exe-os", "exe-employees.json"),
|
|
3580
4503
|
"utf-8"
|
|
3581
4504
|
);
|
|
3582
4505
|
const found = JSON.parse(emps).find(
|