@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
|
@@ -13,7 +13,6 @@ var config_exports = {};
|
|
|
13
13
|
__export(config_exports, {
|
|
14
14
|
CONFIG_MIGRATIONS: () => CONFIG_MIGRATIONS,
|
|
15
15
|
CONFIG_PATH: () => CONFIG_PATH,
|
|
16
|
-
COO_AGENT_NAME: () => COO_AGENT_NAME,
|
|
17
16
|
CURRENT_CONFIG_VERSION: () => CURRENT_CONFIG_VERSION,
|
|
18
17
|
DB_PATH: () => DB_PATH,
|
|
19
18
|
EXE_AI_DIR: () => EXE_AI_DIR,
|
|
@@ -169,7 +168,7 @@ async function loadConfigFrom(configPath) {
|
|
|
169
168
|
return { ...DEFAULT_CONFIG };
|
|
170
169
|
}
|
|
171
170
|
}
|
|
172
|
-
var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH,
|
|
171
|
+
var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
|
|
173
172
|
var init_config = __esm({
|
|
174
173
|
"src/lib/config.ts"() {
|
|
175
174
|
"use strict";
|
|
@@ -177,7 +176,6 @@ var init_config = __esm({
|
|
|
177
176
|
DB_PATH = path.join(EXE_AI_DIR, "memories.db");
|
|
178
177
|
MODELS_DIR = path.join(EXE_AI_DIR, "models");
|
|
179
178
|
CONFIG_PATH = path.join(EXE_AI_DIR, "config.json");
|
|
180
|
-
COO_AGENT_NAME = "exe";
|
|
181
179
|
LEGACY_LANCE_PATH = path.join(EXE_AI_DIR, "local.lance");
|
|
182
180
|
CURRENT_CONFIG_VERSION = 1;
|
|
183
181
|
DEFAULT_CONFIG = {
|
|
@@ -213,13 +211,7 @@ var init_config = __esm({
|
|
|
213
211
|
wikiUrl: "",
|
|
214
212
|
wikiApiKey: "",
|
|
215
213
|
wikiSyncIntervalMs: 30 * 60 * 1e3,
|
|
216
|
-
wikiWorkspaceMapping: {
|
|
217
|
-
exe: "Executive",
|
|
218
|
-
yoshi: "Engineering",
|
|
219
|
-
mari: "Marketing",
|
|
220
|
-
tom: "Engineering",
|
|
221
|
-
sasha: "Production"
|
|
222
|
-
},
|
|
214
|
+
wikiWorkspaceMapping: {},
|
|
223
215
|
wikiAutoUpdate: true,
|
|
224
216
|
wikiAutoUpdateThreshold: 0.5,
|
|
225
217
|
wikiAutoUpdateCreateNew: true,
|
|
@@ -304,7 +296,7 @@ function wrapWithRetry(client) {
|
|
|
304
296
|
return (sql) => retryOnBusy(() => target.execute(sql), "execute");
|
|
305
297
|
}
|
|
306
298
|
if (prop === "batch") {
|
|
307
|
-
return (stmts) => retryOnBusy(() => target.batch(stmts), "batch");
|
|
299
|
+
return (stmts, mode) => retryOnBusy(() => target.batch(stmts, mode), "batch");
|
|
308
300
|
}
|
|
309
301
|
return Reflect.get(target, prop, receiver);
|
|
310
302
|
}
|
|
@@ -320,6 +312,46 @@ var init_db_retry = __esm({
|
|
|
320
312
|
}
|
|
321
313
|
});
|
|
322
314
|
|
|
315
|
+
// src/lib/employees.ts
|
|
316
|
+
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
317
|
+
import { existsSync as existsSync2, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
|
|
318
|
+
import { execSync } from "child_process";
|
|
319
|
+
import path2 from "path";
|
|
320
|
+
import os2 from "os";
|
|
321
|
+
function normalizeRole(role) {
|
|
322
|
+
return (role ?? "").trim().toLowerCase();
|
|
323
|
+
}
|
|
324
|
+
function isCoordinatorRole(role) {
|
|
325
|
+
return normalizeRole(role) === normalizeRole(COORDINATOR_ROLE);
|
|
326
|
+
}
|
|
327
|
+
function getCoordinatorEmployee(employees) {
|
|
328
|
+
return employees.find((e) => isCoordinatorRole(e.role));
|
|
329
|
+
}
|
|
330
|
+
function getCoordinatorName(employees = loadEmployeesSync()) {
|
|
331
|
+
return getCoordinatorEmployee(employees)?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME;
|
|
332
|
+
}
|
|
333
|
+
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
334
|
+
if (!existsSync2(employeesPath)) return [];
|
|
335
|
+
try {
|
|
336
|
+
return JSON.parse(readFileSync2(employeesPath, "utf-8"));
|
|
337
|
+
} catch {
|
|
338
|
+
return [];
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
function getEmployee(employees, name) {
|
|
342
|
+
return employees.find((e) => e.name.toLowerCase() === name.toLowerCase());
|
|
343
|
+
}
|
|
344
|
+
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE;
|
|
345
|
+
var init_employees = __esm({
|
|
346
|
+
"src/lib/employees.ts"() {
|
|
347
|
+
"use strict";
|
|
348
|
+
init_config();
|
|
349
|
+
EMPLOYEES_PATH = path2.join(EXE_AI_DIR, "exe-employees.json");
|
|
350
|
+
DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
|
|
351
|
+
COORDINATOR_ROLE = "COO";
|
|
352
|
+
}
|
|
353
|
+
});
|
|
354
|
+
|
|
323
355
|
// src/lib/database.ts
|
|
324
356
|
import { createClient } from "@libsql/client";
|
|
325
357
|
async function initDatabase(config) {
|
|
@@ -453,22 +485,24 @@ async function ensureSchema() {
|
|
|
453
485
|
ON behaviors(agent_id, active);
|
|
454
486
|
`);
|
|
455
487
|
try {
|
|
488
|
+
const coordinatorName = getCoordinatorName();
|
|
456
489
|
const existing = await client.execute({
|
|
457
|
-
sql: "SELECT COUNT(*) as cnt FROM behaviors WHERE agent_id =
|
|
458
|
-
args: []
|
|
490
|
+
sql: "SELECT COUNT(*) as cnt FROM behaviors WHERE agent_id = ?",
|
|
491
|
+
args: [coordinatorName]
|
|
459
492
|
});
|
|
460
493
|
if (Number(existing.rows[0]?.cnt) === 0) {
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
494
|
+
const seededAt = "2026-03-25T00:00:00Z";
|
|
495
|
+
for (const [domain, content] of [
|
|
496
|
+
["workflow", `Don't ask "keep going?" \u2014 just keep executing phases/plans autonomously`],
|
|
497
|
+
["tool-use", "Always use create_task MCP tool, never write .md files directly for task creation"],
|
|
498
|
+
["workflow", "Auto-start reviewing when idle and reviews are pending \u2014 never ask founder for permission"]
|
|
499
|
+
]) {
|
|
500
|
+
await client.execute({
|
|
501
|
+
sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, content, active, created_at, updated_at)
|
|
502
|
+
VALUES (hex(randomblob(16)), ?, NULL, ?, ?, 1, ?, ?)`,
|
|
503
|
+
args: [coordinatorName, domain, content, seededAt, seededAt]
|
|
504
|
+
});
|
|
505
|
+
}
|
|
472
506
|
}
|
|
473
507
|
} catch {
|
|
474
508
|
}
|
|
@@ -1160,6 +1194,39 @@ async function ensureSchema() {
|
|
|
1160
1194
|
} catch {
|
|
1161
1195
|
}
|
|
1162
1196
|
}
|
|
1197
|
+
try {
|
|
1198
|
+
await client.execute({
|
|
1199
|
+
sql: `ALTER TABLE memories ADD COLUMN draft INTEGER DEFAULT 0`,
|
|
1200
|
+
args: []
|
|
1201
|
+
});
|
|
1202
|
+
} catch {
|
|
1203
|
+
}
|
|
1204
|
+
try {
|
|
1205
|
+
await client.execute(
|
|
1206
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_draft ON memories(draft) WHERE draft = 1`
|
|
1207
|
+
);
|
|
1208
|
+
} catch {
|
|
1209
|
+
}
|
|
1210
|
+
try {
|
|
1211
|
+
await client.execute({
|
|
1212
|
+
sql: `ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'`,
|
|
1213
|
+
args: []
|
|
1214
|
+
});
|
|
1215
|
+
} catch {
|
|
1216
|
+
}
|
|
1217
|
+
try {
|
|
1218
|
+
await client.execute(
|
|
1219
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_type ON memories(memory_type)`
|
|
1220
|
+
);
|
|
1221
|
+
} catch {
|
|
1222
|
+
}
|
|
1223
|
+
try {
|
|
1224
|
+
await client.execute({
|
|
1225
|
+
sql: `ALTER TABLE memories ADD COLUMN trajectory TEXT`,
|
|
1226
|
+
args: []
|
|
1227
|
+
});
|
|
1228
|
+
} catch {
|
|
1229
|
+
}
|
|
1163
1230
|
}
|
|
1164
1231
|
async function disposeDatabase() {
|
|
1165
1232
|
if (_client) {
|
|
@@ -1173,6 +1240,7 @@ var init_database = __esm({
|
|
|
1173
1240
|
"src/lib/database.ts"() {
|
|
1174
1241
|
"use strict";
|
|
1175
1242
|
init_db_retry();
|
|
1243
|
+
init_employees();
|
|
1176
1244
|
_client = null;
|
|
1177
1245
|
_resilientClient = null;
|
|
1178
1246
|
initTurso = initDatabase;
|
|
@@ -1181,15 +1249,15 @@ var init_database = __esm({
|
|
|
1181
1249
|
});
|
|
1182
1250
|
|
|
1183
1251
|
// src/lib/keychain.ts
|
|
1184
|
-
import { readFile as
|
|
1185
|
-
import { existsSync as
|
|
1186
|
-
import
|
|
1187
|
-
import
|
|
1252
|
+
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
1253
|
+
import { existsSync as existsSync3 } from "fs";
|
|
1254
|
+
import path3 from "path";
|
|
1255
|
+
import os3 from "os";
|
|
1188
1256
|
function getKeyDir() {
|
|
1189
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
1257
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path3.join(os3.homedir(), ".exe-os");
|
|
1190
1258
|
}
|
|
1191
1259
|
function getKeyPath() {
|
|
1192
|
-
return
|
|
1260
|
+
return path3.join(getKeyDir(), "master.key");
|
|
1193
1261
|
}
|
|
1194
1262
|
async function tryKeytar() {
|
|
1195
1263
|
try {
|
|
@@ -1210,11 +1278,11 @@ async function getMasterKey() {
|
|
|
1210
1278
|
}
|
|
1211
1279
|
}
|
|
1212
1280
|
const keyPath = getKeyPath();
|
|
1213
|
-
if (!
|
|
1281
|
+
if (!existsSync3(keyPath)) {
|
|
1214
1282
|
return null;
|
|
1215
1283
|
}
|
|
1216
1284
|
try {
|
|
1217
|
-
const content = await
|
|
1285
|
+
const content = await readFile3(keyPath, "utf-8");
|
|
1218
1286
|
return Buffer.from(content.trim(), "base64");
|
|
1219
1287
|
} catch {
|
|
1220
1288
|
return null;
|
|
@@ -1297,12 +1365,12 @@ __export(shard_manager_exports, {
|
|
|
1297
1365
|
listShards: () => listShards,
|
|
1298
1366
|
shardExists: () => shardExists
|
|
1299
1367
|
});
|
|
1300
|
-
import
|
|
1301
|
-
import { existsSync as
|
|
1368
|
+
import path4 from "path";
|
|
1369
|
+
import { existsSync as existsSync4, mkdirSync, readdirSync } from "fs";
|
|
1302
1370
|
import { createClient as createClient2 } from "@libsql/client";
|
|
1303
1371
|
function initShardManager(encryptionKey) {
|
|
1304
1372
|
_encryptionKey = encryptionKey;
|
|
1305
|
-
if (!
|
|
1373
|
+
if (!existsSync4(SHARDS_DIR)) {
|
|
1306
1374
|
mkdirSync(SHARDS_DIR, { recursive: true });
|
|
1307
1375
|
}
|
|
1308
1376
|
_shardingEnabled = true;
|
|
@@ -1323,7 +1391,7 @@ function getShardClient(projectName) {
|
|
|
1323
1391
|
}
|
|
1324
1392
|
const cached = _shards.get(safeName);
|
|
1325
1393
|
if (cached) return cached;
|
|
1326
|
-
const dbPath =
|
|
1394
|
+
const dbPath = path4.join(SHARDS_DIR, `${safeName}.db`);
|
|
1327
1395
|
const client = createClient2({
|
|
1328
1396
|
url: `file:${dbPath}`,
|
|
1329
1397
|
encryptionKey: _encryptionKey
|
|
@@ -1333,10 +1401,10 @@ function getShardClient(projectName) {
|
|
|
1333
1401
|
}
|
|
1334
1402
|
function shardExists(projectName) {
|
|
1335
1403
|
const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
1336
|
-
return
|
|
1404
|
+
return existsSync4(path4.join(SHARDS_DIR, `${safeName}.db`));
|
|
1337
1405
|
}
|
|
1338
1406
|
function listShards() {
|
|
1339
|
-
if (!
|
|
1407
|
+
if (!existsSync4(SHARDS_DIR)) return [];
|
|
1340
1408
|
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
1341
1409
|
}
|
|
1342
1410
|
async function ensureShardSchema(client) {
|
|
@@ -1406,7 +1474,11 @@ async function ensureShardSchema(client) {
|
|
|
1406
1474
|
"ALTER TABLE memories ADD COLUMN source_path TEXT",
|
|
1407
1475
|
"ALTER TABLE memories ADD COLUMN source_type TEXT DEFAULT 'text'",
|
|
1408
1476
|
"ALTER TABLE memories ADD COLUMN tier INTEGER DEFAULT 3",
|
|
1409
|
-
"ALTER TABLE memories ADD COLUMN supersedes_id TEXT"
|
|
1477
|
+
"ALTER TABLE memories ADD COLUMN supersedes_id TEXT",
|
|
1478
|
+
// MS-11: draft staging, MS-6a: memory_type, MS-7: trajectory
|
|
1479
|
+
"ALTER TABLE memories ADD COLUMN draft INTEGER DEFAULT 0",
|
|
1480
|
+
"ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'",
|
|
1481
|
+
"ALTER TABLE memories ADD COLUMN trajectory TEXT"
|
|
1410
1482
|
]) {
|
|
1411
1483
|
try {
|
|
1412
1484
|
await client.execute(col);
|
|
@@ -1518,7 +1590,7 @@ var init_shard_manager = __esm({
|
|
|
1518
1590
|
"src/lib/shard-manager.ts"() {
|
|
1519
1591
|
"use strict";
|
|
1520
1592
|
init_config();
|
|
1521
|
-
SHARDS_DIR =
|
|
1593
|
+
SHARDS_DIR = path4.join(EXE_AI_DIR, "shards");
|
|
1522
1594
|
_shards = /* @__PURE__ */ new Map();
|
|
1523
1595
|
_encryptionKey = null;
|
|
1524
1596
|
_shardingEnabled = false;
|
|
@@ -1536,26 +1608,26 @@ var init_platform_procedures = __esm({
|
|
|
1536
1608
|
title: "What is exe-os \u2014 the operating model every agent must understand",
|
|
1537
1609
|
domain: "architecture",
|
|
1538
1610
|
priority: "p0",
|
|
1539
|
-
content: "Exe OS is an AI employee operating system. A founder runs 5-10 AI agents as a real org: COO
|
|
1611
|
+
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."
|
|
1540
1612
|
},
|
|
1541
1613
|
{
|
|
1542
1614
|
title: "Mode 1 \u2014 how exe-os runs inside Claude Code",
|
|
1543
1615
|
domain: "architecture",
|
|
1544
1616
|
priority: "p0",
|
|
1545
|
-
content: "Mode 1: exe-os runs AS hooks + MCP + skills inside Claude Code. The founder opens CC
|
|
1617
|
+
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."
|
|
1546
1618
|
},
|
|
1547
1619
|
{
|
|
1548
|
-
title: "Sessions explained \u2014
|
|
1620
|
+
title: "Sessions explained \u2014 coordinator session names and projects",
|
|
1549
1621
|
domain: "architecture",
|
|
1550
1622
|
priority: "p0",
|
|
1551
|
-
content: "Each
|
|
1623
|
+
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."
|
|
1552
1624
|
},
|
|
1553
1625
|
// --- Hierarchy and dispatch ---
|
|
1554
1626
|
{
|
|
1555
1627
|
title: "Chain of command \u2014 who talks to whom",
|
|
1556
1628
|
domain: "workflow",
|
|
1557
1629
|
priority: "p0",
|
|
1558
|
-
content: "Founder
|
|
1630
|
+
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."
|
|
1559
1631
|
},
|
|
1560
1632
|
{
|
|
1561
1633
|
title: "Single dispatch path \u2014 create_task only",
|
|
@@ -1565,30 +1637,30 @@ var init_platform_procedures = __esm({
|
|
|
1565
1637
|
},
|
|
1566
1638
|
// --- Session isolation ---
|
|
1567
1639
|
{
|
|
1568
|
-
title: "Session scoping \u2014 stay in your
|
|
1640
|
+
title: "Session scoping \u2014 stay in your coordinator boundary",
|
|
1569
1641
|
domain: "security",
|
|
1570
1642
|
priority: "p0",
|
|
1571
|
-
content: "Session scoping is mandatory. Managers dispatch to workers within their own
|
|
1643
|
+
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."
|
|
1572
1644
|
},
|
|
1573
1645
|
{
|
|
1574
1646
|
title: "Session isolation \u2014 never touch another session's work",
|
|
1575
1647
|
domain: "workflow",
|
|
1576
1648
|
priority: "p0",
|
|
1577
|
-
content:
|
|
1649
|
+
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."
|
|
1578
1650
|
},
|
|
1579
1651
|
// --- Engineering: session scoping in code ---
|
|
1580
1652
|
{
|
|
1581
1653
|
title: "Three-dimensional scoping \u2014 session, project, role \u2014 enforced in every query",
|
|
1582
1654
|
domain: "architecture",
|
|
1583
1655
|
priority: "p0",
|
|
1584
|
-
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
|
|
1656
|
+
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."
|
|
1585
1657
|
},
|
|
1586
1658
|
// --- Hard constraints ---
|
|
1587
1659
|
{
|
|
1588
1660
|
title: "What you CANNOT do in exe-os \u2014 hard constraints",
|
|
1589
1661
|
domain: "security",
|
|
1590
1662
|
priority: "p0",
|
|
1591
|
-
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
|
|
1663
|
+
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."
|
|
1592
1664
|
},
|
|
1593
1665
|
// --- Operations ---
|
|
1594
1666
|
{
|
|
@@ -1828,7 +1900,10 @@ async function writeMemory(record) {
|
|
|
1828
1900
|
source_path: record.source_path ?? null,
|
|
1829
1901
|
source_type: record.source_type ?? null,
|
|
1830
1902
|
tier: record.tier ?? classifyTier(record),
|
|
1831
|
-
supersedes_id: record.supersedes_id ?? null
|
|
1903
|
+
supersedes_id: record.supersedes_id ?? null,
|
|
1904
|
+
draft: record.draft ? 1 : 0,
|
|
1905
|
+
memory_type: record.memory_type ?? "raw",
|
|
1906
|
+
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null
|
|
1832
1907
|
};
|
|
1833
1908
|
_pendingRecords.push(dbRow);
|
|
1834
1909
|
orgBus.emit({
|
|
@@ -1883,6 +1958,9 @@ async function flushBatch() {
|
|
|
1883
1958
|
const sourceType = row.source_type ?? null;
|
|
1884
1959
|
const tier = row.tier ?? 3;
|
|
1885
1960
|
const supersedesId = row.supersedes_id ?? null;
|
|
1961
|
+
const draft = row.draft ? 1 : 0;
|
|
1962
|
+
const memoryType = row.memory_type ?? "raw";
|
|
1963
|
+
const trajectory = row.trajectory ?? null;
|
|
1886
1964
|
return {
|
|
1887
1965
|
sql: hasVector ? `INSERT OR IGNORE INTO memories
|
|
1888
1966
|
(id, agent_id, agent_role, session_id, timestamp,
|
|
@@ -1890,15 +1968,15 @@ async function flushBatch() {
|
|
|
1890
1968
|
has_error, raw_text, vector, version, task_id, importance, status,
|
|
1891
1969
|
confidence, last_accessed,
|
|
1892
1970
|
workspace_id, document_id, user_id, char_offset, page_number,
|
|
1893
|
-
source_path, source_type, tier, supersedes_id)
|
|
1894
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories
|
|
1971
|
+
source_path, source_type, tier, supersedes_id, draft, memory_type, trajectory)
|
|
1972
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories
|
|
1895
1973
|
(id, agent_id, agent_role, session_id, timestamp,
|
|
1896
1974
|
tool_name, project_name,
|
|
1897
1975
|
has_error, raw_text, vector, version, task_id, importance, status,
|
|
1898
1976
|
confidence, last_accessed,
|
|
1899
1977
|
workspace_id, document_id, user_id, char_offset, page_number,
|
|
1900
|
-
source_path, source_type, tier, supersedes_id)
|
|
1901
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
1978
|
+
source_path, source_type, tier, supersedes_id, draft, memory_type, trajectory)
|
|
1979
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
1902
1980
|
args: hasVector ? [
|
|
1903
1981
|
row.id,
|
|
1904
1982
|
row.agent_id,
|
|
@@ -1924,7 +2002,10 @@ async function flushBatch() {
|
|
|
1924
2002
|
sourcePath,
|
|
1925
2003
|
sourceType,
|
|
1926
2004
|
tier,
|
|
1927
|
-
supersedesId
|
|
2005
|
+
supersedesId,
|
|
2006
|
+
draft,
|
|
2007
|
+
memoryType,
|
|
2008
|
+
trajectory
|
|
1928
2009
|
] : [
|
|
1929
2010
|
row.id,
|
|
1930
2011
|
row.agent_id,
|
|
@@ -1949,7 +2030,10 @@ async function flushBatch() {
|
|
|
1949
2030
|
sourcePath,
|
|
1950
2031
|
sourceType,
|
|
1951
2032
|
tier,
|
|
1952
|
-
supersedesId
|
|
2033
|
+
supersedesId,
|
|
2034
|
+
draft,
|
|
2035
|
+
memoryType,
|
|
2036
|
+
trajectory
|
|
1953
2037
|
]
|
|
1954
2038
|
};
|
|
1955
2039
|
};
|
|
@@ -2018,6 +2102,8 @@ async function searchMemories(queryVector, agentId, options) {
|
|
|
2018
2102
|
const limit = options?.limit ?? 10;
|
|
2019
2103
|
const statusFilter = options?.includeArchived ? "" : `
|
|
2020
2104
|
AND COALESCE(status, 'active') = 'active'`;
|
|
2105
|
+
const draftFilter = options?.includeDrafts ? "" : `
|
|
2106
|
+
AND (draft = 0 OR draft IS NULL)`;
|
|
2021
2107
|
let sql = `SELECT id, agent_id, agent_role, session_id, timestamp,
|
|
2022
2108
|
tool_name, project_name,
|
|
2023
2109
|
has_error, raw_text, vector, importance, status,
|
|
@@ -2027,7 +2113,7 @@ async function searchMemories(queryVector, agentId, options) {
|
|
|
2027
2113
|
source_path, source_type
|
|
2028
2114
|
FROM memories
|
|
2029
2115
|
WHERE agent_id = ?
|
|
2030
|
-
AND vector IS NOT NULL${statusFilter}
|
|
2116
|
+
AND vector IS NOT NULL${statusFilter}${draftFilter}
|
|
2031
2117
|
AND COALESCE(confidence, 0.7) >= 0.3`;
|
|
2032
2118
|
const args = [agentId];
|
|
2033
2119
|
const scope = buildWikiScopeFilter(options, "");
|
|
@@ -2049,6 +2135,10 @@ async function searchMemories(queryVector, agentId, options) {
|
|
|
2049
2135
|
sql += ` AND timestamp >= ?`;
|
|
2050
2136
|
args.push(options.since);
|
|
2051
2137
|
}
|
|
2138
|
+
if (options?.memoryType) {
|
|
2139
|
+
sql += ` AND memory_type = ?`;
|
|
2140
|
+
args.push(options.memoryType);
|
|
2141
|
+
}
|
|
2052
2142
|
sql += ` ORDER BY vector_distance_cos(vector, vector32(?))`;
|
|
2053
2143
|
args.push(vectorToBlob(queryVector));
|
|
2054
2144
|
sql += ` LIMIT ?`;
|
|
@@ -2274,7 +2364,7 @@ var init_self_query_router = __esm({
|
|
|
2274
2364
|
},
|
|
2275
2365
|
is_broad_query: {
|
|
2276
2366
|
type: "boolean",
|
|
2277
|
-
description: "True if the query is exploratory/broad (e.g., 'what has
|
|
2367
|
+
description: "True if the query is exploratory/broad (e.g., 'what has the CTO been working on', 'summarize recent activity'). False if targeted (e.g., 'how did we fix the auth bug')."
|
|
2278
2368
|
}
|
|
2279
2369
|
},
|
|
2280
2370
|
required: ["semantic_query", "project_filter", "role_filter", "time_filter", "is_broad_query"]
|
|
@@ -2287,8 +2377,8 @@ var init_self_query_router = __esm({
|
|
|
2287
2377
|
import net from "net";
|
|
2288
2378
|
import { spawn } from "child_process";
|
|
2289
2379
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
2290
|
-
import { existsSync as
|
|
2291
|
-
import
|
|
2380
|
+
import { existsSync as existsSync5, unlinkSync as unlinkSync2, readFileSync as readFileSync3, openSync, closeSync, statSync } from "fs";
|
|
2381
|
+
import path5 from "path";
|
|
2292
2382
|
import { fileURLToPath } from "url";
|
|
2293
2383
|
function handleData(chunk) {
|
|
2294
2384
|
_buffer += chunk.toString();
|
|
@@ -2314,9 +2404,9 @@ function handleData(chunk) {
|
|
|
2314
2404
|
}
|
|
2315
2405
|
}
|
|
2316
2406
|
function cleanupStaleFiles() {
|
|
2317
|
-
if (
|
|
2407
|
+
if (existsSync5(PID_PATH)) {
|
|
2318
2408
|
try {
|
|
2319
|
-
const pid = parseInt(
|
|
2409
|
+
const pid = parseInt(readFileSync3(PID_PATH, "utf8").trim(), 10);
|
|
2320
2410
|
if (pid > 0) {
|
|
2321
2411
|
try {
|
|
2322
2412
|
process.kill(pid, 0);
|
|
@@ -2327,21 +2417,21 @@ function cleanupStaleFiles() {
|
|
|
2327
2417
|
} catch {
|
|
2328
2418
|
}
|
|
2329
2419
|
try {
|
|
2330
|
-
|
|
2420
|
+
unlinkSync2(PID_PATH);
|
|
2331
2421
|
} catch {
|
|
2332
2422
|
}
|
|
2333
2423
|
try {
|
|
2334
|
-
|
|
2424
|
+
unlinkSync2(SOCKET_PATH);
|
|
2335
2425
|
} catch {
|
|
2336
2426
|
}
|
|
2337
2427
|
}
|
|
2338
2428
|
}
|
|
2339
2429
|
function findPackageRoot() {
|
|
2340
|
-
let dir =
|
|
2341
|
-
const { root } =
|
|
2430
|
+
let dir = path5.dirname(fileURLToPath(import.meta.url));
|
|
2431
|
+
const { root } = path5.parse(dir);
|
|
2342
2432
|
while (dir !== root) {
|
|
2343
|
-
if (
|
|
2344
|
-
dir =
|
|
2433
|
+
if (existsSync5(path5.join(dir, "package.json"))) return dir;
|
|
2434
|
+
dir = path5.dirname(dir);
|
|
2345
2435
|
}
|
|
2346
2436
|
return null;
|
|
2347
2437
|
}
|
|
@@ -2351,8 +2441,8 @@ function spawnDaemon() {
|
|
|
2351
2441
|
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
2352
2442
|
return;
|
|
2353
2443
|
}
|
|
2354
|
-
const daemonPath =
|
|
2355
|
-
if (!
|
|
2444
|
+
const daemonPath = path5.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
2445
|
+
if (!existsSync5(daemonPath)) {
|
|
2356
2446
|
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
2357
2447
|
`);
|
|
2358
2448
|
return;
|
|
@@ -2360,7 +2450,7 @@ function spawnDaemon() {
|
|
|
2360
2450
|
const resolvedPath = daemonPath;
|
|
2361
2451
|
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
2362
2452
|
`);
|
|
2363
|
-
const logPath =
|
|
2453
|
+
const logPath = path5.join(path5.dirname(SOCKET_PATH), "exed.log");
|
|
2364
2454
|
let stderrFd = "ignore";
|
|
2365
2455
|
try {
|
|
2366
2456
|
stderrFd = openSync(logPath, "a");
|
|
@@ -2371,6 +2461,10 @@ function spawnDaemon() {
|
|
|
2371
2461
|
stdio: ["ignore", "ignore", stderrFd],
|
|
2372
2462
|
env: {
|
|
2373
2463
|
...process.env,
|
|
2464
|
+
TMUX: void 0,
|
|
2465
|
+
// Daemon is global — must not inherit session scope
|
|
2466
|
+
TMUX_PANE: void 0,
|
|
2467
|
+
// Prevents resolveExeSession() from scoping to one session
|
|
2374
2468
|
EXE_DAEMON_SOCK: SOCKET_PATH,
|
|
2375
2469
|
EXE_DAEMON_PID: PID_PATH
|
|
2376
2470
|
}
|
|
@@ -2393,7 +2487,7 @@ function acquireSpawnLock() {
|
|
|
2393
2487
|
const stat = statSync(SPAWN_LOCK_PATH);
|
|
2394
2488
|
if (Date.now() - stat.mtimeMs > SPAWN_LOCK_STALE_MS) {
|
|
2395
2489
|
try {
|
|
2396
|
-
|
|
2490
|
+
unlinkSync2(SPAWN_LOCK_PATH);
|
|
2397
2491
|
} catch {
|
|
2398
2492
|
}
|
|
2399
2493
|
try {
|
|
@@ -2410,7 +2504,7 @@ function acquireSpawnLock() {
|
|
|
2410
2504
|
}
|
|
2411
2505
|
function releaseSpawnLock() {
|
|
2412
2506
|
try {
|
|
2413
|
-
|
|
2507
|
+
unlinkSync2(SPAWN_LOCK_PATH);
|
|
2414
2508
|
} catch {
|
|
2415
2509
|
}
|
|
2416
2510
|
}
|
|
@@ -2522,9 +2616,9 @@ async function pingDaemon() {
|
|
|
2522
2616
|
}
|
|
2523
2617
|
function killAndRespawnDaemon() {
|
|
2524
2618
|
process.stderr.write("[exed-client] Killing daemon for restart...\n");
|
|
2525
|
-
if (
|
|
2619
|
+
if (existsSync5(PID_PATH)) {
|
|
2526
2620
|
try {
|
|
2527
|
-
const pid = parseInt(
|
|
2621
|
+
const pid = parseInt(readFileSync3(PID_PATH, "utf8").trim(), 10);
|
|
2528
2622
|
if (pid > 0) {
|
|
2529
2623
|
try {
|
|
2530
2624
|
process.kill(pid, "SIGKILL");
|
|
@@ -2541,11 +2635,11 @@ function killAndRespawnDaemon() {
|
|
|
2541
2635
|
_connected = false;
|
|
2542
2636
|
_buffer = "";
|
|
2543
2637
|
try {
|
|
2544
|
-
|
|
2638
|
+
unlinkSync2(PID_PATH);
|
|
2545
2639
|
} catch {
|
|
2546
2640
|
}
|
|
2547
2641
|
try {
|
|
2548
|
-
|
|
2642
|
+
unlinkSync2(SOCKET_PATH);
|
|
2549
2643
|
} catch {
|
|
2550
2644
|
}
|
|
2551
2645
|
spawnDaemon();
|
|
@@ -2608,9 +2702,9 @@ var init_exe_daemon_client = __esm({
|
|
|
2608
2702
|
"src/lib/exe-daemon-client.ts"() {
|
|
2609
2703
|
"use strict";
|
|
2610
2704
|
init_config();
|
|
2611
|
-
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ??
|
|
2612
|
-
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ??
|
|
2613
|
-
SPAWN_LOCK_PATH =
|
|
2705
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path5.join(EXE_AI_DIR, "exed.sock");
|
|
2706
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path5.join(EXE_AI_DIR, "exed.pid");
|
|
2707
|
+
SPAWN_LOCK_PATH = path5.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
2614
2708
|
SPAWN_LOCK_STALE_MS = 3e4;
|
|
2615
2709
|
CONNECT_TIMEOUT_MS = 15e3;
|
|
2616
2710
|
REQUEST_TIMEOUT_MS = 3e4;
|
|
@@ -2661,10 +2755,10 @@ async function disposeEmbedder() {
|
|
|
2661
2755
|
async function embedDirect(text) {
|
|
2662
2756
|
const llamaCpp = await import("node-llama-cpp");
|
|
2663
2757
|
const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
2664
|
-
const { existsSync:
|
|
2665
|
-
const
|
|
2666
|
-
const modelPath =
|
|
2667
|
-
if (!
|
|
2758
|
+
const { existsSync: existsSync8 } = await import("fs");
|
|
2759
|
+
const path10 = await import("path");
|
|
2760
|
+
const modelPath = path10.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
|
|
2761
|
+
if (!existsSync8(modelPath)) {
|
|
2668
2762
|
throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
|
|
2669
2763
|
}
|
|
2670
2764
|
const llama = await llamaCpp.getLlama();
|
|
@@ -2698,34 +2792,34 @@ __export(project_name_exports, {
|
|
|
2698
2792
|
_resetCache: () => _resetCache,
|
|
2699
2793
|
getProjectName: () => getProjectName
|
|
2700
2794
|
});
|
|
2701
|
-
import { execSync } from "child_process";
|
|
2702
|
-
import
|
|
2795
|
+
import { execSync as execSync2 } from "child_process";
|
|
2796
|
+
import path6 from "path";
|
|
2703
2797
|
function getProjectName(cwd) {
|
|
2704
2798
|
const dir = cwd ?? process.cwd();
|
|
2705
2799
|
if (_cached && _cachedCwd === dir) return _cached;
|
|
2706
2800
|
try {
|
|
2707
2801
|
let repoRoot;
|
|
2708
2802
|
try {
|
|
2709
|
-
const gitCommonDir =
|
|
2803
|
+
const gitCommonDir = execSync2("git rev-parse --path-format=absolute --git-common-dir", {
|
|
2710
2804
|
cwd: dir,
|
|
2711
2805
|
encoding: "utf8",
|
|
2712
2806
|
timeout: 2e3,
|
|
2713
2807
|
stdio: ["pipe", "pipe", "pipe"]
|
|
2714
2808
|
}).trim();
|
|
2715
|
-
repoRoot =
|
|
2809
|
+
repoRoot = path6.dirname(gitCommonDir);
|
|
2716
2810
|
} catch {
|
|
2717
|
-
repoRoot =
|
|
2811
|
+
repoRoot = execSync2("git rev-parse --show-toplevel", {
|
|
2718
2812
|
cwd: dir,
|
|
2719
2813
|
encoding: "utf8",
|
|
2720
2814
|
timeout: 2e3,
|
|
2721
2815
|
stdio: ["pipe", "pipe", "pipe"]
|
|
2722
2816
|
}).trim();
|
|
2723
2817
|
}
|
|
2724
|
-
_cached =
|
|
2818
|
+
_cached = path6.basename(repoRoot);
|
|
2725
2819
|
_cachedCwd = dir;
|
|
2726
2820
|
return _cached;
|
|
2727
2821
|
} catch {
|
|
2728
|
-
_cached =
|
|
2822
|
+
_cached = path6.basename(dir);
|
|
2729
2823
|
_cachedCwd = dir;
|
|
2730
2824
|
return _cached;
|
|
2731
2825
|
}
|
|
@@ -2748,14 +2842,14 @@ var file_grep_exports = {};
|
|
|
2748
2842
|
__export(file_grep_exports, {
|
|
2749
2843
|
grepProjectFiles: () => grepProjectFiles
|
|
2750
2844
|
});
|
|
2751
|
-
import { execSync as
|
|
2752
|
-
import { readFileSync as
|
|
2753
|
-
import
|
|
2845
|
+
import { execSync as execSync3 } from "child_process";
|
|
2846
|
+
import { readFileSync as readFileSync4, readdirSync as readdirSync2, statSync as statSync2, existsSync as existsSync6 } from "fs";
|
|
2847
|
+
import path7 from "path";
|
|
2754
2848
|
import crypto2 from "crypto";
|
|
2755
2849
|
function hasRipgrep() {
|
|
2756
2850
|
if (_hasRg === null) {
|
|
2757
2851
|
try {
|
|
2758
|
-
|
|
2852
|
+
execSync3("rg --version", { stdio: "ignore", timeout: 2e3 });
|
|
2759
2853
|
_hasRg = true;
|
|
2760
2854
|
} catch {
|
|
2761
2855
|
_hasRg = false;
|
|
@@ -2790,7 +2884,7 @@ async function grepProjectFiles(query, projectRoot, options) {
|
|
|
2790
2884
|
session_id: "file-grep",
|
|
2791
2885
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2792
2886
|
tool_name: "file_grep",
|
|
2793
|
-
project_name:
|
|
2887
|
+
project_name: path7.basename(projectRoot),
|
|
2794
2888
|
has_error: false,
|
|
2795
2889
|
raw_text: `${prefix} ${buildSnippet(hit, projectRoot)}`,
|
|
2796
2890
|
vector: null,
|
|
@@ -2802,7 +2896,7 @@ function getChunkContext(filePath, lineNumber) {
|
|
|
2802
2896
|
try {
|
|
2803
2897
|
const ext = filePath.split(".").pop()?.toLowerCase();
|
|
2804
2898
|
if (ext !== "ts" && ext !== "tsx" && ext !== "js" && ext !== "jsx") return "";
|
|
2805
|
-
const source =
|
|
2899
|
+
const source = readFileSync4(filePath, "utf8");
|
|
2806
2900
|
const lines = source.split("\n");
|
|
2807
2901
|
for (let i = Math.min(lineNumber - 1, lines.length - 1); i >= 0; i--) {
|
|
2808
2902
|
const line = lines[i];
|
|
@@ -2821,7 +2915,7 @@ function grepWithRipgrep(pattern, projectRoot, patterns) {
|
|
|
2821
2915
|
const globs = (patterns ?? DEFAULT_PATTERNS).map((p) => `--glob '${p}'`).join(" ");
|
|
2822
2916
|
const excludes = EXCLUDE_DIRS.map((d) => `--glob '!${d}'`).join(" ");
|
|
2823
2917
|
const cmd = `rg -i -c --hidden --no-config --no-ignore '${pattern.replace(/'/g, "\\'")}' . ${globs} ${excludes} --max-filesize ${MAX_FILE_SIZE} 2>/dev/null || true`;
|
|
2824
|
-
const output =
|
|
2918
|
+
const output = execSync3(cmd, {
|
|
2825
2919
|
cwd: projectRoot,
|
|
2826
2920
|
encoding: "utf8",
|
|
2827
2921
|
timeout: 3e3,
|
|
@@ -2836,12 +2930,12 @@ function grepWithRipgrep(pattern, projectRoot, patterns) {
|
|
|
2836
2930
|
const matchCount = parseInt(line.slice(colonIdx + 1));
|
|
2837
2931
|
if (isNaN(matchCount) || matchCount === 0) continue;
|
|
2838
2932
|
try {
|
|
2839
|
-
const firstMatch =
|
|
2933
|
+
const firstMatch = execSync3(
|
|
2840
2934
|
`rg -i -n --hidden '${pattern.replace(/'/g, "\\'")}' '${filePath}' --max-count 1 2>/dev/null | head -1`,
|
|
2841
2935
|
{ cwd: projectRoot, encoding: "utf8", timeout: 1e3 }
|
|
2842
2936
|
).trim();
|
|
2843
2937
|
const lineNum = parseInt(firstMatch.split(":")[0] ?? "1");
|
|
2844
|
-
const totalLines =
|
|
2938
|
+
const totalLines = execSync3(`wc -l < '${filePath}'`, {
|
|
2845
2939
|
cwd: projectRoot,
|
|
2846
2940
|
encoding: "utf8",
|
|
2847
2941
|
timeout: 1e3
|
|
@@ -2864,11 +2958,11 @@ function grepWithNodeFs(pattern, projectRoot, patterns) {
|
|
|
2864
2958
|
const files = collectFiles(projectRoot, patterns ?? DEFAULT_PATTERNS);
|
|
2865
2959
|
const hits = [];
|
|
2866
2960
|
for (const filePath of files.slice(0, MAX_FILES)) {
|
|
2867
|
-
const absPath =
|
|
2961
|
+
const absPath = path7.join(projectRoot, filePath);
|
|
2868
2962
|
try {
|
|
2869
2963
|
const stat = statSync2(absPath);
|
|
2870
2964
|
if (stat.size > MAX_FILE_SIZE) continue;
|
|
2871
|
-
const content =
|
|
2965
|
+
const content = readFileSync4(absPath, "utf8");
|
|
2872
2966
|
const lines = content.split("\n");
|
|
2873
2967
|
const matches = content.match(regex);
|
|
2874
2968
|
if (!matches || matches.length === 0) continue;
|
|
@@ -2891,15 +2985,15 @@ function collectFiles(root, patterns) {
|
|
|
2891
2985
|
const files = [];
|
|
2892
2986
|
function walk(dir, relative) {
|
|
2893
2987
|
if (files.length >= MAX_FILES) return;
|
|
2894
|
-
const basename =
|
|
2988
|
+
const basename = path7.basename(dir);
|
|
2895
2989
|
if (EXCLUDE_DIRS.includes(basename)) return;
|
|
2896
2990
|
try {
|
|
2897
2991
|
const entries = readdirSync2(dir, { withFileTypes: true });
|
|
2898
2992
|
for (const entry of entries) {
|
|
2899
2993
|
if (files.length >= MAX_FILES) return;
|
|
2900
|
-
const rel =
|
|
2994
|
+
const rel = path7.join(relative, entry.name);
|
|
2901
2995
|
if (entry.isDirectory()) {
|
|
2902
|
-
walk(
|
|
2996
|
+
walk(path7.join(dir, entry.name), rel);
|
|
2903
2997
|
} else if (entry.isFile()) {
|
|
2904
2998
|
for (const pat of patterns) {
|
|
2905
2999
|
if (matchGlob(rel, pat)) {
|
|
@@ -2931,7 +3025,7 @@ function matchGlob(filePath, pattern) {
|
|
|
2931
3025
|
if (slashIdx !== -1) {
|
|
2932
3026
|
const dir = pattern.slice(0, slashIdx);
|
|
2933
3027
|
const ext2 = pattern.slice(slashIdx + 1).replace("*", "");
|
|
2934
|
-
const fileDir =
|
|
3028
|
+
const fileDir = path7.dirname(filePath);
|
|
2935
3029
|
return fileDir === dir && filePath.endsWith(ext2);
|
|
2936
3030
|
}
|
|
2937
3031
|
const ext = pattern.replace("*", "");
|
|
@@ -2939,9 +3033,9 @@ function matchGlob(filePath, pattern) {
|
|
|
2939
3033
|
}
|
|
2940
3034
|
function buildSnippet(hit, projectRoot) {
|
|
2941
3035
|
try {
|
|
2942
|
-
const absPath =
|
|
2943
|
-
if (!
|
|
2944
|
-
const lines =
|
|
3036
|
+
const absPath = path7.join(projectRoot, hit.filePath);
|
|
3037
|
+
if (!existsSync6(absPath)) return hit.matchLine;
|
|
3038
|
+
const lines = readFileSync4(absPath, "utf8").split("\n");
|
|
2945
3039
|
const start = Math.max(0, hit.lineNumber - 3);
|
|
2946
3040
|
const end = Math.min(lines.length, hit.lineNumber + 2);
|
|
2947
3041
|
return lines.slice(start, end).join("\n").slice(0, 500);
|
|
@@ -2975,8 +3069,8 @@ __export(reranker_exports, {
|
|
|
2975
3069
|
rerank: () => rerank,
|
|
2976
3070
|
rerankWithScores: () => rerankWithScores
|
|
2977
3071
|
});
|
|
2978
|
-
import
|
|
2979
|
-
import { existsSync as
|
|
3072
|
+
import path8 from "path";
|
|
3073
|
+
import { existsSync as existsSync7 } from "fs";
|
|
2980
3074
|
function resetIdleTimer() {
|
|
2981
3075
|
if (_idleTimer) clearTimeout(_idleTimer);
|
|
2982
3076
|
_idleTimer = setTimeout(() => {
|
|
@@ -2987,18 +3081,18 @@ function resetIdleTimer() {
|
|
|
2987
3081
|
}
|
|
2988
3082
|
}
|
|
2989
3083
|
function isRerankerAvailable() {
|
|
2990
|
-
return
|
|
3084
|
+
return existsSync7(path8.join(MODELS_DIR, RERANKER_MODEL_FILE));
|
|
2991
3085
|
}
|
|
2992
3086
|
function getRerankerModelPath() {
|
|
2993
|
-
return
|
|
3087
|
+
return path8.join(MODELS_DIR, RERANKER_MODEL_FILE);
|
|
2994
3088
|
}
|
|
2995
3089
|
async function ensureLoaded() {
|
|
2996
3090
|
if (_rerankerContext) {
|
|
2997
3091
|
resetIdleTimer();
|
|
2998
3092
|
return;
|
|
2999
3093
|
}
|
|
3000
|
-
const modelPath =
|
|
3001
|
-
if (!
|
|
3094
|
+
const modelPath = path8.join(MODELS_DIR, RERANKER_MODEL_FILE);
|
|
3095
|
+
if (!existsSync7(modelPath)) {
|
|
3002
3096
|
throw new Error(
|
|
3003
3097
|
`Reranker model not found at ${modelPath}. Run /exe-setup to download it.`
|
|
3004
3098
|
);
|
|
@@ -3195,6 +3289,8 @@ async function hybridSearch(queryText, agentId, options) {
|
|
|
3195
3289
|
return lightweightSearch(queryText, agentId, options);
|
|
3196
3290
|
}
|
|
3197
3291
|
const limit = options?.limit ?? 10;
|
|
3292
|
+
const trajectoryResults = await trajectoryBypass(queryText, agentId, options, limit);
|
|
3293
|
+
if (trajectoryResults !== null) return trajectoryResults;
|
|
3198
3294
|
let effectiveQuery = queryText;
|
|
3199
3295
|
let effectiveOptions = { ...options };
|
|
3200
3296
|
let _isBroadQuery = false;
|
|
@@ -3418,6 +3514,8 @@ async function lightweightSearch(queryText, agentId, options) {
|
|
|
3418
3514
|
async function ftsQuery(client, matchExpr, agentId, options, limit) {
|
|
3419
3515
|
const statusFilter = options?.includeArchived ? "" : `
|
|
3420
3516
|
AND COALESCE(m.status, 'active') = 'active'`;
|
|
3517
|
+
const draftFilter = options?.includeDrafts ? "" : `
|
|
3518
|
+
AND (m.draft = 0 OR m.draft IS NULL)`;
|
|
3421
3519
|
let sql = `SELECT m.id, m.agent_id, m.agent_role, m.session_id, m.timestamp,
|
|
3422
3520
|
m.tool_name, m.project_name,
|
|
3423
3521
|
m.has_error, m.raw_text, m.vector, m.task_id,
|
|
@@ -3428,7 +3526,7 @@ async function ftsQuery(client, matchExpr, agentId, options, limit) {
|
|
|
3428
3526
|
FROM memories m
|
|
3429
3527
|
JOIN memories_fts fts ON m.rowid = fts.rowid
|
|
3430
3528
|
WHERE memories_fts MATCH ?
|
|
3431
|
-
AND m.agent_id = ?${statusFilter}
|
|
3529
|
+
AND m.agent_id = ?${statusFilter}${draftFilter}
|
|
3432
3530
|
AND COALESCE(m.confidence, 0.7) >= 0.3`;
|
|
3433
3531
|
const args = [matchExpr, agentId];
|
|
3434
3532
|
const scope = buildWikiScopeFilter(options, "m.");
|
|
@@ -3450,6 +3548,10 @@ async function ftsQuery(client, matchExpr, agentId, options, limit) {
|
|
|
3450
3548
|
sql += ` AND m.timestamp >= ?`;
|
|
3451
3549
|
args.push(options.since);
|
|
3452
3550
|
}
|
|
3551
|
+
if (options?.memoryType) {
|
|
3552
|
+
sql += ` AND m.memory_type = ?`;
|
|
3553
|
+
args.push(options.memoryType);
|
|
3554
|
+
}
|
|
3453
3555
|
sql += ` ORDER BY rank LIMIT ?`;
|
|
3454
3556
|
args.push(limit);
|
|
3455
3557
|
const result = await client.execute({ sql, args });
|
|
@@ -3482,6 +3584,8 @@ async function recentRecords(agentId, options, limit) {
|
|
|
3482
3584
|
const client = getClient();
|
|
3483
3585
|
const statusFilter = options?.includeArchived ? "" : `
|
|
3484
3586
|
AND COALESCE(status, 'active') = 'active'`;
|
|
3587
|
+
const draftFilter = options?.includeDrafts ? "" : `
|
|
3588
|
+
AND (draft = 0 OR draft IS NULL)`;
|
|
3485
3589
|
let sql = `SELECT id, agent_id, agent_role, session_id, timestamp,
|
|
3486
3590
|
tool_name, project_name,
|
|
3487
3591
|
has_error, raw_text, vector, task_id,
|
|
@@ -3490,7 +3594,7 @@ async function recentRecords(agentId, options, limit) {
|
|
|
3490
3594
|
char_offset, page_number,
|
|
3491
3595
|
source_path, source_type
|
|
3492
3596
|
FROM memories
|
|
3493
|
-
WHERE agent_id = ?${statusFilter}
|
|
3597
|
+
WHERE agent_id = ?${statusFilter}${draftFilter}
|
|
3494
3598
|
AND COALESCE(confidence, 0.7) >= 0.3`;
|
|
3495
3599
|
const args = [agentId];
|
|
3496
3600
|
const scope = buildWikiScopeFilter(options, "");
|
|
@@ -3512,6 +3616,10 @@ async function recentRecords(agentId, options, limit) {
|
|
|
3512
3616
|
sql += ` AND timestamp >= ?`;
|
|
3513
3617
|
args.push(options.since);
|
|
3514
3618
|
}
|
|
3619
|
+
if (options?.memoryType) {
|
|
3620
|
+
sql += ` AND memory_type = ?`;
|
|
3621
|
+
args.push(options.memoryType);
|
|
3622
|
+
}
|
|
3515
3623
|
sql += ` ORDER BY timestamp DESC LIMIT ?`;
|
|
3516
3624
|
args.push(limit);
|
|
3517
3625
|
const result = await client.execute({ sql, args });
|
|
@@ -3540,25 +3648,107 @@ async function recentRecords(agentId, options, limit) {
|
|
|
3540
3648
|
source_type: row.source_type ?? null
|
|
3541
3649
|
}));
|
|
3542
3650
|
}
|
|
3651
|
+
var KNOWN_TOOLS = /* @__PURE__ */ new Set([
|
|
3652
|
+
"Bash",
|
|
3653
|
+
"Read",
|
|
3654
|
+
"Write",
|
|
3655
|
+
"Edit",
|
|
3656
|
+
"Grep",
|
|
3657
|
+
"Glob",
|
|
3658
|
+
"Agent",
|
|
3659
|
+
"Skill",
|
|
3660
|
+
"WebSearch",
|
|
3661
|
+
"WebFetch"
|
|
3662
|
+
]);
|
|
3663
|
+
function detectToolQuery(query) {
|
|
3664
|
+
const lower = query.toLowerCase().trim();
|
|
3665
|
+
for (const tool of KNOWN_TOOLS) {
|
|
3666
|
+
if (lower === tool.toLowerCase()) return tool;
|
|
3667
|
+
if (lower.startsWith(tool.toLowerCase() + " ")) return tool;
|
|
3668
|
+
if (lower.includes(`tool:${tool.toLowerCase()}`)) return tool;
|
|
3669
|
+
}
|
|
3670
|
+
return null;
|
|
3671
|
+
}
|
|
3672
|
+
async function trajectoryBypass(queryText, agentId, options, limit) {
|
|
3673
|
+
const toolName = detectToolQuery(queryText);
|
|
3674
|
+
if (!toolName) return null;
|
|
3675
|
+
try {
|
|
3676
|
+
const client = getClient();
|
|
3677
|
+
const statusFilter = options?.includeArchived ? "" : `
|
|
3678
|
+
AND COALESCE(status, 'active') = 'active'`;
|
|
3679
|
+
const draftFilter = options?.includeDrafts ? "" : `
|
|
3680
|
+
AND (draft = 0 OR draft IS NULL)`;
|
|
3681
|
+
let sql = `SELECT id, agent_id, agent_role, session_id, timestamp,
|
|
3682
|
+
tool_name, project_name,
|
|
3683
|
+
has_error, raw_text, vector, task_id,
|
|
3684
|
+
importance, status, confidence, last_accessed,
|
|
3685
|
+
workspace_id, document_id, user_id,
|
|
3686
|
+
char_offset, page_number,
|
|
3687
|
+
source_path, source_type
|
|
3688
|
+
FROM memories
|
|
3689
|
+
WHERE trajectory IS NOT NULL
|
|
3690
|
+
AND json_extract(trajectory, '$.tool') = ?
|
|
3691
|
+
AND agent_id = ?${statusFilter}${draftFilter}`;
|
|
3692
|
+
const args = [toolName, agentId];
|
|
3693
|
+
if (options?.projectName) {
|
|
3694
|
+
sql += ` AND project_name = ?`;
|
|
3695
|
+
args.push(options.projectName);
|
|
3696
|
+
}
|
|
3697
|
+
if (options?.since) {
|
|
3698
|
+
sql += ` AND timestamp >= ?`;
|
|
3699
|
+
args.push(options.since);
|
|
3700
|
+
}
|
|
3701
|
+
sql += ` ORDER BY timestamp DESC LIMIT ?`;
|
|
3702
|
+
args.push(limit);
|
|
3703
|
+
const result = await client.execute({ sql, args });
|
|
3704
|
+
if (result.rows.length < 3) return null;
|
|
3705
|
+
return result.rows.map((row) => ({
|
|
3706
|
+
id: row.id,
|
|
3707
|
+
agent_id: row.agent_id,
|
|
3708
|
+
agent_role: row.agent_role,
|
|
3709
|
+
session_id: row.session_id,
|
|
3710
|
+
timestamp: row.timestamp,
|
|
3711
|
+
tool_name: row.tool_name,
|
|
3712
|
+
project_name: row.project_name,
|
|
3713
|
+
has_error: row.has_error === 1,
|
|
3714
|
+
raw_text: row.raw_text,
|
|
3715
|
+
vector: row.vector == null ? [] : Array.isArray(row.vector) ? row.vector : Array.from(row.vector),
|
|
3716
|
+
task_id: row.task_id ?? null,
|
|
3717
|
+
importance: row.importance ?? 5,
|
|
3718
|
+
status: row.status ?? "active",
|
|
3719
|
+
confidence: row.confidence ?? 0.7,
|
|
3720
|
+
last_accessed: row.last_accessed ?? row.timestamp,
|
|
3721
|
+
workspace_id: row.workspace_id ?? null,
|
|
3722
|
+
document_id: row.document_id ?? null,
|
|
3723
|
+
user_id: row.user_id ?? null,
|
|
3724
|
+
char_offset: row.char_offset ?? null,
|
|
3725
|
+
page_number: row.page_number ?? null,
|
|
3726
|
+
source_path: row.source_path ?? null,
|
|
3727
|
+
source_type: row.source_type ?? null
|
|
3728
|
+
}));
|
|
3729
|
+
} catch {
|
|
3730
|
+
return null;
|
|
3731
|
+
}
|
|
3732
|
+
}
|
|
3543
3733
|
|
|
3544
3734
|
// src/adapters/claude/hooks/error-recall.ts
|
|
3545
3735
|
init_database();
|
|
3546
3736
|
|
|
3547
3737
|
// src/adapters/claude/active-agent.ts
|
|
3548
3738
|
init_config();
|
|
3549
|
-
import { readFileSync as
|
|
3550
|
-
import { execSync as
|
|
3551
|
-
import
|
|
3739
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, unlinkSync as unlinkSync3, readdirSync as readdirSync3 } from "fs";
|
|
3740
|
+
import { execSync as execSync5 } from "child_process";
|
|
3741
|
+
import path9 from "path";
|
|
3552
3742
|
|
|
3553
3743
|
// src/lib/session-key.ts
|
|
3554
|
-
import { execSync as
|
|
3744
|
+
import { execSync as execSync4 } from "child_process";
|
|
3555
3745
|
var _cached2 = null;
|
|
3556
3746
|
function getSessionKey() {
|
|
3557
3747
|
if (_cached2) return _cached2;
|
|
3558
3748
|
let pid = process.ppid;
|
|
3559
3749
|
for (let i = 0; i < 10; i++) {
|
|
3560
3750
|
try {
|
|
3561
|
-
const info =
|
|
3751
|
+
const info = execSync4(`ps -p ${pid} -o ppid=,comm=`, {
|
|
3562
3752
|
encoding: "utf8",
|
|
3563
3753
|
timeout: 2e3
|
|
3564
3754
|
}).trim();
|
|
@@ -3580,22 +3770,65 @@ function getSessionKey() {
|
|
|
3580
3770
|
}
|
|
3581
3771
|
|
|
3582
3772
|
// src/adapters/claude/active-agent.ts
|
|
3583
|
-
|
|
3773
|
+
init_employees();
|
|
3774
|
+
var CACHE_DIR = path9.join(EXE_AI_DIR, "session-cache");
|
|
3584
3775
|
var STALE_MS = 24 * 60 * 60 * 1e3;
|
|
3776
|
+
function isNameWithOptionalInstance(candidate, baseName) {
|
|
3777
|
+
if (candidate === baseName) return true;
|
|
3778
|
+
if (!candidate.startsWith(baseName)) return false;
|
|
3779
|
+
return /^\d+$/.test(candidate.slice(baseName.length));
|
|
3780
|
+
}
|
|
3781
|
+
function resolveEmployeeFromSessionPrefix(prefix, employees) {
|
|
3782
|
+
const sorted = [...employees].sort((a, b) => b.name.length - a.name.length);
|
|
3783
|
+
for (const employee of sorted) {
|
|
3784
|
+
if (isNameWithOptionalInstance(prefix, employee.name)) {
|
|
3785
|
+
return { agentId: employee.name, agentRole: employee.role };
|
|
3786
|
+
}
|
|
3787
|
+
}
|
|
3788
|
+
return null;
|
|
3789
|
+
}
|
|
3790
|
+
function resolveActiveAgentFromTmuxSession(sessionName) {
|
|
3791
|
+
const employees = loadEmployeesSync();
|
|
3792
|
+
const coordinator = getCoordinatorEmployee(employees);
|
|
3793
|
+
const coordinatorName = coordinator?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME;
|
|
3794
|
+
if (isNameWithOptionalInstance(sessionName, coordinatorName)) {
|
|
3795
|
+
return {
|
|
3796
|
+
agentId: coordinatorName,
|
|
3797
|
+
agentRole: coordinator?.role ?? "COO"
|
|
3798
|
+
};
|
|
3799
|
+
}
|
|
3800
|
+
if (isNameWithOptionalInstance(sessionName, DEFAULT_COORDINATOR_TEMPLATE_NAME)) {
|
|
3801
|
+
return {
|
|
3802
|
+
agentId: coordinator?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME,
|
|
3803
|
+
agentRole: coordinator?.role ?? "COO"
|
|
3804
|
+
};
|
|
3805
|
+
}
|
|
3806
|
+
if (sessionName.includes("-")) {
|
|
3807
|
+
const prefix = sessionName.split("-")[0] ?? "";
|
|
3808
|
+
const employee = resolveEmployeeFromSessionPrefix(prefix, employees);
|
|
3809
|
+
if (employee) return employee;
|
|
3810
|
+
const legacy = prefix.match(/^([a-zA-Z]+)\d*$/);
|
|
3811
|
+
if (legacy?.[1] && legacy[1] !== DEFAULT_COORDINATOR_TEMPLATE_NAME) {
|
|
3812
|
+
const emp = getEmployee(employees, legacy[1]);
|
|
3813
|
+
return { agentId: emp?.name ?? legacy[1], agentRole: emp?.role ?? "employee" };
|
|
3814
|
+
}
|
|
3815
|
+
}
|
|
3816
|
+
return null;
|
|
3817
|
+
}
|
|
3585
3818
|
function getMarkerPath() {
|
|
3586
|
-
return
|
|
3819
|
+
return path9.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
|
|
3587
3820
|
}
|
|
3588
3821
|
function getActiveAgent() {
|
|
3589
3822
|
try {
|
|
3590
3823
|
const markerPath = getMarkerPath();
|
|
3591
|
-
const raw =
|
|
3824
|
+
const raw = readFileSync5(markerPath, "utf8");
|
|
3592
3825
|
const data = JSON.parse(raw);
|
|
3593
3826
|
if (data.agentId) {
|
|
3594
3827
|
if (data.startedAt) {
|
|
3595
3828
|
const age = Date.now() - new Date(data.startedAt).getTime();
|
|
3596
3829
|
if (age > STALE_MS) {
|
|
3597
3830
|
try {
|
|
3598
|
-
|
|
3831
|
+
unlinkSync3(markerPath);
|
|
3599
3832
|
} catch {
|
|
3600
3833
|
}
|
|
3601
3834
|
} else {
|
|
@@ -3614,17 +3847,12 @@ function getActiveAgent() {
|
|
|
3614
3847
|
} catch {
|
|
3615
3848
|
}
|
|
3616
3849
|
try {
|
|
3617
|
-
const sessionName =
|
|
3850
|
+
const sessionName = execSync5(
|
|
3618
3851
|
"tmux display-message -p '#{session_name}' 2>/dev/null",
|
|
3619
3852
|
{ encoding: "utf8", timeout: 2e3 }
|
|
3620
3853
|
).trim();
|
|
3621
|
-
const
|
|
3622
|
-
if (
|
|
3623
|
-
return { agentId: empMatch[1], agentRole: "employee" };
|
|
3624
|
-
}
|
|
3625
|
-
if (/^exe\d+$/.test(sessionName)) {
|
|
3626
|
-
return { agentId: "exe", agentRole: "COO" };
|
|
3627
|
-
}
|
|
3854
|
+
const resolved = resolveActiveAgentFromTmuxSession(sessionName);
|
|
3855
|
+
if (resolved) return resolved;
|
|
3628
3856
|
} catch {
|
|
3629
3857
|
}
|
|
3630
3858
|
return {
|