@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
|
@@ -24,7 +24,6 @@ var config_exports = {};
|
|
|
24
24
|
__export(config_exports, {
|
|
25
25
|
CONFIG_MIGRATIONS: () => CONFIG_MIGRATIONS,
|
|
26
26
|
CONFIG_PATH: () => CONFIG_PATH,
|
|
27
|
-
COO_AGENT_NAME: () => COO_AGENT_NAME,
|
|
28
27
|
CURRENT_CONFIG_VERSION: () => CURRENT_CONFIG_VERSION,
|
|
29
28
|
DB_PATH: () => DB_PATH,
|
|
30
29
|
EXE_AI_DIR: () => EXE_AI_DIR,
|
|
@@ -180,7 +179,7 @@ async function loadConfigFrom(configPath) {
|
|
|
180
179
|
return { ...DEFAULT_CONFIG };
|
|
181
180
|
}
|
|
182
181
|
}
|
|
183
|
-
var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH,
|
|
182
|
+
var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
|
|
184
183
|
var init_config = __esm({
|
|
185
184
|
"src/lib/config.ts"() {
|
|
186
185
|
"use strict";
|
|
@@ -188,7 +187,6 @@ var init_config = __esm({
|
|
|
188
187
|
DB_PATH = path.join(EXE_AI_DIR, "memories.db");
|
|
189
188
|
MODELS_DIR = path.join(EXE_AI_DIR, "models");
|
|
190
189
|
CONFIG_PATH = path.join(EXE_AI_DIR, "config.json");
|
|
191
|
-
COO_AGENT_NAME = "exe";
|
|
192
190
|
LEGACY_LANCE_PATH = path.join(EXE_AI_DIR, "local.lance");
|
|
193
191
|
CURRENT_CONFIG_VERSION = 1;
|
|
194
192
|
DEFAULT_CONFIG = {
|
|
@@ -224,13 +222,7 @@ var init_config = __esm({
|
|
|
224
222
|
wikiUrl: "",
|
|
225
223
|
wikiApiKey: "",
|
|
226
224
|
wikiSyncIntervalMs: 30 * 60 * 1e3,
|
|
227
|
-
wikiWorkspaceMapping: {
|
|
228
|
-
exe: "Executive",
|
|
229
|
-
yoshi: "Engineering",
|
|
230
|
-
mari: "Marketing",
|
|
231
|
-
tom: "Engineering",
|
|
232
|
-
sasha: "Production"
|
|
233
|
-
},
|
|
225
|
+
wikiWorkspaceMapping: {},
|
|
234
226
|
wikiAutoUpdate: true,
|
|
235
227
|
wikiAutoUpdateThreshold: 0.5,
|
|
236
228
|
wikiAutoUpdateCreateNew: true,
|
|
@@ -315,7 +307,7 @@ function wrapWithRetry(client) {
|
|
|
315
307
|
return (sql) => retryOnBusy(() => target.execute(sql), "execute");
|
|
316
308
|
}
|
|
317
309
|
if (prop === "batch") {
|
|
318
|
-
return (stmts) => retryOnBusy(() => target.batch(stmts), "batch");
|
|
310
|
+
return (stmts, mode) => retryOnBusy(() => target.batch(stmts, mode), "batch");
|
|
319
311
|
}
|
|
320
312
|
return Reflect.get(target, prop, receiver);
|
|
321
313
|
}
|
|
@@ -331,6 +323,53 @@ var init_db_retry = __esm({
|
|
|
331
323
|
}
|
|
332
324
|
});
|
|
333
325
|
|
|
326
|
+
// src/lib/employees.ts
|
|
327
|
+
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
328
|
+
import { existsSync as existsSync2, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
|
|
329
|
+
import { execSync } from "child_process";
|
|
330
|
+
import path2 from "path";
|
|
331
|
+
import os2 from "os";
|
|
332
|
+
function normalizeRole(role) {
|
|
333
|
+
return (role ?? "").trim().toLowerCase();
|
|
334
|
+
}
|
|
335
|
+
function isCoordinatorRole(role) {
|
|
336
|
+
return normalizeRole(role) === normalizeRole(COORDINATOR_ROLE);
|
|
337
|
+
}
|
|
338
|
+
function getCoordinatorEmployee(employees) {
|
|
339
|
+
return employees.find((e) => isCoordinatorRole(e.role));
|
|
340
|
+
}
|
|
341
|
+
function getCoordinatorName(employees = loadEmployeesSync()) {
|
|
342
|
+
return getCoordinatorEmployee(employees)?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME;
|
|
343
|
+
}
|
|
344
|
+
function isCoordinatorName(agentName, employees = loadEmployeesSync()) {
|
|
345
|
+
if (!agentName) return false;
|
|
346
|
+
return agentName.toLowerCase() === getCoordinatorName(employees).toLowerCase();
|
|
347
|
+
}
|
|
348
|
+
function canCoordinate(agentName, agentRole, employees = loadEmployeesSync()) {
|
|
349
|
+
return agentName === "default" || isCoordinatorRole(agentRole) || isCoordinatorName(agentName, employees);
|
|
350
|
+
}
|
|
351
|
+
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
352
|
+
if (!existsSync2(employeesPath)) return [];
|
|
353
|
+
try {
|
|
354
|
+
return JSON.parse(readFileSync2(employeesPath, "utf-8"));
|
|
355
|
+
} catch {
|
|
356
|
+
return [];
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
function getEmployee(employees, name) {
|
|
360
|
+
return employees.find((e) => e.name.toLowerCase() === name.toLowerCase());
|
|
361
|
+
}
|
|
362
|
+
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE;
|
|
363
|
+
var init_employees = __esm({
|
|
364
|
+
"src/lib/employees.ts"() {
|
|
365
|
+
"use strict";
|
|
366
|
+
init_config();
|
|
367
|
+
EMPLOYEES_PATH = path2.join(EXE_AI_DIR, "exe-employees.json");
|
|
368
|
+
DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
|
|
369
|
+
COORDINATOR_ROLE = "COO";
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
|
|
334
373
|
// src/lib/database.ts
|
|
335
374
|
import { createClient } from "@libsql/client";
|
|
336
375
|
async function initDatabase(config) {
|
|
@@ -464,22 +503,24 @@ async function ensureSchema() {
|
|
|
464
503
|
ON behaviors(agent_id, active);
|
|
465
504
|
`);
|
|
466
505
|
try {
|
|
506
|
+
const coordinatorName = getCoordinatorName();
|
|
467
507
|
const existing = await client.execute({
|
|
468
|
-
sql: "SELECT COUNT(*) as cnt FROM behaviors WHERE agent_id =
|
|
469
|
-
args: []
|
|
508
|
+
sql: "SELECT COUNT(*) as cnt FROM behaviors WHERE agent_id = ?",
|
|
509
|
+
args: [coordinatorName]
|
|
470
510
|
});
|
|
471
511
|
if (Number(existing.rows[0]?.cnt) === 0) {
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
512
|
+
const seededAt = "2026-03-25T00:00:00Z";
|
|
513
|
+
for (const [domain, content] of [
|
|
514
|
+
["workflow", `Don't ask "keep going?" \u2014 just keep executing phases/plans autonomously`],
|
|
515
|
+
["tool-use", "Always use create_task MCP tool, never write .md files directly for task creation"],
|
|
516
|
+
["workflow", "Auto-start reviewing when idle and reviews are pending \u2014 never ask founder for permission"]
|
|
517
|
+
]) {
|
|
518
|
+
await client.execute({
|
|
519
|
+
sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, content, active, created_at, updated_at)
|
|
520
|
+
VALUES (hex(randomblob(16)), ?, NULL, ?, ?, 1, ?, ?)`,
|
|
521
|
+
args: [coordinatorName, domain, content, seededAt, seededAt]
|
|
522
|
+
});
|
|
523
|
+
}
|
|
483
524
|
}
|
|
484
525
|
} catch {
|
|
485
526
|
}
|
|
@@ -1171,6 +1212,39 @@ async function ensureSchema() {
|
|
|
1171
1212
|
} catch {
|
|
1172
1213
|
}
|
|
1173
1214
|
}
|
|
1215
|
+
try {
|
|
1216
|
+
await client.execute({
|
|
1217
|
+
sql: `ALTER TABLE memories ADD COLUMN draft INTEGER DEFAULT 0`,
|
|
1218
|
+
args: []
|
|
1219
|
+
});
|
|
1220
|
+
} catch {
|
|
1221
|
+
}
|
|
1222
|
+
try {
|
|
1223
|
+
await client.execute(
|
|
1224
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_draft ON memories(draft) WHERE draft = 1`
|
|
1225
|
+
);
|
|
1226
|
+
} catch {
|
|
1227
|
+
}
|
|
1228
|
+
try {
|
|
1229
|
+
await client.execute({
|
|
1230
|
+
sql: `ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'`,
|
|
1231
|
+
args: []
|
|
1232
|
+
});
|
|
1233
|
+
} catch {
|
|
1234
|
+
}
|
|
1235
|
+
try {
|
|
1236
|
+
await client.execute(
|
|
1237
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_type ON memories(memory_type)`
|
|
1238
|
+
);
|
|
1239
|
+
} catch {
|
|
1240
|
+
}
|
|
1241
|
+
try {
|
|
1242
|
+
await client.execute({
|
|
1243
|
+
sql: `ALTER TABLE memories ADD COLUMN trajectory TEXT`,
|
|
1244
|
+
args: []
|
|
1245
|
+
});
|
|
1246
|
+
} catch {
|
|
1247
|
+
}
|
|
1174
1248
|
}
|
|
1175
1249
|
async function disposeDatabase() {
|
|
1176
1250
|
if (_client) {
|
|
@@ -1184,6 +1258,7 @@ var init_database = __esm({
|
|
|
1184
1258
|
"src/lib/database.ts"() {
|
|
1185
1259
|
"use strict";
|
|
1186
1260
|
init_db_retry();
|
|
1261
|
+
init_employees();
|
|
1187
1262
|
_client = null;
|
|
1188
1263
|
_resilientClient = null;
|
|
1189
1264
|
initTurso = initDatabase;
|
|
@@ -1192,15 +1267,15 @@ var init_database = __esm({
|
|
|
1192
1267
|
});
|
|
1193
1268
|
|
|
1194
1269
|
// src/lib/keychain.ts
|
|
1195
|
-
import { readFile as
|
|
1196
|
-
import { existsSync as
|
|
1197
|
-
import
|
|
1198
|
-
import
|
|
1270
|
+
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
1271
|
+
import { existsSync as existsSync3 } from "fs";
|
|
1272
|
+
import path3 from "path";
|
|
1273
|
+
import os3 from "os";
|
|
1199
1274
|
function getKeyDir() {
|
|
1200
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
1275
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path3.join(os3.homedir(), ".exe-os");
|
|
1201
1276
|
}
|
|
1202
1277
|
function getKeyPath() {
|
|
1203
|
-
return
|
|
1278
|
+
return path3.join(getKeyDir(), "master.key");
|
|
1204
1279
|
}
|
|
1205
1280
|
async function tryKeytar() {
|
|
1206
1281
|
try {
|
|
@@ -1221,11 +1296,11 @@ async function getMasterKey() {
|
|
|
1221
1296
|
}
|
|
1222
1297
|
}
|
|
1223
1298
|
const keyPath = getKeyPath();
|
|
1224
|
-
if (!
|
|
1299
|
+
if (!existsSync3(keyPath)) {
|
|
1225
1300
|
return null;
|
|
1226
1301
|
}
|
|
1227
1302
|
try {
|
|
1228
|
-
const content = await
|
|
1303
|
+
const content = await readFile3(keyPath, "utf-8");
|
|
1229
1304
|
return Buffer.from(content.trim(), "base64");
|
|
1230
1305
|
} catch {
|
|
1231
1306
|
return null;
|
|
@@ -1308,12 +1383,12 @@ __export(shard_manager_exports, {
|
|
|
1308
1383
|
listShards: () => listShards,
|
|
1309
1384
|
shardExists: () => shardExists
|
|
1310
1385
|
});
|
|
1311
|
-
import
|
|
1312
|
-
import { existsSync as
|
|
1386
|
+
import path4 from "path";
|
|
1387
|
+
import { existsSync as existsSync4, mkdirSync, readdirSync } from "fs";
|
|
1313
1388
|
import { createClient as createClient2 } from "@libsql/client";
|
|
1314
1389
|
function initShardManager(encryptionKey) {
|
|
1315
1390
|
_encryptionKey = encryptionKey;
|
|
1316
|
-
if (!
|
|
1391
|
+
if (!existsSync4(SHARDS_DIR)) {
|
|
1317
1392
|
mkdirSync(SHARDS_DIR, { recursive: true });
|
|
1318
1393
|
}
|
|
1319
1394
|
_shardingEnabled = true;
|
|
@@ -1334,7 +1409,7 @@ function getShardClient(projectName) {
|
|
|
1334
1409
|
}
|
|
1335
1410
|
const cached = _shards.get(safeName);
|
|
1336
1411
|
if (cached) return cached;
|
|
1337
|
-
const dbPath =
|
|
1412
|
+
const dbPath = path4.join(SHARDS_DIR, `${safeName}.db`);
|
|
1338
1413
|
const client = createClient2({
|
|
1339
1414
|
url: `file:${dbPath}`,
|
|
1340
1415
|
encryptionKey: _encryptionKey
|
|
@@ -1344,10 +1419,10 @@ function getShardClient(projectName) {
|
|
|
1344
1419
|
}
|
|
1345
1420
|
function shardExists(projectName) {
|
|
1346
1421
|
const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
1347
|
-
return
|
|
1422
|
+
return existsSync4(path4.join(SHARDS_DIR, `${safeName}.db`));
|
|
1348
1423
|
}
|
|
1349
1424
|
function listShards() {
|
|
1350
|
-
if (!
|
|
1425
|
+
if (!existsSync4(SHARDS_DIR)) return [];
|
|
1351
1426
|
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
1352
1427
|
}
|
|
1353
1428
|
async function ensureShardSchema(client) {
|
|
@@ -1417,7 +1492,11 @@ async function ensureShardSchema(client) {
|
|
|
1417
1492
|
"ALTER TABLE memories ADD COLUMN source_path TEXT",
|
|
1418
1493
|
"ALTER TABLE memories ADD COLUMN source_type TEXT DEFAULT 'text'",
|
|
1419
1494
|
"ALTER TABLE memories ADD COLUMN tier INTEGER DEFAULT 3",
|
|
1420
|
-
"ALTER TABLE memories ADD COLUMN supersedes_id TEXT"
|
|
1495
|
+
"ALTER TABLE memories ADD COLUMN supersedes_id TEXT",
|
|
1496
|
+
// MS-11: draft staging, MS-6a: memory_type, MS-7: trajectory
|
|
1497
|
+
"ALTER TABLE memories ADD COLUMN draft INTEGER DEFAULT 0",
|
|
1498
|
+
"ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'",
|
|
1499
|
+
"ALTER TABLE memories ADD COLUMN trajectory TEXT"
|
|
1421
1500
|
]) {
|
|
1422
1501
|
try {
|
|
1423
1502
|
await client.execute(col);
|
|
@@ -1529,7 +1608,7 @@ var init_shard_manager = __esm({
|
|
|
1529
1608
|
"src/lib/shard-manager.ts"() {
|
|
1530
1609
|
"use strict";
|
|
1531
1610
|
init_config();
|
|
1532
|
-
SHARDS_DIR =
|
|
1611
|
+
SHARDS_DIR = path4.join(EXE_AI_DIR, "shards");
|
|
1533
1612
|
_shards = /* @__PURE__ */ new Map();
|
|
1534
1613
|
_encryptionKey = null;
|
|
1535
1614
|
_shardingEnabled = false;
|
|
@@ -1547,26 +1626,26 @@ var init_platform_procedures = __esm({
|
|
|
1547
1626
|
title: "What is exe-os \u2014 the operating model every agent must understand",
|
|
1548
1627
|
domain: "architecture",
|
|
1549
1628
|
priority: "p0",
|
|
1550
|
-
content: "Exe OS is an AI employee operating system. A founder runs 5-10 AI agents as a real org: COO
|
|
1629
|
+
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."
|
|
1551
1630
|
},
|
|
1552
1631
|
{
|
|
1553
1632
|
title: "Mode 1 \u2014 how exe-os runs inside Claude Code",
|
|
1554
1633
|
domain: "architecture",
|
|
1555
1634
|
priority: "p0",
|
|
1556
|
-
content: "Mode 1: exe-os runs AS hooks + MCP + skills inside Claude Code. The founder opens CC
|
|
1635
|
+
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."
|
|
1557
1636
|
},
|
|
1558
1637
|
{
|
|
1559
|
-
title: "Sessions explained \u2014
|
|
1638
|
+
title: "Sessions explained \u2014 coordinator session names and projects",
|
|
1560
1639
|
domain: "architecture",
|
|
1561
1640
|
priority: "p0",
|
|
1562
|
-
content: "Each
|
|
1641
|
+
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."
|
|
1563
1642
|
},
|
|
1564
1643
|
// --- Hierarchy and dispatch ---
|
|
1565
1644
|
{
|
|
1566
1645
|
title: "Chain of command \u2014 who talks to whom",
|
|
1567
1646
|
domain: "workflow",
|
|
1568
1647
|
priority: "p0",
|
|
1569
|
-
content: "Founder
|
|
1648
|
+
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."
|
|
1570
1649
|
},
|
|
1571
1650
|
{
|
|
1572
1651
|
title: "Single dispatch path \u2014 create_task only",
|
|
@@ -1576,30 +1655,30 @@ var init_platform_procedures = __esm({
|
|
|
1576
1655
|
},
|
|
1577
1656
|
// --- Session isolation ---
|
|
1578
1657
|
{
|
|
1579
|
-
title: "Session scoping \u2014 stay in your
|
|
1658
|
+
title: "Session scoping \u2014 stay in your coordinator boundary",
|
|
1580
1659
|
domain: "security",
|
|
1581
1660
|
priority: "p0",
|
|
1582
|
-
content: "Session scoping is mandatory. Managers dispatch to workers within their own
|
|
1661
|
+
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."
|
|
1583
1662
|
},
|
|
1584
1663
|
{
|
|
1585
1664
|
title: "Session isolation \u2014 never touch another session's work",
|
|
1586
1665
|
domain: "workflow",
|
|
1587
1666
|
priority: "p0",
|
|
1588
|
-
content:
|
|
1667
|
+
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."
|
|
1589
1668
|
},
|
|
1590
1669
|
// --- Engineering: session scoping in code ---
|
|
1591
1670
|
{
|
|
1592
1671
|
title: "Three-dimensional scoping \u2014 session, project, role \u2014 enforced in every query",
|
|
1593
1672
|
domain: "architecture",
|
|
1594
1673
|
priority: "p0",
|
|
1595
|
-
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
|
|
1674
|
+
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."
|
|
1596
1675
|
},
|
|
1597
1676
|
// --- Hard constraints ---
|
|
1598
1677
|
{
|
|
1599
1678
|
title: "What you CANNOT do in exe-os \u2014 hard constraints",
|
|
1600
1679
|
domain: "security",
|
|
1601
1680
|
priority: "p0",
|
|
1602
|
-
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
|
|
1681
|
+
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."
|
|
1603
1682
|
},
|
|
1604
1683
|
// --- Operations ---
|
|
1605
1684
|
{
|
|
@@ -1839,7 +1918,10 @@ async function writeMemory(record) {
|
|
|
1839
1918
|
source_path: record.source_path ?? null,
|
|
1840
1919
|
source_type: record.source_type ?? null,
|
|
1841
1920
|
tier: record.tier ?? classifyTier(record),
|
|
1842
|
-
supersedes_id: record.supersedes_id ?? null
|
|
1921
|
+
supersedes_id: record.supersedes_id ?? null,
|
|
1922
|
+
draft: record.draft ? 1 : 0,
|
|
1923
|
+
memory_type: record.memory_type ?? "raw",
|
|
1924
|
+
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null
|
|
1843
1925
|
};
|
|
1844
1926
|
_pendingRecords.push(dbRow);
|
|
1845
1927
|
orgBus.emit({
|
|
@@ -1894,6 +1976,9 @@ async function flushBatch() {
|
|
|
1894
1976
|
const sourceType = row.source_type ?? null;
|
|
1895
1977
|
const tier = row.tier ?? 3;
|
|
1896
1978
|
const supersedesId = row.supersedes_id ?? null;
|
|
1979
|
+
const draft = row.draft ? 1 : 0;
|
|
1980
|
+
const memoryType = row.memory_type ?? "raw";
|
|
1981
|
+
const trajectory = row.trajectory ?? null;
|
|
1897
1982
|
return {
|
|
1898
1983
|
sql: hasVector ? `INSERT OR IGNORE INTO memories
|
|
1899
1984
|
(id, agent_id, agent_role, session_id, timestamp,
|
|
@@ -1901,15 +1986,15 @@ async function flushBatch() {
|
|
|
1901
1986
|
has_error, raw_text, vector, version, task_id, importance, status,
|
|
1902
1987
|
confidence, last_accessed,
|
|
1903
1988
|
workspace_id, document_id, user_id, char_offset, page_number,
|
|
1904
|
-
source_path, source_type, tier, supersedes_id)
|
|
1905
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories
|
|
1989
|
+
source_path, source_type, tier, supersedes_id, draft, memory_type, trajectory)
|
|
1990
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories
|
|
1906
1991
|
(id, agent_id, agent_role, session_id, timestamp,
|
|
1907
1992
|
tool_name, project_name,
|
|
1908
1993
|
has_error, raw_text, vector, version, task_id, importance, status,
|
|
1909
1994
|
confidence, last_accessed,
|
|
1910
1995
|
workspace_id, document_id, user_id, char_offset, page_number,
|
|
1911
|
-
source_path, source_type, tier, supersedes_id)
|
|
1912
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
1996
|
+
source_path, source_type, tier, supersedes_id, draft, memory_type, trajectory)
|
|
1997
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
1913
1998
|
args: hasVector ? [
|
|
1914
1999
|
row.id,
|
|
1915
2000
|
row.agent_id,
|
|
@@ -1935,7 +2020,10 @@ async function flushBatch() {
|
|
|
1935
2020
|
sourcePath,
|
|
1936
2021
|
sourceType,
|
|
1937
2022
|
tier,
|
|
1938
|
-
supersedesId
|
|
2023
|
+
supersedesId,
|
|
2024
|
+
draft,
|
|
2025
|
+
memoryType,
|
|
2026
|
+
trajectory
|
|
1939
2027
|
] : [
|
|
1940
2028
|
row.id,
|
|
1941
2029
|
row.agent_id,
|
|
@@ -1960,7 +2048,10 @@ async function flushBatch() {
|
|
|
1960
2048
|
sourcePath,
|
|
1961
2049
|
sourceType,
|
|
1962
2050
|
tier,
|
|
1963
|
-
supersedesId
|
|
2051
|
+
supersedesId,
|
|
2052
|
+
draft,
|
|
2053
|
+
memoryType,
|
|
2054
|
+
trajectory
|
|
1964
2055
|
]
|
|
1965
2056
|
};
|
|
1966
2057
|
};
|
|
@@ -2029,6 +2120,8 @@ async function searchMemories(queryVector, agentId, options) {
|
|
|
2029
2120
|
const limit = options?.limit ?? 10;
|
|
2030
2121
|
const statusFilter = options?.includeArchived ? "" : `
|
|
2031
2122
|
AND COALESCE(status, 'active') = 'active'`;
|
|
2123
|
+
const draftFilter = options?.includeDrafts ? "" : `
|
|
2124
|
+
AND (draft = 0 OR draft IS NULL)`;
|
|
2032
2125
|
let sql = `SELECT id, agent_id, agent_role, session_id, timestamp,
|
|
2033
2126
|
tool_name, project_name,
|
|
2034
2127
|
has_error, raw_text, vector, importance, status,
|
|
@@ -2038,7 +2131,7 @@ async function searchMemories(queryVector, agentId, options) {
|
|
|
2038
2131
|
source_path, source_type
|
|
2039
2132
|
FROM memories
|
|
2040
2133
|
WHERE agent_id = ?
|
|
2041
|
-
AND vector IS NOT NULL${statusFilter}
|
|
2134
|
+
AND vector IS NOT NULL${statusFilter}${draftFilter}
|
|
2042
2135
|
AND COALESCE(confidence, 0.7) >= 0.3`;
|
|
2043
2136
|
const args = [agentId];
|
|
2044
2137
|
const scope = buildWikiScopeFilter(options, "");
|
|
@@ -2060,6 +2153,10 @@ async function searchMemories(queryVector, agentId, options) {
|
|
|
2060
2153
|
sql += ` AND timestamp >= ?`;
|
|
2061
2154
|
args.push(options.since);
|
|
2062
2155
|
}
|
|
2156
|
+
if (options?.memoryType) {
|
|
2157
|
+
sql += ` AND memory_type = ?`;
|
|
2158
|
+
args.push(options.memoryType);
|
|
2159
|
+
}
|
|
2063
2160
|
sql += ` ORDER BY vector_distance_cos(vector, vector32(?))`;
|
|
2064
2161
|
args.push(vectorToBlob(queryVector));
|
|
2065
2162
|
sql += ` LIMIT ?`;
|
|
@@ -2285,7 +2382,7 @@ var init_self_query_router = __esm({
|
|
|
2285
2382
|
},
|
|
2286
2383
|
is_broad_query: {
|
|
2287
2384
|
type: "boolean",
|
|
2288
|
-
description: "True if the query is exploratory/broad (e.g., 'what has
|
|
2385
|
+
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')."
|
|
2289
2386
|
}
|
|
2290
2387
|
},
|
|
2291
2388
|
required: ["semantic_query", "project_filter", "role_filter", "time_filter", "is_broad_query"]
|
|
@@ -2298,8 +2395,8 @@ var init_self_query_router = __esm({
|
|
|
2298
2395
|
import net from "net";
|
|
2299
2396
|
import { spawn } from "child_process";
|
|
2300
2397
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
2301
|
-
import { existsSync as
|
|
2302
|
-
import
|
|
2398
|
+
import { existsSync as existsSync5, unlinkSync as unlinkSync2, readFileSync as readFileSync3, openSync, closeSync, statSync } from "fs";
|
|
2399
|
+
import path5 from "path";
|
|
2303
2400
|
import { fileURLToPath } from "url";
|
|
2304
2401
|
function handleData(chunk) {
|
|
2305
2402
|
_buffer += chunk.toString();
|
|
@@ -2325,9 +2422,9 @@ function handleData(chunk) {
|
|
|
2325
2422
|
}
|
|
2326
2423
|
}
|
|
2327
2424
|
function cleanupStaleFiles() {
|
|
2328
|
-
if (
|
|
2425
|
+
if (existsSync5(PID_PATH)) {
|
|
2329
2426
|
try {
|
|
2330
|
-
const pid = parseInt(
|
|
2427
|
+
const pid = parseInt(readFileSync3(PID_PATH, "utf8").trim(), 10);
|
|
2331
2428
|
if (pid > 0) {
|
|
2332
2429
|
try {
|
|
2333
2430
|
process.kill(pid, 0);
|
|
@@ -2338,21 +2435,21 @@ function cleanupStaleFiles() {
|
|
|
2338
2435
|
} catch {
|
|
2339
2436
|
}
|
|
2340
2437
|
try {
|
|
2341
|
-
|
|
2438
|
+
unlinkSync2(PID_PATH);
|
|
2342
2439
|
} catch {
|
|
2343
2440
|
}
|
|
2344
2441
|
try {
|
|
2345
|
-
|
|
2442
|
+
unlinkSync2(SOCKET_PATH);
|
|
2346
2443
|
} catch {
|
|
2347
2444
|
}
|
|
2348
2445
|
}
|
|
2349
2446
|
}
|
|
2350
2447
|
function findPackageRoot() {
|
|
2351
|
-
let dir =
|
|
2352
|
-
const { root } =
|
|
2448
|
+
let dir = path5.dirname(fileURLToPath(import.meta.url));
|
|
2449
|
+
const { root } = path5.parse(dir);
|
|
2353
2450
|
while (dir !== root) {
|
|
2354
|
-
if (
|
|
2355
|
-
dir =
|
|
2451
|
+
if (existsSync5(path5.join(dir, "package.json"))) return dir;
|
|
2452
|
+
dir = path5.dirname(dir);
|
|
2356
2453
|
}
|
|
2357
2454
|
return null;
|
|
2358
2455
|
}
|
|
@@ -2362,8 +2459,8 @@ function spawnDaemon() {
|
|
|
2362
2459
|
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
2363
2460
|
return;
|
|
2364
2461
|
}
|
|
2365
|
-
const daemonPath =
|
|
2366
|
-
if (!
|
|
2462
|
+
const daemonPath = path5.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
2463
|
+
if (!existsSync5(daemonPath)) {
|
|
2367
2464
|
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
2368
2465
|
`);
|
|
2369
2466
|
return;
|
|
@@ -2371,7 +2468,7 @@ function spawnDaemon() {
|
|
|
2371
2468
|
const resolvedPath = daemonPath;
|
|
2372
2469
|
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
2373
2470
|
`);
|
|
2374
|
-
const logPath =
|
|
2471
|
+
const logPath = path5.join(path5.dirname(SOCKET_PATH), "exed.log");
|
|
2375
2472
|
let stderrFd = "ignore";
|
|
2376
2473
|
try {
|
|
2377
2474
|
stderrFd = openSync(logPath, "a");
|
|
@@ -2382,6 +2479,10 @@ function spawnDaemon() {
|
|
|
2382
2479
|
stdio: ["ignore", "ignore", stderrFd],
|
|
2383
2480
|
env: {
|
|
2384
2481
|
...process.env,
|
|
2482
|
+
TMUX: void 0,
|
|
2483
|
+
// Daemon is global — must not inherit session scope
|
|
2484
|
+
TMUX_PANE: void 0,
|
|
2485
|
+
// Prevents resolveExeSession() from scoping to one session
|
|
2385
2486
|
EXE_DAEMON_SOCK: SOCKET_PATH,
|
|
2386
2487
|
EXE_DAEMON_PID: PID_PATH
|
|
2387
2488
|
}
|
|
@@ -2404,7 +2505,7 @@ function acquireSpawnLock() {
|
|
|
2404
2505
|
const stat = statSync(SPAWN_LOCK_PATH);
|
|
2405
2506
|
if (Date.now() - stat.mtimeMs > SPAWN_LOCK_STALE_MS) {
|
|
2406
2507
|
try {
|
|
2407
|
-
|
|
2508
|
+
unlinkSync2(SPAWN_LOCK_PATH);
|
|
2408
2509
|
} catch {
|
|
2409
2510
|
}
|
|
2410
2511
|
try {
|
|
@@ -2421,7 +2522,7 @@ function acquireSpawnLock() {
|
|
|
2421
2522
|
}
|
|
2422
2523
|
function releaseSpawnLock() {
|
|
2423
2524
|
try {
|
|
2424
|
-
|
|
2525
|
+
unlinkSync2(SPAWN_LOCK_PATH);
|
|
2425
2526
|
} catch {
|
|
2426
2527
|
}
|
|
2427
2528
|
}
|
|
@@ -2533,9 +2634,9 @@ async function pingDaemon() {
|
|
|
2533
2634
|
}
|
|
2534
2635
|
function killAndRespawnDaemon() {
|
|
2535
2636
|
process.stderr.write("[exed-client] Killing daemon for restart...\n");
|
|
2536
|
-
if (
|
|
2637
|
+
if (existsSync5(PID_PATH)) {
|
|
2537
2638
|
try {
|
|
2538
|
-
const pid = parseInt(
|
|
2639
|
+
const pid = parseInt(readFileSync3(PID_PATH, "utf8").trim(), 10);
|
|
2539
2640
|
if (pid > 0) {
|
|
2540
2641
|
try {
|
|
2541
2642
|
process.kill(pid, "SIGKILL");
|
|
@@ -2552,11 +2653,11 @@ function killAndRespawnDaemon() {
|
|
|
2552
2653
|
_connected = false;
|
|
2553
2654
|
_buffer = "";
|
|
2554
2655
|
try {
|
|
2555
|
-
|
|
2656
|
+
unlinkSync2(PID_PATH);
|
|
2556
2657
|
} catch {
|
|
2557
2658
|
}
|
|
2558
2659
|
try {
|
|
2559
|
-
|
|
2660
|
+
unlinkSync2(SOCKET_PATH);
|
|
2560
2661
|
} catch {
|
|
2561
2662
|
}
|
|
2562
2663
|
spawnDaemon();
|
|
@@ -2619,9 +2720,9 @@ var init_exe_daemon_client = __esm({
|
|
|
2619
2720
|
"src/lib/exe-daemon-client.ts"() {
|
|
2620
2721
|
"use strict";
|
|
2621
2722
|
init_config();
|
|
2622
|
-
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ??
|
|
2623
|
-
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ??
|
|
2624
|
-
SPAWN_LOCK_PATH =
|
|
2723
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path5.join(EXE_AI_DIR, "exed.sock");
|
|
2724
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path5.join(EXE_AI_DIR, "exed.pid");
|
|
2725
|
+
SPAWN_LOCK_PATH = path5.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
2625
2726
|
SPAWN_LOCK_STALE_MS = 3e4;
|
|
2626
2727
|
CONNECT_TIMEOUT_MS = 15e3;
|
|
2627
2728
|
REQUEST_TIMEOUT_MS = 3e4;
|
|
@@ -2709,34 +2810,34 @@ __export(project_name_exports, {
|
|
|
2709
2810
|
_resetCache: () => _resetCache,
|
|
2710
2811
|
getProjectName: () => getProjectName
|
|
2711
2812
|
});
|
|
2712
|
-
import { execSync } from "child_process";
|
|
2713
|
-
import
|
|
2813
|
+
import { execSync as execSync2 } from "child_process";
|
|
2814
|
+
import path6 from "path";
|
|
2714
2815
|
function getProjectName(cwd) {
|
|
2715
2816
|
const dir = cwd ?? process.cwd();
|
|
2716
2817
|
if (_cached && _cachedCwd === dir) return _cached;
|
|
2717
2818
|
try {
|
|
2718
2819
|
let repoRoot;
|
|
2719
2820
|
try {
|
|
2720
|
-
const gitCommonDir =
|
|
2821
|
+
const gitCommonDir = execSync2("git rev-parse --path-format=absolute --git-common-dir", {
|
|
2721
2822
|
cwd: dir,
|
|
2722
2823
|
encoding: "utf8",
|
|
2723
2824
|
timeout: 2e3,
|
|
2724
2825
|
stdio: ["pipe", "pipe", "pipe"]
|
|
2725
2826
|
}).trim();
|
|
2726
|
-
repoRoot =
|
|
2827
|
+
repoRoot = path6.dirname(gitCommonDir);
|
|
2727
2828
|
} catch {
|
|
2728
|
-
repoRoot =
|
|
2829
|
+
repoRoot = execSync2("git rev-parse --show-toplevel", {
|
|
2729
2830
|
cwd: dir,
|
|
2730
2831
|
encoding: "utf8",
|
|
2731
2832
|
timeout: 2e3,
|
|
2732
2833
|
stdio: ["pipe", "pipe", "pipe"]
|
|
2733
2834
|
}).trim();
|
|
2734
2835
|
}
|
|
2735
|
-
_cached =
|
|
2836
|
+
_cached = path6.basename(repoRoot);
|
|
2736
2837
|
_cachedCwd = dir;
|
|
2737
2838
|
return _cached;
|
|
2738
2839
|
} catch {
|
|
2739
|
-
_cached =
|
|
2840
|
+
_cached = path6.basename(dir);
|
|
2740
2841
|
_cachedCwd = dir;
|
|
2741
2842
|
return _cached;
|
|
2742
2843
|
}
|
|
@@ -2759,14 +2860,14 @@ var file_grep_exports = {};
|
|
|
2759
2860
|
__export(file_grep_exports, {
|
|
2760
2861
|
grepProjectFiles: () => grepProjectFiles
|
|
2761
2862
|
});
|
|
2762
|
-
import { execSync as
|
|
2763
|
-
import { readFileSync as
|
|
2764
|
-
import
|
|
2863
|
+
import { execSync as execSync3 } from "child_process";
|
|
2864
|
+
import { readFileSync as readFileSync4, readdirSync as readdirSync2, statSync as statSync2, existsSync as existsSync6 } from "fs";
|
|
2865
|
+
import path7 from "path";
|
|
2765
2866
|
import crypto from "crypto";
|
|
2766
2867
|
function hasRipgrep() {
|
|
2767
2868
|
if (_hasRg === null) {
|
|
2768
2869
|
try {
|
|
2769
|
-
|
|
2870
|
+
execSync3("rg --version", { stdio: "ignore", timeout: 2e3 });
|
|
2770
2871
|
_hasRg = true;
|
|
2771
2872
|
} catch {
|
|
2772
2873
|
_hasRg = false;
|
|
@@ -2801,7 +2902,7 @@ async function grepProjectFiles(query, projectRoot, options) {
|
|
|
2801
2902
|
session_id: "file-grep",
|
|
2802
2903
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2803
2904
|
tool_name: "file_grep",
|
|
2804
|
-
project_name:
|
|
2905
|
+
project_name: path7.basename(projectRoot),
|
|
2805
2906
|
has_error: false,
|
|
2806
2907
|
raw_text: `${prefix} ${buildSnippet(hit, projectRoot)}`,
|
|
2807
2908
|
vector: null,
|
|
@@ -2813,7 +2914,7 @@ function getChunkContext(filePath, lineNumber) {
|
|
|
2813
2914
|
try {
|
|
2814
2915
|
const ext = filePath.split(".").pop()?.toLowerCase();
|
|
2815
2916
|
if (ext !== "ts" && ext !== "tsx" && ext !== "js" && ext !== "jsx") return "";
|
|
2816
|
-
const source =
|
|
2917
|
+
const source = readFileSync4(filePath, "utf8");
|
|
2817
2918
|
const lines = source.split("\n");
|
|
2818
2919
|
for (let i = Math.min(lineNumber - 1, lines.length - 1); i >= 0; i--) {
|
|
2819
2920
|
const line = lines[i];
|
|
@@ -2832,7 +2933,7 @@ function grepWithRipgrep(pattern, projectRoot, patterns) {
|
|
|
2832
2933
|
const globs = (patterns ?? DEFAULT_PATTERNS).map((p) => `--glob '${p}'`).join(" ");
|
|
2833
2934
|
const excludes = EXCLUDE_DIRS.map((d) => `--glob '!${d}'`).join(" ");
|
|
2834
2935
|
const cmd = `rg -i -c --hidden --no-config --no-ignore '${pattern.replace(/'/g, "\\'")}' . ${globs} ${excludes} --max-filesize ${MAX_FILE_SIZE} 2>/dev/null || true`;
|
|
2835
|
-
const output =
|
|
2936
|
+
const output = execSync3(cmd, {
|
|
2836
2937
|
cwd: projectRoot,
|
|
2837
2938
|
encoding: "utf8",
|
|
2838
2939
|
timeout: 3e3,
|
|
@@ -2847,12 +2948,12 @@ function grepWithRipgrep(pattern, projectRoot, patterns) {
|
|
|
2847
2948
|
const matchCount = parseInt(line.slice(colonIdx + 1));
|
|
2848
2949
|
if (isNaN(matchCount) || matchCount === 0) continue;
|
|
2849
2950
|
try {
|
|
2850
|
-
const firstMatch =
|
|
2951
|
+
const firstMatch = execSync3(
|
|
2851
2952
|
`rg -i -n --hidden '${pattern.replace(/'/g, "\\'")}' '${filePath}' --max-count 1 2>/dev/null | head -1`,
|
|
2852
2953
|
{ cwd: projectRoot, encoding: "utf8", timeout: 1e3 }
|
|
2853
2954
|
).trim();
|
|
2854
2955
|
const lineNum = parseInt(firstMatch.split(":")[0] ?? "1");
|
|
2855
|
-
const totalLines =
|
|
2956
|
+
const totalLines = execSync3(`wc -l < '${filePath}'`, {
|
|
2856
2957
|
cwd: projectRoot,
|
|
2857
2958
|
encoding: "utf8",
|
|
2858
2959
|
timeout: 1e3
|
|
@@ -2875,11 +2976,11 @@ function grepWithNodeFs(pattern, projectRoot, patterns) {
|
|
|
2875
2976
|
const files = collectFiles(projectRoot, patterns ?? DEFAULT_PATTERNS);
|
|
2876
2977
|
const hits = [];
|
|
2877
2978
|
for (const filePath of files.slice(0, MAX_FILES)) {
|
|
2878
|
-
const absPath =
|
|
2979
|
+
const absPath = path7.join(projectRoot, filePath);
|
|
2879
2980
|
try {
|
|
2880
2981
|
const stat = statSync2(absPath);
|
|
2881
2982
|
if (stat.size > MAX_FILE_SIZE) continue;
|
|
2882
|
-
const content =
|
|
2983
|
+
const content = readFileSync4(absPath, "utf8");
|
|
2883
2984
|
const lines = content.split("\n");
|
|
2884
2985
|
const matches = content.match(regex);
|
|
2885
2986
|
if (!matches || matches.length === 0) continue;
|
|
@@ -2902,15 +3003,15 @@ function collectFiles(root, patterns) {
|
|
|
2902
3003
|
const files = [];
|
|
2903
3004
|
function walk(dir, relative) {
|
|
2904
3005
|
if (files.length >= MAX_FILES) return;
|
|
2905
|
-
const basename =
|
|
3006
|
+
const basename = path7.basename(dir);
|
|
2906
3007
|
if (EXCLUDE_DIRS.includes(basename)) return;
|
|
2907
3008
|
try {
|
|
2908
3009
|
const entries = readdirSync2(dir, { withFileTypes: true });
|
|
2909
3010
|
for (const entry of entries) {
|
|
2910
3011
|
if (files.length >= MAX_FILES) return;
|
|
2911
|
-
const rel =
|
|
3012
|
+
const rel = path7.join(relative, entry.name);
|
|
2912
3013
|
if (entry.isDirectory()) {
|
|
2913
|
-
walk(
|
|
3014
|
+
walk(path7.join(dir, entry.name), rel);
|
|
2914
3015
|
} else if (entry.isFile()) {
|
|
2915
3016
|
for (const pat of patterns) {
|
|
2916
3017
|
if (matchGlob(rel, pat)) {
|
|
@@ -2942,7 +3043,7 @@ function matchGlob(filePath, pattern) {
|
|
|
2942
3043
|
if (slashIdx !== -1) {
|
|
2943
3044
|
const dir = pattern.slice(0, slashIdx);
|
|
2944
3045
|
const ext2 = pattern.slice(slashIdx + 1).replace("*", "");
|
|
2945
|
-
const fileDir =
|
|
3046
|
+
const fileDir = path7.dirname(filePath);
|
|
2946
3047
|
return fileDir === dir && filePath.endsWith(ext2);
|
|
2947
3048
|
}
|
|
2948
3049
|
const ext = pattern.replace("*", "");
|
|
@@ -2950,9 +3051,9 @@ function matchGlob(filePath, pattern) {
|
|
|
2950
3051
|
}
|
|
2951
3052
|
function buildSnippet(hit, projectRoot) {
|
|
2952
3053
|
try {
|
|
2953
|
-
const absPath =
|
|
2954
|
-
if (!
|
|
2955
|
-
const lines =
|
|
3054
|
+
const absPath = path7.join(projectRoot, hit.filePath);
|
|
3055
|
+
if (!existsSync6(absPath)) return hit.matchLine;
|
|
3056
|
+
const lines = readFileSync4(absPath, "utf8").split("\n");
|
|
2956
3057
|
const start = Math.max(0, hit.lineNumber - 3);
|
|
2957
3058
|
const end = Math.min(lines.length, hit.lineNumber + 2);
|
|
2958
3059
|
return lines.slice(start, end).join("\n").slice(0, 500);
|
|
@@ -2986,8 +3087,8 @@ __export(reranker_exports, {
|
|
|
2986
3087
|
rerank: () => rerank,
|
|
2987
3088
|
rerankWithScores: () => rerankWithScores
|
|
2988
3089
|
});
|
|
2989
|
-
import
|
|
2990
|
-
import { existsSync as
|
|
3090
|
+
import path8 from "path";
|
|
3091
|
+
import { existsSync as existsSync7 } from "fs";
|
|
2991
3092
|
function resetIdleTimer() {
|
|
2992
3093
|
if (_idleTimer) clearTimeout(_idleTimer);
|
|
2993
3094
|
_idleTimer = setTimeout(() => {
|
|
@@ -2998,18 +3099,18 @@ function resetIdleTimer() {
|
|
|
2998
3099
|
}
|
|
2999
3100
|
}
|
|
3000
3101
|
function isRerankerAvailable() {
|
|
3001
|
-
return
|
|
3102
|
+
return existsSync7(path8.join(MODELS_DIR, RERANKER_MODEL_FILE));
|
|
3002
3103
|
}
|
|
3003
3104
|
function getRerankerModelPath() {
|
|
3004
|
-
return
|
|
3105
|
+
return path8.join(MODELS_DIR, RERANKER_MODEL_FILE);
|
|
3005
3106
|
}
|
|
3006
3107
|
async function ensureLoaded() {
|
|
3007
3108
|
if (_rerankerContext) {
|
|
3008
3109
|
resetIdleTimer();
|
|
3009
3110
|
return;
|
|
3010
3111
|
}
|
|
3011
|
-
const modelPath =
|
|
3012
|
-
if (!
|
|
3112
|
+
const modelPath = path8.join(MODELS_DIR, RERANKER_MODEL_FILE);
|
|
3113
|
+
if (!existsSync7(modelPath)) {
|
|
3013
3114
|
throw new Error(
|
|
3014
3115
|
`Reranker model not found at ${modelPath}. Run /exe-setup to download it.`
|
|
3015
3116
|
);
|
|
@@ -3086,13 +3187,13 @@ var init_reranker = __esm({
|
|
|
3086
3187
|
});
|
|
3087
3188
|
|
|
3088
3189
|
// src/lib/session-key.ts
|
|
3089
|
-
import { execSync as
|
|
3190
|
+
import { execSync as execSync4 } from "child_process";
|
|
3090
3191
|
function getSessionKey() {
|
|
3091
3192
|
if (_cached2) return _cached2;
|
|
3092
3193
|
let pid = process.ppid;
|
|
3093
3194
|
for (let i = 0; i < 10; i++) {
|
|
3094
3195
|
try {
|
|
3095
|
-
const info =
|
|
3196
|
+
const info = execSync4(`ps -p ${pid} -o ppid=,comm=`, {
|
|
3096
3197
|
encoding: "utf8",
|
|
3097
3198
|
timeout: 2e3
|
|
3098
3199
|
}).trim();
|
|
@@ -3121,13 +3222,13 @@ var init_session_key = __esm({
|
|
|
3121
3222
|
});
|
|
3122
3223
|
|
|
3123
3224
|
// src/lib/session-registry.ts
|
|
3124
|
-
import
|
|
3125
|
-
import
|
|
3225
|
+
import path10 from "path";
|
|
3226
|
+
import os4 from "os";
|
|
3126
3227
|
var REGISTRY_PATH;
|
|
3127
3228
|
var init_session_registry = __esm({
|
|
3128
3229
|
"src/lib/session-registry.ts"() {
|
|
3129
3230
|
"use strict";
|
|
3130
|
-
REGISTRY_PATH =
|
|
3231
|
+
REGISTRY_PATH = path10.join(os4.homedir(), ".exe-os", "session-registry.json");
|
|
3131
3232
|
}
|
|
3132
3233
|
});
|
|
3133
3234
|
|
|
@@ -3239,7 +3340,7 @@ var init_transport = __esm({
|
|
|
3239
3340
|
});
|
|
3240
3341
|
|
|
3241
3342
|
// src/lib/cc-agent-support.ts
|
|
3242
|
-
import { execSync as
|
|
3343
|
+
import { execSync as execSync6 } from "child_process";
|
|
3243
3344
|
var init_cc_agent_support = __esm({
|
|
3244
3345
|
"src/lib/cc-agent-support.ts"() {
|
|
3245
3346
|
"use strict";
|
|
@@ -3268,31 +3369,16 @@ var init_provider_table = __esm({
|
|
|
3268
3369
|
});
|
|
3269
3370
|
|
|
3270
3371
|
// src/lib/intercom-queue.ts
|
|
3271
|
-
import { readFileSync as
|
|
3272
|
-
import
|
|
3273
|
-
import
|
|
3372
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync3, renameSync as renameSync3, existsSync as existsSync8, mkdirSync as mkdirSync3 } from "fs";
|
|
3373
|
+
import path11 from "path";
|
|
3374
|
+
import os5 from "os";
|
|
3274
3375
|
var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
|
|
3275
3376
|
var init_intercom_queue = __esm({
|
|
3276
3377
|
"src/lib/intercom-queue.ts"() {
|
|
3277
3378
|
"use strict";
|
|
3278
|
-
QUEUE_PATH =
|
|
3379
|
+
QUEUE_PATH = path11.join(os5.homedir(), ".exe-os", "intercom-queue.json");
|
|
3279
3380
|
TTL_MS = 60 * 60 * 1e3;
|
|
3280
|
-
INTERCOM_LOG =
|
|
3281
|
-
}
|
|
3282
|
-
});
|
|
3283
|
-
|
|
3284
|
-
// src/lib/employees.ts
|
|
3285
|
-
import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
|
|
3286
|
-
import { existsSync as existsSync8, symlinkSync, readlinkSync, readFileSync as readFileSync6, renameSync as renameSync3, unlinkSync as unlinkSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
3287
|
-
import { execSync as execSync6 } from "child_process";
|
|
3288
|
-
import path11 from "path";
|
|
3289
|
-
import os5 from "os";
|
|
3290
|
-
var EMPLOYEES_PATH;
|
|
3291
|
-
var init_employees = __esm({
|
|
3292
|
-
"src/lib/employees.ts"() {
|
|
3293
|
-
"use strict";
|
|
3294
|
-
init_config();
|
|
3295
|
-
EMPLOYEES_PATH = path11.join(EXE_AI_DIR, "exe-employees.json");
|
|
3381
|
+
INTERCOM_LOG = path11.join(os5.homedir(), ".exe-os", "intercom.log");
|
|
3296
3382
|
}
|
|
3297
3383
|
});
|
|
3298
3384
|
|
|
@@ -3336,8 +3422,10 @@ function getMySession() {
|
|
|
3336
3422
|
return getTransport().getMySession();
|
|
3337
3423
|
}
|
|
3338
3424
|
function extractRootExe(name) {
|
|
3339
|
-
|
|
3340
|
-
|
|
3425
|
+
if (!name) return null;
|
|
3426
|
+
if (!name.includes("-")) return name;
|
|
3427
|
+
const parts = name.split("-").filter(Boolean);
|
|
3428
|
+
return parts.length > 0 ? parts[parts.length - 1] : null;
|
|
3341
3429
|
}
|
|
3342
3430
|
function getParentExe(sessionKey) {
|
|
3343
3431
|
try {
|
|
@@ -3372,6 +3460,7 @@ var init_tmux_routing = __esm({
|
|
|
3372
3460
|
init_provider_table();
|
|
3373
3461
|
init_intercom_queue();
|
|
3374
3462
|
init_plan_limits();
|
|
3463
|
+
init_employees();
|
|
3375
3464
|
SPAWN_LOCK_DIR = path14.join(os6.homedir(), ".exe-os", "spawn-locks");
|
|
3376
3465
|
SESSION_CACHE = path14.join(os6.homedir(), ".exe-os", "session-cache");
|
|
3377
3466
|
INTERCOM_LOG2 = path14.join(os6.homedir(), ".exe-os", "intercom.log");
|
|
@@ -3423,6 +3512,8 @@ async function hybridSearch(queryText, agentId, options) {
|
|
|
3423
3512
|
return lightweightSearch(queryText, agentId, options);
|
|
3424
3513
|
}
|
|
3425
3514
|
const limit = options?.limit ?? 10;
|
|
3515
|
+
const trajectoryResults = await trajectoryBypass(queryText, agentId, options, limit);
|
|
3516
|
+
if (trajectoryResults !== null) return trajectoryResults;
|
|
3426
3517
|
let effectiveQuery = queryText;
|
|
3427
3518
|
let effectiveOptions = { ...options };
|
|
3428
3519
|
let _isBroadQuery = false;
|
|
@@ -3646,6 +3737,8 @@ async function lightweightSearch(queryText, agentId, options) {
|
|
|
3646
3737
|
async function ftsQuery(client, matchExpr, agentId, options, limit) {
|
|
3647
3738
|
const statusFilter = options?.includeArchived ? "" : `
|
|
3648
3739
|
AND COALESCE(m.status, 'active') = 'active'`;
|
|
3740
|
+
const draftFilter = options?.includeDrafts ? "" : `
|
|
3741
|
+
AND (m.draft = 0 OR m.draft IS NULL)`;
|
|
3649
3742
|
let sql = `SELECT m.id, m.agent_id, m.agent_role, m.session_id, m.timestamp,
|
|
3650
3743
|
m.tool_name, m.project_name,
|
|
3651
3744
|
m.has_error, m.raw_text, m.vector, m.task_id,
|
|
@@ -3656,7 +3749,7 @@ async function ftsQuery(client, matchExpr, agentId, options, limit) {
|
|
|
3656
3749
|
FROM memories m
|
|
3657
3750
|
JOIN memories_fts fts ON m.rowid = fts.rowid
|
|
3658
3751
|
WHERE memories_fts MATCH ?
|
|
3659
|
-
AND m.agent_id = ?${statusFilter}
|
|
3752
|
+
AND m.agent_id = ?${statusFilter}${draftFilter}
|
|
3660
3753
|
AND COALESCE(m.confidence, 0.7) >= 0.3`;
|
|
3661
3754
|
const args = [matchExpr, agentId];
|
|
3662
3755
|
const scope = buildWikiScopeFilter(options, "m.");
|
|
@@ -3678,6 +3771,10 @@ async function ftsQuery(client, matchExpr, agentId, options, limit) {
|
|
|
3678
3771
|
sql += ` AND m.timestamp >= ?`;
|
|
3679
3772
|
args.push(options.since);
|
|
3680
3773
|
}
|
|
3774
|
+
if (options?.memoryType) {
|
|
3775
|
+
sql += ` AND m.memory_type = ?`;
|
|
3776
|
+
args.push(options.memoryType);
|
|
3777
|
+
}
|
|
3681
3778
|
sql += ` ORDER BY rank LIMIT ?`;
|
|
3682
3779
|
args.push(limit);
|
|
3683
3780
|
const result = await client.execute({ sql, args });
|
|
@@ -3710,6 +3807,8 @@ async function recentRecords(agentId, options, limit) {
|
|
|
3710
3807
|
const client = getClient();
|
|
3711
3808
|
const statusFilter = options?.includeArchived ? "" : `
|
|
3712
3809
|
AND COALESCE(status, 'active') = 'active'`;
|
|
3810
|
+
const draftFilter = options?.includeDrafts ? "" : `
|
|
3811
|
+
AND (draft = 0 OR draft IS NULL)`;
|
|
3713
3812
|
let sql = `SELECT id, agent_id, agent_role, session_id, timestamp,
|
|
3714
3813
|
tool_name, project_name,
|
|
3715
3814
|
has_error, raw_text, vector, task_id,
|
|
@@ -3718,7 +3817,7 @@ async function recentRecords(agentId, options, limit) {
|
|
|
3718
3817
|
char_offset, page_number,
|
|
3719
3818
|
source_path, source_type
|
|
3720
3819
|
FROM memories
|
|
3721
|
-
WHERE agent_id = ?${statusFilter}
|
|
3820
|
+
WHERE agent_id = ?${statusFilter}${draftFilter}
|
|
3722
3821
|
AND COALESCE(confidence, 0.7) >= 0.3`;
|
|
3723
3822
|
const args = [agentId];
|
|
3724
3823
|
const scope = buildWikiScopeFilter(options, "");
|
|
@@ -3740,6 +3839,10 @@ async function recentRecords(agentId, options, limit) {
|
|
|
3740
3839
|
sql += ` AND timestamp >= ?`;
|
|
3741
3840
|
args.push(options.since);
|
|
3742
3841
|
}
|
|
3842
|
+
if (options?.memoryType) {
|
|
3843
|
+
sql += ` AND memory_type = ?`;
|
|
3844
|
+
args.push(options.memoryType);
|
|
3845
|
+
}
|
|
3743
3846
|
sql += ` ORDER BY timestamp DESC LIMIT ?`;
|
|
3744
3847
|
args.push(limit);
|
|
3745
3848
|
const result = await client.execute({ sql, args });
|
|
@@ -3768,33 +3871,158 @@ async function recentRecords(agentId, options, limit) {
|
|
|
3768
3871
|
source_type: row.source_type ?? null
|
|
3769
3872
|
}));
|
|
3770
3873
|
}
|
|
3874
|
+
var KNOWN_TOOLS = /* @__PURE__ */ new Set([
|
|
3875
|
+
"Bash",
|
|
3876
|
+
"Read",
|
|
3877
|
+
"Write",
|
|
3878
|
+
"Edit",
|
|
3879
|
+
"Grep",
|
|
3880
|
+
"Glob",
|
|
3881
|
+
"Agent",
|
|
3882
|
+
"Skill",
|
|
3883
|
+
"WebSearch",
|
|
3884
|
+
"WebFetch"
|
|
3885
|
+
]);
|
|
3886
|
+
function detectToolQuery(query) {
|
|
3887
|
+
const lower = query.toLowerCase().trim();
|
|
3888
|
+
for (const tool of KNOWN_TOOLS) {
|
|
3889
|
+
if (lower === tool.toLowerCase()) return tool;
|
|
3890
|
+
if (lower.startsWith(tool.toLowerCase() + " ")) return tool;
|
|
3891
|
+
if (lower.includes(`tool:${tool.toLowerCase()}`)) return tool;
|
|
3892
|
+
}
|
|
3893
|
+
return null;
|
|
3894
|
+
}
|
|
3895
|
+
async function trajectoryBypass(queryText, agentId, options, limit) {
|
|
3896
|
+
const toolName = detectToolQuery(queryText);
|
|
3897
|
+
if (!toolName) return null;
|
|
3898
|
+
try {
|
|
3899
|
+
const client = getClient();
|
|
3900
|
+
const statusFilter = options?.includeArchived ? "" : `
|
|
3901
|
+
AND COALESCE(status, 'active') = 'active'`;
|
|
3902
|
+
const draftFilter = options?.includeDrafts ? "" : `
|
|
3903
|
+
AND (draft = 0 OR draft IS NULL)`;
|
|
3904
|
+
let sql = `SELECT id, agent_id, agent_role, session_id, timestamp,
|
|
3905
|
+
tool_name, project_name,
|
|
3906
|
+
has_error, raw_text, vector, task_id,
|
|
3907
|
+
importance, status, confidence, last_accessed,
|
|
3908
|
+
workspace_id, document_id, user_id,
|
|
3909
|
+
char_offset, page_number,
|
|
3910
|
+
source_path, source_type
|
|
3911
|
+
FROM memories
|
|
3912
|
+
WHERE trajectory IS NOT NULL
|
|
3913
|
+
AND json_extract(trajectory, '$.tool') = ?
|
|
3914
|
+
AND agent_id = ?${statusFilter}${draftFilter}`;
|
|
3915
|
+
const args = [toolName, agentId];
|
|
3916
|
+
if (options?.projectName) {
|
|
3917
|
+
sql += ` AND project_name = ?`;
|
|
3918
|
+
args.push(options.projectName);
|
|
3919
|
+
}
|
|
3920
|
+
if (options?.since) {
|
|
3921
|
+
sql += ` AND timestamp >= ?`;
|
|
3922
|
+
args.push(options.since);
|
|
3923
|
+
}
|
|
3924
|
+
sql += ` ORDER BY timestamp DESC LIMIT ?`;
|
|
3925
|
+
args.push(limit);
|
|
3926
|
+
const result = await client.execute({ sql, args });
|
|
3927
|
+
if (result.rows.length < 3) return null;
|
|
3928
|
+
return result.rows.map((row) => ({
|
|
3929
|
+
id: row.id,
|
|
3930
|
+
agent_id: row.agent_id,
|
|
3931
|
+
agent_role: row.agent_role,
|
|
3932
|
+
session_id: row.session_id,
|
|
3933
|
+
timestamp: row.timestamp,
|
|
3934
|
+
tool_name: row.tool_name,
|
|
3935
|
+
project_name: row.project_name,
|
|
3936
|
+
has_error: row.has_error === 1,
|
|
3937
|
+
raw_text: row.raw_text,
|
|
3938
|
+
vector: row.vector == null ? [] : Array.isArray(row.vector) ? row.vector : Array.from(row.vector),
|
|
3939
|
+
task_id: row.task_id ?? null,
|
|
3940
|
+
importance: row.importance ?? 5,
|
|
3941
|
+
status: row.status ?? "active",
|
|
3942
|
+
confidence: row.confidence ?? 0.7,
|
|
3943
|
+
last_accessed: row.last_accessed ?? row.timestamp,
|
|
3944
|
+
workspace_id: row.workspace_id ?? null,
|
|
3945
|
+
document_id: row.document_id ?? null,
|
|
3946
|
+
user_id: row.user_id ?? null,
|
|
3947
|
+
char_offset: row.char_offset ?? null,
|
|
3948
|
+
page_number: row.page_number ?? null,
|
|
3949
|
+
source_path: row.source_path ?? null,
|
|
3950
|
+
source_type: row.source_type ?? null
|
|
3951
|
+
}));
|
|
3952
|
+
} catch {
|
|
3953
|
+
return null;
|
|
3954
|
+
}
|
|
3955
|
+
}
|
|
3771
3956
|
|
|
3772
3957
|
// src/adapters/claude/active-agent.ts
|
|
3773
3958
|
init_config();
|
|
3774
|
-
import { readFileSync as
|
|
3775
|
-
import { execSync as
|
|
3776
|
-
import
|
|
3959
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, unlinkSync as unlinkSync3, readdirSync as readdirSync3 } from "fs";
|
|
3960
|
+
import { execSync as execSync5 } from "child_process";
|
|
3961
|
+
import path9 from "path";
|
|
3777
3962
|
|
|
3778
3963
|
// src/adapters/claude/session-key.ts
|
|
3779
3964
|
init_session_key();
|
|
3780
3965
|
|
|
3781
3966
|
// src/adapters/claude/active-agent.ts
|
|
3782
|
-
|
|
3967
|
+
init_employees();
|
|
3968
|
+
var CACHE_DIR = path9.join(EXE_AI_DIR, "session-cache");
|
|
3783
3969
|
var STALE_MS = 24 * 60 * 60 * 1e3;
|
|
3970
|
+
function isNameWithOptionalInstance(candidate, baseName) {
|
|
3971
|
+
if (candidate === baseName) return true;
|
|
3972
|
+
if (!candidate.startsWith(baseName)) return false;
|
|
3973
|
+
return /^\d+$/.test(candidate.slice(baseName.length));
|
|
3974
|
+
}
|
|
3975
|
+
function resolveEmployeeFromSessionPrefix(prefix, employees) {
|
|
3976
|
+
const sorted = [...employees].sort((a, b) => b.name.length - a.name.length);
|
|
3977
|
+
for (const employee of sorted) {
|
|
3978
|
+
if (isNameWithOptionalInstance(prefix, employee.name)) {
|
|
3979
|
+
return { agentId: employee.name, agentRole: employee.role };
|
|
3980
|
+
}
|
|
3981
|
+
}
|
|
3982
|
+
return null;
|
|
3983
|
+
}
|
|
3984
|
+
function resolveActiveAgentFromTmuxSession(sessionName) {
|
|
3985
|
+
const employees = loadEmployeesSync();
|
|
3986
|
+
const coordinator = getCoordinatorEmployee(employees);
|
|
3987
|
+
const coordinatorName = coordinator?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME;
|
|
3988
|
+
if (isNameWithOptionalInstance(sessionName, coordinatorName)) {
|
|
3989
|
+
return {
|
|
3990
|
+
agentId: coordinatorName,
|
|
3991
|
+
agentRole: coordinator?.role ?? "COO"
|
|
3992
|
+
};
|
|
3993
|
+
}
|
|
3994
|
+
if (isNameWithOptionalInstance(sessionName, DEFAULT_COORDINATOR_TEMPLATE_NAME)) {
|
|
3995
|
+
return {
|
|
3996
|
+
agentId: coordinator?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME,
|
|
3997
|
+
agentRole: coordinator?.role ?? "COO"
|
|
3998
|
+
};
|
|
3999
|
+
}
|
|
4000
|
+
if (sessionName.includes("-")) {
|
|
4001
|
+
const prefix = sessionName.split("-")[0] ?? "";
|
|
4002
|
+
const employee = resolveEmployeeFromSessionPrefix(prefix, employees);
|
|
4003
|
+
if (employee) return employee;
|
|
4004
|
+
const legacy = prefix.match(/^([a-zA-Z]+)\d*$/);
|
|
4005
|
+
if (legacy?.[1] && legacy[1] !== DEFAULT_COORDINATOR_TEMPLATE_NAME) {
|
|
4006
|
+
const emp = getEmployee(employees, legacy[1]);
|
|
4007
|
+
return { agentId: emp?.name ?? legacy[1], agentRole: emp?.role ?? "employee" };
|
|
4008
|
+
}
|
|
4009
|
+
}
|
|
4010
|
+
return null;
|
|
4011
|
+
}
|
|
3784
4012
|
function getMarkerPath() {
|
|
3785
|
-
return
|
|
4013
|
+
return path9.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
|
|
3786
4014
|
}
|
|
3787
4015
|
function getActiveAgent() {
|
|
3788
4016
|
try {
|
|
3789
4017
|
const markerPath = getMarkerPath();
|
|
3790
|
-
const raw =
|
|
4018
|
+
const raw = readFileSync5(markerPath, "utf8");
|
|
3791
4019
|
const data = JSON.parse(raw);
|
|
3792
4020
|
if (data.agentId) {
|
|
3793
4021
|
if (data.startedAt) {
|
|
3794
4022
|
const age = Date.now() - new Date(data.startedAt).getTime();
|
|
3795
4023
|
if (age > STALE_MS) {
|
|
3796
4024
|
try {
|
|
3797
|
-
|
|
4025
|
+
unlinkSync3(markerPath);
|
|
3798
4026
|
} catch {
|
|
3799
4027
|
}
|
|
3800
4028
|
} else {
|
|
@@ -3813,17 +4041,12 @@ function getActiveAgent() {
|
|
|
3813
4041
|
} catch {
|
|
3814
4042
|
}
|
|
3815
4043
|
try {
|
|
3816
|
-
const sessionName =
|
|
4044
|
+
const sessionName = execSync5(
|
|
3817
4045
|
"tmux display-message -p '#{session_name}' 2>/dev/null",
|
|
3818
4046
|
{ encoding: "utf8", timeout: 2e3 }
|
|
3819
4047
|
).trim();
|
|
3820
|
-
const
|
|
3821
|
-
if (
|
|
3822
|
-
return { agentId: empMatch[1], agentRole: "employee" };
|
|
3823
|
-
}
|
|
3824
|
-
if (/^exe\d+$/.test(sessionName)) {
|
|
3825
|
-
return { agentId: "exe", agentRole: "COO" };
|
|
3826
|
-
}
|
|
4048
|
+
const resolved = resolveActiveAgentFromTmuxSession(sessionName);
|
|
4049
|
+
if (resolved) return resolved;
|
|
3827
4050
|
} catch {
|
|
3828
4051
|
}
|
|
3829
4052
|
return {
|
|
@@ -3835,6 +4058,7 @@ function getActiveAgent() {
|
|
|
3835
4058
|
// src/adapters/claude/hooks/session-start.ts
|
|
3836
4059
|
init_project_name();
|
|
3837
4060
|
init_task_scope();
|
|
4061
|
+
init_employees();
|
|
3838
4062
|
if (!process.env.AGENT_ID) {
|
|
3839
4063
|
process.env.AGENT_ID = "default";
|
|
3840
4064
|
process.env.AGENT_ROLE = "employee";
|
|
@@ -3903,7 +4127,7 @@ process.stdin.on("end", async () => {
|
|
|
3903
4127
|
|
|
3904
4128
|
## Your Tasks
|
|
3905
4129
|
${taskLines.join("\n")}`;
|
|
3906
|
-
if (agentId
|
|
4130
|
+
if (!canCoordinate(agentId, agent.agentRole)) {
|
|
3907
4131
|
taskBrief += `
|
|
3908
4132
|
|
|
3909
4133
|
## MANDATORY PROCESS
|