@askexenow/exe-os 0.9.113 → 0.9.115
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/agentic-ontology-backfill.js +36 -12
- package/dist/bin/agentic-reflection-backfill.js +36 -12
- package/dist/bin/agentic-semantic-label.js +36 -12
- package/dist/bin/backfill-conversations.js +36 -12
- package/dist/bin/backfill-responses.js +36 -12
- package/dist/bin/backfill-vectors.js +36 -12
- package/dist/bin/bulk-sync-postgres.js +36 -12
- package/dist/bin/cleanup-stale-review-tasks.js +470 -113
- package/dist/bin/cli.js +413 -62
- package/dist/bin/exe-agent.js +27 -0
- package/dist/bin/exe-assign.js +36 -12
- package/dist/bin/exe-boot.js +246 -54
- package/dist/bin/exe-call.js +8 -0
- package/dist/bin/exe-cloud.js +47 -12
- package/dist/bin/exe-dispatch.js +348 -53
- package/dist/bin/exe-doctor.js +51 -13
- package/dist/bin/exe-export-behaviors.js +37 -12
- package/dist/bin/exe-forget.js +36 -12
- package/dist/bin/exe-gateway.js +348 -53
- package/dist/bin/exe-heartbeat.js +471 -113
- package/dist/bin/exe-kill.js +36 -12
- package/dist/bin/exe-launch-agent.js +117 -18
- package/dist/bin/exe-new-employee.js +9 -1
- package/dist/bin/exe-pending-messages.js +452 -95
- package/dist/bin/exe-pending-notifications.js +452 -95
- package/dist/bin/exe-pending-reviews.js +452 -95
- package/dist/bin/exe-rename.js +36 -12
- package/dist/bin/exe-review.js +36 -12
- package/dist/bin/exe-search.js +37 -12
- package/dist/bin/exe-session-cleanup.js +348 -53
- package/dist/bin/exe-settings.js +12 -0
- package/dist/bin/exe-start-codex.js +46 -13
- package/dist/bin/exe-start-opencode.js +46 -13
- package/dist/bin/exe-status.js +460 -114
- package/dist/bin/exe-support.js +12 -0
- package/dist/bin/exe-team.js +36 -12
- package/dist/bin/git-sweep.js +348 -53
- package/dist/bin/graph-backfill.js +36 -12
- package/dist/bin/graph-export.js +36 -12
- package/dist/bin/install.js +9 -1
- package/dist/bin/intercom-check.js +255 -53
- package/dist/bin/scan-tasks.js +348 -53
- package/dist/bin/setup.js +74 -12
- package/dist/bin/shard-migrate.js +36 -12
- package/dist/gateway/index.js +348 -53
- package/dist/hooks/bug-report-worker.js +348 -53
- package/dist/hooks/codex-stop-task-finalizer.js +308 -37
- package/dist/hooks/commit-complete.js +348 -53
- package/dist/hooks/error-recall.js +37 -12
- package/dist/hooks/ingest.js +363 -54
- package/dist/hooks/instructions-loaded.js +36 -12
- package/dist/hooks/notification.js +36 -12
- package/dist/hooks/post-compact.js +426 -72
- package/dist/hooks/post-tool-combined.js +501 -146
- package/dist/hooks/pre-compact.js +348 -53
- package/dist/hooks/pre-tool-use.js +92 -13
- package/dist/hooks/prompt-submit.js +348 -53
- package/dist/hooks/session-end.js +158 -53
- package/dist/hooks/session-start.js +66 -13
- package/dist/hooks/stop.js +420 -72
- package/dist/hooks/subagent-stop.js +419 -72
- package/dist/hooks/summary-worker.js +442 -121
- package/dist/index.js +375 -53
- package/dist/lib/agent-config.js +8 -0
- package/dist/lib/cloud-sync.js +35 -12
- package/dist/lib/config.js +13 -0
- package/dist/lib/consolidation.js +9 -1
- package/dist/lib/embedder.js +13 -0
- package/dist/lib/employees.js +8 -0
- package/dist/lib/exe-daemon.js +524 -60
- package/dist/lib/hybrid-search.js +37 -12
- package/dist/lib/keychain.js +25 -13
- package/dist/lib/messaging.js +395 -74
- package/dist/lib/schedules.js +36 -12
- package/dist/lib/skill-learning.js +21 -0
- package/dist/lib/store.js +36 -12
- package/dist/lib/tasks.js +324 -41
- package/dist/lib/tmux-routing.js +324 -41
- package/dist/mcp/server.js +374 -54
- package/dist/mcp/tools/create-task.js +324 -41
- package/dist/mcp/tools/list-tasks.js +406 -57
- package/dist/mcp/tools/send-message.js +395 -74
- package/dist/mcp/tools/update-task.js +324 -41
- package/dist/runtime/index.js +375 -53
- package/dist/tui/App.js +377 -55
- package/package.json +1 -1
|
@@ -29,9 +29,25 @@ var init_db_retry = __esm({
|
|
|
29
29
|
// src/lib/secure-files.ts
|
|
30
30
|
import { chmodSync, existsSync, mkdirSync } from "fs";
|
|
31
31
|
import { chmod, mkdir } from "fs/promises";
|
|
32
|
+
function ensurePrivateDirSync(dirPath) {
|
|
33
|
+
mkdirSync(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
34
|
+
try {
|
|
35
|
+
chmodSync(dirPath, PRIVATE_DIR_MODE);
|
|
36
|
+
} catch {
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function enforcePrivateFileSync(filePath) {
|
|
40
|
+
try {
|
|
41
|
+
if (existsSync(filePath)) chmodSync(filePath, PRIVATE_FILE_MODE);
|
|
42
|
+
} catch {
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
var PRIVATE_DIR_MODE, PRIVATE_FILE_MODE;
|
|
32
46
|
var init_secure_files = __esm({
|
|
33
47
|
"src/lib/secure-files.ts"() {
|
|
34
48
|
"use strict";
|
|
49
|
+
PRIVATE_DIR_MODE = 448;
|
|
50
|
+
PRIVATE_FILE_MODE = 384;
|
|
35
51
|
}
|
|
36
52
|
});
|
|
37
53
|
|
|
@@ -134,11 +150,176 @@ var init_config = __esm({
|
|
|
134
150
|
}
|
|
135
151
|
});
|
|
136
152
|
|
|
153
|
+
// src/lib/runtime-table.ts
|
|
154
|
+
var RUNTIME_TABLE, DEFAULT_RUNTIME;
|
|
155
|
+
var init_runtime_table = __esm({
|
|
156
|
+
"src/lib/runtime-table.ts"() {
|
|
157
|
+
"use strict";
|
|
158
|
+
RUNTIME_TABLE = {
|
|
159
|
+
codex: {
|
|
160
|
+
binary: "codex",
|
|
161
|
+
launchMode: "interactive",
|
|
162
|
+
autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
|
|
163
|
+
inlineFlag: "--no-alt-screen",
|
|
164
|
+
apiKeyEnv: "OPENAI_API_KEY",
|
|
165
|
+
defaultModel: "gpt-5.5"
|
|
166
|
+
},
|
|
167
|
+
opencode: {
|
|
168
|
+
binary: "opencode",
|
|
169
|
+
launchMode: "exec",
|
|
170
|
+
autoApproveFlag: "--dangerously-skip-permissions",
|
|
171
|
+
inlineFlag: "",
|
|
172
|
+
apiKeyEnv: "ANTHROPIC_API_KEY",
|
|
173
|
+
defaultModel: "anthropic/claude-sonnet-4-6"
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
DEFAULT_RUNTIME = "claude";
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
// src/lib/agent-config.ts
|
|
181
|
+
var agent_config_exports = {};
|
|
182
|
+
__export(agent_config_exports, {
|
|
183
|
+
AGENT_CONFIG_PATH: () => AGENT_CONFIG_PATH,
|
|
184
|
+
DEFAULT_MODELS: () => DEFAULT_MODELS,
|
|
185
|
+
KNOWN_RUNTIMES: () => KNOWN_RUNTIMES,
|
|
186
|
+
RUNTIME_LABELS: () => RUNTIME_LABELS,
|
|
187
|
+
clearAgentRuntime: () => clearAgentRuntime,
|
|
188
|
+
getAgentRuntime: () => getAgentRuntime,
|
|
189
|
+
loadAgentConfig: () => loadAgentConfig,
|
|
190
|
+
normalizeCcModelName: () => normalizeCcModelName,
|
|
191
|
+
saveAgentConfig: () => saveAgentConfig,
|
|
192
|
+
setAgentMcps: () => setAgentMcps,
|
|
193
|
+
setAgentRuntime: () => setAgentRuntime
|
|
194
|
+
});
|
|
195
|
+
import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync3 } from "fs";
|
|
196
|
+
import path2 from "path";
|
|
197
|
+
function loadAgentConfig() {
|
|
198
|
+
if (!existsSync3(AGENT_CONFIG_PATH)) return {};
|
|
199
|
+
try {
|
|
200
|
+
return JSON.parse(readFileSync2(AGENT_CONFIG_PATH, "utf-8"));
|
|
201
|
+
} catch {
|
|
202
|
+
return {};
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
function saveAgentConfig(config) {
|
|
206
|
+
const dir = path2.dirname(AGENT_CONFIG_PATH);
|
|
207
|
+
ensurePrivateDirSync(dir);
|
|
208
|
+
writeFileSync(AGENT_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
209
|
+
enforcePrivateFileSync(AGENT_CONFIG_PATH);
|
|
210
|
+
}
|
|
211
|
+
function getAgentRuntime(agentId) {
|
|
212
|
+
const config = loadAgentConfig();
|
|
213
|
+
const entry = config[agentId];
|
|
214
|
+
if (entry) return entry;
|
|
215
|
+
const orgDefault = config["default"];
|
|
216
|
+
if (orgDefault) return orgDefault;
|
|
217
|
+
return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
|
|
218
|
+
}
|
|
219
|
+
function normalizeCcModelName(model) {
|
|
220
|
+
let ccModel = model.replace(/(\d+)\.(\d+)/g, "$1-$2");
|
|
221
|
+
if (/claude-(opus|sonnet)-4-[6-9]/.test(ccModel) && !ccModel.includes("[1m]")) {
|
|
222
|
+
ccModel += "[1m]";
|
|
223
|
+
}
|
|
224
|
+
return ccModel;
|
|
225
|
+
}
|
|
226
|
+
function setAgentRuntime(agentId, runtime, model, reasoning_effort, mcps) {
|
|
227
|
+
const knownModels = KNOWN_RUNTIMES[runtime];
|
|
228
|
+
if (!knownModels) {
|
|
229
|
+
return {
|
|
230
|
+
ok: false,
|
|
231
|
+
error: `Unknown runtime "${runtime}". Valid: ${Object.keys(KNOWN_RUNTIMES).join(", ")}`
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
if (!knownModels.includes(model)) {
|
|
235
|
+
return {
|
|
236
|
+
ok: false,
|
|
237
|
+
error: `Unknown model "${model}" for runtime "${runtime}". Valid: ${knownModels.join(", ")}`
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
const config = loadAgentConfig();
|
|
241
|
+
const existing = config[agentId];
|
|
242
|
+
const entry = { runtime, model };
|
|
243
|
+
if (reasoning_effort) entry.reasoning_effort = reasoning_effort;
|
|
244
|
+
if (mcps !== void 0) {
|
|
245
|
+
entry.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
|
|
246
|
+
} else if (existing?.mcps) {
|
|
247
|
+
entry.mcps = existing.mcps;
|
|
248
|
+
}
|
|
249
|
+
config[agentId] = entry;
|
|
250
|
+
saveAgentConfig(config);
|
|
251
|
+
return { ok: true };
|
|
252
|
+
}
|
|
253
|
+
function setAgentMcps(agentId, mcps) {
|
|
254
|
+
const config = loadAgentConfig();
|
|
255
|
+
const existing = config[agentId] ?? getAgentRuntime(agentId);
|
|
256
|
+
existing.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
|
|
257
|
+
config[agentId] = existing;
|
|
258
|
+
saveAgentConfig(config);
|
|
259
|
+
return { ok: true };
|
|
260
|
+
}
|
|
261
|
+
function clearAgentRuntime(agentId) {
|
|
262
|
+
const config = loadAgentConfig();
|
|
263
|
+
delete config[agentId];
|
|
264
|
+
saveAgentConfig(config);
|
|
265
|
+
}
|
|
266
|
+
var AGENT_CONFIG_PATH, KNOWN_RUNTIMES, RUNTIME_LABELS, DEFAULT_MODELS;
|
|
267
|
+
var init_agent_config = __esm({
|
|
268
|
+
"src/lib/agent-config.ts"() {
|
|
269
|
+
"use strict";
|
|
270
|
+
init_config();
|
|
271
|
+
init_runtime_table();
|
|
272
|
+
init_secure_files();
|
|
273
|
+
AGENT_CONFIG_PATH = path2.join(EXE_AI_DIR, "agent-config.json");
|
|
274
|
+
KNOWN_RUNTIMES = {
|
|
275
|
+
claude: ["claude-opus-4.6", "claude-opus-4", "claude-sonnet-4.6", "claude-sonnet-4", "claude-haiku-4.5"],
|
|
276
|
+
codex: ["gpt-5.4", "gpt-5.5", "gpt-5.3-codex-spark", "o3", "o4-mini"],
|
|
277
|
+
opencode: ["anthropic/claude-sonnet-4-6", "openai/gpt-5.4", "google/gemini-2.5-pro", "deepseek/deepseek-r3", "minimax/minimax-m2.5"]
|
|
278
|
+
};
|
|
279
|
+
RUNTIME_LABELS = {
|
|
280
|
+
claude: "Claude Code (Anthropic)",
|
|
281
|
+
codex: "Codex (OpenAI)",
|
|
282
|
+
opencode: "OpenCode (open source)"
|
|
283
|
+
};
|
|
284
|
+
DEFAULT_MODELS = {
|
|
285
|
+
claude: "claude-opus-4.6",
|
|
286
|
+
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
287
|
+
opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
|
|
137
292
|
// src/lib/employees.ts
|
|
293
|
+
var employees_exports = {};
|
|
294
|
+
__export(employees_exports, {
|
|
295
|
+
COORDINATOR_ROLE: () => COORDINATOR_ROLE,
|
|
296
|
+
DEFAULT_COORDINATOR_TEMPLATE_NAME: () => DEFAULT_COORDINATOR_TEMPLATE_NAME,
|
|
297
|
+
EMPLOYEES_PATH: () => EMPLOYEES_PATH,
|
|
298
|
+
addEmployee: () => addEmployee,
|
|
299
|
+
baseAgentName: () => baseAgentName,
|
|
300
|
+
canCoordinate: () => canCoordinate,
|
|
301
|
+
getCoordinatorEmployee: () => getCoordinatorEmployee,
|
|
302
|
+
getCoordinatorName: () => getCoordinatorName,
|
|
303
|
+
getEmployee: () => getEmployee,
|
|
304
|
+
getEmployeeByRole: () => getEmployeeByRole,
|
|
305
|
+
getEmployeeNamesByRole: () => getEmployeeNamesByRole,
|
|
306
|
+
hasRole: () => hasRole,
|
|
307
|
+
hireEmployee: () => hireEmployee,
|
|
308
|
+
isCoordinatorName: () => isCoordinatorName,
|
|
309
|
+
isCoordinatorRole: () => isCoordinatorRole,
|
|
310
|
+
isMultiInstance: () => isMultiInstance,
|
|
311
|
+
loadEmployees: () => loadEmployees,
|
|
312
|
+
loadEmployeesSync: () => loadEmployeesSync,
|
|
313
|
+
normalizeRole: () => normalizeRole,
|
|
314
|
+
normalizeRosterCase: () => normalizeRosterCase,
|
|
315
|
+
registerBinSymlinks: () => registerBinSymlinks,
|
|
316
|
+
saveEmployees: () => saveEmployees,
|
|
317
|
+
validateEmployeeName: () => validateEmployeeName
|
|
318
|
+
});
|
|
138
319
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
139
|
-
import { existsSync as
|
|
320
|
+
import { existsSync as existsSync4, symlinkSync, readlinkSync, readFileSync as readFileSync3, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
|
|
140
321
|
import { execSync } from "child_process";
|
|
141
|
-
import
|
|
322
|
+
import path3 from "path";
|
|
142
323
|
import os2 from "os";
|
|
143
324
|
function normalizeRole(role) {
|
|
144
325
|
return (role ?? "").trim().toLowerCase();
|
|
@@ -149,10 +330,50 @@ function isCoordinatorRole(role) {
|
|
|
149
330
|
function getCoordinatorEmployee(employees) {
|
|
150
331
|
return employees.find((e) => isCoordinatorRole(e.role));
|
|
151
332
|
}
|
|
333
|
+
function getCoordinatorName(employees = loadEmployeesSync()) {
|
|
334
|
+
return getCoordinatorEmployee(employees)?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME;
|
|
335
|
+
}
|
|
336
|
+
function isCoordinatorName(agentName, employees = loadEmployeesSync()) {
|
|
337
|
+
if (!agentName) return false;
|
|
338
|
+
return agentName.toLowerCase() === getCoordinatorName(employees).toLowerCase();
|
|
339
|
+
}
|
|
340
|
+
function canCoordinate(agentName, agentRole, employees = loadEmployeesSync()) {
|
|
341
|
+
return agentName === "default" || isCoordinatorRole(agentRole) || isCoordinatorName(agentName, employees);
|
|
342
|
+
}
|
|
343
|
+
function validateEmployeeName(name) {
|
|
344
|
+
if (!name) {
|
|
345
|
+
return { valid: false, error: "Name is required" };
|
|
346
|
+
}
|
|
347
|
+
if (name.length > 32) {
|
|
348
|
+
return { valid: false, error: "Name must be 32 characters or fewer" };
|
|
349
|
+
}
|
|
350
|
+
if (!/^[a-z][a-z0-9]*$/.test(name)) {
|
|
351
|
+
return {
|
|
352
|
+
valid: false,
|
|
353
|
+
error: "Name must start with a letter and contain only lowercase alphanumeric characters"
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
return { valid: true };
|
|
357
|
+
}
|
|
358
|
+
async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
359
|
+
if (!existsSync4(employeesPath)) {
|
|
360
|
+
return [];
|
|
361
|
+
}
|
|
362
|
+
const raw = await readFile2(employeesPath, "utf-8");
|
|
363
|
+
try {
|
|
364
|
+
return JSON.parse(raw);
|
|
365
|
+
} catch {
|
|
366
|
+
return [];
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
|
|
370
|
+
await mkdir2(path3.dirname(employeesPath), { recursive: true });
|
|
371
|
+
await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
|
|
372
|
+
}
|
|
152
373
|
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
153
|
-
if (!
|
|
374
|
+
if (!existsSync4(employeesPath)) return [];
|
|
154
375
|
try {
|
|
155
|
-
return JSON.parse(
|
|
376
|
+
return JSON.parse(readFileSync3(employeesPath, "utf-8"));
|
|
156
377
|
} catch {
|
|
157
378
|
return [];
|
|
158
379
|
}
|
|
@@ -160,21 +381,174 @@ function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
|
160
381
|
function getEmployee(employees, name) {
|
|
161
382
|
return employees.find((e) => e.name.toLowerCase() === name.toLowerCase());
|
|
162
383
|
}
|
|
163
|
-
|
|
384
|
+
function getEmployeeByRole(employees, role) {
|
|
385
|
+
const lower = role.toLowerCase();
|
|
386
|
+
return employees.find((e) => e.role.toLowerCase() === lower);
|
|
387
|
+
}
|
|
388
|
+
function getEmployeeNamesByRole(employees, role) {
|
|
389
|
+
const lower = role.toLowerCase();
|
|
390
|
+
return employees.filter((e) => e.role.toLowerCase() === lower).map((e) => e.name);
|
|
391
|
+
}
|
|
392
|
+
function hasRole(agentName, role) {
|
|
393
|
+
const employees = loadEmployeesSync();
|
|
394
|
+
const emp = getEmployee(employees, agentName);
|
|
395
|
+
return emp ? emp.role.toLowerCase() === role.toLowerCase() : false;
|
|
396
|
+
}
|
|
397
|
+
function baseAgentName(name, employees) {
|
|
398
|
+
const match = name.match(/^([a-zA-Z]+)\d+$/);
|
|
399
|
+
if (!match) return name;
|
|
400
|
+
const base = match[1];
|
|
401
|
+
const roster = employees ?? loadEmployeesSync();
|
|
402
|
+
if (getEmployee(roster, base)) return base;
|
|
403
|
+
return name;
|
|
404
|
+
}
|
|
405
|
+
function isMultiInstance(agentName, employees) {
|
|
406
|
+
const roster = employees ?? loadEmployeesSync();
|
|
407
|
+
const emp = getEmployee(roster, agentName);
|
|
408
|
+
if (!emp) return false;
|
|
409
|
+
return MULTI_INSTANCE_ROLES.has(emp.role.toLowerCase());
|
|
410
|
+
}
|
|
411
|
+
function addEmployee(employees, employee) {
|
|
412
|
+
const { systemPrompt: _legacyPrompt, ...rest } = employee;
|
|
413
|
+
const normalized = { ...rest, name: employee.name.toLowerCase() };
|
|
414
|
+
if (employees.some((e) => e.name.toLowerCase() === normalized.name)) {
|
|
415
|
+
throw new Error(`Employee '${normalized.name}' already exists`);
|
|
416
|
+
}
|
|
417
|
+
return [...employees, normalized];
|
|
418
|
+
}
|
|
419
|
+
function appendToCoordinatorTeam(employee) {
|
|
420
|
+
const coordinator = getCoordinatorEmployee(loadEmployeesSync());
|
|
421
|
+
if (!coordinator) return;
|
|
422
|
+
const idPath = path3.join(IDENTITY_DIR, `${coordinator.name}.md`);
|
|
423
|
+
if (!existsSync4(idPath)) return;
|
|
424
|
+
const content = readFileSync3(idPath, "utf-8");
|
|
425
|
+
if (content.includes(`**${capitalize(employee.name)}`)) return;
|
|
426
|
+
const teamMatch = content.match(TEAM_SECTION_RE);
|
|
427
|
+
if (!teamMatch || teamMatch.index === void 0) return;
|
|
428
|
+
const afterTeam = content.slice(teamMatch.index + teamMatch[0].length);
|
|
429
|
+
const nextHeading = afterTeam.match(/\n## /);
|
|
430
|
+
const entry = `
|
|
431
|
+
**${capitalize(employee.name)} (${employee.role}):** Newly hired. Update this description as the role develops.
|
|
432
|
+
`;
|
|
433
|
+
let updated;
|
|
434
|
+
if (nextHeading && nextHeading.index !== void 0) {
|
|
435
|
+
const insertAt = teamMatch.index + teamMatch[0].length + nextHeading.index;
|
|
436
|
+
updated = content.slice(0, insertAt) + entry + content.slice(insertAt);
|
|
437
|
+
} else {
|
|
438
|
+
updated = content.trimEnd() + "\n" + entry;
|
|
439
|
+
}
|
|
440
|
+
writeFileSync2(idPath, updated, "utf-8");
|
|
441
|
+
}
|
|
442
|
+
function capitalize(s) {
|
|
443
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
444
|
+
}
|
|
445
|
+
async function hireEmployee(employee) {
|
|
446
|
+
const employees = await loadEmployees();
|
|
447
|
+
const updated = addEmployee(employees, employee);
|
|
448
|
+
await saveEmployees(updated);
|
|
449
|
+
try {
|
|
450
|
+
appendToCoordinatorTeam(employee);
|
|
451
|
+
} catch {
|
|
452
|
+
}
|
|
453
|
+
try {
|
|
454
|
+
const { loadAgentConfig: loadAgentConfig2, saveAgentConfig: saveAgentConfig2 } = await Promise.resolve().then(() => (init_agent_config(), agent_config_exports));
|
|
455
|
+
const config = loadAgentConfig2();
|
|
456
|
+
const name = employee.name.toLowerCase();
|
|
457
|
+
if (!config[name] && config["default"]) {
|
|
458
|
+
config[name] = { ...config["default"] };
|
|
459
|
+
saveAgentConfig2(config);
|
|
460
|
+
}
|
|
461
|
+
} catch {
|
|
462
|
+
}
|
|
463
|
+
return updated;
|
|
464
|
+
}
|
|
465
|
+
async function normalizeRosterCase(rosterPath) {
|
|
466
|
+
const employees = await loadEmployees(rosterPath);
|
|
467
|
+
let changed = false;
|
|
468
|
+
for (const emp of employees) {
|
|
469
|
+
if (emp.name !== emp.name.toLowerCase()) {
|
|
470
|
+
const oldName = emp.name;
|
|
471
|
+
emp.name = emp.name.toLowerCase();
|
|
472
|
+
changed = true;
|
|
473
|
+
try {
|
|
474
|
+
const identityDir = path3.join(os2.homedir(), ".exe-os", "identity");
|
|
475
|
+
const oldPath = path3.join(identityDir, `${oldName}.md`);
|
|
476
|
+
const newPath = path3.join(identityDir, `${emp.name}.md`);
|
|
477
|
+
if (existsSync4(oldPath) && !existsSync4(newPath)) {
|
|
478
|
+
renameSync2(oldPath, newPath);
|
|
479
|
+
} else if (existsSync4(oldPath) && oldPath !== newPath) {
|
|
480
|
+
const content = readFileSync3(oldPath, "utf-8");
|
|
481
|
+
writeFileSync2(newPath, content, "utf-8");
|
|
482
|
+
if (oldPath.toLowerCase() !== newPath.toLowerCase()) {
|
|
483
|
+
unlinkSync(oldPath);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
} catch {
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
if (changed) {
|
|
491
|
+
await saveEmployees(employees, rosterPath);
|
|
492
|
+
}
|
|
493
|
+
return changed;
|
|
494
|
+
}
|
|
495
|
+
function findExeBin() {
|
|
496
|
+
try {
|
|
497
|
+
return execSync(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
|
|
498
|
+
} catch {
|
|
499
|
+
return null;
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
function registerBinSymlinks(name) {
|
|
503
|
+
const created = [];
|
|
504
|
+
const skipped = [];
|
|
505
|
+
const errors = [];
|
|
506
|
+
const exeBinPath = findExeBin();
|
|
507
|
+
if (!exeBinPath) {
|
|
508
|
+
errors.push("Could not find 'exe-os' in PATH");
|
|
509
|
+
return { created, skipped, errors };
|
|
510
|
+
}
|
|
511
|
+
const binDir = path3.dirname(exeBinPath);
|
|
512
|
+
let target;
|
|
513
|
+
try {
|
|
514
|
+
target = readlinkSync(exeBinPath);
|
|
515
|
+
} catch {
|
|
516
|
+
errors.push("Could not read 'exe' symlink");
|
|
517
|
+
return { created, skipped, errors };
|
|
518
|
+
}
|
|
519
|
+
for (const suffix of ["", "-opencode"]) {
|
|
520
|
+
const linkName = `${name}${suffix}`;
|
|
521
|
+
const linkPath = path3.join(binDir, linkName);
|
|
522
|
+
if (existsSync4(linkPath)) {
|
|
523
|
+
skipped.push(linkName);
|
|
524
|
+
continue;
|
|
525
|
+
}
|
|
526
|
+
try {
|
|
527
|
+
symlinkSync(target, linkPath);
|
|
528
|
+
created.push(linkName);
|
|
529
|
+
} catch (err) {
|
|
530
|
+
errors.push(`${linkName}: ${err instanceof Error ? err.message : String(err)}`);
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
return { created, skipped, errors };
|
|
534
|
+
}
|
|
535
|
+
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES, IDENTITY_DIR, TEAM_SECTION_RE;
|
|
164
536
|
var init_employees = __esm({
|
|
165
537
|
"src/lib/employees.ts"() {
|
|
166
538
|
"use strict";
|
|
167
539
|
init_config();
|
|
168
|
-
EMPLOYEES_PATH =
|
|
540
|
+
EMPLOYEES_PATH = path3.join(EXE_AI_DIR, "exe-employees.json");
|
|
169
541
|
DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
|
|
170
542
|
COORDINATOR_ROLE = "COO";
|
|
171
|
-
|
|
543
|
+
MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
|
|
544
|
+
IDENTITY_DIR = path3.join(EXE_AI_DIR, "identity");
|
|
545
|
+
TEAM_SECTION_RE = /^## Team\b.*$/m;
|
|
172
546
|
}
|
|
173
547
|
});
|
|
174
548
|
|
|
175
549
|
// src/lib/database-adapter.ts
|
|
176
550
|
import os3 from "os";
|
|
177
|
-
import
|
|
551
|
+
import path4 from "path";
|
|
178
552
|
import { createRequire } from "module";
|
|
179
553
|
import { pathToFileURL } from "url";
|
|
180
554
|
var BOOLEAN_COLUMNS_BY_TABLE, BOOLEAN_COLUMN_NAMES;
|
|
@@ -201,7 +575,7 @@ var init_memory = __esm({
|
|
|
201
575
|
});
|
|
202
576
|
|
|
203
577
|
// src/lib/database.ts
|
|
204
|
-
import { chmodSync as chmodSync2, existsSync as
|
|
578
|
+
import { chmodSync as chmodSync2, existsSync as existsSync5, statSync, copyFileSync, unlinkSync as unlinkSync2, openSync, closeSync, mkdirSync as mkdirSync2 } from "fs";
|
|
205
579
|
import { createClient } from "@libsql/client";
|
|
206
580
|
import { homedir } from "os";
|
|
207
581
|
import { join } from "path";
|
|
@@ -254,13 +628,13 @@ var init_database = __esm({
|
|
|
254
628
|
});
|
|
255
629
|
|
|
256
630
|
// src/lib/session-registry.ts
|
|
257
|
-
import
|
|
631
|
+
import path5 from "path";
|
|
258
632
|
import os4 from "os";
|
|
259
633
|
var REGISTRY_PATH;
|
|
260
634
|
var init_session_registry = __esm({
|
|
261
635
|
"src/lib/session-registry.ts"() {
|
|
262
636
|
"use strict";
|
|
263
|
-
REGISTRY_PATH =
|
|
637
|
+
REGISTRY_PATH = path5.join(os4.homedir(), ".exe-os", "session-registry.json");
|
|
264
638
|
}
|
|
265
639
|
});
|
|
266
640
|
|
|
@@ -478,51 +852,6 @@ var init_provider_table = __esm({
|
|
|
478
852
|
}
|
|
479
853
|
});
|
|
480
854
|
|
|
481
|
-
// src/lib/runtime-table.ts
|
|
482
|
-
var RUNTIME_TABLE;
|
|
483
|
-
var init_runtime_table = __esm({
|
|
484
|
-
"src/lib/runtime-table.ts"() {
|
|
485
|
-
"use strict";
|
|
486
|
-
RUNTIME_TABLE = {
|
|
487
|
-
codex: {
|
|
488
|
-
binary: "codex",
|
|
489
|
-
launchMode: "interactive",
|
|
490
|
-
autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
|
|
491
|
-
inlineFlag: "--no-alt-screen",
|
|
492
|
-
apiKeyEnv: "OPENAI_API_KEY",
|
|
493
|
-
defaultModel: "gpt-5.5"
|
|
494
|
-
},
|
|
495
|
-
opencode: {
|
|
496
|
-
binary: "opencode",
|
|
497
|
-
launchMode: "exec",
|
|
498
|
-
autoApproveFlag: "--dangerously-skip-permissions",
|
|
499
|
-
inlineFlag: "",
|
|
500
|
-
apiKeyEnv: "ANTHROPIC_API_KEY",
|
|
501
|
-
defaultModel: "anthropic/claude-sonnet-4-6"
|
|
502
|
-
}
|
|
503
|
-
};
|
|
504
|
-
}
|
|
505
|
-
});
|
|
506
|
-
|
|
507
|
-
// src/lib/agent-config.ts
|
|
508
|
-
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, existsSync as existsSync5 } from "fs";
|
|
509
|
-
import path5 from "path";
|
|
510
|
-
var AGENT_CONFIG_PATH, DEFAULT_MODELS;
|
|
511
|
-
var init_agent_config = __esm({
|
|
512
|
-
"src/lib/agent-config.ts"() {
|
|
513
|
-
"use strict";
|
|
514
|
-
init_config();
|
|
515
|
-
init_runtime_table();
|
|
516
|
-
init_secure_files();
|
|
517
|
-
AGENT_CONFIG_PATH = path5.join(EXE_AI_DIR, "agent-config.json");
|
|
518
|
-
DEFAULT_MODELS = {
|
|
519
|
-
claude: "claude-opus-4.6",
|
|
520
|
-
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
521
|
-
opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
|
|
522
|
-
};
|
|
523
|
-
}
|
|
524
|
-
});
|
|
525
|
-
|
|
526
855
|
// src/lib/intercom-queue.ts
|
|
527
856
|
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, renameSync as renameSync3, existsSync as existsSync6, mkdirSync as mkdirSync3 } from "fs";
|
|
528
857
|
import path6 from "path";
|
|
@@ -603,6 +932,21 @@ function isRootSession(name) {
|
|
|
603
932
|
function extractRootExe(name) {
|
|
604
933
|
if (!name) return null;
|
|
605
934
|
if (!name.includes("-")) return name;
|
|
935
|
+
try {
|
|
936
|
+
const roster = (init_employees(), __toCommonJS(employees_exports)).loadEmployeesSync();
|
|
937
|
+
if (roster.length > 0) {
|
|
938
|
+
const sortedNames = roster.map((e) => e.name).sort((a, b) => b.length - a.length);
|
|
939
|
+
for (const agentName of sortedNames) {
|
|
940
|
+
const escaped = agentName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
941
|
+
const regex = new RegExp(`^${escaped}\\d*-(.+)$`);
|
|
942
|
+
const match = name.match(regex);
|
|
943
|
+
if (match) {
|
|
944
|
+
return extractRootExe(match[1]);
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
} catch {
|
|
949
|
+
}
|
|
606
950
|
const parts = name.split("-").filter(Boolean);
|
|
607
951
|
return parts.length > 0 ? parts[parts.length - 1] : null;
|
|
608
952
|
}
|
|
@@ -621,6 +965,10 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
621
965
|
function getParentExe(sessionKey) {
|
|
622
966
|
try {
|
|
623
967
|
const data = JSON.parse(readFileSync7(path10.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
968
|
+
if (data.registeredAt) {
|
|
969
|
+
const age = Date.now() - new Date(data.registeredAt).getTime();
|
|
970
|
+
if (age > PARENT_EXE_CACHE_TTL_MS) return null;
|
|
971
|
+
}
|
|
624
972
|
return data.parentExe || null;
|
|
625
973
|
} catch {
|
|
626
974
|
return null;
|
|
@@ -693,7 +1041,7 @@ function resolveExeSession() {
|
|
|
693
1041
|
}
|
|
694
1042
|
return candidate;
|
|
695
1043
|
}
|
|
696
|
-
var SPAWN_LOCK_DIR, SESSION_CACHE, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS;
|
|
1044
|
+
var SPAWN_LOCK_DIR, SESSION_CACHE, PARENT_EXE_CACHE_TTL_MS, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS;
|
|
697
1045
|
var init_tmux_routing = __esm({
|
|
698
1046
|
"src/lib/tmux-routing.ts"() {
|
|
699
1047
|
"use strict";
|
|
@@ -711,6 +1059,7 @@ var init_tmux_routing = __esm({
|
|
|
711
1059
|
init_agent_symlinks();
|
|
712
1060
|
SPAWN_LOCK_DIR = path10.join(os8.homedir(), ".exe-os", "spawn-locks");
|
|
713
1061
|
SESSION_CACHE = path10.join(os8.homedir(), ".exe-os", "session-cache");
|
|
1062
|
+
PARENT_EXE_CACHE_TTL_MS = 4 * 60 * 60 * 1e3;
|
|
714
1063
|
INTERCOM_LOG2 = path10.join(os8.homedir(), ".exe-os", "intercom.log");
|
|
715
1064
|
DEBOUNCE_FILE = path10.join(SESSION_CACHE, "intercom-debounce.json");
|
|
716
1065
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|