@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
|
@@ -54,7 +54,7 @@ function wrapWithRetry(client) {
|
|
|
54
54
|
return (sql) => retryOnBusy(() => target.execute(sql), "execute");
|
|
55
55
|
}
|
|
56
56
|
if (prop === "batch") {
|
|
57
|
-
return (stmts) => retryOnBusy(() => target.batch(stmts), "batch");
|
|
57
|
+
return (stmts, mode) => retryOnBusy(() => target.batch(stmts, mode), "batch");
|
|
58
58
|
}
|
|
59
59
|
return Reflect.get(target, prop, receiver);
|
|
60
60
|
}
|
|
@@ -70,6 +70,387 @@ var init_db_retry = __esm({
|
|
|
70
70
|
}
|
|
71
71
|
});
|
|
72
72
|
|
|
73
|
+
// src/lib/config.ts
|
|
74
|
+
import { readFile, writeFile, mkdir, chmod } from "fs/promises";
|
|
75
|
+
import { readFileSync, existsSync, renameSync } from "fs";
|
|
76
|
+
import path from "path";
|
|
77
|
+
import os from "os";
|
|
78
|
+
function resolveDataDir() {
|
|
79
|
+
if (process.env.EXE_OS_DIR) return process.env.EXE_OS_DIR;
|
|
80
|
+
if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
|
|
81
|
+
const newDir = path.join(os.homedir(), ".exe-os");
|
|
82
|
+
const legacyDir = path.join(os.homedir(), ".exe-mem");
|
|
83
|
+
if (!existsSync(newDir) && existsSync(legacyDir)) {
|
|
84
|
+
try {
|
|
85
|
+
renameSync(legacyDir, newDir);
|
|
86
|
+
process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
|
|
87
|
+
`);
|
|
88
|
+
} catch {
|
|
89
|
+
return legacyDir;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return newDir;
|
|
93
|
+
}
|
|
94
|
+
function migrateLegacyConfig(raw) {
|
|
95
|
+
if ("r2" in raw) {
|
|
96
|
+
process.stderr.write(
|
|
97
|
+
"[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"
|
|
98
|
+
);
|
|
99
|
+
delete raw.r2;
|
|
100
|
+
}
|
|
101
|
+
if ("syncIntervalMs" in raw) {
|
|
102
|
+
delete raw.syncIntervalMs;
|
|
103
|
+
}
|
|
104
|
+
return raw;
|
|
105
|
+
}
|
|
106
|
+
function migrateConfig(raw) {
|
|
107
|
+
const fromVersion = typeof raw.config_version === "number" ? raw.config_version : 0;
|
|
108
|
+
let currentVersion = fromVersion;
|
|
109
|
+
let migrated = false;
|
|
110
|
+
if (currentVersion > CURRENT_CONFIG_VERSION) {
|
|
111
|
+
return { config: raw, migrated: false, fromVersion };
|
|
112
|
+
}
|
|
113
|
+
for (const migration of CONFIG_MIGRATIONS) {
|
|
114
|
+
if (currentVersion === migration.from && migration.to <= CURRENT_CONFIG_VERSION) {
|
|
115
|
+
raw = migration.migrate(raw);
|
|
116
|
+
currentVersion = migration.to;
|
|
117
|
+
migrated = true;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return { config: raw, migrated, fromVersion };
|
|
121
|
+
}
|
|
122
|
+
function normalizeScalingRoadmap(raw) {
|
|
123
|
+
const defaultAuto = DEFAULT_CONFIG.scalingRoadmap.rerankerAutoTrigger;
|
|
124
|
+
const userRoadmap = raw.scalingRoadmap ?? {};
|
|
125
|
+
const userAuto = userRoadmap.rerankerAutoTrigger ?? {};
|
|
126
|
+
if (userAuto.enabled === void 0 && raw.rerankerEnabled !== void 0) {
|
|
127
|
+
userAuto.enabled = raw.rerankerEnabled;
|
|
128
|
+
}
|
|
129
|
+
raw.scalingRoadmap = {
|
|
130
|
+
...userRoadmap,
|
|
131
|
+
rerankerAutoTrigger: { ...defaultAuto, ...userAuto }
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
function normalizeSessionLifecycle(raw) {
|
|
135
|
+
const defaultSL = DEFAULT_CONFIG.sessionLifecycle;
|
|
136
|
+
const userSL = raw.sessionLifecycle ?? {};
|
|
137
|
+
raw.sessionLifecycle = { ...defaultSL, ...userSL };
|
|
138
|
+
}
|
|
139
|
+
function normalizeAutoUpdate(raw) {
|
|
140
|
+
const defaultAU = DEFAULT_CONFIG.autoUpdate;
|
|
141
|
+
const userAU = raw.autoUpdate ?? {};
|
|
142
|
+
raw.autoUpdate = { ...defaultAU, ...userAU };
|
|
143
|
+
}
|
|
144
|
+
async function loadConfig() {
|
|
145
|
+
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
146
|
+
await mkdir(dir, { recursive: true });
|
|
147
|
+
const configPath = path.join(dir, "config.json");
|
|
148
|
+
if (!existsSync(configPath)) {
|
|
149
|
+
return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
|
|
150
|
+
}
|
|
151
|
+
const raw = await readFile(configPath, "utf-8");
|
|
152
|
+
try {
|
|
153
|
+
let parsed = JSON.parse(raw);
|
|
154
|
+
parsed = migrateLegacyConfig(parsed);
|
|
155
|
+
const { config: migratedCfg, migrated, fromVersion } = migrateConfig(parsed);
|
|
156
|
+
if (migrated) {
|
|
157
|
+
process.stderr.write(`[exe-os] Config migrated from v${fromVersion} to v${migratedCfg.config_version}
|
|
158
|
+
`);
|
|
159
|
+
try {
|
|
160
|
+
await writeFile(configPath, JSON.stringify(migratedCfg, null, 2) + "\n");
|
|
161
|
+
} catch {
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
normalizeScalingRoadmap(migratedCfg);
|
|
165
|
+
normalizeSessionLifecycle(migratedCfg);
|
|
166
|
+
normalizeAutoUpdate(migratedCfg);
|
|
167
|
+
const config = { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db"), ...migratedCfg };
|
|
168
|
+
if (config.dbPath.startsWith("~")) {
|
|
169
|
+
config.dbPath = config.dbPath.replace(/^~/, os.homedir());
|
|
170
|
+
}
|
|
171
|
+
return config;
|
|
172
|
+
} catch {
|
|
173
|
+
return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
|
|
177
|
+
var init_config = __esm({
|
|
178
|
+
"src/lib/config.ts"() {
|
|
179
|
+
"use strict";
|
|
180
|
+
EXE_AI_DIR = resolveDataDir();
|
|
181
|
+
DB_PATH = path.join(EXE_AI_DIR, "memories.db");
|
|
182
|
+
MODELS_DIR = path.join(EXE_AI_DIR, "models");
|
|
183
|
+
CONFIG_PATH = path.join(EXE_AI_DIR, "config.json");
|
|
184
|
+
LEGACY_LANCE_PATH = path.join(EXE_AI_DIR, "local.lance");
|
|
185
|
+
CURRENT_CONFIG_VERSION = 1;
|
|
186
|
+
DEFAULT_CONFIG = {
|
|
187
|
+
config_version: CURRENT_CONFIG_VERSION,
|
|
188
|
+
dbPath: DB_PATH,
|
|
189
|
+
modelFile: "jina-embeddings-v5-small-q4_k_m.gguf",
|
|
190
|
+
embeddingDim: 1024,
|
|
191
|
+
batchSize: 20,
|
|
192
|
+
flushIntervalMs: 1e4,
|
|
193
|
+
autoIngestion: true,
|
|
194
|
+
autoRetrieval: true,
|
|
195
|
+
searchMode: "hybrid",
|
|
196
|
+
hookSearchMode: "hybrid",
|
|
197
|
+
fileGrepEnabled: true,
|
|
198
|
+
splashEffect: true,
|
|
199
|
+
consolidationEnabled: true,
|
|
200
|
+
consolidationIntervalMs: 6 * 60 * 60 * 1e3,
|
|
201
|
+
consolidationModel: "claude-haiku-4-5-20251001",
|
|
202
|
+
consolidationMaxCallsPerRun: 20,
|
|
203
|
+
selfQueryRouter: true,
|
|
204
|
+
selfQueryModel: "claude-haiku-4-5-20251001",
|
|
205
|
+
rerankerEnabled: true,
|
|
206
|
+
scalingRoadmap: {
|
|
207
|
+
rerankerAutoTrigger: {
|
|
208
|
+
enabled: true,
|
|
209
|
+
broadQueryMinCardinality: 5e4,
|
|
210
|
+
fetchTopK: 150,
|
|
211
|
+
returnTopK: 5
|
|
212
|
+
}
|
|
213
|
+
},
|
|
214
|
+
graphRagEnabled: true,
|
|
215
|
+
wikiEnabled: false,
|
|
216
|
+
wikiUrl: "",
|
|
217
|
+
wikiApiKey: "",
|
|
218
|
+
wikiSyncIntervalMs: 30 * 60 * 1e3,
|
|
219
|
+
wikiWorkspaceMapping: {},
|
|
220
|
+
wikiAutoUpdate: true,
|
|
221
|
+
wikiAutoUpdateThreshold: 0.5,
|
|
222
|
+
wikiAutoUpdateCreateNew: true,
|
|
223
|
+
skillLearning: true,
|
|
224
|
+
skillThreshold: 3,
|
|
225
|
+
skillModel: "claude-haiku-4-5-20251001",
|
|
226
|
+
exeHeartbeat: {
|
|
227
|
+
enabled: true,
|
|
228
|
+
intervalSeconds: 60,
|
|
229
|
+
staleInProgressThresholdHours: 2
|
|
230
|
+
},
|
|
231
|
+
sessionLifecycle: {
|
|
232
|
+
idleKillEnabled: true,
|
|
233
|
+
idleKillTicksRequired: 3,
|
|
234
|
+
idleKillIntercomAckWindowMs: 1e4,
|
|
235
|
+
maxAutoInstances: 10
|
|
236
|
+
},
|
|
237
|
+
autoUpdate: {
|
|
238
|
+
checkOnBoot: true,
|
|
239
|
+
autoInstall: false,
|
|
240
|
+
checkIntervalMs: 24 * 60 * 60 * 1e3
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
CONFIG_MIGRATIONS = [
|
|
244
|
+
{
|
|
245
|
+
from: 0,
|
|
246
|
+
to: 1,
|
|
247
|
+
migrate: (cfg) => {
|
|
248
|
+
cfg.config_version = 1;
|
|
249
|
+
return cfg;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
];
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
// src/lib/employees.ts
|
|
257
|
+
var employees_exports = {};
|
|
258
|
+
__export(employees_exports, {
|
|
259
|
+
COORDINATOR_ROLE: () => COORDINATOR_ROLE,
|
|
260
|
+
DEFAULT_COORDINATOR_TEMPLATE_NAME: () => DEFAULT_COORDINATOR_TEMPLATE_NAME,
|
|
261
|
+
EMPLOYEES_PATH: () => EMPLOYEES_PATH,
|
|
262
|
+
addEmployee: () => addEmployee,
|
|
263
|
+
canCoordinate: () => canCoordinate,
|
|
264
|
+
getCoordinatorEmployee: () => getCoordinatorEmployee,
|
|
265
|
+
getCoordinatorName: () => getCoordinatorName,
|
|
266
|
+
getEmployee: () => getEmployee,
|
|
267
|
+
getEmployeeByRole: () => getEmployeeByRole,
|
|
268
|
+
getEmployeeNamesByRole: () => getEmployeeNamesByRole,
|
|
269
|
+
hasRole: () => hasRole,
|
|
270
|
+
isCoordinatorName: () => isCoordinatorName,
|
|
271
|
+
isCoordinatorRole: () => isCoordinatorRole,
|
|
272
|
+
isMultiInstance: () => isMultiInstance,
|
|
273
|
+
loadEmployees: () => loadEmployees,
|
|
274
|
+
loadEmployeesSync: () => loadEmployeesSync,
|
|
275
|
+
normalizeRole: () => normalizeRole,
|
|
276
|
+
normalizeRosterCase: () => normalizeRosterCase,
|
|
277
|
+
registerBinSymlinks: () => registerBinSymlinks,
|
|
278
|
+
saveEmployees: () => saveEmployees,
|
|
279
|
+
validateEmployeeName: () => validateEmployeeName
|
|
280
|
+
});
|
|
281
|
+
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
282
|
+
import { existsSync as existsSync2, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
|
|
283
|
+
import { execSync } from "child_process";
|
|
284
|
+
import path2 from "path";
|
|
285
|
+
import os2 from "os";
|
|
286
|
+
function normalizeRole(role) {
|
|
287
|
+
return (role ?? "").trim().toLowerCase();
|
|
288
|
+
}
|
|
289
|
+
function isCoordinatorRole(role) {
|
|
290
|
+
return normalizeRole(role) === normalizeRole(COORDINATOR_ROLE);
|
|
291
|
+
}
|
|
292
|
+
function getCoordinatorEmployee(employees) {
|
|
293
|
+
return employees.find((e) => isCoordinatorRole(e.role));
|
|
294
|
+
}
|
|
295
|
+
function getCoordinatorName(employees = loadEmployeesSync()) {
|
|
296
|
+
return getCoordinatorEmployee(employees)?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME;
|
|
297
|
+
}
|
|
298
|
+
function isCoordinatorName(agentName, employees = loadEmployeesSync()) {
|
|
299
|
+
if (!agentName) return false;
|
|
300
|
+
return agentName.toLowerCase() === getCoordinatorName(employees).toLowerCase();
|
|
301
|
+
}
|
|
302
|
+
function canCoordinate(agentName, agentRole, employees = loadEmployeesSync()) {
|
|
303
|
+
return agentName === "default" || isCoordinatorRole(agentRole) || isCoordinatorName(agentName, employees);
|
|
304
|
+
}
|
|
305
|
+
function validateEmployeeName(name) {
|
|
306
|
+
if (!name) {
|
|
307
|
+
return { valid: false, error: "Name is required" };
|
|
308
|
+
}
|
|
309
|
+
if (name.length > 32) {
|
|
310
|
+
return { valid: false, error: "Name must be 32 characters or fewer" };
|
|
311
|
+
}
|
|
312
|
+
if (!/^[a-z][a-z0-9]*$/.test(name)) {
|
|
313
|
+
return {
|
|
314
|
+
valid: false,
|
|
315
|
+
error: "Name must start with a letter and contain only lowercase alphanumeric characters"
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
return { valid: true };
|
|
319
|
+
}
|
|
320
|
+
async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
321
|
+
if (!existsSync2(employeesPath)) {
|
|
322
|
+
return [];
|
|
323
|
+
}
|
|
324
|
+
const raw = await readFile2(employeesPath, "utf-8");
|
|
325
|
+
try {
|
|
326
|
+
return JSON.parse(raw);
|
|
327
|
+
} catch {
|
|
328
|
+
return [];
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
|
|
332
|
+
await mkdir2(path2.dirname(employeesPath), { recursive: true });
|
|
333
|
+
await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
|
|
334
|
+
}
|
|
335
|
+
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
336
|
+
if (!existsSync2(employeesPath)) return [];
|
|
337
|
+
try {
|
|
338
|
+
return JSON.parse(readFileSync2(employeesPath, "utf-8"));
|
|
339
|
+
} catch {
|
|
340
|
+
return [];
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
function getEmployee(employees, name) {
|
|
344
|
+
return employees.find((e) => e.name.toLowerCase() === name.toLowerCase());
|
|
345
|
+
}
|
|
346
|
+
function getEmployeeByRole(employees, role) {
|
|
347
|
+
const lower = role.toLowerCase();
|
|
348
|
+
return employees.find((e) => e.role.toLowerCase() === lower);
|
|
349
|
+
}
|
|
350
|
+
function getEmployeeNamesByRole(employees, role) {
|
|
351
|
+
const lower = role.toLowerCase();
|
|
352
|
+
return employees.filter((e) => e.role.toLowerCase() === lower).map((e) => e.name);
|
|
353
|
+
}
|
|
354
|
+
function hasRole(agentName, role) {
|
|
355
|
+
const employees = loadEmployeesSync();
|
|
356
|
+
const emp = getEmployee(employees, agentName);
|
|
357
|
+
return emp ? emp.role.toLowerCase() === role.toLowerCase() : false;
|
|
358
|
+
}
|
|
359
|
+
function isMultiInstance(agentName, employees) {
|
|
360
|
+
const roster = employees ?? loadEmployeesSync();
|
|
361
|
+
const emp = getEmployee(roster, agentName);
|
|
362
|
+
if (!emp) return false;
|
|
363
|
+
return MULTI_INSTANCE_ROLES.has(emp.role.toLowerCase());
|
|
364
|
+
}
|
|
365
|
+
function addEmployee(employees, employee) {
|
|
366
|
+
const normalized = { ...employee, name: employee.name.toLowerCase() };
|
|
367
|
+
if (employees.some((e) => e.name.toLowerCase() === normalized.name)) {
|
|
368
|
+
throw new Error(`Employee '${normalized.name}' already exists`);
|
|
369
|
+
}
|
|
370
|
+
return [...employees, normalized];
|
|
371
|
+
}
|
|
372
|
+
async function normalizeRosterCase(rosterPath) {
|
|
373
|
+
const employees = await loadEmployees(rosterPath);
|
|
374
|
+
let changed = false;
|
|
375
|
+
for (const emp of employees) {
|
|
376
|
+
if (emp.name !== emp.name.toLowerCase()) {
|
|
377
|
+
const oldName = emp.name;
|
|
378
|
+
emp.name = emp.name.toLowerCase();
|
|
379
|
+
changed = true;
|
|
380
|
+
try {
|
|
381
|
+
const identityDir = path2.join(os2.homedir(), ".exe-os", "identity");
|
|
382
|
+
const oldPath = path2.join(identityDir, `${oldName}.md`);
|
|
383
|
+
const newPath = path2.join(identityDir, `${emp.name}.md`);
|
|
384
|
+
if (existsSync2(oldPath) && !existsSync2(newPath)) {
|
|
385
|
+
renameSync2(oldPath, newPath);
|
|
386
|
+
} else if (existsSync2(oldPath) && oldPath !== newPath) {
|
|
387
|
+
const content = readFileSync2(oldPath, "utf-8");
|
|
388
|
+
writeFileSync(newPath, content, "utf-8");
|
|
389
|
+
if (oldPath.toLowerCase() !== newPath.toLowerCase()) {
|
|
390
|
+
unlinkSync(oldPath);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
} catch {
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
if (changed) {
|
|
398
|
+
await saveEmployees(employees, rosterPath);
|
|
399
|
+
}
|
|
400
|
+
return changed;
|
|
401
|
+
}
|
|
402
|
+
function findExeBin() {
|
|
403
|
+
try {
|
|
404
|
+
return execSync(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
|
|
405
|
+
} catch {
|
|
406
|
+
return null;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
function registerBinSymlinks(name) {
|
|
410
|
+
const created = [];
|
|
411
|
+
const skipped = [];
|
|
412
|
+
const errors = [];
|
|
413
|
+
const exeBinPath = findExeBin();
|
|
414
|
+
if (!exeBinPath) {
|
|
415
|
+
errors.push("Could not find 'exe-os' in PATH");
|
|
416
|
+
return { created, skipped, errors };
|
|
417
|
+
}
|
|
418
|
+
const binDir = path2.dirname(exeBinPath);
|
|
419
|
+
let target;
|
|
420
|
+
try {
|
|
421
|
+
target = readlinkSync(exeBinPath);
|
|
422
|
+
} catch {
|
|
423
|
+
errors.push("Could not read 'exe' symlink");
|
|
424
|
+
return { created, skipped, errors };
|
|
425
|
+
}
|
|
426
|
+
for (const suffix of ["", "-opencode"]) {
|
|
427
|
+
const linkName = `${name}${suffix}`;
|
|
428
|
+
const linkPath = path2.join(binDir, linkName);
|
|
429
|
+
if (existsSync2(linkPath)) {
|
|
430
|
+
skipped.push(linkName);
|
|
431
|
+
continue;
|
|
432
|
+
}
|
|
433
|
+
try {
|
|
434
|
+
symlinkSync(target, linkPath);
|
|
435
|
+
created.push(linkName);
|
|
436
|
+
} catch (err) {
|
|
437
|
+
errors.push(`${linkName}: ${err instanceof Error ? err.message : String(err)}`);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
return { created, skipped, errors };
|
|
441
|
+
}
|
|
442
|
+
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES;
|
|
443
|
+
var init_employees = __esm({
|
|
444
|
+
"src/lib/employees.ts"() {
|
|
445
|
+
"use strict";
|
|
446
|
+
init_config();
|
|
447
|
+
EMPLOYEES_PATH = path2.join(EXE_AI_DIR, "exe-employees.json");
|
|
448
|
+
DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
|
|
449
|
+
COORDINATOR_ROLE = "COO";
|
|
450
|
+
MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
|
|
451
|
+
}
|
|
452
|
+
});
|
|
453
|
+
|
|
73
454
|
// src/lib/database.ts
|
|
74
455
|
import { createClient } from "@libsql/client";
|
|
75
456
|
async function initDatabase(config) {
|
|
@@ -203,22 +584,24 @@ async function ensureSchema() {
|
|
|
203
584
|
ON behaviors(agent_id, active);
|
|
204
585
|
`);
|
|
205
586
|
try {
|
|
587
|
+
const coordinatorName = getCoordinatorName();
|
|
206
588
|
const existing = await client.execute({
|
|
207
|
-
sql: "SELECT COUNT(*) as cnt FROM behaviors WHERE agent_id =
|
|
208
|
-
args: []
|
|
589
|
+
sql: "SELECT COUNT(*) as cnt FROM behaviors WHERE agent_id = ?",
|
|
590
|
+
args: [coordinatorName]
|
|
209
591
|
});
|
|
210
592
|
if (Number(existing.rows[0]?.cnt) === 0) {
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
593
|
+
const seededAt = "2026-03-25T00:00:00Z";
|
|
594
|
+
for (const [domain, content] of [
|
|
595
|
+
["workflow", `Don't ask "keep going?" \u2014 just keep executing phases/plans autonomously`],
|
|
596
|
+
["tool-use", "Always use create_task MCP tool, never write .md files directly for task creation"],
|
|
597
|
+
["workflow", "Auto-start reviewing when idle and reviews are pending \u2014 never ask founder for permission"]
|
|
598
|
+
]) {
|
|
599
|
+
await client.execute({
|
|
600
|
+
sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, content, active, created_at, updated_at)
|
|
601
|
+
VALUES (hex(randomblob(16)), ?, NULL, ?, ?, 1, ?, ?)`,
|
|
602
|
+
args: [coordinatorName, domain, content, seededAt, seededAt]
|
|
603
|
+
});
|
|
604
|
+
}
|
|
222
605
|
}
|
|
223
606
|
} catch {
|
|
224
607
|
}
|
|
@@ -910,212 +1293,57 @@ async function ensureSchema() {
|
|
|
910
1293
|
} catch {
|
|
911
1294
|
}
|
|
912
1295
|
}
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
}
|
|
920
|
-
}
|
|
921
|
-
var _client, _resilientClient, initTurso, disposeTurso;
|
|
922
|
-
var init_database = __esm({
|
|
923
|
-
"src/lib/database.ts"() {
|
|
924
|
-
"use strict";
|
|
925
|
-
init_db_retry();
|
|
926
|
-
_client = null;
|
|
927
|
-
_resilientClient = null;
|
|
928
|
-
initTurso = initDatabase;
|
|
929
|
-
disposeTurso = disposeDatabase;
|
|
930
|
-
}
|
|
931
|
-
});
|
|
932
|
-
|
|
933
|
-
// src/lib/config.ts
|
|
934
|
-
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, chmod as chmod2 } from "fs/promises";
|
|
935
|
-
import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
|
|
936
|
-
import path2 from "path";
|
|
937
|
-
import os2 from "os";
|
|
938
|
-
function resolveDataDir() {
|
|
939
|
-
if (process.env.EXE_OS_DIR) return process.env.EXE_OS_DIR;
|
|
940
|
-
if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
|
|
941
|
-
const newDir = path2.join(os2.homedir(), ".exe-os");
|
|
942
|
-
const legacyDir = path2.join(os2.homedir(), ".exe-mem");
|
|
943
|
-
if (!existsSync2(newDir) && existsSync2(legacyDir)) {
|
|
944
|
-
try {
|
|
945
|
-
renameSync(legacyDir, newDir);
|
|
946
|
-
process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
|
|
947
|
-
`);
|
|
948
|
-
} catch {
|
|
949
|
-
return legacyDir;
|
|
950
|
-
}
|
|
1296
|
+
try {
|
|
1297
|
+
await client.execute({
|
|
1298
|
+
sql: `ALTER TABLE memories ADD COLUMN draft INTEGER DEFAULT 0`,
|
|
1299
|
+
args: []
|
|
1300
|
+
});
|
|
1301
|
+
} catch {
|
|
951
1302
|
}
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
if ("r2" in raw) {
|
|
956
|
-
process.stderr.write(
|
|
957
|
-
"[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"
|
|
1303
|
+
try {
|
|
1304
|
+
await client.execute(
|
|
1305
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_draft ON memories(draft) WHERE draft = 1`
|
|
958
1306
|
);
|
|
959
|
-
|
|
960
|
-
}
|
|
961
|
-
if ("syncIntervalMs" in raw) {
|
|
962
|
-
delete raw.syncIntervalMs;
|
|
963
|
-
}
|
|
964
|
-
return raw;
|
|
965
|
-
}
|
|
966
|
-
function migrateConfig(raw) {
|
|
967
|
-
const fromVersion = typeof raw.config_version === "number" ? raw.config_version : 0;
|
|
968
|
-
let currentVersion = fromVersion;
|
|
969
|
-
let migrated = false;
|
|
970
|
-
if (currentVersion > CURRENT_CONFIG_VERSION) {
|
|
971
|
-
return { config: raw, migrated: false, fromVersion };
|
|
972
|
-
}
|
|
973
|
-
for (const migration of CONFIG_MIGRATIONS) {
|
|
974
|
-
if (currentVersion === migration.from && migration.to <= CURRENT_CONFIG_VERSION) {
|
|
975
|
-
raw = migration.migrate(raw);
|
|
976
|
-
currentVersion = migration.to;
|
|
977
|
-
migrated = true;
|
|
978
|
-
}
|
|
1307
|
+
} catch {
|
|
979
1308
|
}
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
if (userAuto.enabled === void 0 && raw.rerankerEnabled !== void 0) {
|
|
987
|
-
userAuto.enabled = raw.rerankerEnabled;
|
|
1309
|
+
try {
|
|
1310
|
+
await client.execute({
|
|
1311
|
+
sql: `ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'`,
|
|
1312
|
+
args: []
|
|
1313
|
+
});
|
|
1314
|
+
} catch {
|
|
988
1315
|
}
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
}
|
|
994
|
-
function normalizeSessionLifecycle(raw) {
|
|
995
|
-
const defaultSL = DEFAULT_CONFIG.sessionLifecycle;
|
|
996
|
-
const userSL = raw.sessionLifecycle ?? {};
|
|
997
|
-
raw.sessionLifecycle = { ...defaultSL, ...userSL };
|
|
998
|
-
}
|
|
999
|
-
function normalizeAutoUpdate(raw) {
|
|
1000
|
-
const defaultAU = DEFAULT_CONFIG.autoUpdate;
|
|
1001
|
-
const userAU = raw.autoUpdate ?? {};
|
|
1002
|
-
raw.autoUpdate = { ...defaultAU, ...userAU };
|
|
1003
|
-
}
|
|
1004
|
-
async function loadConfig() {
|
|
1005
|
-
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
1006
|
-
await mkdir2(dir, { recursive: true });
|
|
1007
|
-
const configPath = path2.join(dir, "config.json");
|
|
1008
|
-
if (!existsSync2(configPath)) {
|
|
1009
|
-
return { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db") };
|
|
1316
|
+
try {
|
|
1317
|
+
await client.execute(
|
|
1318
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_type ON memories(memory_type)`
|
|
1319
|
+
);
|
|
1320
|
+
} catch {
|
|
1010
1321
|
}
|
|
1011
|
-
const raw = await readFile2(configPath, "utf-8");
|
|
1012
1322
|
try {
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
process.stderr.write(`[exe-os] Config migrated from v${fromVersion} to v${migratedCfg.config_version}
|
|
1018
|
-
`);
|
|
1019
|
-
try {
|
|
1020
|
-
await writeFile2(configPath, JSON.stringify(migratedCfg, null, 2) + "\n");
|
|
1021
|
-
} catch {
|
|
1022
|
-
}
|
|
1023
|
-
}
|
|
1024
|
-
normalizeScalingRoadmap(migratedCfg);
|
|
1025
|
-
normalizeSessionLifecycle(migratedCfg);
|
|
1026
|
-
normalizeAutoUpdate(migratedCfg);
|
|
1027
|
-
const config = { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db"), ...migratedCfg };
|
|
1028
|
-
if (config.dbPath.startsWith("~")) {
|
|
1029
|
-
config.dbPath = config.dbPath.replace(/^~/, os2.homedir());
|
|
1030
|
-
}
|
|
1031
|
-
return config;
|
|
1323
|
+
await client.execute({
|
|
1324
|
+
sql: `ALTER TABLE memories ADD COLUMN trajectory TEXT`,
|
|
1325
|
+
args: []
|
|
1326
|
+
});
|
|
1032
1327
|
} catch {
|
|
1033
|
-
return { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db") };
|
|
1034
1328
|
}
|
|
1035
1329
|
}
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1330
|
+
async function disposeDatabase() {
|
|
1331
|
+
if (_client) {
|
|
1332
|
+
_client.close();
|
|
1333
|
+
_client = null;
|
|
1334
|
+
_resilientClient = null;
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
var _client, _resilientClient, initTurso, disposeTurso;
|
|
1338
|
+
var init_database = __esm({
|
|
1339
|
+
"src/lib/database.ts"() {
|
|
1039
1340
|
"use strict";
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
DEFAULT_CONFIG = {
|
|
1047
|
-
config_version: CURRENT_CONFIG_VERSION,
|
|
1048
|
-
dbPath: DB_PATH,
|
|
1049
|
-
modelFile: "jina-embeddings-v5-small-q4_k_m.gguf",
|
|
1050
|
-
embeddingDim: 1024,
|
|
1051
|
-
batchSize: 20,
|
|
1052
|
-
flushIntervalMs: 1e4,
|
|
1053
|
-
autoIngestion: true,
|
|
1054
|
-
autoRetrieval: true,
|
|
1055
|
-
searchMode: "hybrid",
|
|
1056
|
-
hookSearchMode: "hybrid",
|
|
1057
|
-
fileGrepEnabled: true,
|
|
1058
|
-
splashEffect: true,
|
|
1059
|
-
consolidationEnabled: true,
|
|
1060
|
-
consolidationIntervalMs: 6 * 60 * 60 * 1e3,
|
|
1061
|
-
consolidationModel: "claude-haiku-4-5-20251001",
|
|
1062
|
-
consolidationMaxCallsPerRun: 20,
|
|
1063
|
-
selfQueryRouter: true,
|
|
1064
|
-
selfQueryModel: "claude-haiku-4-5-20251001",
|
|
1065
|
-
rerankerEnabled: true,
|
|
1066
|
-
scalingRoadmap: {
|
|
1067
|
-
rerankerAutoTrigger: {
|
|
1068
|
-
enabled: true,
|
|
1069
|
-
broadQueryMinCardinality: 5e4,
|
|
1070
|
-
fetchTopK: 150,
|
|
1071
|
-
returnTopK: 5
|
|
1072
|
-
}
|
|
1073
|
-
},
|
|
1074
|
-
graphRagEnabled: true,
|
|
1075
|
-
wikiEnabled: false,
|
|
1076
|
-
wikiUrl: "",
|
|
1077
|
-
wikiApiKey: "",
|
|
1078
|
-
wikiSyncIntervalMs: 30 * 60 * 1e3,
|
|
1079
|
-
wikiWorkspaceMapping: {
|
|
1080
|
-
exe: "Executive",
|
|
1081
|
-
yoshi: "Engineering",
|
|
1082
|
-
mari: "Marketing",
|
|
1083
|
-
tom: "Engineering",
|
|
1084
|
-
sasha: "Production"
|
|
1085
|
-
},
|
|
1086
|
-
wikiAutoUpdate: true,
|
|
1087
|
-
wikiAutoUpdateThreshold: 0.5,
|
|
1088
|
-
wikiAutoUpdateCreateNew: true,
|
|
1089
|
-
skillLearning: true,
|
|
1090
|
-
skillThreshold: 3,
|
|
1091
|
-
skillModel: "claude-haiku-4-5-20251001",
|
|
1092
|
-
exeHeartbeat: {
|
|
1093
|
-
enabled: true,
|
|
1094
|
-
intervalSeconds: 60,
|
|
1095
|
-
staleInProgressThresholdHours: 2
|
|
1096
|
-
},
|
|
1097
|
-
sessionLifecycle: {
|
|
1098
|
-
idleKillEnabled: true,
|
|
1099
|
-
idleKillTicksRequired: 3,
|
|
1100
|
-
idleKillIntercomAckWindowMs: 1e4,
|
|
1101
|
-
maxAutoInstances: 10
|
|
1102
|
-
},
|
|
1103
|
-
autoUpdate: {
|
|
1104
|
-
checkOnBoot: true,
|
|
1105
|
-
autoInstall: false,
|
|
1106
|
-
checkIntervalMs: 24 * 60 * 60 * 1e3
|
|
1107
|
-
}
|
|
1108
|
-
};
|
|
1109
|
-
CONFIG_MIGRATIONS = [
|
|
1110
|
-
{
|
|
1111
|
-
from: 0,
|
|
1112
|
-
to: 1,
|
|
1113
|
-
migrate: (cfg) => {
|
|
1114
|
-
cfg.config_version = 1;
|
|
1115
|
-
return cfg;
|
|
1116
|
-
}
|
|
1117
|
-
}
|
|
1118
|
-
];
|
|
1341
|
+
init_db_retry();
|
|
1342
|
+
init_employees();
|
|
1343
|
+
_client = null;
|
|
1344
|
+
_resilientClient = null;
|
|
1345
|
+
initTurso = initDatabase;
|
|
1346
|
+
disposeTurso = disposeDatabase;
|
|
1119
1347
|
}
|
|
1120
1348
|
});
|
|
1121
1349
|
|
|
@@ -1132,12 +1360,12 @@ __export(shard_manager_exports, {
|
|
|
1132
1360
|
listShards: () => listShards,
|
|
1133
1361
|
shardExists: () => shardExists
|
|
1134
1362
|
});
|
|
1135
|
-
import
|
|
1136
|
-
import { existsSync as
|
|
1363
|
+
import path4 from "path";
|
|
1364
|
+
import { existsSync as existsSync4, mkdirSync, readdirSync } from "fs";
|
|
1137
1365
|
import { createClient as createClient2 } from "@libsql/client";
|
|
1138
1366
|
function initShardManager(encryptionKey) {
|
|
1139
1367
|
_encryptionKey = encryptionKey;
|
|
1140
|
-
if (!
|
|
1368
|
+
if (!existsSync4(SHARDS_DIR)) {
|
|
1141
1369
|
mkdirSync(SHARDS_DIR, { recursive: true });
|
|
1142
1370
|
}
|
|
1143
1371
|
_shardingEnabled = true;
|
|
@@ -1158,7 +1386,7 @@ function getShardClient(projectName) {
|
|
|
1158
1386
|
}
|
|
1159
1387
|
const cached = _shards.get(safeName);
|
|
1160
1388
|
if (cached) return cached;
|
|
1161
|
-
const dbPath =
|
|
1389
|
+
const dbPath = path4.join(SHARDS_DIR, `${safeName}.db`);
|
|
1162
1390
|
const client = createClient2({
|
|
1163
1391
|
url: `file:${dbPath}`,
|
|
1164
1392
|
encryptionKey: _encryptionKey
|
|
@@ -1168,10 +1396,10 @@ function getShardClient(projectName) {
|
|
|
1168
1396
|
}
|
|
1169
1397
|
function shardExists(projectName) {
|
|
1170
1398
|
const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
1171
|
-
return
|
|
1399
|
+
return existsSync4(path4.join(SHARDS_DIR, `${safeName}.db`));
|
|
1172
1400
|
}
|
|
1173
1401
|
function listShards() {
|
|
1174
|
-
if (!
|
|
1402
|
+
if (!existsSync4(SHARDS_DIR)) return [];
|
|
1175
1403
|
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
1176
1404
|
}
|
|
1177
1405
|
async function ensureShardSchema(client) {
|
|
@@ -1241,7 +1469,11 @@ async function ensureShardSchema(client) {
|
|
|
1241
1469
|
"ALTER TABLE memories ADD COLUMN source_path TEXT",
|
|
1242
1470
|
"ALTER TABLE memories ADD COLUMN source_type TEXT DEFAULT 'text'",
|
|
1243
1471
|
"ALTER TABLE memories ADD COLUMN tier INTEGER DEFAULT 3",
|
|
1244
|
-
"ALTER TABLE memories ADD COLUMN supersedes_id TEXT"
|
|
1472
|
+
"ALTER TABLE memories ADD COLUMN supersedes_id TEXT",
|
|
1473
|
+
// MS-11: draft staging, MS-6a: memory_type, MS-7: trajectory
|
|
1474
|
+
"ALTER TABLE memories ADD COLUMN draft INTEGER DEFAULT 0",
|
|
1475
|
+
"ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'",
|
|
1476
|
+
"ALTER TABLE memories ADD COLUMN trajectory TEXT"
|
|
1245
1477
|
]) {
|
|
1246
1478
|
try {
|
|
1247
1479
|
await client.execute(col);
|
|
@@ -1353,7 +1585,7 @@ var init_shard_manager = __esm({
|
|
|
1353
1585
|
"src/lib/shard-manager.ts"() {
|
|
1354
1586
|
"use strict";
|
|
1355
1587
|
init_config();
|
|
1356
|
-
SHARDS_DIR =
|
|
1588
|
+
SHARDS_DIR = path4.join(EXE_AI_DIR, "shards");
|
|
1357
1589
|
_shards = /* @__PURE__ */ new Map();
|
|
1358
1590
|
_encryptionKey = null;
|
|
1359
1591
|
_shardingEnabled = false;
|
|
@@ -1371,26 +1603,26 @@ var init_platform_procedures = __esm({
|
|
|
1371
1603
|
title: "What is exe-os \u2014 the operating model every agent must understand",
|
|
1372
1604
|
domain: "architecture",
|
|
1373
1605
|
priority: "p0",
|
|
1374
|
-
content: "Exe OS is an AI employee operating system. A founder runs 5-10 AI agents as a real org: COO
|
|
1606
|
+
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."
|
|
1375
1607
|
},
|
|
1376
1608
|
{
|
|
1377
1609
|
title: "Mode 1 \u2014 how exe-os runs inside Claude Code",
|
|
1378
1610
|
domain: "architecture",
|
|
1379
1611
|
priority: "p0",
|
|
1380
|
-
content: "Mode 1: exe-os runs AS hooks + MCP + skills inside Claude Code. The founder opens CC
|
|
1612
|
+
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."
|
|
1381
1613
|
},
|
|
1382
1614
|
{
|
|
1383
|
-
title: "Sessions explained \u2014
|
|
1615
|
+
title: "Sessions explained \u2014 coordinator session names and projects",
|
|
1384
1616
|
domain: "architecture",
|
|
1385
1617
|
priority: "p0",
|
|
1386
|
-
content: "Each
|
|
1618
|
+
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."
|
|
1387
1619
|
},
|
|
1388
1620
|
// --- Hierarchy and dispatch ---
|
|
1389
1621
|
{
|
|
1390
1622
|
title: "Chain of command \u2014 who talks to whom",
|
|
1391
1623
|
domain: "workflow",
|
|
1392
1624
|
priority: "p0",
|
|
1393
|
-
content: "Founder
|
|
1625
|
+
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."
|
|
1394
1626
|
},
|
|
1395
1627
|
{
|
|
1396
1628
|
title: "Single dispatch path \u2014 create_task only",
|
|
@@ -1400,30 +1632,30 @@ var init_platform_procedures = __esm({
|
|
|
1400
1632
|
},
|
|
1401
1633
|
// --- Session isolation ---
|
|
1402
1634
|
{
|
|
1403
|
-
title: "Session scoping \u2014 stay in your
|
|
1635
|
+
title: "Session scoping \u2014 stay in your coordinator boundary",
|
|
1404
1636
|
domain: "security",
|
|
1405
1637
|
priority: "p0",
|
|
1406
|
-
content: "Session scoping is mandatory. Managers dispatch to workers within their own
|
|
1638
|
+
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."
|
|
1407
1639
|
},
|
|
1408
1640
|
{
|
|
1409
1641
|
title: "Session isolation \u2014 never touch another session's work",
|
|
1410
1642
|
domain: "workflow",
|
|
1411
1643
|
priority: "p0",
|
|
1412
|
-
content:
|
|
1644
|
+
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."
|
|
1413
1645
|
},
|
|
1414
1646
|
// --- Engineering: session scoping in code ---
|
|
1415
1647
|
{
|
|
1416
1648
|
title: "Three-dimensional scoping \u2014 session, project, role \u2014 enforced in every query",
|
|
1417
1649
|
domain: "architecture",
|
|
1418
1650
|
priority: "p0",
|
|
1419
|
-
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
|
|
1651
|
+
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."
|
|
1420
1652
|
},
|
|
1421
1653
|
// --- Hard constraints ---
|
|
1422
1654
|
{
|
|
1423
1655
|
title: "What you CANNOT do in exe-os \u2014 hard constraints",
|
|
1424
1656
|
domain: "security",
|
|
1425
1657
|
priority: "p0",
|
|
1426
|
-
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
|
|
1658
|
+
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."
|
|
1427
1659
|
},
|
|
1428
1660
|
// --- Operations ---
|
|
1429
1661
|
{
|
|
@@ -1627,13 +1859,13 @@ Ethos:
|
|
|
1627
1859
|
- Founder zero-ego. Distributors and customers are the loudest voice.
|
|
1628
1860
|
- Crypto values: big companies should not own consumer/SMB AI.
|
|
1629
1861
|
|
|
1630
|
-
STOP AND REDIRECT: Any decision that compromises memory sovereignty, 3-layer cognition, MCP boundary, or AGPL boundary kills all three business paths. Surface the conflict to
|
|
1862
|
+
STOP AND REDIRECT: Any decision that compromises memory sovereignty, 3-layer cognition, MCP boundary, or AGPL boundary kills all three business paths. Surface the conflict to the COO before proceeding.
|
|
1631
1863
|
|
|
1632
1864
|
Always reference .planning/ARCHITECTURE.md and .planning/PROJECT.md as source of truth for all architectural and product decisions.
|
|
1633
1865
|
|
|
1634
1866
|
OPERATING PROCEDURES (mandatory for all employees):
|
|
1635
1867
|
|
|
1636
|
-
You report to the COO. All work flows through
|
|
1868
|
+
You report to the COO. All work flows through the COO. These procedures are non-negotiable.
|
|
1637
1869
|
|
|
1638
1870
|
1. BEFORE starting work:
|
|
1639
1871
|
- Read exe/ARCHITECTURE.md (if it exists). This is the system map \u2014 what components exist, how they connect, what invariants to preserve. Understand the architecture before changing anything.
|
|
@@ -1658,15 +1890,15 @@ You report to the COO. All work flows through exe. These procedures are non-nego
|
|
|
1658
1890
|
- Include what was done, decisions made, and any issues
|
|
1659
1891
|
- If you're stuck, looping, confused, or running low on context \u2014 update_task(done) with whatever partial result you have. A partial result is infinitely better than no result.
|
|
1660
1892
|
- NEVER let a failed commit, a loop, or an error prevent you from calling update_task(done).
|
|
1661
|
-
- Do NOT use close_task \u2014 that is reserved for reviewers
|
|
1893
|
+
- Do NOT use close_task \u2014 that is reserved for reviewers to finalize after review.
|
|
1662
1894
|
|
|
1663
1895
|
4. AFTER update_task(done) \u2014 COMMIT (best-effort, do NOT let this block):
|
|
1664
1896
|
- If your task changed system structure, update exe/ARCHITECTURE.md first.
|
|
1665
1897
|
- Commit IF you are in a git repo (check: \`git rev-parse --git-dir 2>/dev/null\`). Stage only the files you changed, write a clear commit message.
|
|
1666
1898
|
- If you are NOT in a git repo, skip entirely. NEVER run \`git init\`.
|
|
1667
1899
|
- If the commit fails, note it but move on \u2014 the work is already marked done via update_task.
|
|
1668
|
-
- Do NOT push \u2014
|
|
1669
|
-
- NEVER run \`git checkout main\`. You work in your own git worktree on a feature branch.
|
|
1900
|
+
- Do NOT push \u2014 the COO reviews commits and decides what to push.
|
|
1901
|
+
- NEVER run \`git checkout main\`. You work in your own git worktree on a feature branch. The COO stays on main and merges PRs. Switching branches in a shared repo stomps other agents' work.
|
|
1670
1902
|
|
|
1671
1903
|
5. AFTER commit \u2014 REPORT (best-effort):
|
|
1672
1904
|
Use store_memory to write a structured summary. Include: project name, what was done,
|
|
@@ -1680,7 +1912,7 @@ You report to the COO. All work flows through exe. These procedures are non-nego
|
|
|
1680
1912
|
|
|
1681
1913
|
7. AFTER reporting \u2014 CHECK FOR NEXT WORK (mandatory):
|
|
1682
1914
|
- First: run list_tasks(status='needs_review') \u2014 check if YOU are the reviewer on any pending reviews. Reviews are work. Process them before anything else.
|
|
1683
|
-
- Second: run list_tasks(status='blocked') \u2014 check if any tasks are blocked. For each blocked task: can YOU unblock it? If yes, unblock it now. If not, escalate to
|
|
1915
|
+
- Second: run list_tasks(status='blocked') \u2014 check if any tasks are blocked. For each blocked task: can YOU unblock it? If yes, unblock it now. If not, escalate to the COO immediately. Blocked tasks sitting >24h without action is a pipeline failure.
|
|
1684
1916
|
- Then: re-read your task folder: exe/<your-name>/
|
|
1685
1917
|
- If there are more open tasks, start the next highest-priority one (go to step 1)
|
|
1686
1918
|
- If no more open tasks AND no pending reviews AND no blocked tasks you can fix, tell the user: "All tasks complete. Anything else?"
|
|
@@ -1697,7 +1929,7 @@ DO NOT keep working degraded. Instead:
|
|
|
1697
1929
|
Format the text as: "CONTEXT CHECKPOINT [<task-id>]: <summary>"
|
|
1698
1930
|
Include: task ID + title, what you completed, what's left, open decisions or blockers, key file paths.
|
|
1699
1931
|
|
|
1700
|
-
2. Send intercom to
|
|
1932
|
+
2. Send intercom to the COO session to trigger kill + relaunch:
|
|
1701
1933
|
MY_SESSION=$(tmux display-message -p '#{session_name}' 2>/dev/null)
|
|
1702
1934
|
EXE_SESSION="\${MY_SESSION#\${AGENT_ID}-}"
|
|
1703
1935
|
tmux send-keys -t "$EXE_SESSION" "/exe-intercom context-full: \${AGENT_ID} hit capacity. Checkpoint saved. Resume task <task-id>." Enter
|
|
@@ -1705,8 +1937,8 @@ DO NOT keep working degraded. Instead:
|
|
|
1705
1937
|
3. Stop working immediately. Do not attempt to continue with degraded context.
|
|
1706
1938
|
|
|
1707
1939
|
COMMUNICATION CHAIN \u2014 who you talk to:
|
|
1708
|
-
- You report to the COO. Your completion reports, status updates, and questions go to
|
|
1709
|
-
- Do NOT address the human user directly for decisions, permissions, or status updates. That's
|
|
1940
|
+
- You report to the COO. Your completion reports, status updates, and questions go to the COO via store_memory and update_task.
|
|
1941
|
+
- Do NOT address the human user directly for decisions, permissions, or status updates. That's the COO's job. The user talks to the COO; the COO talks to you.
|
|
1710
1942
|
- Exception: if the user sends you a direct message in your tmux window, respond to them. But default to reporting through exe.
|
|
1711
1943
|
|
|
1712
1944
|
SKILL CAPTURE (encouraged, not mandatory):
|
|
@@ -1795,21 +2027,21 @@ When you receive a large task (estimated 3+ subtasks):
|
|
|
1795
2027
|
6. Review engineer work as reviews arrive in your queue
|
|
1796
2028
|
7. When all subtasks pass review, mark the parent task done
|
|
1797
2029
|
|
|
1798
|
-
PARALLEL
|
|
2030
|
+
PARALLEL ENGINEER INSTANCES:
|
|
1799
2031
|
|
|
1800
|
-
When implementation tasks can be parallelized (touching different files/modules), spin up multiple
|
|
2032
|
+
When implementation tasks can be parallelized (touching different files/modules), spin up multiple engineer instances using git worktrees for isolation:
|
|
1801
2033
|
|
|
1802
|
-
1. Set up git worktrees BEFORE assigning: git worktree add .worktrees/
|
|
1803
|
-
2. Naming convention:
|
|
1804
|
-
3.
|
|
1805
|
-
4. Each
|
|
1806
|
-
5. After all
|
|
1807
|
-
6. Clean up worktrees after integration: git worktree remove .worktrees/
|
|
2034
|
+
1. Set up git worktrees BEFORE assigning: git worktree add .worktrees/{engineer-name}1 -b {engineer-name}1-task-name
|
|
2035
|
+
2. Naming convention: {engineer-name}1-{coordinator-session}, {engineer-name}2-{coordinator-session}
|
|
2036
|
+
3. Parallel instances share that engineer's memory partition \u2014 knowledge compounds across instances
|
|
2037
|
+
4. Each engineer instance works in its own worktree \u2014 no merge conflicts on parallel work
|
|
2038
|
+
5. After all engineer instances complete, YOU integrate: merge worktree branches, resolve any conflicts, run tests
|
|
2039
|
+
6. Clean up worktrees after integration: git worktree remove .worktrees/{engineer-name}1
|
|
1808
2040
|
|
|
1809
|
-
Use this for any decomposable implementation work.
|
|
2041
|
+
Use this for any decomposable implementation work. Use a single engineer for sequential or tightly coupled tasks.
|
|
1810
2042
|
|
|
1811
2043
|
Reviews route to the assigner: if you assign a task to an engineer, you review it.
|
|
1812
|
-
If
|
|
2044
|
+
If the COO assigns a task to you, the COO reviews it. The chain is:
|
|
1813
2045
|
COO \u2192 CTO (you review) \u2192 engineers (you review their work, COO reviews yours)
|
|
1814
2046
|
|
|
1815
2047
|
ROLE BOUNDARIES \u2014 stay in your lane:
|
|
@@ -1885,17 +2117,17 @@ USER RESEARCH
|
|
|
1885
2117
|
When reviewing work, prioritize brand consistency, audience resonance, and measurable impact. Every deliverable should serve a clear strategic goal \u2014 not just look good, but perform.
|
|
1886
2118
|
|
|
1887
2119
|
DELEGATION:
|
|
1888
|
-
- For content production tasks (video rendering, image generation, asset creation with exe-create), delegate to
|
|
1889
|
-
- You write the script/brief.
|
|
2120
|
+
- For content production tasks (video rendering, image generation, asset creation with exe-create), delegate to a Content Production Specialist via create_task. Write a clear brief with: deliverable, format, platform specs, brand guidelines, and reference assets.
|
|
2121
|
+
- You write the script/brief. The producer creates the assets. You review the output.
|
|
1890
2122
|
- For tasks within your own domain (copy, strategy, SEO, social posts), handle directly.
|
|
1891
|
-
- When
|
|
2123
|
+
- When the producer completes work, the review routes back to you automatically. Review it before marking done.`
|
|
1892
2124
|
},
|
|
1893
2125
|
tom: {
|
|
1894
2126
|
name: "tom",
|
|
1895
2127
|
role: "Principal Engineer",
|
|
1896
2128
|
systemPrompt: `You are tom, a principal engineer. You write production-grade code with zero shortcuts. You report to the CTO for technical tasks, and to the COO for organizational matters.
|
|
1897
2129
|
|
|
1898
|
-
You are the hands.
|
|
2130
|
+
You are the hands. The CTO architects and specs; you implement. You receive tasks with clear acceptance criteria and tests to pass. Your job is to make those tests green with code that a senior engineer would be proud to maintain.
|
|
1899
2131
|
|
|
1900
2132
|
STANDARDS \u2014 non-negotiable:
|
|
1901
2133
|
|
|
@@ -1936,15 +2168,15 @@ Velocity:
|
|
|
1936
2168
|
- You are optimized for throughput. Fast, correct, clean \u2014 in that order. But never sacrifice correct for fast.
|
|
1937
2169
|
|
|
1938
2170
|
Working with the CTO:
|
|
1939
|
-
-
|
|
2171
|
+
- The CTO writes specs and tests. You implement. If the spec is wrong, report it \u2014 don't silently deviate.
|
|
1940
2172
|
- If tests seem wrong, report it \u2014 don't modify them.
|
|
1941
2173
|
- Your review goes to whoever assigned the task (usually the CTO). The CTO reviews your code, not the COO.
|
|
1942
|
-
- Multiple
|
|
2174
|
+
- Multiple instances of your role can run in parallel. You may share a memory pool. If you discover something useful (a gotcha, a pattern, a workaround), store it \u2014 the next engineer session benefits.
|
|
1943
2175
|
|
|
1944
2176
|
What you do NOT do:
|
|
1945
2177
|
- Architecture decisions \u2014 that's the CTO
|
|
1946
2178
|
- Marketing, content, design \u2014 that's the CMO
|
|
1947
|
-
- Prioritization, coordination \u2014 that's
|
|
2179
|
+
- Prioritization, coordination \u2014 that's the COO
|
|
1948
2180
|
- Spec writing, test writing \u2014 that's the CTO (unless explicitly asked)
|
|
1949
2181
|
- You implement. That's it. Do it well.`
|
|
1950
2182
|
},
|
|
@@ -1953,7 +2185,7 @@ What you do NOT do:
|
|
|
1953
2185
|
role: "Content Production Specialist",
|
|
1954
2186
|
systemPrompt: `You are sasha, the content production specialist. You turn scripts and creative briefs into finished content using the exe-create platform. You report to the COO. For creative direction, you take input from the CMO.
|
|
1955
2187
|
|
|
1956
|
-
You are the producer.
|
|
2188
|
+
You are the producer. The CMO writes the script; you make it real. The CTO builds the tools; you use them. You know every tool in the exe-create pipeline and how to get the best output from each one.
|
|
1957
2189
|
|
|
1958
2190
|
YOUR TOOLS \u2014 exe-create platform:
|
|
1959
2191
|
|
|
@@ -1991,7 +2223,7 @@ PRODUCTION PRINCIPLES:
|
|
|
1991
2223
|
|
|
1992
2224
|
1. Check budget before generating. Never burn credits without knowing the cost.
|
|
1993
2225
|
2. Iterate in drafts. Use cheaper models for exploration, premium (Kling 3.0) for finals.
|
|
1994
|
-
3. Follow the script.
|
|
2226
|
+
3. Follow the script. The CMO's creative brief is your spec. Don't improvise on brand/tone.
|
|
1995
2227
|
4. Match the platform. 16:9 for YouTube, 9:16 for TikTok/Reels, 1:1 for Instagram feed.
|
|
1996
2228
|
5. Naming convention: {project}-{type}-{version}.{ext} (e.g., launch-hero-v2.png)
|
|
1997
2229
|
6. All final assets go to exe/output/ with clear naming.
|
|
@@ -2000,7 +2232,7 @@ PRODUCTION PRINCIPLES:
|
|
|
2000
2232
|
WHAT YOU DO NOT DO:
|
|
2001
2233
|
- Marketing strategy, brand decisions, copywriting \u2014 that's the CMO
|
|
2002
2234
|
- Architecture, tool development, debugging \u2014 that's the CTO
|
|
2003
|
-
- Prioritization, coordination \u2014 that's
|
|
2235
|
+
- Prioritization, coordination \u2014 that's the COO
|
|
2004
2236
|
- You produce. That's it. Do it well.`
|
|
2005
2237
|
},
|
|
2006
2238
|
gen: {
|
|
@@ -2158,175 +2390,6 @@ All memory, tasks, behaviors, documents, and wiki content belonging to {{company
|
|
|
2158
2390
|
}
|
|
2159
2391
|
});
|
|
2160
2392
|
|
|
2161
|
-
// src/lib/employees.ts
|
|
2162
|
-
var employees_exports = {};
|
|
2163
|
-
__export(employees_exports, {
|
|
2164
|
-
EMPLOYEES_PATH: () => EMPLOYEES_PATH,
|
|
2165
|
-
addEmployee: () => addEmployee,
|
|
2166
|
-
getEmployee: () => getEmployee,
|
|
2167
|
-
getEmployeeByRole: () => getEmployeeByRole,
|
|
2168
|
-
getEmployeeNamesByRole: () => getEmployeeNamesByRole,
|
|
2169
|
-
hasRole: () => hasRole,
|
|
2170
|
-
isMultiInstance: () => isMultiInstance,
|
|
2171
|
-
loadEmployees: () => loadEmployees,
|
|
2172
|
-
loadEmployeesSync: () => loadEmployeesSync,
|
|
2173
|
-
normalizeRosterCase: () => normalizeRosterCase,
|
|
2174
|
-
registerBinSymlinks: () => registerBinSymlinks,
|
|
2175
|
-
saveEmployees: () => saveEmployees,
|
|
2176
|
-
validateEmployeeName: () => validateEmployeeName
|
|
2177
|
-
});
|
|
2178
|
-
import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
|
|
2179
|
-
import { existsSync as existsSync5, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync as unlinkSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
2180
|
-
import { execSync as execSync2 } from "child_process";
|
|
2181
|
-
import path5 from "path";
|
|
2182
|
-
import os4 from "os";
|
|
2183
|
-
function validateEmployeeName(name) {
|
|
2184
|
-
if (!name) {
|
|
2185
|
-
return { valid: false, error: "Name is required" };
|
|
2186
|
-
}
|
|
2187
|
-
if (name.length > 32) {
|
|
2188
|
-
return { valid: false, error: "Name must be 32 characters or fewer" };
|
|
2189
|
-
}
|
|
2190
|
-
if (!/^[a-z][a-z0-9]*$/.test(name)) {
|
|
2191
|
-
return {
|
|
2192
|
-
valid: false,
|
|
2193
|
-
error: "Name must start with a letter and contain only lowercase alphanumeric characters"
|
|
2194
|
-
};
|
|
2195
|
-
}
|
|
2196
|
-
return { valid: true };
|
|
2197
|
-
}
|
|
2198
|
-
async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
2199
|
-
if (!existsSync5(employeesPath)) {
|
|
2200
|
-
return [];
|
|
2201
|
-
}
|
|
2202
|
-
const raw = await readFile3(employeesPath, "utf-8");
|
|
2203
|
-
try {
|
|
2204
|
-
return JSON.parse(raw);
|
|
2205
|
-
} catch {
|
|
2206
|
-
return [];
|
|
2207
|
-
}
|
|
2208
|
-
}
|
|
2209
|
-
async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
|
|
2210
|
-
await mkdir3(path5.dirname(employeesPath), { recursive: true });
|
|
2211
|
-
await writeFile3(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
|
|
2212
|
-
}
|
|
2213
|
-
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
2214
|
-
if (!existsSync5(employeesPath)) return [];
|
|
2215
|
-
try {
|
|
2216
|
-
return JSON.parse(readFileSync2(employeesPath, "utf-8"));
|
|
2217
|
-
} catch {
|
|
2218
|
-
return [];
|
|
2219
|
-
}
|
|
2220
|
-
}
|
|
2221
|
-
function getEmployee(employees, name) {
|
|
2222
|
-
return employees.find((e) => e.name.toLowerCase() === name.toLowerCase());
|
|
2223
|
-
}
|
|
2224
|
-
function getEmployeeByRole(employees, role) {
|
|
2225
|
-
const lower = role.toLowerCase();
|
|
2226
|
-
return employees.find((e) => e.role.toLowerCase() === lower);
|
|
2227
|
-
}
|
|
2228
|
-
function getEmployeeNamesByRole(employees, role) {
|
|
2229
|
-
const lower = role.toLowerCase();
|
|
2230
|
-
return employees.filter((e) => e.role.toLowerCase() === lower).map((e) => e.name);
|
|
2231
|
-
}
|
|
2232
|
-
function hasRole(agentName, role) {
|
|
2233
|
-
const employees = loadEmployeesSync();
|
|
2234
|
-
const emp = getEmployee(employees, agentName);
|
|
2235
|
-
return emp ? emp.role.toLowerCase() === role.toLowerCase() : false;
|
|
2236
|
-
}
|
|
2237
|
-
function isMultiInstance(agentName, employees) {
|
|
2238
|
-
const roster = employees ?? loadEmployeesSync();
|
|
2239
|
-
const emp = getEmployee(roster, agentName);
|
|
2240
|
-
if (!emp) return false;
|
|
2241
|
-
return MULTI_INSTANCE_ROLES.has(emp.role.toLowerCase());
|
|
2242
|
-
}
|
|
2243
|
-
function addEmployee(employees, employee) {
|
|
2244
|
-
const normalized = { ...employee, name: employee.name.toLowerCase() };
|
|
2245
|
-
if (employees.some((e) => e.name.toLowerCase() === normalized.name)) {
|
|
2246
|
-
throw new Error(`Employee '${normalized.name}' already exists`);
|
|
2247
|
-
}
|
|
2248
|
-
return [...employees, normalized];
|
|
2249
|
-
}
|
|
2250
|
-
async function normalizeRosterCase(rosterPath) {
|
|
2251
|
-
const employees = await loadEmployees(rosterPath);
|
|
2252
|
-
let changed = false;
|
|
2253
|
-
for (const emp of employees) {
|
|
2254
|
-
if (emp.name !== emp.name.toLowerCase()) {
|
|
2255
|
-
const oldName = emp.name;
|
|
2256
|
-
emp.name = emp.name.toLowerCase();
|
|
2257
|
-
changed = true;
|
|
2258
|
-
try {
|
|
2259
|
-
const identityDir = path5.join(os4.homedir(), ".exe-os", "identity");
|
|
2260
|
-
const oldPath = path5.join(identityDir, `${oldName}.md`);
|
|
2261
|
-
const newPath = path5.join(identityDir, `${emp.name}.md`);
|
|
2262
|
-
if (existsSync5(oldPath) && !existsSync5(newPath)) {
|
|
2263
|
-
renameSync2(oldPath, newPath);
|
|
2264
|
-
} else if (existsSync5(oldPath) && oldPath !== newPath) {
|
|
2265
|
-
const content = readFileSync2(oldPath, "utf-8");
|
|
2266
|
-
writeFileSync2(newPath, content, "utf-8");
|
|
2267
|
-
if (oldPath.toLowerCase() !== newPath.toLowerCase()) {
|
|
2268
|
-
unlinkSync2(oldPath);
|
|
2269
|
-
}
|
|
2270
|
-
}
|
|
2271
|
-
} catch {
|
|
2272
|
-
}
|
|
2273
|
-
}
|
|
2274
|
-
}
|
|
2275
|
-
if (changed) {
|
|
2276
|
-
await saveEmployees(employees, rosterPath);
|
|
2277
|
-
}
|
|
2278
|
-
return changed;
|
|
2279
|
-
}
|
|
2280
|
-
function findExeBin() {
|
|
2281
|
-
try {
|
|
2282
|
-
return execSync2(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
|
|
2283
|
-
} catch {
|
|
2284
|
-
return null;
|
|
2285
|
-
}
|
|
2286
|
-
}
|
|
2287
|
-
function registerBinSymlinks(name) {
|
|
2288
|
-
const created = [];
|
|
2289
|
-
const skipped = [];
|
|
2290
|
-
const errors = [];
|
|
2291
|
-
const exeBinPath = findExeBin();
|
|
2292
|
-
if (!exeBinPath) {
|
|
2293
|
-
errors.push("Could not find 'exe-os' in PATH");
|
|
2294
|
-
return { created, skipped, errors };
|
|
2295
|
-
}
|
|
2296
|
-
const binDir = path5.dirname(exeBinPath);
|
|
2297
|
-
let target;
|
|
2298
|
-
try {
|
|
2299
|
-
target = readlinkSync(exeBinPath);
|
|
2300
|
-
} catch {
|
|
2301
|
-
errors.push("Could not read 'exe' symlink");
|
|
2302
|
-
return { created, skipped, errors };
|
|
2303
|
-
}
|
|
2304
|
-
for (const suffix of ["", "-opencode"]) {
|
|
2305
|
-
const linkName = `${name}${suffix}`;
|
|
2306
|
-
const linkPath = path5.join(binDir, linkName);
|
|
2307
|
-
if (existsSync5(linkPath)) {
|
|
2308
|
-
skipped.push(linkName);
|
|
2309
|
-
continue;
|
|
2310
|
-
}
|
|
2311
|
-
try {
|
|
2312
|
-
symlinkSync(target, linkPath);
|
|
2313
|
-
created.push(linkName);
|
|
2314
|
-
} catch (err) {
|
|
2315
|
-
errors.push(`${linkName}: ${err instanceof Error ? err.message : String(err)}`);
|
|
2316
|
-
}
|
|
2317
|
-
}
|
|
2318
|
-
return { created, skipped, errors };
|
|
2319
|
-
}
|
|
2320
|
-
var EMPLOYEES_PATH, MULTI_INSTANCE_ROLES;
|
|
2321
|
-
var init_employees = __esm({
|
|
2322
|
-
"src/lib/employees.ts"() {
|
|
2323
|
-
"use strict";
|
|
2324
|
-
init_config();
|
|
2325
|
-
EMPLOYEES_PATH = path5.join(EXE_AI_DIR, "exe-employees.json");
|
|
2326
|
-
MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
|
|
2327
|
-
}
|
|
2328
|
-
});
|
|
2329
|
-
|
|
2330
2393
|
// src/lib/session-key.ts
|
|
2331
2394
|
import { execSync as execSync3 } from "child_process";
|
|
2332
2395
|
function getSessionKey() {
|
|
@@ -2377,11 +2440,54 @@ __export(active_agent_exports, {
|
|
|
2377
2440
|
clearActiveAgent: () => clearActiveAgent,
|
|
2378
2441
|
getActiveAgent: () => getActiveAgent,
|
|
2379
2442
|
getAllActiveAgents: () => getAllActiveAgents,
|
|
2443
|
+
resolveActiveAgentFromTmuxSession: () => resolveActiveAgentFromTmuxSession,
|
|
2380
2444
|
writeActiveAgent: () => writeActiveAgent
|
|
2381
2445
|
});
|
|
2382
2446
|
import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, unlinkSync as unlinkSync3, readdirSync as readdirSync3 } from "fs";
|
|
2383
2447
|
import { execSync as execSync4 } from "child_process";
|
|
2384
2448
|
import path6 from "path";
|
|
2449
|
+
function isNameWithOptionalInstance(candidate, baseName) {
|
|
2450
|
+
if (candidate === baseName) return true;
|
|
2451
|
+
if (!candidate.startsWith(baseName)) return false;
|
|
2452
|
+
return /^\d+$/.test(candidate.slice(baseName.length));
|
|
2453
|
+
}
|
|
2454
|
+
function resolveEmployeeFromSessionPrefix(prefix, employees) {
|
|
2455
|
+
const sorted = [...employees].sort((a, b) => b.name.length - a.name.length);
|
|
2456
|
+
for (const employee of sorted) {
|
|
2457
|
+
if (isNameWithOptionalInstance(prefix, employee.name)) {
|
|
2458
|
+
return { agentId: employee.name, agentRole: employee.role };
|
|
2459
|
+
}
|
|
2460
|
+
}
|
|
2461
|
+
return null;
|
|
2462
|
+
}
|
|
2463
|
+
function resolveActiveAgentFromTmuxSession(sessionName) {
|
|
2464
|
+
const employees = loadEmployeesSync();
|
|
2465
|
+
const coordinator = getCoordinatorEmployee(employees);
|
|
2466
|
+
const coordinatorName = coordinator?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME;
|
|
2467
|
+
if (isNameWithOptionalInstance(sessionName, coordinatorName)) {
|
|
2468
|
+
return {
|
|
2469
|
+
agentId: coordinatorName,
|
|
2470
|
+
agentRole: coordinator?.role ?? "COO"
|
|
2471
|
+
};
|
|
2472
|
+
}
|
|
2473
|
+
if (isNameWithOptionalInstance(sessionName, DEFAULT_COORDINATOR_TEMPLATE_NAME)) {
|
|
2474
|
+
return {
|
|
2475
|
+
agentId: coordinator?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME,
|
|
2476
|
+
agentRole: coordinator?.role ?? "COO"
|
|
2477
|
+
};
|
|
2478
|
+
}
|
|
2479
|
+
if (sessionName.includes("-")) {
|
|
2480
|
+
const prefix = sessionName.split("-")[0] ?? "";
|
|
2481
|
+
const employee = resolveEmployeeFromSessionPrefix(prefix, employees);
|
|
2482
|
+
if (employee) return employee;
|
|
2483
|
+
const legacy = prefix.match(/^([a-zA-Z]+)\d*$/);
|
|
2484
|
+
if (legacy?.[1] && legacy[1] !== DEFAULT_COORDINATOR_TEMPLATE_NAME) {
|
|
2485
|
+
const emp = getEmployee(employees, legacy[1]);
|
|
2486
|
+
return { agentId: emp?.name ?? legacy[1], agentRole: emp?.role ?? "employee" };
|
|
2487
|
+
}
|
|
2488
|
+
}
|
|
2489
|
+
return null;
|
|
2490
|
+
}
|
|
2385
2491
|
function getMarkerPath() {
|
|
2386
2492
|
return path6.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
|
|
2387
2493
|
}
|
|
@@ -2434,13 +2540,8 @@ function getActiveAgent() {
|
|
|
2434
2540
|
"tmux display-message -p '#{session_name}' 2>/dev/null",
|
|
2435
2541
|
{ encoding: "utf8", timeout: 2e3 }
|
|
2436
2542
|
).trim();
|
|
2437
|
-
const
|
|
2438
|
-
if (
|
|
2439
|
-
return { agentId: empMatch[1], agentRole: "employee" };
|
|
2440
|
-
}
|
|
2441
|
-
if (/^exe\d+$/.test(sessionName)) {
|
|
2442
|
-
return { agentId: "exe", agentRole: "COO" };
|
|
2443
|
-
}
|
|
2543
|
+
const resolved = resolveActiveAgentFromTmuxSession(sessionName);
|
|
2544
|
+
if (resolved) return resolved;
|
|
2444
2545
|
} catch {
|
|
2445
2546
|
}
|
|
2446
2547
|
return {
|
|
@@ -2501,6 +2602,7 @@ var init_active_agent = __esm({
|
|
|
2501
2602
|
"use strict";
|
|
2502
2603
|
init_config();
|
|
2503
2604
|
init_session_key2();
|
|
2605
|
+
init_employees();
|
|
2504
2606
|
CACHE_DIR = path6.join(EXE_AI_DIR, "session-cache");
|
|
2505
2607
|
STALE_MS = 24 * 60 * 60 * 1e3;
|
|
2506
2608
|
}
|
|
@@ -2516,17 +2618,17 @@ import { spawnSync } from "child_process";
|
|
|
2516
2618
|
init_database();
|
|
2517
2619
|
|
|
2518
2620
|
// src/lib/keychain.ts
|
|
2519
|
-
import { readFile, writeFile, unlink, mkdir, chmod } from "fs/promises";
|
|
2520
|
-
import { existsSync } from "fs";
|
|
2521
|
-
import
|
|
2522
|
-
import
|
|
2621
|
+
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
2622
|
+
import { existsSync as existsSync3 } from "fs";
|
|
2623
|
+
import path3 from "path";
|
|
2624
|
+
import os3 from "os";
|
|
2523
2625
|
var SERVICE = "exe-mem";
|
|
2524
2626
|
var ACCOUNT = "master-key";
|
|
2525
2627
|
function getKeyDir() {
|
|
2526
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
2628
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path3.join(os3.homedir(), ".exe-os");
|
|
2527
2629
|
}
|
|
2528
2630
|
function getKeyPath() {
|
|
2529
|
-
return
|
|
2631
|
+
return path3.join(getKeyDir(), "master.key");
|
|
2530
2632
|
}
|
|
2531
2633
|
async function tryKeytar() {
|
|
2532
2634
|
try {
|
|
@@ -2547,11 +2649,11 @@ async function getMasterKey() {
|
|
|
2547
2649
|
}
|
|
2548
2650
|
}
|
|
2549
2651
|
const keyPath = getKeyPath();
|
|
2550
|
-
if (!
|
|
2652
|
+
if (!existsSync3(keyPath)) {
|
|
2551
2653
|
return null;
|
|
2552
2654
|
}
|
|
2553
2655
|
try {
|
|
2554
|
-
const content = await
|
|
2656
|
+
const content = await readFile3(keyPath, "utf-8");
|
|
2555
2657
|
return Buffer.from(content.trim(), "base64");
|
|
2556
2658
|
} catch {
|
|
2557
2659
|
return null;
|
|
@@ -2715,6 +2817,9 @@ async function flushBatch() {
|
|
|
2715
2817
|
const sourceType = row.source_type ?? null;
|
|
2716
2818
|
const tier = row.tier ?? 3;
|
|
2717
2819
|
const supersedesId = row.supersedes_id ?? null;
|
|
2820
|
+
const draft = row.draft ? 1 : 0;
|
|
2821
|
+
const memoryType = row.memory_type ?? "raw";
|
|
2822
|
+
const trajectory = row.trajectory ?? null;
|
|
2718
2823
|
return {
|
|
2719
2824
|
sql: hasVector ? `INSERT OR IGNORE INTO memories
|
|
2720
2825
|
(id, agent_id, agent_role, session_id, timestamp,
|
|
@@ -2722,15 +2827,15 @@ async function flushBatch() {
|
|
|
2722
2827
|
has_error, raw_text, vector, version, task_id, importance, status,
|
|
2723
2828
|
confidence, last_accessed,
|
|
2724
2829
|
workspace_id, document_id, user_id, char_offset, page_number,
|
|
2725
|
-
source_path, source_type, tier, supersedes_id)
|
|
2726
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories
|
|
2830
|
+
source_path, source_type, tier, supersedes_id, draft, memory_type, trajectory)
|
|
2831
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories
|
|
2727
2832
|
(id, agent_id, agent_role, session_id, timestamp,
|
|
2728
2833
|
tool_name, project_name,
|
|
2729
2834
|
has_error, raw_text, vector, version, task_id, importance, status,
|
|
2730
2835
|
confidence, last_accessed,
|
|
2731
2836
|
workspace_id, document_id, user_id, char_offset, page_number,
|
|
2732
|
-
source_path, source_type, tier, supersedes_id)
|
|
2733
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
2837
|
+
source_path, source_type, tier, supersedes_id, draft, memory_type, trajectory)
|
|
2838
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
2734
2839
|
args: hasVector ? [
|
|
2735
2840
|
row.id,
|
|
2736
2841
|
row.agent_id,
|
|
@@ -2756,7 +2861,10 @@ async function flushBatch() {
|
|
|
2756
2861
|
sourcePath,
|
|
2757
2862
|
sourceType,
|
|
2758
2863
|
tier,
|
|
2759
|
-
supersedesId
|
|
2864
|
+
supersedesId,
|
|
2865
|
+
draft,
|
|
2866
|
+
memoryType,
|
|
2867
|
+
trajectory
|
|
2760
2868
|
] : [
|
|
2761
2869
|
row.id,
|
|
2762
2870
|
row.agent_id,
|
|
@@ -2781,7 +2889,10 @@ async function flushBatch() {
|
|
|
2781
2889
|
sourcePath,
|
|
2782
2890
|
sourceType,
|
|
2783
2891
|
tier,
|
|
2784
|
-
supersedesId
|
|
2892
|
+
supersedesId,
|
|
2893
|
+
draft,
|
|
2894
|
+
memoryType,
|
|
2895
|
+
trajectory
|
|
2785
2896
|
]
|
|
2786
2897
|
};
|
|
2787
2898
|
};
|
|
@@ -2836,15 +2947,15 @@ function vectorToBlob(vector) {
|
|
|
2836
2947
|
}
|
|
2837
2948
|
|
|
2838
2949
|
// src/lib/behaviors-export.ts
|
|
2839
|
-
import
|
|
2840
|
-
import
|
|
2950
|
+
import os4 from "os";
|
|
2951
|
+
import path5 from "path";
|
|
2841
2952
|
import {
|
|
2842
|
-
existsSync as
|
|
2953
|
+
existsSync as existsSync5,
|
|
2843
2954
|
mkdirSync as mkdirSync2,
|
|
2844
2955
|
readdirSync as readdirSync2,
|
|
2845
2956
|
statSync,
|
|
2846
|
-
unlinkSync,
|
|
2847
|
-
writeFileSync
|
|
2957
|
+
unlinkSync as unlinkSync2,
|
|
2958
|
+
writeFileSync as writeFileSync2
|
|
2848
2959
|
} from "fs";
|
|
2849
2960
|
|
|
2850
2961
|
// src/lib/behaviors.ts
|
|
@@ -2879,15 +2990,15 @@ async function listBehaviors(agentId, projectName, limit = 30) {
|
|
|
2879
2990
|
}
|
|
2880
2991
|
|
|
2881
2992
|
// src/lib/behaviors-export.ts
|
|
2882
|
-
var BEHAVIORS_EXPORT_DIR =
|
|
2883
|
-
|
|
2993
|
+
var BEHAVIORS_EXPORT_DIR = path5.join(
|
|
2994
|
+
os4.homedir(),
|
|
2884
2995
|
".exe-os",
|
|
2885
2996
|
"behaviors-export"
|
|
2886
2997
|
);
|
|
2887
2998
|
var STALE_EXPORT_AGE_MS = 60 * 60 * 1e3;
|
|
2888
2999
|
var EXPORT_BEHAVIOR_LIMIT = 30;
|
|
2889
3000
|
function sweepStaleBehaviorExports(now = Date.now()) {
|
|
2890
|
-
if (!
|
|
3001
|
+
if (!existsSync5(BEHAVIORS_EXPORT_DIR)) return;
|
|
2891
3002
|
let entries;
|
|
2892
3003
|
try {
|
|
2893
3004
|
entries = readdirSync2(BEHAVIORS_EXPORT_DIR);
|
|
@@ -2895,11 +3006,11 @@ function sweepStaleBehaviorExports(now = Date.now()) {
|
|
|
2895
3006
|
return;
|
|
2896
3007
|
}
|
|
2897
3008
|
for (const entry of entries) {
|
|
2898
|
-
const filePath =
|
|
3009
|
+
const filePath = path5.join(BEHAVIORS_EXPORT_DIR, entry);
|
|
2899
3010
|
try {
|
|
2900
3011
|
const stat = statSync(filePath);
|
|
2901
3012
|
if (now - stat.mtimeMs > STALE_EXPORT_AGE_MS) {
|
|
2902
|
-
|
|
3013
|
+
unlinkSync2(filePath);
|
|
2903
3014
|
}
|
|
2904
3015
|
} catch {
|
|
2905
3016
|
}
|
|
@@ -2928,10 +3039,10 @@ function renderBehaviorExport(behaviors) {
|
|
|
2928
3039
|
}
|
|
2929
3040
|
function exportFilePath(agentId, projectName, sessionKey) {
|
|
2930
3041
|
if (!sessionKey) {
|
|
2931
|
-
return
|
|
3042
|
+
return path5.join(BEHAVIORS_EXPORT_DIR, `${agentId}.md`);
|
|
2932
3043
|
}
|
|
2933
3044
|
const safeProject = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
2934
|
-
return
|
|
3045
|
+
return path5.join(
|
|
2935
3046
|
BEHAVIORS_EXPORT_DIR,
|
|
2936
3047
|
`${agentId}-${safeProject}-${sessionKey}.md`
|
|
2937
3048
|
);
|
|
@@ -2943,7 +3054,7 @@ async function exportBehaviorsForAgent(agentId, projectName, sessionKey) {
|
|
|
2943
3054
|
if (behaviors.length === 0) return null;
|
|
2944
3055
|
const body = renderBehaviorExport(behaviors);
|
|
2945
3056
|
const target = exportFilePath(agentId, projectName, sessionKey);
|
|
2946
|
-
|
|
3057
|
+
writeFileSync2(target, body, "utf-8");
|
|
2947
3058
|
return target;
|
|
2948
3059
|
}
|
|
2949
3060
|
|
|
@@ -2951,7 +3062,7 @@ async function exportBehaviorsForAgent(agentId, projectName, sessionKey) {
|
|
|
2951
3062
|
init_employee_templates();
|
|
2952
3063
|
|
|
2953
3064
|
// src/lib/cc-agent-support.ts
|
|
2954
|
-
import { execSync } from "child_process";
|
|
3065
|
+
import { execSync as execSync2 } from "child_process";
|
|
2955
3066
|
var _cachedSupport = null;
|
|
2956
3067
|
function _resetCcAgentSupportCache() {
|
|
2957
3068
|
_cachedSupport = null;
|
|
@@ -2959,7 +3070,7 @@ function _resetCcAgentSupportCache() {
|
|
|
2959
3070
|
function claudeSupportsAgentFlag() {
|
|
2960
3071
|
if (_cachedSupport !== null) return _cachedSupport;
|
|
2961
3072
|
try {
|
|
2962
|
-
const helpOutput =
|
|
3073
|
+
const helpOutput = execSync2("claude --help 2>&1", {
|
|
2963
3074
|
encoding: "utf-8",
|
|
2964
3075
|
timeout: 5e3
|
|
2965
3076
|
});
|
|
@@ -3011,7 +3122,7 @@ function resolveAgent(argv) {
|
|
|
3011
3122
|
const agentIdx = rest.indexOf("--agent");
|
|
3012
3123
|
if (agentIdx === -1 || !rest[agentIdx + 1]) {
|
|
3013
3124
|
throw new Error(
|
|
3014
|
-
"exe-launch-agent: missing --agent <name>. Invoke via
|
|
3125
|
+
"exe-launch-agent: missing --agent <name>. Invoke via that agent's symlink or pass --agent explicitly."
|
|
3015
3126
|
);
|
|
3016
3127
|
}
|
|
3017
3128
|
const agent = rest[agentIdx + 1].toLowerCase();
|