@askexenow/exe-os 0.9.8 → 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 +222 -49
- package/dist/bin/backfill-responses.js +221 -48
- package/dist/bin/backfill-vectors.js +225 -52
- package/dist/bin/cleanup-stale-review-tasks.js +150 -28
- package/dist/bin/cli.js +1295 -856
- package/dist/bin/exe-agent-config.js +36 -8
- package/dist/bin/exe-agent.js +14 -4
- package/dist/bin/exe-assign.js +221 -48
- package/dist/bin/exe-boot.js +778 -427
- package/dist/bin/exe-call.js +41 -13
- package/dist/bin/exe-cloud.js +163 -58
- package/dist/bin/exe-dispatch.js +276 -139
- package/dist/bin/exe-doctor.js +145 -27
- package/dist/bin/exe-export-behaviors.js +141 -23
- package/dist/bin/exe-forget.js +137 -19
- package/dist/bin/exe-gateway.js +677 -388
- package/dist/bin/exe-heartbeat.js +227 -108
- package/dist/bin/exe-kill.js +138 -20
- package/dist/bin/exe-launch-agent.js +172 -39
- package/dist/bin/exe-link.js +291 -100
- package/dist/bin/exe-new-employee.js +214 -106
- package/dist/bin/exe-pending-messages.js +395 -33
- package/dist/bin/exe-pending-notifications.js +684 -99
- package/dist/bin/exe-pending-reviews.js +420 -74
- package/dist/bin/exe-rename.js +147 -49
- package/dist/bin/exe-review.js +138 -20
- package/dist/bin/exe-search.js +240 -69
- package/dist/bin/exe-session-cleanup.js +440 -250
- package/dist/bin/exe-settings.js +61 -17
- package/dist/bin/exe-start-codex.js +158 -39
- package/dist/bin/exe-start-opencode.js +157 -38
- package/dist/bin/exe-status.js +151 -29
- package/dist/bin/exe-team.js +138 -20
- package/dist/bin/git-sweep.js +404 -212
- package/dist/bin/graph-backfill.js +137 -19
- package/dist/bin/graph-export.js +140 -22
- package/dist/bin/install.js +90 -61
- package/dist/bin/scan-tasks.js +412 -220
- package/dist/bin/setup.js +564 -293
- package/dist/bin/shard-migrate.js +139 -21
- package/dist/bin/update.js +138 -49
- package/dist/bin/wiki-sync.js +137 -19
- package/dist/gateway/index.js +533 -320
- package/dist/hooks/bug-report-worker.js +344 -193
- package/dist/hooks/codex-stop-task-finalizer.js +4678 -0
- package/dist/hooks/commit-complete.js +402 -210
- package/dist/hooks/error-recall.js +245 -74
- package/dist/hooks/exe-heartbeat-hook.js +16 -6
- package/dist/hooks/ingest-worker.js +3423 -3157
- package/dist/hooks/ingest.js +832 -97
- package/dist/hooks/instructions-loaded.js +227 -54
- package/dist/hooks/notification.js +216 -43
- package/dist/hooks/post-compact.js +239 -62
- package/dist/hooks/pre-compact.js +408 -216
- package/dist/hooks/pre-tool-use.js +268 -90
- package/dist/hooks/prompt-ingest-worker.js +352 -102
- package/dist/hooks/prompt-submit.js +541 -328
- package/dist/hooks/response-ingest-worker.js +372 -122
- package/dist/hooks/session-end.js +443 -240
- package/dist/hooks/session-start.js +313 -127
- package/dist/hooks/stop.js +293 -98
- package/dist/hooks/subagent-stop.js +239 -62
- package/dist/hooks/summary-worker.js +568 -236
- package/dist/index.js +538 -324
- package/dist/lib/agent-config.js +28 -6
- package/dist/lib/cloud-sync.js +284 -105
- package/dist/lib/config.js +30 -10
- package/dist/lib/consolidation.js +16 -6
- package/dist/lib/database.js +123 -25
- package/dist/lib/db-daemon-client.js +73 -19
- package/dist/lib/db.js +123 -25
- package/dist/lib/device-registry.js +133 -35
- package/dist/lib/embedder.js +107 -32
- package/dist/lib/employee-templates.js +14 -4
- package/dist/lib/employees.js +41 -13
- package/dist/lib/exe-daemon-client.js +88 -22
- package/dist/lib/exe-daemon.js +935 -587
- package/dist/lib/hybrid-search.js +240 -69
- package/dist/lib/identity.js +18 -8
- package/dist/lib/license.js +133 -48
- package/dist/lib/messaging.js +116 -56
- package/dist/lib/reminders.js +14 -4
- package/dist/lib/schedules.js +137 -19
- package/dist/lib/skill-learning.js +33 -6
- package/dist/lib/store.js +137 -19
- package/dist/lib/task-router.js +14 -4
- package/dist/lib/tasks.js +280 -234
- package/dist/lib/tmux-routing.js +172 -125
- package/dist/lib/token-spend.js +26 -8
- package/dist/mcp/server.js +1326 -609
- package/dist/mcp/tools/complete-reminder.js +14 -4
- package/dist/mcp/tools/create-reminder.js +14 -4
- package/dist/mcp/tools/create-task.js +306 -248
- package/dist/mcp/tools/deactivate-behavior.js +16 -6
- package/dist/mcp/tools/list-reminders.js +14 -4
- package/dist/mcp/tools/list-tasks.js +123 -107
- package/dist/mcp/tools/send-message.js +75 -29
- package/dist/mcp/tools/update-task.js +1848 -199
- package/dist/runtime/index.js +441 -248
- package/dist/tui/App.js +761 -424
- package/package.json +1 -1
|
@@ -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");
|
|
@@ -293,10 +333,10 @@ __export(agent_config_exports, {
|
|
|
293
333
|
saveAgentConfig: () => saveAgentConfig,
|
|
294
334
|
setAgentRuntime: () => setAgentRuntime
|
|
295
335
|
});
|
|
296
|
-
import { readFileSync as readFileSync2, writeFileSync, existsSync as
|
|
336
|
+
import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync3 } from "fs";
|
|
297
337
|
import path2 from "path";
|
|
298
338
|
function loadAgentConfig() {
|
|
299
|
-
if (!
|
|
339
|
+
if (!existsSync3(AGENT_CONFIG_PATH)) return {};
|
|
300
340
|
try {
|
|
301
341
|
return JSON.parse(readFileSync2(AGENT_CONFIG_PATH, "utf-8"));
|
|
302
342
|
} catch {
|
|
@@ -305,8 +345,9 @@ function loadAgentConfig() {
|
|
|
305
345
|
}
|
|
306
346
|
function saveAgentConfig(config) {
|
|
307
347
|
const dir = path2.dirname(AGENT_CONFIG_PATH);
|
|
308
|
-
|
|
348
|
+
ensurePrivateDirSync(dir);
|
|
309
349
|
writeFileSync(AGENT_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
350
|
+
enforcePrivateFileSync(AGENT_CONFIG_PATH);
|
|
310
351
|
}
|
|
311
352
|
function getAgentRuntime(agentId) {
|
|
312
353
|
const config = loadAgentConfig();
|
|
@@ -346,6 +387,7 @@ var init_agent_config = __esm({
|
|
|
346
387
|
"use strict";
|
|
347
388
|
init_config();
|
|
348
389
|
init_runtime_table();
|
|
390
|
+
init_secure_files();
|
|
349
391
|
AGENT_CONFIG_PATH = path2.join(EXE_AI_DIR, "agent-config.json");
|
|
350
392
|
KNOWN_RUNTIMES = {
|
|
351
393
|
claude: ["claude-opus-4", "claude-sonnet-4", "claude-haiku-4.5"],
|
|
@@ -393,7 +435,7 @@ __export(employees_exports, {
|
|
|
393
435
|
validateEmployeeName: () => validateEmployeeName
|
|
394
436
|
});
|
|
395
437
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
396
|
-
import { existsSync as
|
|
438
|
+
import { existsSync as existsSync4, symlinkSync, readlinkSync, readFileSync as readFileSync3, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
|
|
397
439
|
import { execSync } from "child_process";
|
|
398
440
|
import path3 from "path";
|
|
399
441
|
import os2 from "os";
|
|
@@ -432,7 +474,7 @@ function validateEmployeeName(name) {
|
|
|
432
474
|
return { valid: true };
|
|
433
475
|
}
|
|
434
476
|
async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
435
|
-
if (!
|
|
477
|
+
if (!existsSync4(employeesPath)) {
|
|
436
478
|
return [];
|
|
437
479
|
}
|
|
438
480
|
const raw = await readFile2(employeesPath, "utf-8");
|
|
@@ -447,7 +489,7 @@ async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
|
|
|
447
489
|
await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
|
|
448
490
|
}
|
|
449
491
|
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
450
|
-
if (!
|
|
492
|
+
if (!existsSync4(employeesPath)) return [];
|
|
451
493
|
try {
|
|
452
494
|
return JSON.parse(readFileSync3(employeesPath, "utf-8"));
|
|
453
495
|
} catch {
|
|
@@ -495,7 +537,7 @@ function appendToCoordinatorTeam(employee) {
|
|
|
495
537
|
const coordinator = getCoordinatorEmployee(loadEmployeesSync());
|
|
496
538
|
if (!coordinator) return;
|
|
497
539
|
const idPath = path3.join(IDENTITY_DIR, `${coordinator.name}.md`);
|
|
498
|
-
if (!
|
|
540
|
+
if (!existsSync4(idPath)) return;
|
|
499
541
|
const content = readFileSync3(idPath, "utf-8");
|
|
500
542
|
if (content.includes(`**${capitalize(employee.name)}`)) return;
|
|
501
543
|
const teamMatch = content.match(TEAM_SECTION_RE);
|
|
@@ -549,9 +591,9 @@ async function normalizeRosterCase(rosterPath) {
|
|
|
549
591
|
const identityDir = path3.join(os2.homedir(), ".exe-os", "identity");
|
|
550
592
|
const oldPath = path3.join(identityDir, `${oldName}.md`);
|
|
551
593
|
const newPath = path3.join(identityDir, `${emp.name}.md`);
|
|
552
|
-
if (
|
|
594
|
+
if (existsSync4(oldPath) && !existsSync4(newPath)) {
|
|
553
595
|
renameSync2(oldPath, newPath);
|
|
554
|
-
} else if (
|
|
596
|
+
} else if (existsSync4(oldPath) && oldPath !== newPath) {
|
|
555
597
|
const content = readFileSync3(oldPath, "utf-8");
|
|
556
598
|
writeFileSync2(newPath, content, "utf-8");
|
|
557
599
|
if (oldPath.toLowerCase() !== newPath.toLowerCase()) {
|
|
@@ -594,7 +636,7 @@ function registerBinSymlinks(name) {
|
|
|
594
636
|
for (const suffix of ["", "-opencode"]) {
|
|
595
637
|
const linkName = `${name}${suffix}`;
|
|
596
638
|
const linkPath = path3.join(binDir, linkName);
|
|
597
|
-
if (
|
|
639
|
+
if (existsSync4(linkPath)) {
|
|
598
640
|
skipped.push(linkName);
|
|
599
641
|
continue;
|
|
600
642
|
}
|
|
@@ -1547,6 +1589,7 @@ async function ensureSchema() {
|
|
|
1547
1589
|
project TEXT NOT NULL,
|
|
1548
1590
|
summary TEXT NOT NULL,
|
|
1549
1591
|
task_file TEXT,
|
|
1592
|
+
session_scope TEXT,
|
|
1550
1593
|
read INTEGER NOT NULL DEFAULT 0,
|
|
1551
1594
|
created_at TEXT NOT NULL
|
|
1552
1595
|
);
|
|
@@ -1555,7 +1598,7 @@ async function ensureSchema() {
|
|
|
1555
1598
|
ON notifications(read);
|
|
1556
1599
|
|
|
1557
1600
|
CREATE INDEX IF NOT EXISTS idx_notifications_agent
|
|
1558
|
-
ON notifications(agent_id);
|
|
1601
|
+
ON notifications(agent_id, session_scope);
|
|
1559
1602
|
|
|
1560
1603
|
CREATE INDEX IF NOT EXISTS idx_notifications_task_file
|
|
1561
1604
|
ON notifications(task_file);
|
|
@@ -1593,6 +1636,7 @@ async function ensureSchema() {
|
|
|
1593
1636
|
target_agent TEXT NOT NULL,
|
|
1594
1637
|
target_project TEXT,
|
|
1595
1638
|
target_device TEXT NOT NULL DEFAULT 'local',
|
|
1639
|
+
session_scope TEXT,
|
|
1596
1640
|
content TEXT NOT NULL,
|
|
1597
1641
|
priority TEXT DEFAULT 'normal',
|
|
1598
1642
|
status TEXT DEFAULT 'pending',
|
|
@@ -1606,10 +1650,31 @@ async function ensureSchema() {
|
|
|
1606
1650
|
);
|
|
1607
1651
|
|
|
1608
1652
|
CREATE INDEX IF NOT EXISTS idx_messages_target
|
|
1609
|
-
ON messages(target_agent, status);
|
|
1653
|
+
ON messages(target_agent, session_scope, status);
|
|
1610
1654
|
|
|
1611
1655
|
CREATE INDEX IF NOT EXISTS idx_messages_conversation_order
|
|
1612
|
-
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);
|
|
1613
1678
|
`);
|
|
1614
1679
|
try {
|
|
1615
1680
|
await client.execute({
|
|
@@ -2193,6 +2258,13 @@ async function ensureSchema() {
|
|
|
2193
2258
|
} catch {
|
|
2194
2259
|
}
|
|
2195
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
|
+
}
|
|
2196
2268
|
}
|
|
2197
2269
|
async function disposeDatabase() {
|
|
2198
2270
|
if (_walCheckpointTimer) {
|
|
@@ -2235,6 +2307,7 @@ var shard_manager_exports = {};
|
|
|
2235
2307
|
__export(shard_manager_exports, {
|
|
2236
2308
|
disposeShards: () => disposeShards,
|
|
2237
2309
|
ensureShardSchema: () => ensureShardSchema,
|
|
2310
|
+
getOpenShardCount: () => getOpenShardCount,
|
|
2238
2311
|
getReadyShardClient: () => getReadyShardClient,
|
|
2239
2312
|
getShardClient: () => getShardClient,
|
|
2240
2313
|
getShardsDir: () => getShardsDir,
|
|
@@ -2244,14 +2317,17 @@ __export(shard_manager_exports, {
|
|
|
2244
2317
|
shardExists: () => shardExists
|
|
2245
2318
|
});
|
|
2246
2319
|
import path6 from "path";
|
|
2247
|
-
import { existsSync as
|
|
2320
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync2, readdirSync } from "fs";
|
|
2248
2321
|
import { createClient as createClient2 } from "@libsql/client";
|
|
2249
2322
|
function initShardManager(encryptionKey) {
|
|
2250
2323
|
_encryptionKey = encryptionKey;
|
|
2251
|
-
if (!
|
|
2324
|
+
if (!existsSync6(SHARDS_DIR)) {
|
|
2252
2325
|
mkdirSync2(SHARDS_DIR, { recursive: true });
|
|
2253
2326
|
}
|
|
2254
2327
|
_shardingEnabled = true;
|
|
2328
|
+
if (_evictionTimer) clearInterval(_evictionTimer);
|
|
2329
|
+
_evictionTimer = setInterval(evictIdleShards, EVICTION_INTERVAL_MS);
|
|
2330
|
+
_evictionTimer.unref();
|
|
2255
2331
|
}
|
|
2256
2332
|
function isShardingEnabled() {
|
|
2257
2333
|
return _shardingEnabled;
|
|
@@ -2268,21 +2344,28 @@ function getShardClient(projectName) {
|
|
|
2268
2344
|
throw new Error(`Invalid project name for shard: "${projectName}"`);
|
|
2269
2345
|
}
|
|
2270
2346
|
const cached = _shards.get(safeName);
|
|
2271
|
-
if (cached)
|
|
2347
|
+
if (cached) {
|
|
2348
|
+
_shardLastAccess.set(safeName, Date.now());
|
|
2349
|
+
return cached;
|
|
2350
|
+
}
|
|
2351
|
+
while (_shards.size >= MAX_OPEN_SHARDS) {
|
|
2352
|
+
evictLRU();
|
|
2353
|
+
}
|
|
2272
2354
|
const dbPath = path6.join(SHARDS_DIR, `${safeName}.db`);
|
|
2273
2355
|
const client = createClient2({
|
|
2274
2356
|
url: `file:${dbPath}`,
|
|
2275
2357
|
encryptionKey: _encryptionKey
|
|
2276
2358
|
});
|
|
2277
2359
|
_shards.set(safeName, client);
|
|
2360
|
+
_shardLastAccess.set(safeName, Date.now());
|
|
2278
2361
|
return client;
|
|
2279
2362
|
}
|
|
2280
2363
|
function shardExists(projectName) {
|
|
2281
2364
|
const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
2282
|
-
return
|
|
2365
|
+
return existsSync6(path6.join(SHARDS_DIR, `${safeName}.db`));
|
|
2283
2366
|
}
|
|
2284
2367
|
function listShards() {
|
|
2285
|
-
if (!
|
|
2368
|
+
if (!existsSync6(SHARDS_DIR)) return [];
|
|
2286
2369
|
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
2287
2370
|
}
|
|
2288
2371
|
async function ensureShardSchema(client) {
|
|
@@ -2334,6 +2417,8 @@ async function ensureShardSchema(client) {
|
|
|
2334
2417
|
for (const col of [
|
|
2335
2418
|
"ALTER TABLE memories ADD COLUMN task_id TEXT",
|
|
2336
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'",
|
|
2337
2422
|
"ALTER TABLE memories ADD COLUMN importance INTEGER DEFAULT 5",
|
|
2338
2423
|
"ALTER TABLE memories ADD COLUMN status TEXT DEFAULT 'active'",
|
|
2339
2424
|
"ALTER TABLE memories ADD COLUMN wiki_synced INTEGER DEFAULT 0",
|
|
@@ -2471,21 +2556,69 @@ async function getReadyShardClient(projectName) {
|
|
|
2471
2556
|
await ensureShardSchema(client);
|
|
2472
2557
|
return client;
|
|
2473
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
|
+
}
|
|
2474
2597
|
function disposeShards() {
|
|
2598
|
+
if (_evictionTimer) {
|
|
2599
|
+
clearInterval(_evictionTimer);
|
|
2600
|
+
_evictionTimer = null;
|
|
2601
|
+
}
|
|
2475
2602
|
for (const [, client] of _shards) {
|
|
2476
2603
|
client.close();
|
|
2477
2604
|
}
|
|
2478
2605
|
_shards.clear();
|
|
2606
|
+
_shardLastAccess.clear();
|
|
2479
2607
|
_shardingEnabled = false;
|
|
2480
2608
|
_encryptionKey = null;
|
|
2481
2609
|
}
|
|
2482
|
-
var SHARDS_DIR, _shards, _encryptionKey, _shardingEnabled;
|
|
2610
|
+
var SHARDS_DIR, SHARD_IDLE_MS, MAX_OPEN_SHARDS, EVICTION_INTERVAL_MS, _shards, _shardLastAccess, _evictionTimer, _encryptionKey, _shardingEnabled;
|
|
2483
2611
|
var init_shard_manager = __esm({
|
|
2484
2612
|
"src/lib/shard-manager.ts"() {
|
|
2485
2613
|
"use strict";
|
|
2486
2614
|
init_config();
|
|
2487
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;
|
|
2488
2619
|
_shards = /* @__PURE__ */ new Map();
|
|
2620
|
+
_shardLastAccess = /* @__PURE__ */ new Map();
|
|
2621
|
+
_evictionTimer = null;
|
|
2489
2622
|
_encryptionKey = null;
|
|
2490
2623
|
_shardingEnabled = false;
|
|
2491
2624
|
}
|
|
@@ -3541,7 +3674,7 @@ var init_active_agent = __esm({
|
|
|
3541
3674
|
// src/bin/exe-launch-agent.ts
|
|
3542
3675
|
import os6 from "os";
|
|
3543
3676
|
import path9 from "path";
|
|
3544
|
-
import { existsSync as
|
|
3677
|
+
import { existsSync as existsSync8, readFileSync as readFileSync5, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, readdirSync as readdirSync4 } from "fs";
|
|
3545
3678
|
import { spawnSync } from "child_process";
|
|
3546
3679
|
|
|
3547
3680
|
// src/lib/store.ts
|
|
@@ -3550,7 +3683,7 @@ init_database();
|
|
|
3550
3683
|
|
|
3551
3684
|
// src/lib/keychain.ts
|
|
3552
3685
|
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
3553
|
-
import { existsSync as
|
|
3686
|
+
import { existsSync as existsSync5 } from "fs";
|
|
3554
3687
|
import path5 from "path";
|
|
3555
3688
|
import os4 from "os";
|
|
3556
3689
|
var SERVICE = "exe-mem";
|
|
@@ -3580,7 +3713,7 @@ async function getMasterKey() {
|
|
|
3580
3713
|
}
|
|
3581
3714
|
}
|
|
3582
3715
|
const keyPath = getKeyPath();
|
|
3583
|
-
if (!
|
|
3716
|
+
if (!existsSync5(keyPath)) {
|
|
3584
3717
|
process.stderr.write(
|
|
3585
3718
|
`[keychain] Key not found at ${keyPath} (HOME=${os4.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
3586
3719
|
`
|
|
@@ -3896,7 +4029,7 @@ function vectorToBlob(vector) {
|
|
|
3896
4029
|
import os5 from "os";
|
|
3897
4030
|
import path7 from "path";
|
|
3898
4031
|
import {
|
|
3899
|
-
existsSync as
|
|
4032
|
+
existsSync as existsSync7,
|
|
3900
4033
|
mkdirSync as mkdirSync3,
|
|
3901
4034
|
readdirSync as readdirSync2,
|
|
3902
4035
|
statSync,
|
|
@@ -3944,7 +4077,7 @@ var BEHAVIORS_EXPORT_DIR = path7.join(
|
|
|
3944
4077
|
var STALE_EXPORT_AGE_MS = 60 * 60 * 1e3;
|
|
3945
4078
|
var EXPORT_BEHAVIOR_LIMIT = 30;
|
|
3946
4079
|
function sweepStaleBehaviorExports(now = Date.now()) {
|
|
3947
|
-
if (!
|
|
4080
|
+
if (!existsSync7(BEHAVIORS_EXPORT_DIR)) return;
|
|
3948
4081
|
let entries;
|
|
3949
4082
|
try {
|
|
3950
4083
|
entries = readdirSync2(BEHAVIORS_EXPORT_DIR);
|
|
@@ -4090,7 +4223,7 @@ async function isKnownAgent(agent) {
|
|
|
4090
4223
|
function identityPathFor(agent) {
|
|
4091
4224
|
const dir = path9.join(os6.homedir(), ".exe-os", "identity");
|
|
4092
4225
|
const exactPath = path9.join(dir, `${agent}.md`);
|
|
4093
|
-
if (
|
|
4226
|
+
if (existsSync8(exactPath)) return exactPath;
|
|
4094
4227
|
try {
|
|
4095
4228
|
const files = readdirSync4(dir);
|
|
4096
4229
|
const match = files.find((f) => f.toLowerCase() === `${agent.toLowerCase()}.md`);
|
|
@@ -4101,7 +4234,7 @@ function identityPathFor(agent) {
|
|
|
4101
4234
|
}
|
|
4102
4235
|
function leanMcpConfigFor(agent) {
|
|
4103
4236
|
const p = path9.join(os6.homedir(), ".exe-os", "mcp-configs", `${agent}-lean.json`);
|
|
4104
|
-
return
|
|
4237
|
+
return existsSync8(p) ? p : null;
|
|
4105
4238
|
}
|
|
4106
4239
|
var _ccHelpOutput = null;
|
|
4107
4240
|
function getCcHelpOutput() {
|
|
@@ -4126,7 +4259,7 @@ function buildLaunchPlan(agent, behaviorsPath, passthrough, _hasAgentFlag, _prov
|
|
|
4126
4259
|
const idPath = identityPathFor(agent);
|
|
4127
4260
|
const ccAgentPath = path9.join(os6.homedir(), ".claude", "agents", `${agent}.md`);
|
|
4128
4261
|
let effectiveCcPath = null;
|
|
4129
|
-
if (
|
|
4262
|
+
if (existsSync8(ccAgentPath)) {
|
|
4130
4263
|
effectiveCcPath = ccAgentPath;
|
|
4131
4264
|
} else {
|
|
4132
4265
|
try {
|
|
@@ -4137,9 +4270,9 @@ function buildLaunchPlan(agent, behaviorsPath, passthrough, _hasAgentFlag, _prov
|
|
|
4137
4270
|
} catch {
|
|
4138
4271
|
}
|
|
4139
4272
|
}
|
|
4140
|
-
const effectiveIdPath =
|
|
4273
|
+
const effectiveIdPath = existsSync8(idPath) ? idPath : effectiveCcPath;
|
|
4141
4274
|
let identityContent = null;
|
|
4142
|
-
if (effectiveIdPath &&
|
|
4275
|
+
if (effectiveIdPath && existsSync8(effectiveIdPath)) {
|
|
4143
4276
|
try {
|
|
4144
4277
|
const content = readFileSync5(effectiveIdPath, "utf-8");
|
|
4145
4278
|
if (content.trim().length > 0) identityContent = content;
|
|
@@ -4149,14 +4282,14 @@ function buildLaunchPlan(agent, behaviorsPath, passthrough, _hasAgentFlag, _prov
|
|
|
4149
4282
|
if (!identityContent) {
|
|
4150
4283
|
try {
|
|
4151
4284
|
const rosterPath = path9.join(os6.homedir(), ".exe-os", "exe-employees.json");
|
|
4152
|
-
if (
|
|
4285
|
+
if (existsSync8(rosterPath)) {
|
|
4153
4286
|
const roster = JSON.parse(readFileSync5(rosterPath, "utf8"));
|
|
4154
4287
|
const emp = roster.find((e) => e.name.toLowerCase() === agent.toLowerCase());
|
|
4155
4288
|
if (emp?.systemPrompt && emp.systemPrompt.trim().length > 20) {
|
|
4156
4289
|
identityContent = emp.systemPrompt;
|
|
4157
4290
|
try {
|
|
4158
4291
|
const dir = path9.dirname(idPath);
|
|
4159
|
-
if (!
|
|
4292
|
+
if (!existsSync8(dir)) mkdirSync5(dir, { recursive: true });
|
|
4160
4293
|
const hasFrontmatter = identityContent.trimStart().startsWith("---");
|
|
4161
4294
|
const fileContent = hasFrontmatter ? identityContent : `---
|
|
4162
4295
|
role: ${(emp.role ?? "employee").toLowerCase()}
|
|
@@ -4204,7 +4337,7 @@ ${identityContent}`;
|
|
|
4204
4337
|
}
|
|
4205
4338
|
}
|
|
4206
4339
|
}
|
|
4207
|
-
if (behaviorsPath &&
|
|
4340
|
+
if (behaviorsPath && existsSync8(behaviorsPath)) {
|
|
4208
4341
|
args.push("--append-system-prompt-file", behaviorsPath);
|
|
4209
4342
|
}
|
|
4210
4343
|
const leanMcp = leanMcpConfigFor(agent);
|
|
@@ -4327,10 +4460,10 @@ async function main() {
|
|
|
4327
4460
|
if (hasAgentFlag) {
|
|
4328
4461
|
const ccAgentDir = path9.join(os6.homedir(), ".claude", "agents");
|
|
4329
4462
|
const ccAgentFile = path9.join(ccAgentDir, `${agent}.md`);
|
|
4330
|
-
if (!
|
|
4463
|
+
if (!existsSync8(ccAgentFile)) {
|
|
4331
4464
|
const exeIdentity = identityPathFor(agent);
|
|
4332
4465
|
let sourceFile = null;
|
|
4333
|
-
if (
|
|
4466
|
+
if (existsSync8(exeIdentity)) {
|
|
4334
4467
|
sourceFile = exeIdentity;
|
|
4335
4468
|
} else {
|
|
4336
4469
|
try {
|