@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
|
@@ -154,13 +154,7 @@ var init_config = __esm({
|
|
|
154
154
|
wikiUrl: "",
|
|
155
155
|
wikiApiKey: "",
|
|
156
156
|
wikiSyncIntervalMs: 30 * 60 * 1e3,
|
|
157
|
-
wikiWorkspaceMapping: {
|
|
158
|
-
exe: "Executive",
|
|
159
|
-
yoshi: "Engineering",
|
|
160
|
-
mari: "Marketing",
|
|
161
|
-
tom: "Engineering",
|
|
162
|
-
sasha: "Production"
|
|
163
|
-
},
|
|
157
|
+
wikiWorkspaceMapping: {},
|
|
164
158
|
wikiAutoUpdate: true,
|
|
165
159
|
wikiAutoUpdateThreshold: 0.5,
|
|
166
160
|
wikiAutoUpdateCreateNew: true,
|
|
@@ -197,6 +191,46 @@ var init_config = __esm({
|
|
|
197
191
|
}
|
|
198
192
|
});
|
|
199
193
|
|
|
194
|
+
// src/lib/employees.ts
|
|
195
|
+
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
196
|
+
import { existsSync as existsSync2, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
|
|
197
|
+
import { execSync as execSync2 } from "child_process";
|
|
198
|
+
import path2 from "path";
|
|
199
|
+
import os2 from "os";
|
|
200
|
+
function normalizeRole(role) {
|
|
201
|
+
return (role ?? "").trim().toLowerCase();
|
|
202
|
+
}
|
|
203
|
+
function isCoordinatorRole(role) {
|
|
204
|
+
return normalizeRole(role) === normalizeRole(COORDINATOR_ROLE);
|
|
205
|
+
}
|
|
206
|
+
function getCoordinatorEmployee(employees) {
|
|
207
|
+
return employees.find((e) => isCoordinatorRole(e.role));
|
|
208
|
+
}
|
|
209
|
+
function getCoordinatorName(employees = loadEmployeesSync()) {
|
|
210
|
+
return getCoordinatorEmployee(employees)?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME;
|
|
211
|
+
}
|
|
212
|
+
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
213
|
+
if (!existsSync2(employeesPath)) return [];
|
|
214
|
+
try {
|
|
215
|
+
return JSON.parse(readFileSync2(employeesPath, "utf-8"));
|
|
216
|
+
} catch {
|
|
217
|
+
return [];
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
function getEmployee(employees, name) {
|
|
221
|
+
return employees.find((e) => e.name.toLowerCase() === name.toLowerCase());
|
|
222
|
+
}
|
|
223
|
+
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE;
|
|
224
|
+
var init_employees = __esm({
|
|
225
|
+
"src/lib/employees.ts"() {
|
|
226
|
+
"use strict";
|
|
227
|
+
init_config();
|
|
228
|
+
EMPLOYEES_PATH = path2.join(EXE_AI_DIR, "exe-employees.json");
|
|
229
|
+
DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
|
|
230
|
+
COORDINATOR_ROLE = "COO";
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
|
|
200
234
|
// src/types/memory.ts
|
|
201
235
|
var EMBEDDING_DIM;
|
|
202
236
|
var init_memory = __esm({
|
|
@@ -245,7 +279,7 @@ function wrapWithRetry(client) {
|
|
|
245
279
|
return (sql) => retryOnBusy(() => target.execute(sql), "execute");
|
|
246
280
|
}
|
|
247
281
|
if (prop === "batch") {
|
|
248
|
-
return (stmts) => retryOnBusy(() => target.batch(stmts), "batch");
|
|
282
|
+
return (stmts, mode) => retryOnBusy(() => target.batch(stmts, mode), "batch");
|
|
249
283
|
}
|
|
250
284
|
return Reflect.get(target, prop, receiver);
|
|
251
285
|
}
|
|
@@ -408,22 +442,24 @@ async function ensureSchema() {
|
|
|
408
442
|
ON behaviors(agent_id, active);
|
|
409
443
|
`);
|
|
410
444
|
try {
|
|
445
|
+
const coordinatorName = getCoordinatorName();
|
|
411
446
|
const existing = await client.execute({
|
|
412
|
-
sql: "SELECT COUNT(*) as cnt FROM behaviors WHERE agent_id =
|
|
413
|
-
args: []
|
|
447
|
+
sql: "SELECT COUNT(*) as cnt FROM behaviors WHERE agent_id = ?",
|
|
448
|
+
args: [coordinatorName]
|
|
414
449
|
});
|
|
415
450
|
if (Number(existing.rows[0]?.cnt) === 0) {
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
451
|
+
const seededAt = "2026-03-25T00:00:00Z";
|
|
452
|
+
for (const [domain, content] of [
|
|
453
|
+
["workflow", `Don't ask "keep going?" \u2014 just keep executing phases/plans autonomously`],
|
|
454
|
+
["tool-use", "Always use create_task MCP tool, never write .md files directly for task creation"],
|
|
455
|
+
["workflow", "Auto-start reviewing when idle and reviews are pending \u2014 never ask founder for permission"]
|
|
456
|
+
]) {
|
|
457
|
+
await client.execute({
|
|
458
|
+
sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, content, active, created_at, updated_at)
|
|
459
|
+
VALUES (hex(randomblob(16)), ?, NULL, ?, ?, 1, ?, ?)`,
|
|
460
|
+
args: [coordinatorName, domain, content, seededAt, seededAt]
|
|
461
|
+
});
|
|
462
|
+
}
|
|
427
463
|
}
|
|
428
464
|
} catch {
|
|
429
465
|
}
|
|
@@ -1115,6 +1151,39 @@ async function ensureSchema() {
|
|
|
1115
1151
|
} catch {
|
|
1116
1152
|
}
|
|
1117
1153
|
}
|
|
1154
|
+
try {
|
|
1155
|
+
await client.execute({
|
|
1156
|
+
sql: `ALTER TABLE memories ADD COLUMN draft INTEGER DEFAULT 0`,
|
|
1157
|
+
args: []
|
|
1158
|
+
});
|
|
1159
|
+
} catch {
|
|
1160
|
+
}
|
|
1161
|
+
try {
|
|
1162
|
+
await client.execute(
|
|
1163
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_draft ON memories(draft) WHERE draft = 1`
|
|
1164
|
+
);
|
|
1165
|
+
} catch {
|
|
1166
|
+
}
|
|
1167
|
+
try {
|
|
1168
|
+
await client.execute({
|
|
1169
|
+
sql: `ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'`,
|
|
1170
|
+
args: []
|
|
1171
|
+
});
|
|
1172
|
+
} catch {
|
|
1173
|
+
}
|
|
1174
|
+
try {
|
|
1175
|
+
await client.execute(
|
|
1176
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_type ON memories(memory_type)`
|
|
1177
|
+
);
|
|
1178
|
+
} catch {
|
|
1179
|
+
}
|
|
1180
|
+
try {
|
|
1181
|
+
await client.execute({
|
|
1182
|
+
sql: `ALTER TABLE memories ADD COLUMN trajectory TEXT`,
|
|
1183
|
+
args: []
|
|
1184
|
+
});
|
|
1185
|
+
} catch {
|
|
1186
|
+
}
|
|
1118
1187
|
}
|
|
1119
1188
|
async function disposeDatabase() {
|
|
1120
1189
|
if (_client) {
|
|
@@ -1128,6 +1197,7 @@ var init_database = __esm({
|
|
|
1128
1197
|
"src/lib/database.ts"() {
|
|
1129
1198
|
"use strict";
|
|
1130
1199
|
init_db_retry();
|
|
1200
|
+
init_employees();
|
|
1131
1201
|
_client = null;
|
|
1132
1202
|
_resilientClient = null;
|
|
1133
1203
|
initTurso = initDatabase;
|
|
@@ -1136,15 +1206,15 @@ var init_database = __esm({
|
|
|
1136
1206
|
});
|
|
1137
1207
|
|
|
1138
1208
|
// src/lib/keychain.ts
|
|
1139
|
-
import { readFile as
|
|
1140
|
-
import { existsSync as
|
|
1141
|
-
import
|
|
1142
|
-
import
|
|
1209
|
+
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
1210
|
+
import { existsSync as existsSync3 } from "fs";
|
|
1211
|
+
import path4 from "path";
|
|
1212
|
+
import os3 from "os";
|
|
1143
1213
|
function getKeyDir() {
|
|
1144
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
1214
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path4.join(os3.homedir(), ".exe-os");
|
|
1145
1215
|
}
|
|
1146
1216
|
function getKeyPath() {
|
|
1147
|
-
return
|
|
1217
|
+
return path4.join(getKeyDir(), "master.key");
|
|
1148
1218
|
}
|
|
1149
1219
|
async function tryKeytar() {
|
|
1150
1220
|
try {
|
|
@@ -1165,11 +1235,11 @@ async function getMasterKey() {
|
|
|
1165
1235
|
}
|
|
1166
1236
|
}
|
|
1167
1237
|
const keyPath = getKeyPath();
|
|
1168
|
-
if (!
|
|
1238
|
+
if (!existsSync3(keyPath)) {
|
|
1169
1239
|
return null;
|
|
1170
1240
|
}
|
|
1171
1241
|
try {
|
|
1172
|
-
const content = await
|
|
1242
|
+
const content = await readFile3(keyPath, "utf-8");
|
|
1173
1243
|
return Buffer.from(content.trim(), "base64");
|
|
1174
1244
|
} catch {
|
|
1175
1245
|
return null;
|
|
@@ -1252,12 +1322,12 @@ __export(shard_manager_exports, {
|
|
|
1252
1322
|
listShards: () => listShards,
|
|
1253
1323
|
shardExists: () => shardExists
|
|
1254
1324
|
});
|
|
1255
|
-
import
|
|
1256
|
-
import { existsSync as
|
|
1325
|
+
import path5 from "path";
|
|
1326
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync2, readdirSync as readdirSync2 } from "fs";
|
|
1257
1327
|
import { createClient as createClient2 } from "@libsql/client";
|
|
1258
1328
|
function initShardManager(encryptionKey) {
|
|
1259
1329
|
_encryptionKey = encryptionKey;
|
|
1260
|
-
if (!
|
|
1330
|
+
if (!existsSync4(SHARDS_DIR)) {
|
|
1261
1331
|
mkdirSync2(SHARDS_DIR, { recursive: true });
|
|
1262
1332
|
}
|
|
1263
1333
|
_shardingEnabled = true;
|
|
@@ -1278,7 +1348,7 @@ function getShardClient(projectName) {
|
|
|
1278
1348
|
}
|
|
1279
1349
|
const cached = _shards.get(safeName);
|
|
1280
1350
|
if (cached) return cached;
|
|
1281
|
-
const dbPath =
|
|
1351
|
+
const dbPath = path5.join(SHARDS_DIR, `${safeName}.db`);
|
|
1282
1352
|
const client = createClient2({
|
|
1283
1353
|
url: `file:${dbPath}`,
|
|
1284
1354
|
encryptionKey: _encryptionKey
|
|
@@ -1288,10 +1358,10 @@ function getShardClient(projectName) {
|
|
|
1288
1358
|
}
|
|
1289
1359
|
function shardExists(projectName) {
|
|
1290
1360
|
const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
1291
|
-
return
|
|
1361
|
+
return existsSync4(path5.join(SHARDS_DIR, `${safeName}.db`));
|
|
1292
1362
|
}
|
|
1293
1363
|
function listShards() {
|
|
1294
|
-
if (!
|
|
1364
|
+
if (!existsSync4(SHARDS_DIR)) return [];
|
|
1295
1365
|
return readdirSync2(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
1296
1366
|
}
|
|
1297
1367
|
async function ensureShardSchema(client) {
|
|
@@ -1361,7 +1431,11 @@ async function ensureShardSchema(client) {
|
|
|
1361
1431
|
"ALTER TABLE memories ADD COLUMN source_path TEXT",
|
|
1362
1432
|
"ALTER TABLE memories ADD COLUMN source_type TEXT DEFAULT 'text'",
|
|
1363
1433
|
"ALTER TABLE memories ADD COLUMN tier INTEGER DEFAULT 3",
|
|
1364
|
-
"ALTER TABLE memories ADD COLUMN supersedes_id TEXT"
|
|
1434
|
+
"ALTER TABLE memories ADD COLUMN supersedes_id TEXT",
|
|
1435
|
+
// MS-11: draft staging, MS-6a: memory_type, MS-7: trajectory
|
|
1436
|
+
"ALTER TABLE memories ADD COLUMN draft INTEGER DEFAULT 0",
|
|
1437
|
+
"ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'",
|
|
1438
|
+
"ALTER TABLE memories ADD COLUMN trajectory TEXT"
|
|
1365
1439
|
]) {
|
|
1366
1440
|
try {
|
|
1367
1441
|
await client.execute(col);
|
|
@@ -1473,7 +1547,7 @@ var init_shard_manager = __esm({
|
|
|
1473
1547
|
"src/lib/shard-manager.ts"() {
|
|
1474
1548
|
"use strict";
|
|
1475
1549
|
init_config();
|
|
1476
|
-
SHARDS_DIR =
|
|
1550
|
+
SHARDS_DIR = path5.join(EXE_AI_DIR, "shards");
|
|
1477
1551
|
_shards = /* @__PURE__ */ new Map();
|
|
1478
1552
|
_encryptionKey = null;
|
|
1479
1553
|
_shardingEnabled = false;
|
|
@@ -1491,26 +1565,26 @@ var init_platform_procedures = __esm({
|
|
|
1491
1565
|
title: "What is exe-os \u2014 the operating model every agent must understand",
|
|
1492
1566
|
domain: "architecture",
|
|
1493
1567
|
priority: "p0",
|
|
1494
|
-
content: "Exe OS is an AI employee operating system. A founder runs 5-10 AI agents as a real org: COO
|
|
1568
|
+
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."
|
|
1495
1569
|
},
|
|
1496
1570
|
{
|
|
1497
1571
|
title: "Mode 1 \u2014 how exe-os runs inside Claude Code",
|
|
1498
1572
|
domain: "architecture",
|
|
1499
1573
|
priority: "p0",
|
|
1500
|
-
content: "Mode 1: exe-os runs AS hooks + MCP + skills inside Claude Code. The founder opens CC
|
|
1574
|
+
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."
|
|
1501
1575
|
},
|
|
1502
1576
|
{
|
|
1503
|
-
title: "Sessions explained \u2014
|
|
1577
|
+
title: "Sessions explained \u2014 coordinator session names and projects",
|
|
1504
1578
|
domain: "architecture",
|
|
1505
1579
|
priority: "p0",
|
|
1506
|
-
content: "Each
|
|
1580
|
+
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."
|
|
1507
1581
|
},
|
|
1508
1582
|
// --- Hierarchy and dispatch ---
|
|
1509
1583
|
{
|
|
1510
1584
|
title: "Chain of command \u2014 who talks to whom",
|
|
1511
1585
|
domain: "workflow",
|
|
1512
1586
|
priority: "p0",
|
|
1513
|
-
content: "Founder
|
|
1587
|
+
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."
|
|
1514
1588
|
},
|
|
1515
1589
|
{
|
|
1516
1590
|
title: "Single dispatch path \u2014 create_task only",
|
|
@@ -1520,30 +1594,30 @@ var init_platform_procedures = __esm({
|
|
|
1520
1594
|
},
|
|
1521
1595
|
// --- Session isolation ---
|
|
1522
1596
|
{
|
|
1523
|
-
title: "Session scoping \u2014 stay in your
|
|
1597
|
+
title: "Session scoping \u2014 stay in your coordinator boundary",
|
|
1524
1598
|
domain: "security",
|
|
1525
1599
|
priority: "p0",
|
|
1526
|
-
content: "Session scoping is mandatory. Managers dispatch to workers within their own
|
|
1600
|
+
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."
|
|
1527
1601
|
},
|
|
1528
1602
|
{
|
|
1529
1603
|
title: "Session isolation \u2014 never touch another session's work",
|
|
1530
1604
|
domain: "workflow",
|
|
1531
1605
|
priority: "p0",
|
|
1532
|
-
content:
|
|
1606
|
+
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."
|
|
1533
1607
|
},
|
|
1534
1608
|
// --- Engineering: session scoping in code ---
|
|
1535
1609
|
{
|
|
1536
1610
|
title: "Three-dimensional scoping \u2014 session, project, role \u2014 enforced in every query",
|
|
1537
1611
|
domain: "architecture",
|
|
1538
1612
|
priority: "p0",
|
|
1539
|
-
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
|
|
1613
|
+
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."
|
|
1540
1614
|
},
|
|
1541
1615
|
// --- Hard constraints ---
|
|
1542
1616
|
{
|
|
1543
1617
|
title: "What you CANNOT do in exe-os \u2014 hard constraints",
|
|
1544
1618
|
domain: "security",
|
|
1545
1619
|
priority: "p0",
|
|
1546
|
-
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
|
|
1620
|
+
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."
|
|
1547
1621
|
},
|
|
1548
1622
|
// --- Operations ---
|
|
1549
1623
|
{
|
|
@@ -1783,7 +1857,10 @@ async function writeMemory(record) {
|
|
|
1783
1857
|
source_path: record.source_path ?? null,
|
|
1784
1858
|
source_type: record.source_type ?? null,
|
|
1785
1859
|
tier: record.tier ?? classifyTier(record),
|
|
1786
|
-
supersedes_id: record.supersedes_id ?? null
|
|
1860
|
+
supersedes_id: record.supersedes_id ?? null,
|
|
1861
|
+
draft: record.draft ? 1 : 0,
|
|
1862
|
+
memory_type: record.memory_type ?? "raw",
|
|
1863
|
+
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null
|
|
1787
1864
|
};
|
|
1788
1865
|
_pendingRecords.push(dbRow);
|
|
1789
1866
|
orgBus.emit({
|
|
@@ -1838,6 +1915,9 @@ async function flushBatch() {
|
|
|
1838
1915
|
const sourceType = row.source_type ?? null;
|
|
1839
1916
|
const tier = row.tier ?? 3;
|
|
1840
1917
|
const supersedesId = row.supersedes_id ?? null;
|
|
1918
|
+
const draft = row.draft ? 1 : 0;
|
|
1919
|
+
const memoryType = row.memory_type ?? "raw";
|
|
1920
|
+
const trajectory = row.trajectory ?? null;
|
|
1841
1921
|
return {
|
|
1842
1922
|
sql: hasVector ? `INSERT OR IGNORE INTO memories
|
|
1843
1923
|
(id, agent_id, agent_role, session_id, timestamp,
|
|
@@ -1845,15 +1925,15 @@ async function flushBatch() {
|
|
|
1845
1925
|
has_error, raw_text, vector, version, task_id, importance, status,
|
|
1846
1926
|
confidence, last_accessed,
|
|
1847
1927
|
workspace_id, document_id, user_id, char_offset, page_number,
|
|
1848
|
-
source_path, source_type, tier, supersedes_id)
|
|
1849
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories
|
|
1928
|
+
source_path, source_type, tier, supersedes_id, draft, memory_type, trajectory)
|
|
1929
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories
|
|
1850
1930
|
(id, agent_id, agent_role, session_id, timestamp,
|
|
1851
1931
|
tool_name, project_name,
|
|
1852
1932
|
has_error, raw_text, vector, version, task_id, importance, status,
|
|
1853
1933
|
confidence, last_accessed,
|
|
1854
1934
|
workspace_id, document_id, user_id, char_offset, page_number,
|
|
1855
|
-
source_path, source_type, tier, supersedes_id)
|
|
1856
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
1935
|
+
source_path, source_type, tier, supersedes_id, draft, memory_type, trajectory)
|
|
1936
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
1857
1937
|
args: hasVector ? [
|
|
1858
1938
|
row.id,
|
|
1859
1939
|
row.agent_id,
|
|
@@ -1879,7 +1959,10 @@ async function flushBatch() {
|
|
|
1879
1959
|
sourcePath,
|
|
1880
1960
|
sourceType,
|
|
1881
1961
|
tier,
|
|
1882
|
-
supersedesId
|
|
1962
|
+
supersedesId,
|
|
1963
|
+
draft,
|
|
1964
|
+
memoryType,
|
|
1965
|
+
trajectory
|
|
1883
1966
|
] : [
|
|
1884
1967
|
row.id,
|
|
1885
1968
|
row.agent_id,
|
|
@@ -1904,7 +1987,10 @@ async function flushBatch() {
|
|
|
1904
1987
|
sourcePath,
|
|
1905
1988
|
sourceType,
|
|
1906
1989
|
tier,
|
|
1907
|
-
supersedesId
|
|
1990
|
+
supersedesId,
|
|
1991
|
+
draft,
|
|
1992
|
+
memoryType,
|
|
1993
|
+
trajectory
|
|
1908
1994
|
]
|
|
1909
1995
|
};
|
|
1910
1996
|
};
|
|
@@ -1973,6 +2059,8 @@ async function searchMemories(queryVector, agentId, options) {
|
|
|
1973
2059
|
const limit = options?.limit ?? 10;
|
|
1974
2060
|
const statusFilter = options?.includeArchived ? "" : `
|
|
1975
2061
|
AND COALESCE(status, 'active') = 'active'`;
|
|
2062
|
+
const draftFilter = options?.includeDrafts ? "" : `
|
|
2063
|
+
AND (draft = 0 OR draft IS NULL)`;
|
|
1976
2064
|
let sql = `SELECT id, agent_id, agent_role, session_id, timestamp,
|
|
1977
2065
|
tool_name, project_name,
|
|
1978
2066
|
has_error, raw_text, vector, importance, status,
|
|
@@ -1982,7 +2070,7 @@ async function searchMemories(queryVector, agentId, options) {
|
|
|
1982
2070
|
source_path, source_type
|
|
1983
2071
|
FROM memories
|
|
1984
2072
|
WHERE agent_id = ?
|
|
1985
|
-
AND vector IS NOT NULL${statusFilter}
|
|
2073
|
+
AND vector IS NOT NULL${statusFilter}${draftFilter}
|
|
1986
2074
|
AND COALESCE(confidence, 0.7) >= 0.3`;
|
|
1987
2075
|
const args = [agentId];
|
|
1988
2076
|
const scope = buildWikiScopeFilter(options, "");
|
|
@@ -2004,6 +2092,10 @@ async function searchMemories(queryVector, agentId, options) {
|
|
|
2004
2092
|
sql += ` AND timestamp >= ?`;
|
|
2005
2093
|
args.push(options.since);
|
|
2006
2094
|
}
|
|
2095
|
+
if (options?.memoryType) {
|
|
2096
|
+
sql += ` AND memory_type = ?`;
|
|
2097
|
+
args.push(options.memoryType);
|
|
2098
|
+
}
|
|
2007
2099
|
sql += ` ORDER BY vector_distance_cos(vector, vector32(?))`;
|
|
2008
2100
|
args.push(vectorToBlob(queryVector));
|
|
2009
2101
|
sql += ` LIMIT ?`;
|
|
@@ -2151,9 +2243,9 @@ var init_store = __esm({
|
|
|
2151
2243
|
|
|
2152
2244
|
// src/adapters/claude/active-agent.ts
|
|
2153
2245
|
init_config();
|
|
2154
|
-
import { readFileSync as
|
|
2155
|
-
import { execSync as
|
|
2156
|
-
import
|
|
2246
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync, unlinkSync as unlinkSync2, readdirSync } from "fs";
|
|
2247
|
+
import { execSync as execSync3 } from "child_process";
|
|
2248
|
+
import path3 from "path";
|
|
2157
2249
|
|
|
2158
2250
|
// src/lib/session-key.ts
|
|
2159
2251
|
import { execSync } from "child_process";
|
|
@@ -2185,22 +2277,65 @@ function getSessionKey() {
|
|
|
2185
2277
|
}
|
|
2186
2278
|
|
|
2187
2279
|
// src/adapters/claude/active-agent.ts
|
|
2188
|
-
|
|
2280
|
+
init_employees();
|
|
2281
|
+
var CACHE_DIR = path3.join(EXE_AI_DIR, "session-cache");
|
|
2189
2282
|
var STALE_MS = 24 * 60 * 60 * 1e3;
|
|
2283
|
+
function isNameWithOptionalInstance(candidate, baseName) {
|
|
2284
|
+
if (candidate === baseName) return true;
|
|
2285
|
+
if (!candidate.startsWith(baseName)) return false;
|
|
2286
|
+
return /^\d+$/.test(candidate.slice(baseName.length));
|
|
2287
|
+
}
|
|
2288
|
+
function resolveEmployeeFromSessionPrefix(prefix, employees) {
|
|
2289
|
+
const sorted = [...employees].sort((a, b) => b.name.length - a.name.length);
|
|
2290
|
+
for (const employee of sorted) {
|
|
2291
|
+
if (isNameWithOptionalInstance(prefix, employee.name)) {
|
|
2292
|
+
return { agentId: employee.name, agentRole: employee.role };
|
|
2293
|
+
}
|
|
2294
|
+
}
|
|
2295
|
+
return null;
|
|
2296
|
+
}
|
|
2297
|
+
function resolveActiveAgentFromTmuxSession(sessionName) {
|
|
2298
|
+
const employees = loadEmployeesSync();
|
|
2299
|
+
const coordinator = getCoordinatorEmployee(employees);
|
|
2300
|
+
const coordinatorName = coordinator?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME;
|
|
2301
|
+
if (isNameWithOptionalInstance(sessionName, coordinatorName)) {
|
|
2302
|
+
return {
|
|
2303
|
+
agentId: coordinatorName,
|
|
2304
|
+
agentRole: coordinator?.role ?? "COO"
|
|
2305
|
+
};
|
|
2306
|
+
}
|
|
2307
|
+
if (isNameWithOptionalInstance(sessionName, DEFAULT_COORDINATOR_TEMPLATE_NAME)) {
|
|
2308
|
+
return {
|
|
2309
|
+
agentId: coordinator?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME,
|
|
2310
|
+
agentRole: coordinator?.role ?? "COO"
|
|
2311
|
+
};
|
|
2312
|
+
}
|
|
2313
|
+
if (sessionName.includes("-")) {
|
|
2314
|
+
const prefix = sessionName.split("-")[0] ?? "";
|
|
2315
|
+
const employee = resolveEmployeeFromSessionPrefix(prefix, employees);
|
|
2316
|
+
if (employee) return employee;
|
|
2317
|
+
const legacy = prefix.match(/^([a-zA-Z]+)\d*$/);
|
|
2318
|
+
if (legacy?.[1] && legacy[1] !== DEFAULT_COORDINATOR_TEMPLATE_NAME) {
|
|
2319
|
+
const emp = getEmployee(employees, legacy[1]);
|
|
2320
|
+
return { agentId: emp?.name ?? legacy[1], agentRole: emp?.role ?? "employee" };
|
|
2321
|
+
}
|
|
2322
|
+
}
|
|
2323
|
+
return null;
|
|
2324
|
+
}
|
|
2190
2325
|
function getMarkerPath() {
|
|
2191
|
-
return
|
|
2326
|
+
return path3.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
|
|
2192
2327
|
}
|
|
2193
2328
|
function getActiveAgent() {
|
|
2194
2329
|
try {
|
|
2195
2330
|
const markerPath = getMarkerPath();
|
|
2196
|
-
const raw =
|
|
2331
|
+
const raw = readFileSync3(markerPath, "utf8");
|
|
2197
2332
|
const data = JSON.parse(raw);
|
|
2198
2333
|
if (data.agentId) {
|
|
2199
2334
|
if (data.startedAt) {
|
|
2200
2335
|
const age = Date.now() - new Date(data.startedAt).getTime();
|
|
2201
2336
|
if (age > STALE_MS) {
|
|
2202
2337
|
try {
|
|
2203
|
-
|
|
2338
|
+
unlinkSync2(markerPath);
|
|
2204
2339
|
} catch {
|
|
2205
2340
|
}
|
|
2206
2341
|
} else {
|
|
@@ -2219,17 +2354,12 @@ function getActiveAgent() {
|
|
|
2219
2354
|
} catch {
|
|
2220
2355
|
}
|
|
2221
2356
|
try {
|
|
2222
|
-
const sessionName =
|
|
2357
|
+
const sessionName = execSync3(
|
|
2223
2358
|
"tmux display-message -p '#{session_name}' 2>/dev/null",
|
|
2224
2359
|
{ encoding: "utf8", timeout: 2e3 }
|
|
2225
2360
|
).trim();
|
|
2226
|
-
const
|
|
2227
|
-
if (
|
|
2228
|
-
return { agentId: empMatch[1], agentRole: "employee" };
|
|
2229
|
-
}
|
|
2230
|
-
if (/^exe\d+$/.test(sessionName)) {
|
|
2231
|
-
return { agentId: "exe", agentRole: "COO" };
|
|
2232
|
-
}
|
|
2361
|
+
const resolved = resolveActiveAgentFromTmuxSession(sessionName);
|
|
2362
|
+
if (resolved) return resolved;
|
|
2233
2363
|
} catch {
|
|
2234
2364
|
}
|
|
2235
2365
|
return {
|