@askexenow/exe-os 0.8.80 → 0.8.82
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 +359 -267
- package/dist/bin/backfill-responses.js +357 -265
- package/dist/bin/backfill-vectors.js +339 -264
- package/dist/bin/cleanup-stale-review-tasks.js +315 -256
- package/dist/bin/cli.js +494 -240
- package/dist/bin/exe-agent.js +141 -46
- package/dist/bin/exe-assign.js +151 -63
- package/dist/bin/exe-boot.js +294 -115
- package/dist/bin/exe-call.js +76 -51
- package/dist/bin/exe-cloud.js +58 -45
- package/dist/bin/exe-dispatch.js +434 -277
- package/dist/bin/exe-doctor.js +317 -246
- package/dist/bin/exe-export-behaviors.js +328 -248
- package/dist/bin/exe-forget.js +314 -231
- package/dist/bin/exe-gateway.js +2676 -1402
- package/dist/bin/exe-heartbeat.js +329 -264
- package/dist/bin/exe-kill.js +324 -244
- package/dist/bin/exe-launch-agent.js +574 -463
- package/dist/bin/exe-link.js +1055 -95
- package/dist/bin/exe-new-employee.js +49 -54
- package/dist/bin/exe-pending-messages.js +310 -253
- package/dist/bin/exe-pending-notifications.js +299 -228
- package/dist/bin/exe-pending-reviews.js +314 -245
- package/dist/bin/exe-rename.js +259 -195
- package/dist/bin/exe-review.js +140 -64
- package/dist/bin/exe-search.js +543 -356
- package/dist/bin/exe-session-cleanup.js +463 -382
- package/dist/bin/exe-settings.js +129 -99
- package/dist/bin/exe-start.sh +6 -6
- package/dist/bin/exe-status.js +95 -36
- package/dist/bin/exe-team.js +116 -51
- package/dist/bin/git-sweep.js +482 -307
- package/dist/bin/graph-backfill.js +357 -245
- package/dist/bin/graph-export.js +324 -244
- package/dist/bin/install.js +33 -10
- package/dist/bin/scan-tasks.js +481 -307
- package/dist/bin/setup.js +1147 -140
- package/dist/bin/shard-migrate.js +321 -241
- package/dist/bin/update.js +1 -7
- package/dist/bin/wiki-sync.js +318 -238
- package/dist/gateway/index.js +2656 -1383
- package/dist/hooks/bug-report-worker.js +641 -472
- package/dist/hooks/commit-complete.js +482 -307
- package/dist/hooks/error-recall.js +363 -135
- package/dist/hooks/exe-heartbeat-hook.js +97 -27
- package/dist/hooks/ingest-worker.js +584 -397
- package/dist/hooks/ingest.js +123 -58
- package/dist/hooks/instructions-loaded.js +212 -82
- package/dist/hooks/notification.js +200 -70
- package/dist/hooks/post-compact.js +199 -81
- package/dist/hooks/pre-compact.js +352 -140
- package/dist/hooks/pre-tool-use.js +416 -278
- package/dist/hooks/prompt-ingest-worker.js +376 -299
- package/dist/hooks/prompt-submit.js +414 -188
- package/dist/hooks/response-ingest-worker.js +408 -338
- package/dist/hooks/session-end.js +209 -83
- package/dist/hooks/session-start.js +382 -158
- package/dist/hooks/stop.js +209 -83
- package/dist/hooks/subagent-stop.js +209 -85
- package/dist/hooks/summary-worker.js +606 -510
- package/dist/index.js +2133 -855
- package/dist/lib/cloud-sync.js +1175 -184
- package/dist/lib/config.js +1 -9
- package/dist/lib/consolidation.js +71 -34
- package/dist/lib/database.js +166 -14
- package/dist/lib/device-registry.js +189 -117
- package/dist/lib/embedder.js +6 -10
- package/dist/lib/employee-templates.js +134 -39
- package/dist/lib/employees.js +30 -7
- package/dist/lib/exe-daemon-client.js +5 -7
- package/dist/lib/exe-daemon.js +514 -152
- package/dist/lib/hybrid-search.js +543 -356
- package/dist/lib/identity-templates.js +15 -15
- package/dist/lib/identity.js +19 -15
- package/dist/lib/license.js +1 -7
- package/dist/lib/messaging.js +157 -135
- package/dist/lib/reminders.js +97 -0
- package/dist/lib/schedules.js +302 -231
- package/dist/lib/skill-learning.js +33 -27
- package/dist/lib/status-brief.js +11 -14
- package/dist/lib/store.js +326 -237
- package/dist/lib/task-router.js +105 -1
- package/dist/lib/tasks.js +233 -116
- package/dist/lib/tmux-routing.js +173 -56
- package/dist/lib/ws-client.js +13 -3
- package/dist/mcp/server.js +2009 -1015
- package/dist/mcp/tools/complete-reminder.js +97 -0
- package/dist/mcp/tools/create-reminder.js +97 -0
- package/dist/mcp/tools/create-task.js +426 -262
- package/dist/mcp/tools/deactivate-behavior.js +119 -44
- package/dist/mcp/tools/list-reminders.js +97 -0
- package/dist/mcp/tools/list-tasks.js +56 -57
- package/dist/mcp/tools/send-message.js +206 -143
- package/dist/mcp/tools/update-task.js +259 -85
- package/dist/runtime/index.js +495 -316
- package/dist/tui/App.js +1128 -919
- package/package.json +2 -10
- package/src/commands/exe/afk.md +8 -8
- package/src/commands/exe/assign.md +1 -1
- package/src/commands/exe/build-adv.md +1 -1
- package/src/commands/exe/call.md +10 -10
- package/src/commands/exe/employee-heartbeat.md +9 -6
- package/src/commands/exe/heartbeat.md +5 -5
- package/src/commands/exe/intercom.md +26 -15
- package/src/commands/exe/launch.md +2 -2
- package/src/commands/exe/new-employee.md +1 -1
- package/src/commands/exe/review.md +2 -2
- package/src/commands/exe/schedule.md +1 -1
- package/src/commands/exe/sessions.md +2 -2
- package/src/commands/exe.md +22 -20
|
@@ -73,7 +73,7 @@ function wrapWithRetry(client) {
|
|
|
73
73
|
return (sql) => retryOnBusy(() => target.execute(sql), "execute");
|
|
74
74
|
}
|
|
75
75
|
if (prop === "batch") {
|
|
76
|
-
return (stmts) => retryOnBusy(() => target.batch(stmts), "batch");
|
|
76
|
+
return (stmts, mode) => retryOnBusy(() => target.batch(stmts, mode), "batch");
|
|
77
77
|
}
|
|
78
78
|
return Reflect.get(target, prop, receiver);
|
|
79
79
|
}
|
|
@@ -89,6 +89,345 @@ var init_db_retry = __esm({
|
|
|
89
89
|
}
|
|
90
90
|
});
|
|
91
91
|
|
|
92
|
+
// src/lib/config.ts
|
|
93
|
+
var config_exports = {};
|
|
94
|
+
__export(config_exports, {
|
|
95
|
+
CONFIG_MIGRATIONS: () => CONFIG_MIGRATIONS,
|
|
96
|
+
CONFIG_PATH: () => CONFIG_PATH,
|
|
97
|
+
CURRENT_CONFIG_VERSION: () => CURRENT_CONFIG_VERSION,
|
|
98
|
+
DB_PATH: () => DB_PATH,
|
|
99
|
+
EXE_AI_DIR: () => EXE_AI_DIR,
|
|
100
|
+
LEGACY_LANCE_PATH: () => LEGACY_LANCE_PATH,
|
|
101
|
+
MODELS_DIR: () => MODELS_DIR,
|
|
102
|
+
loadConfig: () => loadConfig,
|
|
103
|
+
loadConfigFrom: () => loadConfigFrom,
|
|
104
|
+
loadConfigSync: () => loadConfigSync,
|
|
105
|
+
migrateConfig: () => migrateConfig,
|
|
106
|
+
saveConfig: () => saveConfig
|
|
107
|
+
});
|
|
108
|
+
import { readFile, writeFile, mkdir, chmod } from "fs/promises";
|
|
109
|
+
import { readFileSync, existsSync, renameSync } from "fs";
|
|
110
|
+
import path from "path";
|
|
111
|
+
import os from "os";
|
|
112
|
+
function resolveDataDir() {
|
|
113
|
+
if (process.env.EXE_OS_DIR) return process.env.EXE_OS_DIR;
|
|
114
|
+
if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
|
|
115
|
+
const newDir = path.join(os.homedir(), ".exe-os");
|
|
116
|
+
const legacyDir = path.join(os.homedir(), ".exe-mem");
|
|
117
|
+
if (!existsSync(newDir) && existsSync(legacyDir)) {
|
|
118
|
+
try {
|
|
119
|
+
renameSync(legacyDir, newDir);
|
|
120
|
+
process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
|
|
121
|
+
`);
|
|
122
|
+
} catch {
|
|
123
|
+
return legacyDir;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return newDir;
|
|
127
|
+
}
|
|
128
|
+
function migrateLegacyConfig(raw) {
|
|
129
|
+
if ("r2" in raw) {
|
|
130
|
+
process.stderr.write(
|
|
131
|
+
"[exe-os] Warning: config.json contains deprecated 'r2' field from v1.0. R2 sync has been replaced in v1.1. The 'r2' field will be ignored.\n"
|
|
132
|
+
);
|
|
133
|
+
delete raw.r2;
|
|
134
|
+
}
|
|
135
|
+
if ("syncIntervalMs" in raw) {
|
|
136
|
+
delete raw.syncIntervalMs;
|
|
137
|
+
}
|
|
138
|
+
return raw;
|
|
139
|
+
}
|
|
140
|
+
function migrateConfig(raw) {
|
|
141
|
+
const fromVersion = typeof raw.config_version === "number" ? raw.config_version : 0;
|
|
142
|
+
let currentVersion = fromVersion;
|
|
143
|
+
let migrated = false;
|
|
144
|
+
if (currentVersion > CURRENT_CONFIG_VERSION) {
|
|
145
|
+
return { config: raw, migrated: false, fromVersion };
|
|
146
|
+
}
|
|
147
|
+
for (const migration of CONFIG_MIGRATIONS) {
|
|
148
|
+
if (currentVersion === migration.from && migration.to <= CURRENT_CONFIG_VERSION) {
|
|
149
|
+
raw = migration.migrate(raw);
|
|
150
|
+
currentVersion = migration.to;
|
|
151
|
+
migrated = true;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return { config: raw, migrated, fromVersion };
|
|
155
|
+
}
|
|
156
|
+
function normalizeScalingRoadmap(raw) {
|
|
157
|
+
const defaultAuto = DEFAULT_CONFIG.scalingRoadmap.rerankerAutoTrigger;
|
|
158
|
+
const userRoadmap = raw.scalingRoadmap ?? {};
|
|
159
|
+
const userAuto = userRoadmap.rerankerAutoTrigger ?? {};
|
|
160
|
+
if (userAuto.enabled === void 0 && raw.rerankerEnabled !== void 0) {
|
|
161
|
+
userAuto.enabled = raw.rerankerEnabled;
|
|
162
|
+
}
|
|
163
|
+
raw.scalingRoadmap = {
|
|
164
|
+
...userRoadmap,
|
|
165
|
+
rerankerAutoTrigger: { ...defaultAuto, ...userAuto }
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
function normalizeSessionLifecycle(raw) {
|
|
169
|
+
const defaultSL = DEFAULT_CONFIG.sessionLifecycle;
|
|
170
|
+
const userSL = raw.sessionLifecycle ?? {};
|
|
171
|
+
raw.sessionLifecycle = { ...defaultSL, ...userSL };
|
|
172
|
+
}
|
|
173
|
+
function normalizeAutoUpdate(raw) {
|
|
174
|
+
const defaultAU = DEFAULT_CONFIG.autoUpdate;
|
|
175
|
+
const userAU = raw.autoUpdate ?? {};
|
|
176
|
+
raw.autoUpdate = { ...defaultAU, ...userAU };
|
|
177
|
+
}
|
|
178
|
+
async function loadConfig() {
|
|
179
|
+
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
180
|
+
await mkdir(dir, { recursive: true });
|
|
181
|
+
const configPath = path.join(dir, "config.json");
|
|
182
|
+
if (!existsSync(configPath)) {
|
|
183
|
+
return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
|
|
184
|
+
}
|
|
185
|
+
const raw = await readFile(configPath, "utf-8");
|
|
186
|
+
try {
|
|
187
|
+
let parsed = JSON.parse(raw);
|
|
188
|
+
parsed = migrateLegacyConfig(parsed);
|
|
189
|
+
const { config: migratedCfg, migrated, fromVersion } = migrateConfig(parsed);
|
|
190
|
+
if (migrated) {
|
|
191
|
+
process.stderr.write(`[exe-os] Config migrated from v${fromVersion} to v${migratedCfg.config_version}
|
|
192
|
+
`);
|
|
193
|
+
try {
|
|
194
|
+
await writeFile(configPath, JSON.stringify(migratedCfg, null, 2) + "\n");
|
|
195
|
+
} catch {
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
normalizeScalingRoadmap(migratedCfg);
|
|
199
|
+
normalizeSessionLifecycle(migratedCfg);
|
|
200
|
+
normalizeAutoUpdate(migratedCfg);
|
|
201
|
+
const config = { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db"), ...migratedCfg };
|
|
202
|
+
if (config.dbPath.startsWith("~")) {
|
|
203
|
+
config.dbPath = config.dbPath.replace(/^~/, os.homedir());
|
|
204
|
+
}
|
|
205
|
+
return config;
|
|
206
|
+
} catch {
|
|
207
|
+
return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
function loadConfigSync() {
|
|
211
|
+
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
212
|
+
const configPath = path.join(dir, "config.json");
|
|
213
|
+
if (!existsSync(configPath)) {
|
|
214
|
+
return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
|
|
215
|
+
}
|
|
216
|
+
try {
|
|
217
|
+
const raw = readFileSync(configPath, "utf-8");
|
|
218
|
+
let parsed = JSON.parse(raw);
|
|
219
|
+
parsed = migrateLegacyConfig(parsed);
|
|
220
|
+
const { config: migratedCfg } = migrateConfig(parsed);
|
|
221
|
+
normalizeScalingRoadmap(migratedCfg);
|
|
222
|
+
normalizeSessionLifecycle(migratedCfg);
|
|
223
|
+
normalizeAutoUpdate(migratedCfg);
|
|
224
|
+
return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db"), ...migratedCfg };
|
|
225
|
+
} catch {
|
|
226
|
+
return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
async function saveConfig(config) {
|
|
230
|
+
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
231
|
+
await mkdir(dir, { recursive: true });
|
|
232
|
+
const configPath = path.join(dir, "config.json");
|
|
233
|
+
await writeFile(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
234
|
+
if (config.cloud?.apiKey) {
|
|
235
|
+
await chmod(configPath, 384);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
async function loadConfigFrom(configPath) {
|
|
239
|
+
const raw = await readFile(configPath, "utf-8");
|
|
240
|
+
try {
|
|
241
|
+
let parsed = JSON.parse(raw);
|
|
242
|
+
parsed = migrateLegacyConfig(parsed);
|
|
243
|
+
const { config: migratedCfg } = migrateConfig(parsed);
|
|
244
|
+
normalizeScalingRoadmap(migratedCfg);
|
|
245
|
+
normalizeSessionLifecycle(migratedCfg);
|
|
246
|
+
normalizeAutoUpdate(migratedCfg);
|
|
247
|
+
return { ...DEFAULT_CONFIG, ...migratedCfg };
|
|
248
|
+
} catch {
|
|
249
|
+
return { ...DEFAULT_CONFIG };
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
|
|
253
|
+
var init_config = __esm({
|
|
254
|
+
"src/lib/config.ts"() {
|
|
255
|
+
"use strict";
|
|
256
|
+
EXE_AI_DIR = resolveDataDir();
|
|
257
|
+
DB_PATH = path.join(EXE_AI_DIR, "memories.db");
|
|
258
|
+
MODELS_DIR = path.join(EXE_AI_DIR, "models");
|
|
259
|
+
CONFIG_PATH = path.join(EXE_AI_DIR, "config.json");
|
|
260
|
+
LEGACY_LANCE_PATH = path.join(EXE_AI_DIR, "local.lance");
|
|
261
|
+
CURRENT_CONFIG_VERSION = 1;
|
|
262
|
+
DEFAULT_CONFIG = {
|
|
263
|
+
config_version: CURRENT_CONFIG_VERSION,
|
|
264
|
+
dbPath: DB_PATH,
|
|
265
|
+
modelFile: "jina-embeddings-v5-small-q4_k_m.gguf",
|
|
266
|
+
embeddingDim: 1024,
|
|
267
|
+
batchSize: 20,
|
|
268
|
+
flushIntervalMs: 1e4,
|
|
269
|
+
autoIngestion: true,
|
|
270
|
+
autoRetrieval: true,
|
|
271
|
+
searchMode: "hybrid",
|
|
272
|
+
hookSearchMode: "hybrid",
|
|
273
|
+
fileGrepEnabled: true,
|
|
274
|
+
splashEffect: true,
|
|
275
|
+
consolidationEnabled: true,
|
|
276
|
+
consolidationIntervalMs: 6 * 60 * 60 * 1e3,
|
|
277
|
+
consolidationModel: "claude-haiku-4-5-20251001",
|
|
278
|
+
consolidationMaxCallsPerRun: 20,
|
|
279
|
+
selfQueryRouter: true,
|
|
280
|
+
selfQueryModel: "claude-haiku-4-5-20251001",
|
|
281
|
+
rerankerEnabled: true,
|
|
282
|
+
scalingRoadmap: {
|
|
283
|
+
rerankerAutoTrigger: {
|
|
284
|
+
enabled: true,
|
|
285
|
+
broadQueryMinCardinality: 5e4,
|
|
286
|
+
fetchTopK: 150,
|
|
287
|
+
returnTopK: 5
|
|
288
|
+
}
|
|
289
|
+
},
|
|
290
|
+
graphRagEnabled: true,
|
|
291
|
+
wikiEnabled: false,
|
|
292
|
+
wikiUrl: "",
|
|
293
|
+
wikiApiKey: "",
|
|
294
|
+
wikiSyncIntervalMs: 30 * 60 * 1e3,
|
|
295
|
+
wikiWorkspaceMapping: {},
|
|
296
|
+
wikiAutoUpdate: true,
|
|
297
|
+
wikiAutoUpdateThreshold: 0.5,
|
|
298
|
+
wikiAutoUpdateCreateNew: true,
|
|
299
|
+
skillLearning: true,
|
|
300
|
+
skillThreshold: 3,
|
|
301
|
+
skillModel: "claude-haiku-4-5-20251001",
|
|
302
|
+
exeHeartbeat: {
|
|
303
|
+
enabled: true,
|
|
304
|
+
intervalSeconds: 60,
|
|
305
|
+
staleInProgressThresholdHours: 2
|
|
306
|
+
},
|
|
307
|
+
sessionLifecycle: {
|
|
308
|
+
idleKillEnabled: true,
|
|
309
|
+
idleKillTicksRequired: 3,
|
|
310
|
+
idleKillIntercomAckWindowMs: 1e4,
|
|
311
|
+
maxAutoInstances: 10
|
|
312
|
+
},
|
|
313
|
+
autoUpdate: {
|
|
314
|
+
checkOnBoot: true,
|
|
315
|
+
autoInstall: false,
|
|
316
|
+
checkIntervalMs: 24 * 60 * 60 * 1e3
|
|
317
|
+
}
|
|
318
|
+
};
|
|
319
|
+
CONFIG_MIGRATIONS = [
|
|
320
|
+
{
|
|
321
|
+
from: 0,
|
|
322
|
+
to: 1,
|
|
323
|
+
migrate: (cfg) => {
|
|
324
|
+
cfg.config_version = 1;
|
|
325
|
+
return cfg;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
];
|
|
329
|
+
}
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
// src/lib/employees.ts
|
|
333
|
+
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
334
|
+
import { existsSync as existsSync2, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
|
|
335
|
+
import { execSync } from "child_process";
|
|
336
|
+
import path2 from "path";
|
|
337
|
+
import os2 from "os";
|
|
338
|
+
function normalizeRole(role) {
|
|
339
|
+
return (role ?? "").trim().toLowerCase();
|
|
340
|
+
}
|
|
341
|
+
function isCoordinatorRole(role) {
|
|
342
|
+
return normalizeRole(role) === normalizeRole(COORDINATOR_ROLE);
|
|
343
|
+
}
|
|
344
|
+
function getCoordinatorEmployee(employees) {
|
|
345
|
+
return employees.find((e) => isCoordinatorRole(e.role));
|
|
346
|
+
}
|
|
347
|
+
function getCoordinatorName(employees = loadEmployeesSync()) {
|
|
348
|
+
return getCoordinatorEmployee(employees)?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME;
|
|
349
|
+
}
|
|
350
|
+
function isCoordinatorName(agentName, employees = loadEmployeesSync()) {
|
|
351
|
+
if (!agentName) return false;
|
|
352
|
+
return agentName.toLowerCase() === getCoordinatorName(employees).toLowerCase();
|
|
353
|
+
}
|
|
354
|
+
function canCoordinate(agentName, agentRole, employees = loadEmployeesSync()) {
|
|
355
|
+
return agentName === "default" || isCoordinatorRole(agentRole) || isCoordinatorName(agentName, employees);
|
|
356
|
+
}
|
|
357
|
+
async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
358
|
+
if (!existsSync2(employeesPath)) {
|
|
359
|
+
return [];
|
|
360
|
+
}
|
|
361
|
+
const raw = await readFile2(employeesPath, "utf-8");
|
|
362
|
+
try {
|
|
363
|
+
return JSON.parse(raw);
|
|
364
|
+
} catch {
|
|
365
|
+
return [];
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
|
|
369
|
+
await mkdir2(path2.dirname(employeesPath), { recursive: true });
|
|
370
|
+
await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
|
|
371
|
+
}
|
|
372
|
+
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
373
|
+
if (!existsSync2(employeesPath)) return [];
|
|
374
|
+
try {
|
|
375
|
+
return JSON.parse(readFileSync2(employeesPath, "utf-8"));
|
|
376
|
+
} catch {
|
|
377
|
+
return [];
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
function findExeBin() {
|
|
381
|
+
try {
|
|
382
|
+
return execSync(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
|
|
383
|
+
} catch {
|
|
384
|
+
return null;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
function registerBinSymlinks(name) {
|
|
388
|
+
const created = [];
|
|
389
|
+
const skipped = [];
|
|
390
|
+
const errors = [];
|
|
391
|
+
const exeBinPath = findExeBin();
|
|
392
|
+
if (!exeBinPath) {
|
|
393
|
+
errors.push("Could not find 'exe-os' in PATH");
|
|
394
|
+
return { created, skipped, errors };
|
|
395
|
+
}
|
|
396
|
+
const binDir = path2.dirname(exeBinPath);
|
|
397
|
+
let target;
|
|
398
|
+
try {
|
|
399
|
+
target = readlinkSync(exeBinPath);
|
|
400
|
+
} catch {
|
|
401
|
+
errors.push("Could not read 'exe' symlink");
|
|
402
|
+
return { created, skipped, errors };
|
|
403
|
+
}
|
|
404
|
+
for (const suffix of ["", "-opencode"]) {
|
|
405
|
+
const linkName = `${name}${suffix}`;
|
|
406
|
+
const linkPath = path2.join(binDir, linkName);
|
|
407
|
+
if (existsSync2(linkPath)) {
|
|
408
|
+
skipped.push(linkName);
|
|
409
|
+
continue;
|
|
410
|
+
}
|
|
411
|
+
try {
|
|
412
|
+
symlinkSync(target, linkPath);
|
|
413
|
+
created.push(linkName);
|
|
414
|
+
} catch (err) {
|
|
415
|
+
errors.push(`${linkName}: ${err instanceof Error ? err.message : String(err)}`);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
return { created, skipped, errors };
|
|
419
|
+
}
|
|
420
|
+
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE;
|
|
421
|
+
var init_employees = __esm({
|
|
422
|
+
"src/lib/employees.ts"() {
|
|
423
|
+
"use strict";
|
|
424
|
+
init_config();
|
|
425
|
+
EMPLOYEES_PATH = path2.join(EXE_AI_DIR, "exe-employees.json");
|
|
426
|
+
DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
|
|
427
|
+
COORDINATOR_ROLE = "COO";
|
|
428
|
+
}
|
|
429
|
+
});
|
|
430
|
+
|
|
92
431
|
// src/lib/database.ts
|
|
93
432
|
var database_exports = {};
|
|
94
433
|
__export(database_exports, {
|
|
@@ -236,22 +575,24 @@ async function ensureSchema() {
|
|
|
236
575
|
ON behaviors(agent_id, active);
|
|
237
576
|
`);
|
|
238
577
|
try {
|
|
578
|
+
const coordinatorName = getCoordinatorName();
|
|
239
579
|
const existing = await client.execute({
|
|
240
|
-
sql: "SELECT COUNT(*) as cnt FROM behaviors WHERE agent_id =
|
|
241
|
-
args: []
|
|
580
|
+
sql: "SELECT COUNT(*) as cnt FROM behaviors WHERE agent_id = ?",
|
|
581
|
+
args: [coordinatorName]
|
|
242
582
|
});
|
|
243
583
|
if (Number(existing.rows[0]?.cnt) === 0) {
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
584
|
+
const seededAt = "2026-03-25T00:00:00Z";
|
|
585
|
+
for (const [domain, content] of [
|
|
586
|
+
["workflow", `Don't ask "keep going?" \u2014 just keep executing phases/plans autonomously`],
|
|
587
|
+
["tool-use", "Always use create_task MCP tool, never write .md files directly for task creation"],
|
|
588
|
+
["workflow", "Auto-start reviewing when idle and reviews are pending \u2014 never ask founder for permission"]
|
|
589
|
+
]) {
|
|
590
|
+
await client.execute({
|
|
591
|
+
sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, content, active, created_at, updated_at)
|
|
592
|
+
VALUES (hex(randomblob(16)), ?, NULL, ?, ?, 1, ?, ?)`,
|
|
593
|
+
args: [coordinatorName, domain, content, seededAt, seededAt]
|
|
594
|
+
});
|
|
595
|
+
}
|
|
255
596
|
}
|
|
256
597
|
} catch {
|
|
257
598
|
}
|
|
@@ -943,6 +1284,39 @@ async function ensureSchema() {
|
|
|
943
1284
|
} catch {
|
|
944
1285
|
}
|
|
945
1286
|
}
|
|
1287
|
+
try {
|
|
1288
|
+
await client.execute({
|
|
1289
|
+
sql: `ALTER TABLE memories ADD COLUMN draft INTEGER DEFAULT 0`,
|
|
1290
|
+
args: []
|
|
1291
|
+
});
|
|
1292
|
+
} catch {
|
|
1293
|
+
}
|
|
1294
|
+
try {
|
|
1295
|
+
await client.execute(
|
|
1296
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_draft ON memories(draft) WHERE draft = 1`
|
|
1297
|
+
);
|
|
1298
|
+
} catch {
|
|
1299
|
+
}
|
|
1300
|
+
try {
|
|
1301
|
+
await client.execute({
|
|
1302
|
+
sql: `ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'`,
|
|
1303
|
+
args: []
|
|
1304
|
+
});
|
|
1305
|
+
} catch {
|
|
1306
|
+
}
|
|
1307
|
+
try {
|
|
1308
|
+
await client.execute(
|
|
1309
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_type ON memories(memory_type)`
|
|
1310
|
+
);
|
|
1311
|
+
} catch {
|
|
1312
|
+
}
|
|
1313
|
+
try {
|
|
1314
|
+
await client.execute({
|
|
1315
|
+
sql: `ALTER TABLE memories ADD COLUMN trajectory TEXT`,
|
|
1316
|
+
args: []
|
|
1317
|
+
});
|
|
1318
|
+
} catch {
|
|
1319
|
+
}
|
|
946
1320
|
}
|
|
947
1321
|
async function disposeDatabase() {
|
|
948
1322
|
if (_client) {
|
|
@@ -956,6 +1330,7 @@ var init_database = __esm({
|
|
|
956
1330
|
"src/lib/database.ts"() {
|
|
957
1331
|
"use strict";
|
|
958
1332
|
init_db_retry();
|
|
1333
|
+
init_employees();
|
|
959
1334
|
_client = null;
|
|
960
1335
|
_resilientClient = null;
|
|
961
1336
|
initTurso = initDatabase;
|
|
@@ -972,15 +1347,15 @@ __export(keychain_exports, {
|
|
|
972
1347
|
importMnemonic: () => importMnemonic,
|
|
973
1348
|
setMasterKey: () => setMasterKey
|
|
974
1349
|
});
|
|
975
|
-
import { readFile, writeFile, unlink, mkdir, chmod } from "fs/promises";
|
|
976
|
-
import { existsSync } from "fs";
|
|
977
|
-
import
|
|
978
|
-
import
|
|
1350
|
+
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
1351
|
+
import { existsSync as existsSync3 } from "fs";
|
|
1352
|
+
import path3 from "path";
|
|
1353
|
+
import os3 from "os";
|
|
979
1354
|
function getKeyDir() {
|
|
980
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
1355
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path3.join(os3.homedir(), ".exe-os");
|
|
981
1356
|
}
|
|
982
1357
|
function getKeyPath() {
|
|
983
|
-
return
|
|
1358
|
+
return path3.join(getKeyDir(), "master.key");
|
|
984
1359
|
}
|
|
985
1360
|
async function tryKeytar() {
|
|
986
1361
|
try {
|
|
@@ -1001,11 +1376,11 @@ async function getMasterKey() {
|
|
|
1001
1376
|
}
|
|
1002
1377
|
}
|
|
1003
1378
|
const keyPath = getKeyPath();
|
|
1004
|
-
if (!
|
|
1379
|
+
if (!existsSync3(keyPath)) {
|
|
1005
1380
|
return null;
|
|
1006
1381
|
}
|
|
1007
1382
|
try {
|
|
1008
|
-
const content = await
|
|
1383
|
+
const content = await readFile3(keyPath, "utf-8");
|
|
1009
1384
|
return Buffer.from(content.trim(), "base64");
|
|
1010
1385
|
} catch {
|
|
1011
1386
|
return null;
|
|
@@ -1018,311 +1393,63 @@ async function setMasterKey(key) {
|
|
|
1018
1393
|
try {
|
|
1019
1394
|
await keytar.setPassword(SERVICE, ACCOUNT, b64);
|
|
1020
1395
|
return;
|
|
1021
|
-
} catch {
|
|
1022
|
-
}
|
|
1023
|
-
}
|
|
1024
|
-
const dir = getKeyDir();
|
|
1025
|
-
await mkdir(dir, { recursive: true });
|
|
1026
|
-
const keyPath = getKeyPath();
|
|
1027
|
-
await writeFile(keyPath, b64 + "\n", "utf-8");
|
|
1028
|
-
await chmod(keyPath, 384);
|
|
1029
|
-
}
|
|
1030
|
-
async function deleteMasterKey() {
|
|
1031
|
-
const keytar = await tryKeytar();
|
|
1032
|
-
if (keytar) {
|
|
1033
|
-
try {
|
|
1034
|
-
await keytar.deletePassword(SERVICE, ACCOUNT);
|
|
1035
|
-
} catch {
|
|
1036
|
-
}
|
|
1037
|
-
}
|
|
1038
|
-
const keyPath = getKeyPath();
|
|
1039
|
-
if (existsSync(keyPath)) {
|
|
1040
|
-
await unlink(keyPath);
|
|
1041
|
-
}
|
|
1042
|
-
}
|
|
1043
|
-
async function loadBip39() {
|
|
1044
|
-
try {
|
|
1045
|
-
return await import("bip39");
|
|
1046
|
-
} catch {
|
|
1047
|
-
throw new Error(
|
|
1048
|
-
"bip39 package not found. Run: npm install -g bip39\nOr reinstall exe-os: npm install -g @askexenow/exe-os"
|
|
1049
|
-
);
|
|
1050
|
-
}
|
|
1051
|
-
}
|
|
1052
|
-
async function exportMnemonic(key) {
|
|
1053
|
-
if (key.length !== 32) {
|
|
1054
|
-
throw new Error(`Key must be 32 bytes, got ${key.length}`);
|
|
1055
|
-
}
|
|
1056
|
-
const { entropyToMnemonic } = await loadBip39();
|
|
1057
|
-
return entropyToMnemonic(key.toString("hex"));
|
|
1058
|
-
}
|
|
1059
|
-
async function importMnemonic(mnemonic) {
|
|
1060
|
-
const trimmed = mnemonic.trim();
|
|
1061
|
-
const words = trimmed.split(/\s+/);
|
|
1062
|
-
if (words.length !== 24) {
|
|
1063
|
-
throw new Error(`Expected 24 words, got ${words.length}`);
|
|
1064
|
-
}
|
|
1065
|
-
const { validateMnemonic, mnemonicToEntropy } = await loadBip39();
|
|
1066
|
-
if (!validateMnemonic(trimmed)) {
|
|
1067
|
-
throw new Error("Invalid mnemonic \u2014 check for typos or missing words");
|
|
1068
|
-
}
|
|
1069
|
-
const entropy = mnemonicToEntropy(trimmed);
|
|
1070
|
-
return Buffer.from(entropy, "hex");
|
|
1071
|
-
}
|
|
1072
|
-
var SERVICE, ACCOUNT;
|
|
1073
|
-
var init_keychain = __esm({
|
|
1074
|
-
"src/lib/keychain.ts"() {
|
|
1075
|
-
"use strict";
|
|
1076
|
-
SERVICE = "exe-mem";
|
|
1077
|
-
ACCOUNT = "master-key";
|
|
1078
|
-
}
|
|
1079
|
-
});
|
|
1080
|
-
|
|
1081
|
-
// src/lib/config.ts
|
|
1082
|
-
var config_exports = {};
|
|
1083
|
-
__export(config_exports, {
|
|
1084
|
-
CONFIG_MIGRATIONS: () => CONFIG_MIGRATIONS,
|
|
1085
|
-
CONFIG_PATH: () => CONFIG_PATH,
|
|
1086
|
-
COO_AGENT_NAME: () => COO_AGENT_NAME,
|
|
1087
|
-
CURRENT_CONFIG_VERSION: () => CURRENT_CONFIG_VERSION,
|
|
1088
|
-
DB_PATH: () => DB_PATH,
|
|
1089
|
-
EXE_AI_DIR: () => EXE_AI_DIR,
|
|
1090
|
-
LEGACY_LANCE_PATH: () => LEGACY_LANCE_PATH,
|
|
1091
|
-
MODELS_DIR: () => MODELS_DIR,
|
|
1092
|
-
loadConfig: () => loadConfig,
|
|
1093
|
-
loadConfigFrom: () => loadConfigFrom,
|
|
1094
|
-
loadConfigSync: () => loadConfigSync,
|
|
1095
|
-
migrateConfig: () => migrateConfig,
|
|
1096
|
-
saveConfig: () => saveConfig
|
|
1097
|
-
});
|
|
1098
|
-
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, chmod as chmod2 } from "fs/promises";
|
|
1099
|
-
import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
|
|
1100
|
-
import path2 from "path";
|
|
1101
|
-
import os2 from "os";
|
|
1102
|
-
function resolveDataDir() {
|
|
1103
|
-
if (process.env.EXE_OS_DIR) return process.env.EXE_OS_DIR;
|
|
1104
|
-
if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
|
|
1105
|
-
const newDir = path2.join(os2.homedir(), ".exe-os");
|
|
1106
|
-
const legacyDir = path2.join(os2.homedir(), ".exe-mem");
|
|
1107
|
-
if (!existsSync2(newDir) && existsSync2(legacyDir)) {
|
|
1108
|
-
try {
|
|
1109
|
-
renameSync(legacyDir, newDir);
|
|
1110
|
-
process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
|
|
1111
|
-
`);
|
|
1112
|
-
} catch {
|
|
1113
|
-
return legacyDir;
|
|
1114
|
-
}
|
|
1115
|
-
}
|
|
1116
|
-
return newDir;
|
|
1117
|
-
}
|
|
1118
|
-
function migrateLegacyConfig(raw) {
|
|
1119
|
-
if ("r2" in raw) {
|
|
1120
|
-
process.stderr.write(
|
|
1121
|
-
"[exe-os] Warning: config.json contains deprecated 'r2' field from v1.0. R2 sync has been replaced in v1.1. The 'r2' field will be ignored.\n"
|
|
1122
|
-
);
|
|
1123
|
-
delete raw.r2;
|
|
1124
|
-
}
|
|
1125
|
-
if ("syncIntervalMs" in raw) {
|
|
1126
|
-
delete raw.syncIntervalMs;
|
|
1127
|
-
}
|
|
1128
|
-
return raw;
|
|
1129
|
-
}
|
|
1130
|
-
function migrateConfig(raw) {
|
|
1131
|
-
const fromVersion = typeof raw.config_version === "number" ? raw.config_version : 0;
|
|
1132
|
-
let currentVersion = fromVersion;
|
|
1133
|
-
let migrated = false;
|
|
1134
|
-
if (currentVersion > CURRENT_CONFIG_VERSION) {
|
|
1135
|
-
return { config: raw, migrated: false, fromVersion };
|
|
1136
|
-
}
|
|
1137
|
-
for (const migration of CONFIG_MIGRATIONS) {
|
|
1138
|
-
if (currentVersion === migration.from && migration.to <= CURRENT_CONFIG_VERSION) {
|
|
1139
|
-
raw = migration.migrate(raw);
|
|
1140
|
-
currentVersion = migration.to;
|
|
1141
|
-
migrated = true;
|
|
1142
|
-
}
|
|
1143
|
-
}
|
|
1144
|
-
return { config: raw, migrated, fromVersion };
|
|
1145
|
-
}
|
|
1146
|
-
function normalizeScalingRoadmap(raw) {
|
|
1147
|
-
const defaultAuto = DEFAULT_CONFIG.scalingRoadmap.rerankerAutoTrigger;
|
|
1148
|
-
const userRoadmap = raw.scalingRoadmap ?? {};
|
|
1149
|
-
const userAuto = userRoadmap.rerankerAutoTrigger ?? {};
|
|
1150
|
-
if (userAuto.enabled === void 0 && raw.rerankerEnabled !== void 0) {
|
|
1151
|
-
userAuto.enabled = raw.rerankerEnabled;
|
|
1152
|
-
}
|
|
1153
|
-
raw.scalingRoadmap = {
|
|
1154
|
-
...userRoadmap,
|
|
1155
|
-
rerankerAutoTrigger: { ...defaultAuto, ...userAuto }
|
|
1156
|
-
};
|
|
1157
|
-
}
|
|
1158
|
-
function normalizeSessionLifecycle(raw) {
|
|
1159
|
-
const defaultSL = DEFAULT_CONFIG.sessionLifecycle;
|
|
1160
|
-
const userSL = raw.sessionLifecycle ?? {};
|
|
1161
|
-
raw.sessionLifecycle = { ...defaultSL, ...userSL };
|
|
1162
|
-
}
|
|
1163
|
-
function normalizeAutoUpdate(raw) {
|
|
1164
|
-
const defaultAU = DEFAULT_CONFIG.autoUpdate;
|
|
1165
|
-
const userAU = raw.autoUpdate ?? {};
|
|
1166
|
-
raw.autoUpdate = { ...defaultAU, ...userAU };
|
|
1167
|
-
}
|
|
1168
|
-
async function loadConfig() {
|
|
1169
|
-
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
1170
|
-
await mkdir2(dir, { recursive: true });
|
|
1171
|
-
const configPath = path2.join(dir, "config.json");
|
|
1172
|
-
if (!existsSync2(configPath)) {
|
|
1173
|
-
return { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db") };
|
|
1174
|
-
}
|
|
1175
|
-
const raw = await readFile2(configPath, "utf-8");
|
|
1176
|
-
try {
|
|
1177
|
-
let parsed = JSON.parse(raw);
|
|
1178
|
-
parsed = migrateLegacyConfig(parsed);
|
|
1179
|
-
const { config: migratedCfg, migrated, fromVersion } = migrateConfig(parsed);
|
|
1180
|
-
if (migrated) {
|
|
1181
|
-
process.stderr.write(`[exe-os] Config migrated from v${fromVersion} to v${migratedCfg.config_version}
|
|
1182
|
-
`);
|
|
1183
|
-
try {
|
|
1184
|
-
await writeFile2(configPath, JSON.stringify(migratedCfg, null, 2) + "\n");
|
|
1185
|
-
} catch {
|
|
1186
|
-
}
|
|
1187
|
-
}
|
|
1188
|
-
normalizeScalingRoadmap(migratedCfg);
|
|
1189
|
-
normalizeSessionLifecycle(migratedCfg);
|
|
1190
|
-
normalizeAutoUpdate(migratedCfg);
|
|
1191
|
-
const config = { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db"), ...migratedCfg };
|
|
1192
|
-
if (config.dbPath.startsWith("~")) {
|
|
1193
|
-
config.dbPath = config.dbPath.replace(/^~/, os2.homedir());
|
|
1396
|
+
} catch {
|
|
1194
1397
|
}
|
|
1195
|
-
return config;
|
|
1196
|
-
} catch {
|
|
1197
|
-
return { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db") };
|
|
1198
1398
|
}
|
|
1399
|
+
const dir = getKeyDir();
|
|
1400
|
+
await mkdir3(dir, { recursive: true });
|
|
1401
|
+
const keyPath = getKeyPath();
|
|
1402
|
+
await writeFile3(keyPath, b64 + "\n", "utf-8");
|
|
1403
|
+
await chmod2(keyPath, 384);
|
|
1199
1404
|
}
|
|
1200
|
-
function
|
|
1201
|
-
const
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1405
|
+
async function deleteMasterKey() {
|
|
1406
|
+
const keytar = await tryKeytar();
|
|
1407
|
+
if (keytar) {
|
|
1408
|
+
try {
|
|
1409
|
+
await keytar.deletePassword(SERVICE, ACCOUNT);
|
|
1410
|
+
} catch {
|
|
1411
|
+
}
|
|
1412
|
+
}
|
|
1413
|
+
const keyPath = getKeyPath();
|
|
1414
|
+
if (existsSync3(keyPath)) {
|
|
1415
|
+
await unlink(keyPath);
|
|
1205
1416
|
}
|
|
1417
|
+
}
|
|
1418
|
+
async function loadBip39() {
|
|
1206
1419
|
try {
|
|
1207
|
-
|
|
1208
|
-
let parsed = JSON.parse(raw);
|
|
1209
|
-
parsed = migrateLegacyConfig(parsed);
|
|
1210
|
-
const { config: migratedCfg } = migrateConfig(parsed);
|
|
1211
|
-
normalizeScalingRoadmap(migratedCfg);
|
|
1212
|
-
normalizeSessionLifecycle(migratedCfg);
|
|
1213
|
-
normalizeAutoUpdate(migratedCfg);
|
|
1214
|
-
return { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db"), ...migratedCfg };
|
|
1420
|
+
return await import("bip39");
|
|
1215
1421
|
} catch {
|
|
1216
|
-
|
|
1422
|
+
throw new Error(
|
|
1423
|
+
"bip39 package not found. Run: npm install -g bip39\nOr reinstall exe-os: npm install -g @askexenow/exe-os"
|
|
1424
|
+
);
|
|
1217
1425
|
}
|
|
1218
1426
|
}
|
|
1219
|
-
async function
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
const configPath = path2.join(dir, "config.json");
|
|
1223
|
-
await writeFile2(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
1224
|
-
if (config.cloud?.apiKey) {
|
|
1225
|
-
await chmod2(configPath, 384);
|
|
1427
|
+
async function exportMnemonic(key) {
|
|
1428
|
+
if (key.length !== 32) {
|
|
1429
|
+
throw new Error(`Key must be 32 bytes, got ${key.length}`);
|
|
1226
1430
|
}
|
|
1431
|
+
const { entropyToMnemonic } = await loadBip39();
|
|
1432
|
+
return entropyToMnemonic(key.toString("hex"));
|
|
1227
1433
|
}
|
|
1228
|
-
async function
|
|
1229
|
-
const
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
return { ...DEFAULT_CONFIG, ...migratedCfg };
|
|
1238
|
-
} catch {
|
|
1239
|
-
return { ...DEFAULT_CONFIG };
|
|
1434
|
+
async function importMnemonic(mnemonic) {
|
|
1435
|
+
const trimmed = mnemonic.trim();
|
|
1436
|
+
const words = trimmed.split(/\s+/);
|
|
1437
|
+
if (words.length !== 24) {
|
|
1438
|
+
throw new Error(`Expected 24 words, got ${words.length}`);
|
|
1439
|
+
}
|
|
1440
|
+
const { validateMnemonic, mnemonicToEntropy } = await loadBip39();
|
|
1441
|
+
if (!validateMnemonic(trimmed)) {
|
|
1442
|
+
throw new Error("Invalid mnemonic \u2014 check for typos or missing words");
|
|
1240
1443
|
}
|
|
1444
|
+
const entropy = mnemonicToEntropy(trimmed);
|
|
1445
|
+
return Buffer.from(entropy, "hex");
|
|
1241
1446
|
}
|
|
1242
|
-
var
|
|
1243
|
-
var
|
|
1244
|
-
"src/lib/
|
|
1447
|
+
var SERVICE, ACCOUNT;
|
|
1448
|
+
var init_keychain = __esm({
|
|
1449
|
+
"src/lib/keychain.ts"() {
|
|
1245
1450
|
"use strict";
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
MODELS_DIR = path2.join(EXE_AI_DIR, "models");
|
|
1249
|
-
CONFIG_PATH = path2.join(EXE_AI_DIR, "config.json");
|
|
1250
|
-
COO_AGENT_NAME = "exe";
|
|
1251
|
-
LEGACY_LANCE_PATH = path2.join(EXE_AI_DIR, "local.lance");
|
|
1252
|
-
CURRENT_CONFIG_VERSION = 1;
|
|
1253
|
-
DEFAULT_CONFIG = {
|
|
1254
|
-
config_version: CURRENT_CONFIG_VERSION,
|
|
1255
|
-
dbPath: DB_PATH,
|
|
1256
|
-
modelFile: "jina-embeddings-v5-small-q4_k_m.gguf",
|
|
1257
|
-
embeddingDim: 1024,
|
|
1258
|
-
batchSize: 20,
|
|
1259
|
-
flushIntervalMs: 1e4,
|
|
1260
|
-
autoIngestion: true,
|
|
1261
|
-
autoRetrieval: true,
|
|
1262
|
-
searchMode: "hybrid",
|
|
1263
|
-
hookSearchMode: "hybrid",
|
|
1264
|
-
fileGrepEnabled: true,
|
|
1265
|
-
splashEffect: true,
|
|
1266
|
-
consolidationEnabled: true,
|
|
1267
|
-
consolidationIntervalMs: 6 * 60 * 60 * 1e3,
|
|
1268
|
-
consolidationModel: "claude-haiku-4-5-20251001",
|
|
1269
|
-
consolidationMaxCallsPerRun: 20,
|
|
1270
|
-
selfQueryRouter: true,
|
|
1271
|
-
selfQueryModel: "claude-haiku-4-5-20251001",
|
|
1272
|
-
rerankerEnabled: true,
|
|
1273
|
-
scalingRoadmap: {
|
|
1274
|
-
rerankerAutoTrigger: {
|
|
1275
|
-
enabled: true,
|
|
1276
|
-
broadQueryMinCardinality: 5e4,
|
|
1277
|
-
fetchTopK: 150,
|
|
1278
|
-
returnTopK: 5
|
|
1279
|
-
}
|
|
1280
|
-
},
|
|
1281
|
-
graphRagEnabled: true,
|
|
1282
|
-
wikiEnabled: false,
|
|
1283
|
-
wikiUrl: "",
|
|
1284
|
-
wikiApiKey: "",
|
|
1285
|
-
wikiSyncIntervalMs: 30 * 60 * 1e3,
|
|
1286
|
-
wikiWorkspaceMapping: {
|
|
1287
|
-
exe: "Executive",
|
|
1288
|
-
yoshi: "Engineering",
|
|
1289
|
-
mari: "Marketing",
|
|
1290
|
-
tom: "Engineering",
|
|
1291
|
-
sasha: "Production"
|
|
1292
|
-
},
|
|
1293
|
-
wikiAutoUpdate: true,
|
|
1294
|
-
wikiAutoUpdateThreshold: 0.5,
|
|
1295
|
-
wikiAutoUpdateCreateNew: true,
|
|
1296
|
-
skillLearning: true,
|
|
1297
|
-
skillThreshold: 3,
|
|
1298
|
-
skillModel: "claude-haiku-4-5-20251001",
|
|
1299
|
-
exeHeartbeat: {
|
|
1300
|
-
enabled: true,
|
|
1301
|
-
intervalSeconds: 60,
|
|
1302
|
-
staleInProgressThresholdHours: 2
|
|
1303
|
-
},
|
|
1304
|
-
sessionLifecycle: {
|
|
1305
|
-
idleKillEnabled: true,
|
|
1306
|
-
idleKillTicksRequired: 3,
|
|
1307
|
-
idleKillIntercomAckWindowMs: 1e4,
|
|
1308
|
-
maxAutoInstances: 10
|
|
1309
|
-
},
|
|
1310
|
-
autoUpdate: {
|
|
1311
|
-
checkOnBoot: true,
|
|
1312
|
-
autoInstall: false,
|
|
1313
|
-
checkIntervalMs: 24 * 60 * 60 * 1e3
|
|
1314
|
-
}
|
|
1315
|
-
};
|
|
1316
|
-
CONFIG_MIGRATIONS = [
|
|
1317
|
-
{
|
|
1318
|
-
from: 0,
|
|
1319
|
-
to: 1,
|
|
1320
|
-
migrate: (cfg) => {
|
|
1321
|
-
cfg.config_version = 1;
|
|
1322
|
-
return cfg;
|
|
1323
|
-
}
|
|
1324
|
-
}
|
|
1325
|
-
];
|
|
1451
|
+
SERVICE = "exe-mem";
|
|
1452
|
+
ACCOUNT = "master-key";
|
|
1326
1453
|
}
|
|
1327
1454
|
});
|
|
1328
1455
|
|
|
@@ -1394,12 +1521,12 @@ __export(shard_manager_exports, {
|
|
|
1394
1521
|
listShards: () => listShards,
|
|
1395
1522
|
shardExists: () => shardExists
|
|
1396
1523
|
});
|
|
1397
|
-
import
|
|
1398
|
-
import { existsSync as
|
|
1524
|
+
import path4 from "path";
|
|
1525
|
+
import { existsSync as existsSync4, mkdirSync, readdirSync } from "fs";
|
|
1399
1526
|
import { createClient as createClient2 } from "@libsql/client";
|
|
1400
1527
|
function initShardManager(encryptionKey) {
|
|
1401
1528
|
_encryptionKey = encryptionKey;
|
|
1402
|
-
if (!
|
|
1529
|
+
if (!existsSync4(SHARDS_DIR)) {
|
|
1403
1530
|
mkdirSync(SHARDS_DIR, { recursive: true });
|
|
1404
1531
|
}
|
|
1405
1532
|
_shardingEnabled = true;
|
|
@@ -1420,7 +1547,7 @@ function getShardClient(projectName) {
|
|
|
1420
1547
|
}
|
|
1421
1548
|
const cached = _shards.get(safeName);
|
|
1422
1549
|
if (cached) return cached;
|
|
1423
|
-
const dbPath =
|
|
1550
|
+
const dbPath = path4.join(SHARDS_DIR, `${safeName}.db`);
|
|
1424
1551
|
const client = createClient2({
|
|
1425
1552
|
url: `file:${dbPath}`,
|
|
1426
1553
|
encryptionKey: _encryptionKey
|
|
@@ -1430,10 +1557,10 @@ function getShardClient(projectName) {
|
|
|
1430
1557
|
}
|
|
1431
1558
|
function shardExists(projectName) {
|
|
1432
1559
|
const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
1433
|
-
return
|
|
1560
|
+
return existsSync4(path4.join(SHARDS_DIR, `${safeName}.db`));
|
|
1434
1561
|
}
|
|
1435
1562
|
function listShards() {
|
|
1436
|
-
if (!
|
|
1563
|
+
if (!existsSync4(SHARDS_DIR)) return [];
|
|
1437
1564
|
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
1438
1565
|
}
|
|
1439
1566
|
async function ensureShardSchema(client) {
|
|
@@ -1503,7 +1630,11 @@ async function ensureShardSchema(client) {
|
|
|
1503
1630
|
"ALTER TABLE memories ADD COLUMN source_path TEXT",
|
|
1504
1631
|
"ALTER TABLE memories ADD COLUMN source_type TEXT DEFAULT 'text'",
|
|
1505
1632
|
"ALTER TABLE memories ADD COLUMN tier INTEGER DEFAULT 3",
|
|
1506
|
-
"ALTER TABLE memories ADD COLUMN supersedes_id TEXT"
|
|
1633
|
+
"ALTER TABLE memories ADD COLUMN supersedes_id TEXT",
|
|
1634
|
+
// MS-11: draft staging, MS-6a: memory_type, MS-7: trajectory
|
|
1635
|
+
"ALTER TABLE memories ADD COLUMN draft INTEGER DEFAULT 0",
|
|
1636
|
+
"ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'",
|
|
1637
|
+
"ALTER TABLE memories ADD COLUMN trajectory TEXT"
|
|
1507
1638
|
]) {
|
|
1508
1639
|
try {
|
|
1509
1640
|
await client.execute(col);
|
|
@@ -1615,7 +1746,7 @@ var init_shard_manager = __esm({
|
|
|
1615
1746
|
"src/lib/shard-manager.ts"() {
|
|
1616
1747
|
"use strict";
|
|
1617
1748
|
init_config();
|
|
1618
|
-
SHARDS_DIR =
|
|
1749
|
+
SHARDS_DIR = path4.join(EXE_AI_DIR, "shards");
|
|
1619
1750
|
_shards = /* @__PURE__ */ new Map();
|
|
1620
1751
|
_encryptionKey = null;
|
|
1621
1752
|
_shardingEnabled = false;
|
|
@@ -1633,26 +1764,26 @@ var init_platform_procedures = __esm({
|
|
|
1633
1764
|
title: "What is exe-os \u2014 the operating model every agent must understand",
|
|
1634
1765
|
domain: "architecture",
|
|
1635
1766
|
priority: "p0",
|
|
1636
|
-
content: "Exe OS is an AI employee operating system. A founder runs 5-10 AI agents as a real org: COO
|
|
1767
|
+
content: "Exe OS is an AI employee operating system. A founder runs 5-10 AI agents as a real org: COO, CTO, CMO, engineers, and content production specialists. Each agent has identity, expertise, and experience layers \u2014 persistent memory that makes them better over time. All data is local-first, E2EE, owned by the user. The MCP server is the ONLY data interface \u2014 never access the DB directly."
|
|
1637
1768
|
},
|
|
1638
1769
|
{
|
|
1639
1770
|
title: "Mode 1 \u2014 how exe-os runs inside Claude Code",
|
|
1640
1771
|
domain: "architecture",
|
|
1641
1772
|
priority: "p0",
|
|
1642
|
-
content: "Mode 1: exe-os runs AS hooks + MCP + skills inside Claude Code. The founder opens CC
|
|
1773
|
+
content: "Mode 1: exe-os runs AS hooks + MCP + skills inside Claude Code. The founder opens CC and boots the COO. The COO manages employees in tmux sessions. Each coordinator session is a separate CC window/project. Employees run in their own tmux panes via create_task auto-spawn. The founder talks to the COO; the COO orchestrates the team. CC is the shell, exe-os is the brain."
|
|
1643
1774
|
},
|
|
1644
1775
|
{
|
|
1645
|
-
title: "Sessions explained \u2014
|
|
1776
|
+
title: "Sessions explained \u2014 coordinator session names and projects",
|
|
1646
1777
|
domain: "architecture",
|
|
1647
1778
|
priority: "p0",
|
|
1648
|
-
content: "Each
|
|
1779
|
+
content: "Each coordinator session is an isolated project session. One might be exe-os development, another might be exe-wiki. Each session spawns its own employees using {employee}-{coordinatorSession}. Sessions share the same memory DB but tasks are scoped to the session that created them. A founder can run multiple projects simultaneously. Sessions never interfere with each other."
|
|
1649
1780
|
},
|
|
1650
1781
|
// --- Hierarchy and dispatch ---
|
|
1651
1782
|
{
|
|
1652
1783
|
title: "Chain of command \u2014 who talks to whom",
|
|
1653
1784
|
domain: "workflow",
|
|
1654
1785
|
priority: "p0",
|
|
1655
|
-
content: "Founder
|
|
1786
|
+
content: "Founder -> COO -> CTO/CMO. CTO -> engineers. CMO -> content production. Never skip levels: the COO does not bypass managers for specialist work. Specialists report to their manager. If you need cross-team info, use ask_team_memory \u2014 don't read other agents' task folders. Each level owns dispatch downward and review upward."
|
|
1656
1787
|
},
|
|
1657
1788
|
{
|
|
1658
1789
|
title: "Single dispatch path \u2014 create_task only",
|
|
@@ -1662,30 +1793,30 @@ var init_platform_procedures = __esm({
|
|
|
1662
1793
|
},
|
|
1663
1794
|
// --- Session isolation ---
|
|
1664
1795
|
{
|
|
1665
|
-
title: "Session scoping \u2014 stay in your
|
|
1796
|
+
title: "Session scoping \u2014 stay in your coordinator boundary",
|
|
1666
1797
|
domain: "security",
|
|
1667
1798
|
priority: "p0",
|
|
1668
|
-
content: "Session scoping is mandatory. Managers dispatch to workers within their own
|
|
1799
|
+
content: "Session scoping is mandatory. Managers dispatch to workers within their own coordinator session ONLY. Employee sessions use {employee}-{coordinatorSession}. Cross-session dispatch is blocked by the system. Verify session names before dispatch. Tasks are scoped to the creating coordinator session."
|
|
1669
1800
|
},
|
|
1670
1801
|
{
|
|
1671
1802
|
title: "Session isolation \u2014 never touch another session's work",
|
|
1672
1803
|
domain: "workflow",
|
|
1673
1804
|
priority: "p0",
|
|
1674
|
-
content:
|
|
1805
|
+
content: "Sessions are isolated. A coordinator session owns ONLY tasks it dispatched. (1) Never close/update/cancel tasks from another coordinator session. (2) Never review work from a different session \u2014 report that it belongs to another session and skip. (3) Ignore other sessions' items in list_tasks results. (4) Employees inherit session: employee sessions work ONLY on their parent coordinator session's tasks. Cross-session work is a system violation."
|
|
1675
1806
|
},
|
|
1676
1807
|
// --- Engineering: session scoping in code ---
|
|
1677
1808
|
{
|
|
1678
1809
|
title: "Three-dimensional scoping \u2014 session, project, role \u2014 enforced in every query",
|
|
1679
1810
|
domain: "architecture",
|
|
1680
1811
|
priority: "p0",
|
|
1681
|
-
content: "Every DB query, notification, review count, and task operation MUST be scoped on 3 dimensions: (1) Session \u2014 filter by session_scope matching current
|
|
1812
|
+
content: "Every DB query, notification, review count, and task operation MUST be scoped on 3 dimensions: (1) Session \u2014 filter by session_scope matching the current coordinator session. (2) Project \u2014 filter by project_name. (3) Role \u2014 agents only see data at their hierarchy level. When writing ANY function that touches tasks, reviews, messages, or notifications: always accept a sessionScope parameter and pass it to the SQL WHERE clause. Unscoped queries are bugs. Test by running 2+ coordinator sessions simultaneously."
|
|
1682
1813
|
},
|
|
1683
1814
|
// --- Hard constraints ---
|
|
1684
1815
|
{
|
|
1685
1816
|
title: "What you CANNOT do in exe-os \u2014 hard constraints",
|
|
1686
1817
|
domain: "security",
|
|
1687
1818
|
priority: "p0",
|
|
1688
|
-
content: "NEVER: (1) Access the database directly \u2014 it's SQLCipher encrypted, always fails. Use MCP tools only. (2) Manually spawn tmux sessions \u2014 create_task handles it. (3) Run git checkout main \u2014 agents work in worktrees. (4) Modify another agent's in-progress task. (5) Push to remote \u2014
|
|
1819
|
+
content: "NEVER: (1) Access the database directly \u2014 it's SQLCipher encrypted, always fails. Use MCP tools only. (2) Manually spawn tmux sessions \u2014 create_task handles it. (3) Run git checkout main \u2014 agents work in worktrees. (4) Modify another agent's in-progress task. (5) Push to remote \u2014 the COO reviews and pushes. (6) Skip update_task(done) \u2014 it's the ONLY way your work gets reviewed. (7) Run git init."
|
|
1689
1820
|
},
|
|
1690
1821
|
// --- Operations ---
|
|
1691
1822
|
{
|
|
@@ -1805,13 +1936,13 @@ ${p.content}`).join("\n\n");
|
|
|
1805
1936
|
|
|
1806
1937
|
// src/lib/notifications.ts
|
|
1807
1938
|
import crypto from "crypto";
|
|
1808
|
-
import
|
|
1809
|
-
import
|
|
1939
|
+
import path5 from "path";
|
|
1940
|
+
import os4 from "os";
|
|
1810
1941
|
import {
|
|
1811
|
-
readFileSync as
|
|
1942
|
+
readFileSync as readFileSync3,
|
|
1812
1943
|
readdirSync as readdirSync2,
|
|
1813
|
-
unlinkSync,
|
|
1814
|
-
existsSync as
|
|
1944
|
+
unlinkSync as unlinkSync2,
|
|
1945
|
+
existsSync as existsSync5,
|
|
1815
1946
|
rmdirSync
|
|
1816
1947
|
} from "fs";
|
|
1817
1948
|
async function writeNotification(notification) {
|
|
@@ -1846,24 +1977,24 @@ var init_notifications = __esm({
|
|
|
1846
1977
|
});
|
|
1847
1978
|
|
|
1848
1979
|
// src/lib/session-registry.ts
|
|
1849
|
-
import
|
|
1850
|
-
import
|
|
1980
|
+
import path6 from "path";
|
|
1981
|
+
import os5 from "os";
|
|
1851
1982
|
var REGISTRY_PATH;
|
|
1852
1983
|
var init_session_registry = __esm({
|
|
1853
1984
|
"src/lib/session-registry.ts"() {
|
|
1854
1985
|
"use strict";
|
|
1855
|
-
REGISTRY_PATH =
|
|
1986
|
+
REGISTRY_PATH = path6.join(os5.homedir(), ".exe-os", "session-registry.json");
|
|
1856
1987
|
}
|
|
1857
1988
|
});
|
|
1858
1989
|
|
|
1859
1990
|
// src/lib/session-key.ts
|
|
1860
|
-
import { execSync } from "child_process";
|
|
1991
|
+
import { execSync as execSync2 } from "child_process";
|
|
1861
1992
|
function getSessionKey() {
|
|
1862
1993
|
if (_cached) return _cached;
|
|
1863
1994
|
let pid = process.ppid;
|
|
1864
1995
|
for (let i = 0; i < 10; i++) {
|
|
1865
1996
|
try {
|
|
1866
|
-
const info =
|
|
1997
|
+
const info = execSync2(`ps -p ${pid} -o ppid=,comm=`, {
|
|
1867
1998
|
encoding: "utf8",
|
|
1868
1999
|
timeout: 2e3
|
|
1869
2000
|
}).trim();
|
|
@@ -1999,7 +2130,7 @@ var init_transport = __esm({
|
|
|
1999
2130
|
});
|
|
2000
2131
|
|
|
2001
2132
|
// src/lib/cc-agent-support.ts
|
|
2002
|
-
import { execSync as
|
|
2133
|
+
import { execSync as execSync3 } from "child_process";
|
|
2003
2134
|
var init_cc_agent_support = __esm({
|
|
2004
2135
|
"src/lib/cc-agent-support.ts"() {
|
|
2005
2136
|
"use strict";
|
|
@@ -2028,86 +2159,16 @@ var init_provider_table = __esm({
|
|
|
2028
2159
|
});
|
|
2029
2160
|
|
|
2030
2161
|
// src/lib/intercom-queue.ts
|
|
2031
|
-
import { readFileSync as
|
|
2032
|
-
import
|
|
2033
|
-
import
|
|
2162
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, renameSync as renameSync3, existsSync as existsSync6, mkdirSync as mkdirSync2 } from "fs";
|
|
2163
|
+
import path7 from "path";
|
|
2164
|
+
import os6 from "os";
|
|
2034
2165
|
var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
|
|
2035
2166
|
var init_intercom_queue = __esm({
|
|
2036
2167
|
"src/lib/intercom-queue.ts"() {
|
|
2037
2168
|
"use strict";
|
|
2038
|
-
QUEUE_PATH =
|
|
2169
|
+
QUEUE_PATH = path7.join(os6.homedir(), ".exe-os", "intercom-queue.json");
|
|
2039
2170
|
TTL_MS = 60 * 60 * 1e3;
|
|
2040
|
-
INTERCOM_LOG =
|
|
2041
|
-
}
|
|
2042
|
-
});
|
|
2043
|
-
|
|
2044
|
-
// src/lib/employees.ts
|
|
2045
|
-
import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
|
|
2046
|
-
import { existsSync as existsSync6, symlinkSync, readlinkSync, readFileSync as readFileSync4, renameSync as renameSync3, unlinkSync as unlinkSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
2047
|
-
import { execSync as execSync3 } from "child_process";
|
|
2048
|
-
import path7 from "path";
|
|
2049
|
-
import os6 from "os";
|
|
2050
|
-
async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
2051
|
-
if (!existsSync6(employeesPath)) {
|
|
2052
|
-
return [];
|
|
2053
|
-
}
|
|
2054
|
-
const raw = await readFile3(employeesPath, "utf-8");
|
|
2055
|
-
try {
|
|
2056
|
-
return JSON.parse(raw);
|
|
2057
|
-
} catch {
|
|
2058
|
-
return [];
|
|
2059
|
-
}
|
|
2060
|
-
}
|
|
2061
|
-
async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
|
|
2062
|
-
await mkdir3(path7.dirname(employeesPath), { recursive: true });
|
|
2063
|
-
await writeFile3(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
|
|
2064
|
-
}
|
|
2065
|
-
function findExeBin() {
|
|
2066
|
-
try {
|
|
2067
|
-
return execSync3(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
|
|
2068
|
-
} catch {
|
|
2069
|
-
return null;
|
|
2070
|
-
}
|
|
2071
|
-
}
|
|
2072
|
-
function registerBinSymlinks(name) {
|
|
2073
|
-
const created = [];
|
|
2074
|
-
const skipped = [];
|
|
2075
|
-
const errors = [];
|
|
2076
|
-
const exeBinPath = findExeBin();
|
|
2077
|
-
if (!exeBinPath) {
|
|
2078
|
-
errors.push("Could not find 'exe-os' in PATH");
|
|
2079
|
-
return { created, skipped, errors };
|
|
2080
|
-
}
|
|
2081
|
-
const binDir = path7.dirname(exeBinPath);
|
|
2082
|
-
let target;
|
|
2083
|
-
try {
|
|
2084
|
-
target = readlinkSync(exeBinPath);
|
|
2085
|
-
} catch {
|
|
2086
|
-
errors.push("Could not read 'exe' symlink");
|
|
2087
|
-
return { created, skipped, errors };
|
|
2088
|
-
}
|
|
2089
|
-
for (const suffix of ["", "-opencode"]) {
|
|
2090
|
-
const linkName = `${name}${suffix}`;
|
|
2091
|
-
const linkPath = path7.join(binDir, linkName);
|
|
2092
|
-
if (existsSync6(linkPath)) {
|
|
2093
|
-
skipped.push(linkName);
|
|
2094
|
-
continue;
|
|
2095
|
-
}
|
|
2096
|
-
try {
|
|
2097
|
-
symlinkSync(target, linkPath);
|
|
2098
|
-
created.push(linkName);
|
|
2099
|
-
} catch (err) {
|
|
2100
|
-
errors.push(`${linkName}: ${err instanceof Error ? err.message : String(err)}`);
|
|
2101
|
-
}
|
|
2102
|
-
}
|
|
2103
|
-
return { created, skipped, errors };
|
|
2104
|
-
}
|
|
2105
|
-
var EMPLOYEES_PATH;
|
|
2106
|
-
var init_employees = __esm({
|
|
2107
|
-
"src/lib/employees.ts"() {
|
|
2108
|
-
"use strict";
|
|
2109
|
-
init_config();
|
|
2110
|
-
EMPLOYEES_PATH = path7.join(EXE_AI_DIR, "exe-employees.json");
|
|
2171
|
+
INTERCOM_LOG = path7.join(os6.homedir(), ".exe-os", "intercom.log");
|
|
2111
2172
|
}
|
|
2112
2173
|
});
|
|
2113
2174
|
|
|
@@ -2632,8 +2693,10 @@ function getMySession() {
|
|
|
2632
2693
|
return getTransport().getMySession();
|
|
2633
2694
|
}
|
|
2634
2695
|
function extractRootExe(name) {
|
|
2635
|
-
|
|
2636
|
-
|
|
2696
|
+
if (!name) return null;
|
|
2697
|
+
if (!name.includes("-")) return name;
|
|
2698
|
+
const parts = name.split("-").filter(Boolean);
|
|
2699
|
+
return parts.length > 0 ? parts[parts.length - 1] : null;
|
|
2637
2700
|
}
|
|
2638
2701
|
function getParentExe(sessionKey) {
|
|
2639
2702
|
try {
|
|
@@ -2668,6 +2731,7 @@ var init_tmux_routing = __esm({
|
|
|
2668
2731
|
init_provider_table();
|
|
2669
2732
|
init_intercom_queue();
|
|
2670
2733
|
init_plan_limits();
|
|
2734
|
+
init_employees();
|
|
2671
2735
|
SPAWN_LOCK_DIR = path10.join(os7.homedir(), ".exe-os", "spawn-locks");
|
|
2672
2736
|
SESSION_CACHE = path10.join(os7.homedir(), ".exe-os", "session-cache");
|
|
2673
2737
|
INTERCOM_LOG2 = path10.join(os7.homedir(), ".exe-os", "intercom.log");
|
|
@@ -2788,6 +2852,10 @@ function spawnDaemon() {
|
|
|
2788
2852
|
stdio: ["ignore", "ignore", stderrFd],
|
|
2789
2853
|
env: {
|
|
2790
2854
|
...process.env,
|
|
2855
|
+
TMUX: void 0,
|
|
2856
|
+
// Daemon is global — must not inherit session scope
|
|
2857
|
+
TMUX_PANE: void 0,
|
|
2858
|
+
// Prevents resolveExeSession() from scoping to one session
|
|
2791
2859
|
EXE_DAEMON_SOCK: SOCKET_PATH,
|
|
2792
2860
|
EXE_DAEMON_PID: PID_PATH
|
|
2793
2861
|
}
|
|
@@ -3495,6 +3563,11 @@ async function cloudSync(config) {
|
|
|
3495
3563
|
} catch {
|
|
3496
3564
|
throw new Error("[cloud-sync] Database not initialized. Call initStore() before cloudSync().");
|
|
3497
3565
|
}
|
|
3566
|
+
try {
|
|
3567
|
+
const { getRawClient: getRawClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3568
|
+
await getRawClient2().execute("PRAGMA wal_checkpoint(PASSIVE)");
|
|
3569
|
+
} catch {
|
|
3570
|
+
}
|
|
3498
3571
|
try {
|
|
3499
3572
|
await client.execute(
|
|
3500
3573
|
"CREATE TABLE IF NOT EXISTS sync_meta (key TEXT PRIMARY KEY, value TEXT NOT NULL)"
|
|
@@ -4445,7 +4518,10 @@ async function writeMemory(record) {
|
|
|
4445
4518
|
source_path: record.source_path ?? null,
|
|
4446
4519
|
source_type: record.source_type ?? null,
|
|
4447
4520
|
tier: record.tier ?? classifyTier(record),
|
|
4448
|
-
supersedes_id: record.supersedes_id ?? null
|
|
4521
|
+
supersedes_id: record.supersedes_id ?? null,
|
|
4522
|
+
draft: record.draft ? 1 : 0,
|
|
4523
|
+
memory_type: record.memory_type ?? "raw",
|
|
4524
|
+
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null
|
|
4449
4525
|
};
|
|
4450
4526
|
_pendingRecords.push(dbRow);
|
|
4451
4527
|
orgBus.emit({
|
|
@@ -4500,6 +4576,9 @@ async function flushBatch() {
|
|
|
4500
4576
|
const sourceType = row.source_type ?? null;
|
|
4501
4577
|
const tier = row.tier ?? 3;
|
|
4502
4578
|
const supersedesId = row.supersedes_id ?? null;
|
|
4579
|
+
const draft = row.draft ? 1 : 0;
|
|
4580
|
+
const memoryType = row.memory_type ?? "raw";
|
|
4581
|
+
const trajectory = row.trajectory ?? null;
|
|
4503
4582
|
return {
|
|
4504
4583
|
sql: hasVector ? `INSERT OR IGNORE INTO memories
|
|
4505
4584
|
(id, agent_id, agent_role, session_id, timestamp,
|
|
@@ -4507,15 +4586,15 @@ async function flushBatch() {
|
|
|
4507
4586
|
has_error, raw_text, vector, version, task_id, importance, status,
|
|
4508
4587
|
confidence, last_accessed,
|
|
4509
4588
|
workspace_id, document_id, user_id, char_offset, page_number,
|
|
4510
|
-
source_path, source_type, tier, supersedes_id)
|
|
4511
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories
|
|
4589
|
+
source_path, source_type, tier, supersedes_id, draft, memory_type, trajectory)
|
|
4590
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories
|
|
4512
4591
|
(id, agent_id, agent_role, session_id, timestamp,
|
|
4513
4592
|
tool_name, project_name,
|
|
4514
4593
|
has_error, raw_text, vector, version, task_id, importance, status,
|
|
4515
4594
|
confidence, last_accessed,
|
|
4516
4595
|
workspace_id, document_id, user_id, char_offset, page_number,
|
|
4517
|
-
source_path, source_type, tier, supersedes_id)
|
|
4518
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
4596
|
+
source_path, source_type, tier, supersedes_id, draft, memory_type, trajectory)
|
|
4597
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
4519
4598
|
args: hasVector ? [
|
|
4520
4599
|
row.id,
|
|
4521
4600
|
row.agent_id,
|
|
@@ -4541,7 +4620,10 @@ async function flushBatch() {
|
|
|
4541
4620
|
sourcePath,
|
|
4542
4621
|
sourceType,
|
|
4543
4622
|
tier,
|
|
4544
|
-
supersedesId
|
|
4623
|
+
supersedesId,
|
|
4624
|
+
draft,
|
|
4625
|
+
memoryType,
|
|
4626
|
+
trajectory
|
|
4545
4627
|
] : [
|
|
4546
4628
|
row.id,
|
|
4547
4629
|
row.agent_id,
|
|
@@ -4566,7 +4648,10 @@ async function flushBatch() {
|
|
|
4566
4648
|
sourcePath,
|
|
4567
4649
|
sourceType,
|
|
4568
4650
|
tier,
|
|
4569
|
-
supersedesId
|
|
4651
|
+
supersedesId,
|
|
4652
|
+
draft,
|
|
4653
|
+
memoryType,
|
|
4654
|
+
trajectory
|
|
4570
4655
|
]
|
|
4571
4656
|
};
|
|
4572
4657
|
};
|
|
@@ -4612,6 +4697,7 @@ function vectorToBlob(vector) {
|
|
|
4612
4697
|
init_database();
|
|
4613
4698
|
init_notifications();
|
|
4614
4699
|
init_task_scope();
|
|
4700
|
+
init_employees();
|
|
4615
4701
|
import crypto4 from "crypto";
|
|
4616
4702
|
import { execSync as execSync4 } from "child_process";
|
|
4617
4703
|
import { existsSync as existsSync13, mkdirSync as mkdirSync7, openSync as openSync3, closeSync as closeSync3 } from "fs";
|
|
@@ -4677,104 +4763,114 @@ async function main() {
|
|
|
4677
4763
|
primaryProject = p;
|
|
4678
4764
|
}
|
|
4679
4765
|
}
|
|
4766
|
+
let limitReached = false;
|
|
4680
4767
|
try {
|
|
4681
4768
|
const { assertMemoryLimit: assertMemoryLimit2 } = await Promise.resolve().then(() => (init_plan_limits(), plan_limits_exports));
|
|
4682
4769
|
await assertMemoryLimit2();
|
|
4683
4770
|
} catch (err) {
|
|
4684
|
-
|
|
4771
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
4772
|
+
if (msg.includes("limit") || msg.includes("upgrade")) {
|
|
4773
|
+
process.stderr.write(`[summary-worker] LIMIT: ${msg}
|
|
4685
4774
|
`);
|
|
4686
|
-
|
|
4687
|
-
}
|
|
4688
|
-
await writeMemory({
|
|
4689
|
-
id: crypto4.randomUUID(),
|
|
4690
|
-
agent_id: agentId,
|
|
4691
|
-
agent_role: agentRole,
|
|
4692
|
-
session_id: `auto-summary-${Date.now()}`,
|
|
4693
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4694
|
-
tool_name: "auto-summary",
|
|
4695
|
-
project_name: primaryProject,
|
|
4696
|
-
has_error: false,
|
|
4697
|
-
raw_text: summaryText,
|
|
4698
|
-
vector: null
|
|
4699
|
-
});
|
|
4700
|
-
await flushBatch();
|
|
4701
|
-
if (agentId !== "exe" && agentId !== "default") {
|
|
4702
|
-
let totalErrors = 0;
|
|
4703
|
-
for (const [, data] of projects) {
|
|
4704
|
-
totalErrors += data.errors;
|
|
4705
|
-
}
|
|
4706
|
-
if (totalErrors >= 3) {
|
|
4707
|
-
await writeNotification({
|
|
4708
|
-
agentId,
|
|
4709
|
-
agentRole,
|
|
4710
|
-
event: "error_spike",
|
|
4711
|
-
project: primaryProject,
|
|
4712
|
-
summary: `${totalErrors} errors in last ${totalCalls} tool calls`
|
|
4713
|
-
});
|
|
4775
|
+
limitReached = true;
|
|
4714
4776
|
} else {
|
|
4715
|
-
|
|
4716
|
-
|
|
4717
|
-
agentRole,
|
|
4718
|
-
event: "session_summary",
|
|
4719
|
-
project: primaryProject,
|
|
4720
|
-
summary: `${totalCalls} tool calls (${writeCalls} writes) on ${primaryProject}`
|
|
4721
|
-
});
|
|
4777
|
+
process.stderr.write(`[summary-worker] plan check failed (continuing): ${msg}
|
|
4778
|
+
`);
|
|
4722
4779
|
}
|
|
4723
4780
|
}
|
|
4724
|
-
|
|
4725
|
-
|
|
4726
|
-
|
|
4727
|
-
|
|
4728
|
-
|
|
4729
|
-
|
|
4730
|
-
|
|
4781
|
+
if (limitReached) {
|
|
4782
|
+
} else {
|
|
4783
|
+
await writeMemory({
|
|
4784
|
+
id: crypto4.randomUUID(),
|
|
4785
|
+
agent_id: agentId,
|
|
4786
|
+
agent_role: agentRole,
|
|
4787
|
+
session_id: `auto-summary-${Date.now()}`,
|
|
4788
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4789
|
+
tool_name: "auto-summary",
|
|
4790
|
+
project_name: primaryProject,
|
|
4791
|
+
has_error: false,
|
|
4792
|
+
raw_text: summaryText,
|
|
4793
|
+
vector: null
|
|
4731
4794
|
});
|
|
4732
|
-
|
|
4733
|
-
if (
|
|
4734
|
-
|
|
4735
|
-
|
|
4736
|
-
|
|
4795
|
+
await flushBatch();
|
|
4796
|
+
if (!canCoordinate(agentId, agentRole)) {
|
|
4797
|
+
let totalErrors = 0;
|
|
4798
|
+
for (const [, data] of projects) {
|
|
4799
|
+
totalErrors += data.errors;
|
|
4800
|
+
}
|
|
4801
|
+
if (totalErrors >= 3) {
|
|
4802
|
+
await writeNotification({
|
|
4803
|
+
agentId,
|
|
4804
|
+
agentRole,
|
|
4805
|
+
event: "error_spike",
|
|
4806
|
+
project: primaryProject,
|
|
4807
|
+
summary: `${totalErrors} errors in last ${totalCalls} tool calls`
|
|
4808
|
+
});
|
|
4809
|
+
} else {
|
|
4810
|
+
await writeNotification({
|
|
4811
|
+
agentId,
|
|
4812
|
+
agentRole,
|
|
4813
|
+
event: "session_summary",
|
|
4814
|
+
project: primaryProject,
|
|
4815
|
+
summary: `${totalCalls} tool calls (${writeCalls} writes) on ${primaryProject}`
|
|
4816
|
+
});
|
|
4817
|
+
}
|
|
4818
|
+
}
|
|
4819
|
+
try {
|
|
4820
|
+
const { embed: embed2 } = await Promise.resolve().then(() => (init_embedder(), embedder_exports));
|
|
4821
|
+
const vector = await embed2(summaryText);
|
|
4822
|
+
const rowResult = await client.execute({
|
|
4823
|
+
sql: `SELECT rowid FROM memories WHERE agent_id = ? AND tool_name = 'auto-summary'
|
|
4824
|
+
ORDER BY timestamp DESC LIMIT 1`,
|
|
4825
|
+
args: [agentId]
|
|
4737
4826
|
});
|
|
4827
|
+
const targetRowid = rowResult.rows[0]?.rowid;
|
|
4828
|
+
if (targetRowid != null) {
|
|
4829
|
+
await client.execute({
|
|
4830
|
+
sql: `UPDATE memories SET vector = vector32(?) WHERE rowid = ?`,
|
|
4831
|
+
args: [vectorToBlob(vector), targetRowid]
|
|
4832
|
+
});
|
|
4833
|
+
}
|
|
4834
|
+
} catch (err) {
|
|
4835
|
+
process.stderr.write("[summary-worker] embed failed: " + (err instanceof Error ? err.message : String(err)) + "\n");
|
|
4738
4836
|
}
|
|
4739
|
-
|
|
4740
|
-
|
|
4741
|
-
|
|
4742
|
-
|
|
4743
|
-
|
|
4744
|
-
|
|
4745
|
-
|
|
4746
|
-
const { tryAcquireWorkerSlot: tryAcquireWorkerSlot2, registerWorkerPid: registerWorkerPid2 } = await Promise.resolve().then(() => (init_worker_gate(), worker_gate_exports));
|
|
4747
|
-
if (!tryAcquireWorkerSlot2()) {
|
|
4748
|
-
process.stderr.write("[summary-worker] Backfill needed but worker gate full \u2014 skipping\n");
|
|
4749
|
-
} else {
|
|
4750
|
-
const { spawn: spawn2 } = await import("child_process");
|
|
4751
|
-
const { fileURLToPath: fileURLToPath3 } = await import("url");
|
|
4752
|
-
const thisFile = fileURLToPath3(import.meta.url);
|
|
4753
|
-
const backfillPath = path14.resolve(path14.dirname(thisFile), "backfill-vectors.js");
|
|
4754
|
-
if (existsSync13(backfillPath)) {
|
|
4755
|
-
const { EXE_AI_DIR: exeDir2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
4756
|
-
const bLogPath = path14.join(exeDir2, "workers.log");
|
|
4757
|
-
mkdirSync7(path14.dirname(bLogPath), { recursive: true });
|
|
4758
|
-
const bLogFd = openSync3(bLogPath, "a");
|
|
4759
|
-
const child = spawn2(process.execPath, [backfillPath], {
|
|
4760
|
-
detached: true,
|
|
4761
|
-
stdio: ["ignore", "ignore", bLogFd]
|
|
4762
|
-
});
|
|
4763
|
-
child.unref();
|
|
4764
|
-
if (child.pid) registerWorkerPid2(child.pid);
|
|
4765
|
-
try {
|
|
4766
|
-
closeSync3(bLogFd);
|
|
4767
|
-
} catch {
|
|
4768
|
-
}
|
|
4769
|
-
process.stderr.write("[summary-worker] Spawned backfill job\n");
|
|
4837
|
+
try {
|
|
4838
|
+
const { EXE_AI_DIR: EXE_AI_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
4839
|
+
const flagPath = path14.join(EXE_AI_DIR2, "session-cache", "needs-backfill");
|
|
4840
|
+
if (existsSync13(flagPath)) {
|
|
4841
|
+
const { tryAcquireWorkerSlot: tryAcquireWorkerSlot2, registerWorkerPid: registerWorkerPid2 } = await Promise.resolve().then(() => (init_worker_gate(), worker_gate_exports));
|
|
4842
|
+
if (!tryAcquireWorkerSlot2()) {
|
|
4843
|
+
process.stderr.write("[summary-worker] Backfill needed but worker gate full \u2014 skipping\n");
|
|
4770
4844
|
} else {
|
|
4771
|
-
|
|
4845
|
+
const { spawn: spawn2 } = await import("child_process");
|
|
4846
|
+
const { fileURLToPath: fileURLToPath3 } = await import("url");
|
|
4847
|
+
const thisFile = fileURLToPath3(import.meta.url);
|
|
4848
|
+
const backfillPath = path14.resolve(path14.dirname(thisFile), "backfill-vectors.js");
|
|
4849
|
+
if (existsSync13(backfillPath)) {
|
|
4850
|
+
const { EXE_AI_DIR: exeDir2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
4851
|
+
const bLogPath = path14.join(exeDir2, "workers.log");
|
|
4852
|
+
mkdirSync7(path14.dirname(bLogPath), { recursive: true });
|
|
4853
|
+
const bLogFd = openSync3(bLogPath, "a");
|
|
4854
|
+
const child = spawn2(process.execPath, [backfillPath], {
|
|
4855
|
+
detached: true,
|
|
4856
|
+
stdio: ["ignore", "ignore", bLogFd]
|
|
4857
|
+
});
|
|
4858
|
+
child.unref();
|
|
4859
|
+
if (child.pid) registerWorkerPid2(child.pid);
|
|
4860
|
+
try {
|
|
4861
|
+
closeSync3(bLogFd);
|
|
4862
|
+
} catch {
|
|
4863
|
+
}
|
|
4864
|
+
process.stderr.write("[summary-worker] Spawned backfill job\n");
|
|
4865
|
+
} else {
|
|
4866
|
+
process.stderr.write(`[summary-worker] WARN: backfill-vectors not found at ${backfillPath}
|
|
4772
4867
|
`);
|
|
4868
|
+
}
|
|
4773
4869
|
}
|
|
4774
4870
|
}
|
|
4871
|
+
} catch (err) {
|
|
4872
|
+
process.stderr.write("[summary-worker] backfill spawn failed: " + (err instanceof Error ? err.message : String(err)) + "\n");
|
|
4775
4873
|
}
|
|
4776
|
-
} catch (err) {
|
|
4777
|
-
process.stderr.write("[summary-worker] backfill spawn failed: " + (err instanceof Error ? err.message : String(err)) + "\n");
|
|
4778
4874
|
}
|
|
4779
4875
|
try {
|
|
4780
4876
|
const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
@@ -4801,7 +4897,7 @@ async function main() {
|
|
|
4801
4897
|
`);
|
|
4802
4898
|
}
|
|
4803
4899
|
}
|
|
4804
|
-
if (agentId
|
|
4900
|
+
if (!canCoordinate(agentId, agentRole)) {
|
|
4805
4901
|
try {
|
|
4806
4902
|
const swScope = sessionScopeFilter();
|
|
4807
4903
|
const inProgressResult = await client.execute({
|