@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
|
@@ -122,7 +122,7 @@ async function loadConfig() {
|
|
|
122
122
|
return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
|
|
123
123
|
}
|
|
124
124
|
}
|
|
125
|
-
var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH,
|
|
125
|
+
var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
|
|
126
126
|
var init_config = __esm({
|
|
127
127
|
"src/lib/config.ts"() {
|
|
128
128
|
"use strict";
|
|
@@ -130,7 +130,6 @@ var init_config = __esm({
|
|
|
130
130
|
DB_PATH = path.join(EXE_AI_DIR, "memories.db");
|
|
131
131
|
MODELS_DIR = path.join(EXE_AI_DIR, "models");
|
|
132
132
|
CONFIG_PATH = path.join(EXE_AI_DIR, "config.json");
|
|
133
|
-
COO_AGENT_NAME = "exe";
|
|
134
133
|
LEGACY_LANCE_PATH = path.join(EXE_AI_DIR, "local.lance");
|
|
135
134
|
CURRENT_CONFIG_VERSION = 1;
|
|
136
135
|
DEFAULT_CONFIG = {
|
|
@@ -166,13 +165,7 @@ var init_config = __esm({
|
|
|
166
165
|
wikiUrl: "",
|
|
167
166
|
wikiApiKey: "",
|
|
168
167
|
wikiSyncIntervalMs: 30 * 60 * 1e3,
|
|
169
|
-
wikiWorkspaceMapping: {
|
|
170
|
-
exe: "Executive",
|
|
171
|
-
yoshi: "Engineering",
|
|
172
|
-
mari: "Marketing",
|
|
173
|
-
tom: "Engineering",
|
|
174
|
-
sasha: "Production"
|
|
175
|
-
},
|
|
168
|
+
wikiWorkspaceMapping: {},
|
|
176
169
|
wikiAutoUpdate: true,
|
|
177
170
|
wikiAutoUpdateThreshold: 0.5,
|
|
178
171
|
wikiAutoUpdateCreateNew: true,
|
|
@@ -244,14 +237,61 @@ var init_session_key = __esm({
|
|
|
244
237
|
}
|
|
245
238
|
});
|
|
246
239
|
|
|
247
|
-
// src/lib/
|
|
248
|
-
import
|
|
240
|
+
// src/lib/employees.ts
|
|
241
|
+
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
242
|
+
import { existsSync as existsSync2, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
|
|
243
|
+
import { execSync as execSync2 } from "child_process";
|
|
244
|
+
import path2 from "path";
|
|
249
245
|
import os2 from "os";
|
|
246
|
+
function normalizeRole(role) {
|
|
247
|
+
return (role ?? "").trim().toLowerCase();
|
|
248
|
+
}
|
|
249
|
+
function isCoordinatorRole(role) {
|
|
250
|
+
return normalizeRole(role) === normalizeRole(COORDINATOR_ROLE);
|
|
251
|
+
}
|
|
252
|
+
function getCoordinatorEmployee(employees) {
|
|
253
|
+
return employees.find((e) => isCoordinatorRole(e.role));
|
|
254
|
+
}
|
|
255
|
+
function getCoordinatorName(employees = loadEmployeesSync()) {
|
|
256
|
+
return getCoordinatorEmployee(employees)?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME;
|
|
257
|
+
}
|
|
258
|
+
function isCoordinatorName(agentName, employees = loadEmployeesSync()) {
|
|
259
|
+
if (!agentName) return false;
|
|
260
|
+
return agentName.toLowerCase() === getCoordinatorName(employees).toLowerCase();
|
|
261
|
+
}
|
|
262
|
+
function canCoordinate(agentName, agentRole, employees = loadEmployeesSync()) {
|
|
263
|
+
return agentName === "default" || isCoordinatorRole(agentRole) || isCoordinatorName(agentName, employees);
|
|
264
|
+
}
|
|
265
|
+
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
266
|
+
if (!existsSync2(employeesPath)) return [];
|
|
267
|
+
try {
|
|
268
|
+
return JSON.parse(readFileSync2(employeesPath, "utf-8"));
|
|
269
|
+
} catch {
|
|
270
|
+
return [];
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
function getEmployee(employees, name) {
|
|
274
|
+
return employees.find((e) => e.name.toLowerCase() === name.toLowerCase());
|
|
275
|
+
}
|
|
276
|
+
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE;
|
|
277
|
+
var init_employees = __esm({
|
|
278
|
+
"src/lib/employees.ts"() {
|
|
279
|
+
"use strict";
|
|
280
|
+
init_config();
|
|
281
|
+
EMPLOYEES_PATH = path2.join(EXE_AI_DIR, "exe-employees.json");
|
|
282
|
+
DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
|
|
283
|
+
COORDINATOR_ROLE = "COO";
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
// src/lib/session-registry.ts
|
|
288
|
+
import path4 from "path";
|
|
289
|
+
import os3 from "os";
|
|
250
290
|
var REGISTRY_PATH;
|
|
251
291
|
var init_session_registry = __esm({
|
|
252
292
|
"src/lib/session-registry.ts"() {
|
|
253
293
|
"use strict";
|
|
254
|
-
REGISTRY_PATH =
|
|
294
|
+
REGISTRY_PATH = path4.join(os3.homedir(), ".exe-os", "session-registry.json");
|
|
255
295
|
}
|
|
256
296
|
});
|
|
257
297
|
|
|
@@ -363,7 +403,7 @@ var init_transport = __esm({
|
|
|
363
403
|
});
|
|
364
404
|
|
|
365
405
|
// src/lib/cc-agent-support.ts
|
|
366
|
-
import { execSync as
|
|
406
|
+
import { execSync as execSync4 } from "child_process";
|
|
367
407
|
var init_cc_agent_support = __esm({
|
|
368
408
|
"src/lib/cc-agent-support.ts"() {
|
|
369
409
|
"use strict";
|
|
@@ -392,16 +432,16 @@ var init_provider_table = __esm({
|
|
|
392
432
|
});
|
|
393
433
|
|
|
394
434
|
// src/lib/intercom-queue.ts
|
|
395
|
-
import { readFileSync as
|
|
396
|
-
import
|
|
397
|
-
import
|
|
435
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, renameSync as renameSync3, existsSync as existsSync3, mkdirSync as mkdirSync2 } from "fs";
|
|
436
|
+
import path5 from "path";
|
|
437
|
+
import os4 from "os";
|
|
398
438
|
var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
|
|
399
439
|
var init_intercom_queue = __esm({
|
|
400
440
|
"src/lib/intercom-queue.ts"() {
|
|
401
441
|
"use strict";
|
|
402
|
-
QUEUE_PATH =
|
|
442
|
+
QUEUE_PATH = path5.join(os4.homedir(), ".exe-os", "intercom-queue.json");
|
|
403
443
|
TTL_MS = 60 * 60 * 1e3;
|
|
404
|
-
INTERCOM_LOG =
|
|
444
|
+
INTERCOM_LOG = path5.join(os4.homedir(), ".exe-os", "intercom.log");
|
|
405
445
|
}
|
|
406
446
|
});
|
|
407
447
|
|
|
@@ -444,7 +484,7 @@ function wrapWithRetry(client) {
|
|
|
444
484
|
return (sql) => retryOnBusy(() => target.execute(sql), "execute");
|
|
445
485
|
}
|
|
446
486
|
if (prop === "batch") {
|
|
447
|
-
return (stmts) => retryOnBusy(() => target.batch(stmts), "batch");
|
|
487
|
+
return (stmts, mode) => retryOnBusy(() => target.batch(stmts, mode), "batch");
|
|
448
488
|
}
|
|
449
489
|
return Reflect.get(target, prop, receiver);
|
|
450
490
|
}
|
|
@@ -607,22 +647,24 @@ async function ensureSchema() {
|
|
|
607
647
|
ON behaviors(agent_id, active);
|
|
608
648
|
`);
|
|
609
649
|
try {
|
|
650
|
+
const coordinatorName = getCoordinatorName();
|
|
610
651
|
const existing = await client.execute({
|
|
611
|
-
sql: "SELECT COUNT(*) as cnt FROM behaviors WHERE agent_id =
|
|
612
|
-
args: []
|
|
652
|
+
sql: "SELECT COUNT(*) as cnt FROM behaviors WHERE agent_id = ?",
|
|
653
|
+
args: [coordinatorName]
|
|
613
654
|
});
|
|
614
655
|
if (Number(existing.rows[0]?.cnt) === 0) {
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
656
|
+
const seededAt = "2026-03-25T00:00:00Z";
|
|
657
|
+
for (const [domain, content] of [
|
|
658
|
+
["workflow", `Don't ask "keep going?" \u2014 just keep executing phases/plans autonomously`],
|
|
659
|
+
["tool-use", "Always use create_task MCP tool, never write .md files directly for task creation"],
|
|
660
|
+
["workflow", "Auto-start reviewing when idle and reviews are pending \u2014 never ask founder for permission"]
|
|
661
|
+
]) {
|
|
662
|
+
await client.execute({
|
|
663
|
+
sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, content, active, created_at, updated_at)
|
|
664
|
+
VALUES (hex(randomblob(16)), ?, NULL, ?, ?, 1, ?, ?)`,
|
|
665
|
+
args: [coordinatorName, domain, content, seededAt, seededAt]
|
|
666
|
+
});
|
|
667
|
+
}
|
|
626
668
|
}
|
|
627
669
|
} catch {
|
|
628
670
|
}
|
|
@@ -1314,6 +1356,39 @@ async function ensureSchema() {
|
|
|
1314
1356
|
} catch {
|
|
1315
1357
|
}
|
|
1316
1358
|
}
|
|
1359
|
+
try {
|
|
1360
|
+
await client.execute({
|
|
1361
|
+
sql: `ALTER TABLE memories ADD COLUMN draft INTEGER DEFAULT 0`,
|
|
1362
|
+
args: []
|
|
1363
|
+
});
|
|
1364
|
+
} catch {
|
|
1365
|
+
}
|
|
1366
|
+
try {
|
|
1367
|
+
await client.execute(
|
|
1368
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_draft ON memories(draft) WHERE draft = 1`
|
|
1369
|
+
);
|
|
1370
|
+
} catch {
|
|
1371
|
+
}
|
|
1372
|
+
try {
|
|
1373
|
+
await client.execute({
|
|
1374
|
+
sql: `ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'`,
|
|
1375
|
+
args: []
|
|
1376
|
+
});
|
|
1377
|
+
} catch {
|
|
1378
|
+
}
|
|
1379
|
+
try {
|
|
1380
|
+
await client.execute(
|
|
1381
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_type ON memories(memory_type)`
|
|
1382
|
+
);
|
|
1383
|
+
} catch {
|
|
1384
|
+
}
|
|
1385
|
+
try {
|
|
1386
|
+
await client.execute({
|
|
1387
|
+
sql: `ALTER TABLE memories ADD COLUMN trajectory TEXT`,
|
|
1388
|
+
args: []
|
|
1389
|
+
});
|
|
1390
|
+
} catch {
|
|
1391
|
+
}
|
|
1317
1392
|
}
|
|
1318
1393
|
async function disposeDatabase() {
|
|
1319
1394
|
if (_client) {
|
|
@@ -1327,6 +1402,7 @@ var init_database = __esm({
|
|
|
1327
1402
|
"src/lib/database.ts"() {
|
|
1328
1403
|
"use strict";
|
|
1329
1404
|
init_db_retry();
|
|
1405
|
+
init_employees();
|
|
1330
1406
|
_client = null;
|
|
1331
1407
|
_resilientClient = null;
|
|
1332
1408
|
initTurso = initDatabase;
|
|
@@ -1334,21 +1410,6 @@ var init_database = __esm({
|
|
|
1334
1410
|
}
|
|
1335
1411
|
});
|
|
1336
1412
|
|
|
1337
|
-
// src/lib/employees.ts
|
|
1338
|
-
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
1339
|
-
import { existsSync as existsSync3, symlinkSync, readlinkSync, readFileSync as readFileSync4, renameSync as renameSync3, unlinkSync as unlinkSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
1340
|
-
import { execSync as execSync4 } from "child_process";
|
|
1341
|
-
import path5 from "path";
|
|
1342
|
-
import os4 from "os";
|
|
1343
|
-
var EMPLOYEES_PATH;
|
|
1344
|
-
var init_employees = __esm({
|
|
1345
|
-
"src/lib/employees.ts"() {
|
|
1346
|
-
"use strict";
|
|
1347
|
-
init_config();
|
|
1348
|
-
EMPLOYEES_PATH = path5.join(EXE_AI_DIR, "exe-employees.json");
|
|
1349
|
-
}
|
|
1350
|
-
});
|
|
1351
|
-
|
|
1352
1413
|
// src/lib/license.ts
|
|
1353
1414
|
import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, existsSync as existsSync4, mkdirSync as mkdirSync3 } from "fs";
|
|
1354
1415
|
import { randomUUID } from "crypto";
|
|
@@ -1389,8 +1450,10 @@ function getMySession() {
|
|
|
1389
1450
|
return getTransport().getMySession();
|
|
1390
1451
|
}
|
|
1391
1452
|
function extractRootExe(name) {
|
|
1392
|
-
|
|
1393
|
-
|
|
1453
|
+
if (!name) return null;
|
|
1454
|
+
if (!name.includes("-")) return name;
|
|
1455
|
+
const parts = name.split("-").filter(Boolean);
|
|
1456
|
+
return parts.length > 0 ? parts[parts.length - 1] : null;
|
|
1394
1457
|
}
|
|
1395
1458
|
function getParentExe(sessionKey) {
|
|
1396
1459
|
try {
|
|
@@ -1425,6 +1488,7 @@ var init_tmux_routing = __esm({
|
|
|
1425
1488
|
init_provider_table();
|
|
1426
1489
|
init_intercom_queue();
|
|
1427
1490
|
init_plan_limits();
|
|
1491
|
+
init_employees();
|
|
1428
1492
|
SPAWN_LOCK_DIR = path8.join(os5.homedir(), ".exe-os", "spawn-locks");
|
|
1429
1493
|
SESSION_CACHE = path8.join(os5.homedir(), ".exe-os", "session-cache");
|
|
1430
1494
|
INTERCOM_LOG2 = path8.join(os5.homedir(), ".exe-os", "intercom.log");
|
|
@@ -1692,7 +1756,11 @@ async function ensureShardSchema(client) {
|
|
|
1692
1756
|
"ALTER TABLE memories ADD COLUMN source_path TEXT",
|
|
1693
1757
|
"ALTER TABLE memories ADD COLUMN source_type TEXT DEFAULT 'text'",
|
|
1694
1758
|
"ALTER TABLE memories ADD COLUMN tier INTEGER DEFAULT 3",
|
|
1695
|
-
"ALTER TABLE memories ADD COLUMN supersedes_id TEXT"
|
|
1759
|
+
"ALTER TABLE memories ADD COLUMN supersedes_id TEXT",
|
|
1760
|
+
// MS-11: draft staging, MS-6a: memory_type, MS-7: trajectory
|
|
1761
|
+
"ALTER TABLE memories ADD COLUMN draft INTEGER DEFAULT 0",
|
|
1762
|
+
"ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'",
|
|
1763
|
+
"ALTER TABLE memories ADD COLUMN trajectory TEXT"
|
|
1696
1764
|
]) {
|
|
1697
1765
|
try {
|
|
1698
1766
|
await client.execute(col);
|
|
@@ -1822,26 +1890,26 @@ var init_platform_procedures = __esm({
|
|
|
1822
1890
|
title: "What is exe-os \u2014 the operating model every agent must understand",
|
|
1823
1891
|
domain: "architecture",
|
|
1824
1892
|
priority: "p0",
|
|
1825
|
-
content: "Exe OS is an AI employee operating system. A founder runs 5-10 AI agents as a real org: COO
|
|
1893
|
+
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."
|
|
1826
1894
|
},
|
|
1827
1895
|
{
|
|
1828
1896
|
title: "Mode 1 \u2014 how exe-os runs inside Claude Code",
|
|
1829
1897
|
domain: "architecture",
|
|
1830
1898
|
priority: "p0",
|
|
1831
|
-
content: "Mode 1: exe-os runs AS hooks + MCP + skills inside Claude Code. The founder opens CC
|
|
1899
|
+
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."
|
|
1832
1900
|
},
|
|
1833
1901
|
{
|
|
1834
|
-
title: "Sessions explained \u2014
|
|
1902
|
+
title: "Sessions explained \u2014 coordinator session names and projects",
|
|
1835
1903
|
domain: "architecture",
|
|
1836
1904
|
priority: "p0",
|
|
1837
|
-
content: "Each
|
|
1905
|
+
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."
|
|
1838
1906
|
},
|
|
1839
1907
|
// --- Hierarchy and dispatch ---
|
|
1840
1908
|
{
|
|
1841
1909
|
title: "Chain of command \u2014 who talks to whom",
|
|
1842
1910
|
domain: "workflow",
|
|
1843
1911
|
priority: "p0",
|
|
1844
|
-
content: "Founder
|
|
1912
|
+
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."
|
|
1845
1913
|
},
|
|
1846
1914
|
{
|
|
1847
1915
|
title: "Single dispatch path \u2014 create_task only",
|
|
@@ -1851,30 +1919,30 @@ var init_platform_procedures = __esm({
|
|
|
1851
1919
|
},
|
|
1852
1920
|
// --- Session isolation ---
|
|
1853
1921
|
{
|
|
1854
|
-
title: "Session scoping \u2014 stay in your
|
|
1922
|
+
title: "Session scoping \u2014 stay in your coordinator boundary",
|
|
1855
1923
|
domain: "security",
|
|
1856
1924
|
priority: "p0",
|
|
1857
|
-
content: "Session scoping is mandatory. Managers dispatch to workers within their own
|
|
1925
|
+
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."
|
|
1858
1926
|
},
|
|
1859
1927
|
{
|
|
1860
1928
|
title: "Session isolation \u2014 never touch another session's work",
|
|
1861
1929
|
domain: "workflow",
|
|
1862
1930
|
priority: "p0",
|
|
1863
|
-
content:
|
|
1931
|
+
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."
|
|
1864
1932
|
},
|
|
1865
1933
|
// --- Engineering: session scoping in code ---
|
|
1866
1934
|
{
|
|
1867
1935
|
title: "Three-dimensional scoping \u2014 session, project, role \u2014 enforced in every query",
|
|
1868
1936
|
domain: "architecture",
|
|
1869
1937
|
priority: "p0",
|
|
1870
|
-
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
|
|
1938
|
+
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."
|
|
1871
1939
|
},
|
|
1872
1940
|
// --- Hard constraints ---
|
|
1873
1941
|
{
|
|
1874
1942
|
title: "What you CANNOT do in exe-os \u2014 hard constraints",
|
|
1875
1943
|
domain: "security",
|
|
1876
1944
|
priority: "p0",
|
|
1877
|
-
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
|
|
1945
|
+
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."
|
|
1878
1946
|
},
|
|
1879
1947
|
// --- Operations ---
|
|
1880
1948
|
{
|
|
@@ -2114,7 +2182,10 @@ async function writeMemory(record) {
|
|
|
2114
2182
|
source_path: record.source_path ?? null,
|
|
2115
2183
|
source_type: record.source_type ?? null,
|
|
2116
2184
|
tier: record.tier ?? classifyTier(record),
|
|
2117
|
-
supersedes_id: record.supersedes_id ?? null
|
|
2185
|
+
supersedes_id: record.supersedes_id ?? null,
|
|
2186
|
+
draft: record.draft ? 1 : 0,
|
|
2187
|
+
memory_type: record.memory_type ?? "raw",
|
|
2188
|
+
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null
|
|
2118
2189
|
};
|
|
2119
2190
|
_pendingRecords.push(dbRow);
|
|
2120
2191
|
orgBus.emit({
|
|
@@ -2169,6 +2240,9 @@ async function flushBatch() {
|
|
|
2169
2240
|
const sourceType = row.source_type ?? null;
|
|
2170
2241
|
const tier = row.tier ?? 3;
|
|
2171
2242
|
const supersedesId = row.supersedes_id ?? null;
|
|
2243
|
+
const draft = row.draft ? 1 : 0;
|
|
2244
|
+
const memoryType = row.memory_type ?? "raw";
|
|
2245
|
+
const trajectory = row.trajectory ?? null;
|
|
2172
2246
|
return {
|
|
2173
2247
|
sql: hasVector ? `INSERT OR IGNORE INTO memories
|
|
2174
2248
|
(id, agent_id, agent_role, session_id, timestamp,
|
|
@@ -2176,15 +2250,15 @@ async function flushBatch() {
|
|
|
2176
2250
|
has_error, raw_text, vector, version, task_id, importance, status,
|
|
2177
2251
|
confidence, last_accessed,
|
|
2178
2252
|
workspace_id, document_id, user_id, char_offset, page_number,
|
|
2179
|
-
source_path, source_type, tier, supersedes_id)
|
|
2180
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories
|
|
2253
|
+
source_path, source_type, tier, supersedes_id, draft, memory_type, trajectory)
|
|
2254
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories
|
|
2181
2255
|
(id, agent_id, agent_role, session_id, timestamp,
|
|
2182
2256
|
tool_name, project_name,
|
|
2183
2257
|
has_error, raw_text, vector, version, task_id, importance, status,
|
|
2184
2258
|
confidence, last_accessed,
|
|
2185
2259
|
workspace_id, document_id, user_id, char_offset, page_number,
|
|
2186
|
-
source_path, source_type, tier, supersedes_id)
|
|
2187
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
2260
|
+
source_path, source_type, tier, supersedes_id, draft, memory_type, trajectory)
|
|
2261
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
2188
2262
|
args: hasVector ? [
|
|
2189
2263
|
row.id,
|
|
2190
2264
|
row.agent_id,
|
|
@@ -2210,7 +2284,10 @@ async function flushBatch() {
|
|
|
2210
2284
|
sourcePath,
|
|
2211
2285
|
sourceType,
|
|
2212
2286
|
tier,
|
|
2213
|
-
supersedesId
|
|
2287
|
+
supersedesId,
|
|
2288
|
+
draft,
|
|
2289
|
+
memoryType,
|
|
2290
|
+
trajectory
|
|
2214
2291
|
] : [
|
|
2215
2292
|
row.id,
|
|
2216
2293
|
row.agent_id,
|
|
@@ -2235,7 +2312,10 @@ async function flushBatch() {
|
|
|
2235
2312
|
sourcePath,
|
|
2236
2313
|
sourceType,
|
|
2237
2314
|
tier,
|
|
2238
|
-
supersedesId
|
|
2315
|
+
supersedesId,
|
|
2316
|
+
draft,
|
|
2317
|
+
memoryType,
|
|
2318
|
+
trajectory
|
|
2239
2319
|
]
|
|
2240
2320
|
};
|
|
2241
2321
|
};
|
|
@@ -2304,6 +2384,8 @@ async function searchMemories(queryVector, agentId, options) {
|
|
|
2304
2384
|
const limit = options?.limit ?? 10;
|
|
2305
2385
|
const statusFilter = options?.includeArchived ? "" : `
|
|
2306
2386
|
AND COALESCE(status, 'active') = 'active'`;
|
|
2387
|
+
const draftFilter = options?.includeDrafts ? "" : `
|
|
2388
|
+
AND (draft = 0 OR draft IS NULL)`;
|
|
2307
2389
|
let sql = `SELECT id, agent_id, agent_role, session_id, timestamp,
|
|
2308
2390
|
tool_name, project_name,
|
|
2309
2391
|
has_error, raw_text, vector, importance, status,
|
|
@@ -2313,7 +2395,7 @@ async function searchMemories(queryVector, agentId, options) {
|
|
|
2313
2395
|
source_path, source_type
|
|
2314
2396
|
FROM memories
|
|
2315
2397
|
WHERE agent_id = ?
|
|
2316
|
-
AND vector IS NOT NULL${statusFilter}
|
|
2398
|
+
AND vector IS NOT NULL${statusFilter}${draftFilter}
|
|
2317
2399
|
AND COALESCE(confidence, 0.7) >= 0.3`;
|
|
2318
2400
|
const args = [agentId];
|
|
2319
2401
|
const scope = buildWikiScopeFilter(options, "");
|
|
@@ -2335,6 +2417,10 @@ async function searchMemories(queryVector, agentId, options) {
|
|
|
2335
2417
|
sql += ` AND timestamp >= ?`;
|
|
2336
2418
|
args.push(options.since);
|
|
2337
2419
|
}
|
|
2420
|
+
if (options?.memoryType) {
|
|
2421
|
+
sql += ` AND memory_type = ?`;
|
|
2422
|
+
args.push(options.memoryType);
|
|
2423
|
+
}
|
|
2338
2424
|
sql += ` ORDER BY vector_distance_cos(vector, vector32(?))`;
|
|
2339
2425
|
args.push(vectorToBlob(queryVector));
|
|
2340
2426
|
sql += ` LIMIT ?`;
|
|
@@ -2482,30 +2568,73 @@ var init_store = __esm({
|
|
|
2482
2568
|
|
|
2483
2569
|
// src/adapters/claude/active-agent.ts
|
|
2484
2570
|
init_config();
|
|
2485
|
-
import { readFileSync as
|
|
2486
|
-
import { execSync as
|
|
2487
|
-
import
|
|
2571
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync, unlinkSync as unlinkSync2, readdirSync } from "fs";
|
|
2572
|
+
import { execSync as execSync3 } from "child_process";
|
|
2573
|
+
import path3 from "path";
|
|
2488
2574
|
|
|
2489
2575
|
// src/adapters/claude/session-key.ts
|
|
2490
2576
|
init_session_key();
|
|
2491
2577
|
|
|
2492
2578
|
// src/adapters/claude/active-agent.ts
|
|
2493
|
-
|
|
2579
|
+
init_employees();
|
|
2580
|
+
var CACHE_DIR = path3.join(EXE_AI_DIR, "session-cache");
|
|
2494
2581
|
var STALE_MS = 24 * 60 * 60 * 1e3;
|
|
2582
|
+
function isNameWithOptionalInstance(candidate, baseName) {
|
|
2583
|
+
if (candidate === baseName) return true;
|
|
2584
|
+
if (!candidate.startsWith(baseName)) return false;
|
|
2585
|
+
return /^\d+$/.test(candidate.slice(baseName.length));
|
|
2586
|
+
}
|
|
2587
|
+
function resolveEmployeeFromSessionPrefix(prefix, employees) {
|
|
2588
|
+
const sorted = [...employees].sort((a, b) => b.name.length - a.name.length);
|
|
2589
|
+
for (const employee of sorted) {
|
|
2590
|
+
if (isNameWithOptionalInstance(prefix, employee.name)) {
|
|
2591
|
+
return { agentId: employee.name, agentRole: employee.role };
|
|
2592
|
+
}
|
|
2593
|
+
}
|
|
2594
|
+
return null;
|
|
2595
|
+
}
|
|
2596
|
+
function resolveActiveAgentFromTmuxSession(sessionName) {
|
|
2597
|
+
const employees = loadEmployeesSync();
|
|
2598
|
+
const coordinator = getCoordinatorEmployee(employees);
|
|
2599
|
+
const coordinatorName = coordinator?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME;
|
|
2600
|
+
if (isNameWithOptionalInstance(sessionName, coordinatorName)) {
|
|
2601
|
+
return {
|
|
2602
|
+
agentId: coordinatorName,
|
|
2603
|
+
agentRole: coordinator?.role ?? "COO"
|
|
2604
|
+
};
|
|
2605
|
+
}
|
|
2606
|
+
if (isNameWithOptionalInstance(sessionName, DEFAULT_COORDINATOR_TEMPLATE_NAME)) {
|
|
2607
|
+
return {
|
|
2608
|
+
agentId: coordinator?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME,
|
|
2609
|
+
agentRole: coordinator?.role ?? "COO"
|
|
2610
|
+
};
|
|
2611
|
+
}
|
|
2612
|
+
if (sessionName.includes("-")) {
|
|
2613
|
+
const prefix = sessionName.split("-")[0] ?? "";
|
|
2614
|
+
const employee = resolveEmployeeFromSessionPrefix(prefix, employees);
|
|
2615
|
+
if (employee) return employee;
|
|
2616
|
+
const legacy = prefix.match(/^([a-zA-Z]+)\d*$/);
|
|
2617
|
+
if (legacy?.[1] && legacy[1] !== DEFAULT_COORDINATOR_TEMPLATE_NAME) {
|
|
2618
|
+
const emp = getEmployee(employees, legacy[1]);
|
|
2619
|
+
return { agentId: emp?.name ?? legacy[1], agentRole: emp?.role ?? "employee" };
|
|
2620
|
+
}
|
|
2621
|
+
}
|
|
2622
|
+
return null;
|
|
2623
|
+
}
|
|
2495
2624
|
function getMarkerPath() {
|
|
2496
|
-
return
|
|
2625
|
+
return path3.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
|
|
2497
2626
|
}
|
|
2498
2627
|
function getActiveAgent() {
|
|
2499
2628
|
try {
|
|
2500
2629
|
const markerPath = getMarkerPath();
|
|
2501
|
-
const raw =
|
|
2630
|
+
const raw = readFileSync3(markerPath, "utf8");
|
|
2502
2631
|
const data = JSON.parse(raw);
|
|
2503
2632
|
if (data.agentId) {
|
|
2504
2633
|
if (data.startedAt) {
|
|
2505
2634
|
const age = Date.now() - new Date(data.startedAt).getTime();
|
|
2506
2635
|
if (age > STALE_MS) {
|
|
2507
2636
|
try {
|
|
2508
|
-
|
|
2637
|
+
unlinkSync2(markerPath);
|
|
2509
2638
|
} catch {
|
|
2510
2639
|
}
|
|
2511
2640
|
} else {
|
|
@@ -2524,17 +2653,12 @@ function getActiveAgent() {
|
|
|
2524
2653
|
} catch {
|
|
2525
2654
|
}
|
|
2526
2655
|
try {
|
|
2527
|
-
const sessionName =
|
|
2656
|
+
const sessionName = execSync3(
|
|
2528
2657
|
"tmux display-message -p '#{session_name}' 2>/dev/null",
|
|
2529
2658
|
{ encoding: "utf8", timeout: 2e3 }
|
|
2530
2659
|
).trim();
|
|
2531
|
-
const
|
|
2532
|
-
if (
|
|
2533
|
-
return { agentId: empMatch[1], agentRole: "employee" };
|
|
2534
|
-
}
|
|
2535
|
-
if (/^exe\d+$/.test(sessionName)) {
|
|
2536
|
-
return { agentId: "exe", agentRole: "COO" };
|
|
2537
|
-
}
|
|
2660
|
+
const resolved = resolveActiveAgentFromTmuxSession(sessionName);
|
|
2661
|
+
if (resolved) return resolved;
|
|
2538
2662
|
} catch {
|
|
2539
2663
|
}
|
|
2540
2664
|
return {
|
|
@@ -2544,8 +2668,8 @@ function getActiveAgent() {
|
|
|
2544
2668
|
}
|
|
2545
2669
|
|
|
2546
2670
|
// src/adapters/claude/hooks/subagent-stop.ts
|
|
2547
|
-
init_config();
|
|
2548
2671
|
init_task_scope();
|
|
2672
|
+
init_employees();
|
|
2549
2673
|
if (!process.env.AGENT_ID) {
|
|
2550
2674
|
process.env.AGENT_ID = "default";
|
|
2551
2675
|
process.env.AGENT_ROLE = "employee";
|
|
@@ -2564,7 +2688,7 @@ process.stdin.on("end", async () => {
|
|
|
2564
2688
|
try {
|
|
2565
2689
|
JSON.parse(input);
|
|
2566
2690
|
const agent = getActiveAgent();
|
|
2567
|
-
if (agent.agentId
|
|
2691
|
+
if (canCoordinate(agent.agentId, agent.agentRole)) {
|
|
2568
2692
|
process.exit(0);
|
|
2569
2693
|
}
|
|
2570
2694
|
const { initStore: initStore2 } = await Promise.resolve().then(() => (init_store(), store_exports));
|