@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
|
@@ -30,7 +30,6 @@ var config_exports = {};
|
|
|
30
30
|
__export(config_exports, {
|
|
31
31
|
CONFIG_MIGRATIONS: () => CONFIG_MIGRATIONS,
|
|
32
32
|
CONFIG_PATH: () => CONFIG_PATH,
|
|
33
|
-
COO_AGENT_NAME: () => COO_AGENT_NAME,
|
|
34
33
|
CURRENT_CONFIG_VERSION: () => CURRENT_CONFIG_VERSION,
|
|
35
34
|
DB_PATH: () => DB_PATH,
|
|
36
35
|
EXE_AI_DIR: () => EXE_AI_DIR,
|
|
@@ -186,7 +185,7 @@ async function loadConfigFrom(configPath) {
|
|
|
186
185
|
return { ...DEFAULT_CONFIG };
|
|
187
186
|
}
|
|
188
187
|
}
|
|
189
|
-
var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH,
|
|
188
|
+
var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
|
|
190
189
|
var init_config = __esm({
|
|
191
190
|
"src/lib/config.ts"() {
|
|
192
191
|
"use strict";
|
|
@@ -194,7 +193,6 @@ var init_config = __esm({
|
|
|
194
193
|
DB_PATH = path.join(EXE_AI_DIR, "memories.db");
|
|
195
194
|
MODELS_DIR = path.join(EXE_AI_DIR, "models");
|
|
196
195
|
CONFIG_PATH = path.join(EXE_AI_DIR, "config.json");
|
|
197
|
-
COO_AGENT_NAME = "exe";
|
|
198
196
|
LEGACY_LANCE_PATH = path.join(EXE_AI_DIR, "local.lance");
|
|
199
197
|
CURRENT_CONFIG_VERSION = 1;
|
|
200
198
|
DEFAULT_CONFIG = {
|
|
@@ -230,13 +228,7 @@ var init_config = __esm({
|
|
|
230
228
|
wikiUrl: "",
|
|
231
229
|
wikiApiKey: "",
|
|
232
230
|
wikiSyncIntervalMs: 30 * 60 * 1e3,
|
|
233
|
-
wikiWorkspaceMapping: {
|
|
234
|
-
exe: "Executive",
|
|
235
|
-
yoshi: "Engineering",
|
|
236
|
-
mari: "Marketing",
|
|
237
|
-
tom: "Engineering",
|
|
238
|
-
sasha: "Production"
|
|
239
|
-
},
|
|
231
|
+
wikiWorkspaceMapping: {},
|
|
240
232
|
wikiAutoUpdate: true,
|
|
241
233
|
wikiAutoUpdateThreshold: 0.5,
|
|
242
234
|
wikiAutoUpdateCreateNew: true,
|
|
@@ -321,7 +313,7 @@ function wrapWithRetry(client) {
|
|
|
321
313
|
return (sql) => retryOnBusy(() => target.execute(sql), "execute");
|
|
322
314
|
}
|
|
323
315
|
if (prop === "batch") {
|
|
324
|
-
return (stmts) => retryOnBusy(() => target.batch(stmts), "batch");
|
|
316
|
+
return (stmts, mode) => retryOnBusy(() => target.batch(stmts, mode), "batch");
|
|
325
317
|
}
|
|
326
318
|
return Reflect.get(target, prop, receiver);
|
|
327
319
|
}
|
|
@@ -337,6 +329,64 @@ var init_db_retry = __esm({
|
|
|
337
329
|
}
|
|
338
330
|
});
|
|
339
331
|
|
|
332
|
+
// src/lib/employees.ts
|
|
333
|
+
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
334
|
+
import { existsSync as existsSync2, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
|
|
335
|
+
import { execSync } from "child_process";
|
|
336
|
+
import path2 from "path";
|
|
337
|
+
import os2 from "os";
|
|
338
|
+
function normalizeRole(role) {
|
|
339
|
+
return (role ?? "").trim().toLowerCase();
|
|
340
|
+
}
|
|
341
|
+
function isCoordinatorRole(role) {
|
|
342
|
+
return normalizeRole(role) === normalizeRole(COORDINATOR_ROLE);
|
|
343
|
+
}
|
|
344
|
+
function getCoordinatorEmployee(employees) {
|
|
345
|
+
return employees.find((e) => isCoordinatorRole(e.role));
|
|
346
|
+
}
|
|
347
|
+
function getCoordinatorName(employees = loadEmployeesSync()) {
|
|
348
|
+
return getCoordinatorEmployee(employees)?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME;
|
|
349
|
+
}
|
|
350
|
+
function isCoordinatorName(agentName, employees = loadEmployeesSync()) {
|
|
351
|
+
if (!agentName) return false;
|
|
352
|
+
return agentName.toLowerCase() === getCoordinatorName(employees).toLowerCase();
|
|
353
|
+
}
|
|
354
|
+
function canCoordinate(agentName, agentRole, employees = loadEmployeesSync()) {
|
|
355
|
+
return agentName === "default" || isCoordinatorRole(agentRole) || isCoordinatorName(agentName, employees);
|
|
356
|
+
}
|
|
357
|
+
async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
358
|
+
if (!existsSync2(employeesPath)) {
|
|
359
|
+
return [];
|
|
360
|
+
}
|
|
361
|
+
const raw = await readFile2(employeesPath, "utf-8");
|
|
362
|
+
try {
|
|
363
|
+
return JSON.parse(raw);
|
|
364
|
+
} catch {
|
|
365
|
+
return [];
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
369
|
+
if (!existsSync2(employeesPath)) return [];
|
|
370
|
+
try {
|
|
371
|
+
return JSON.parse(readFileSync2(employeesPath, "utf-8"));
|
|
372
|
+
} catch {
|
|
373
|
+
return [];
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
function getEmployee(employees, name) {
|
|
377
|
+
return employees.find((e) => e.name.toLowerCase() === name.toLowerCase());
|
|
378
|
+
}
|
|
379
|
+
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE;
|
|
380
|
+
var init_employees = __esm({
|
|
381
|
+
"src/lib/employees.ts"() {
|
|
382
|
+
"use strict";
|
|
383
|
+
init_config();
|
|
384
|
+
EMPLOYEES_PATH = path2.join(EXE_AI_DIR, "exe-employees.json");
|
|
385
|
+
DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
|
|
386
|
+
COORDINATOR_ROLE = "COO";
|
|
387
|
+
}
|
|
388
|
+
});
|
|
389
|
+
|
|
340
390
|
// src/lib/database.ts
|
|
341
391
|
import { createClient } from "@libsql/client";
|
|
342
392
|
async function initDatabase(config) {
|
|
@@ -470,22 +520,24 @@ async function ensureSchema() {
|
|
|
470
520
|
ON behaviors(agent_id, active);
|
|
471
521
|
`);
|
|
472
522
|
try {
|
|
523
|
+
const coordinatorName = getCoordinatorName();
|
|
473
524
|
const existing = await client.execute({
|
|
474
|
-
sql: "SELECT COUNT(*) as cnt FROM behaviors WHERE agent_id =
|
|
475
|
-
args: []
|
|
525
|
+
sql: "SELECT COUNT(*) as cnt FROM behaviors WHERE agent_id = ?",
|
|
526
|
+
args: [coordinatorName]
|
|
476
527
|
});
|
|
477
528
|
if (Number(existing.rows[0]?.cnt) === 0) {
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
529
|
+
const seededAt = "2026-03-25T00:00:00Z";
|
|
530
|
+
for (const [domain, content] of [
|
|
531
|
+
["workflow", `Don't ask "keep going?" \u2014 just keep executing phases/plans autonomously`],
|
|
532
|
+
["tool-use", "Always use create_task MCP tool, never write .md files directly for task creation"],
|
|
533
|
+
["workflow", "Auto-start reviewing when idle and reviews are pending \u2014 never ask founder for permission"]
|
|
534
|
+
]) {
|
|
535
|
+
await client.execute({
|
|
536
|
+
sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, content, active, created_at, updated_at)
|
|
537
|
+
VALUES (hex(randomblob(16)), ?, NULL, ?, ?, 1, ?, ?)`,
|
|
538
|
+
args: [coordinatorName, domain, content, seededAt, seededAt]
|
|
539
|
+
});
|
|
540
|
+
}
|
|
489
541
|
}
|
|
490
542
|
} catch {
|
|
491
543
|
}
|
|
@@ -1177,6 +1229,39 @@ async function ensureSchema() {
|
|
|
1177
1229
|
} catch {
|
|
1178
1230
|
}
|
|
1179
1231
|
}
|
|
1232
|
+
try {
|
|
1233
|
+
await client.execute({
|
|
1234
|
+
sql: `ALTER TABLE memories ADD COLUMN draft INTEGER DEFAULT 0`,
|
|
1235
|
+
args: []
|
|
1236
|
+
});
|
|
1237
|
+
} catch {
|
|
1238
|
+
}
|
|
1239
|
+
try {
|
|
1240
|
+
await client.execute(
|
|
1241
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_draft ON memories(draft) WHERE draft = 1`
|
|
1242
|
+
);
|
|
1243
|
+
} catch {
|
|
1244
|
+
}
|
|
1245
|
+
try {
|
|
1246
|
+
await client.execute({
|
|
1247
|
+
sql: `ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'`,
|
|
1248
|
+
args: []
|
|
1249
|
+
});
|
|
1250
|
+
} catch {
|
|
1251
|
+
}
|
|
1252
|
+
try {
|
|
1253
|
+
await client.execute(
|
|
1254
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_type ON memories(memory_type)`
|
|
1255
|
+
);
|
|
1256
|
+
} catch {
|
|
1257
|
+
}
|
|
1258
|
+
try {
|
|
1259
|
+
await client.execute({
|
|
1260
|
+
sql: `ALTER TABLE memories ADD COLUMN trajectory TEXT`,
|
|
1261
|
+
args: []
|
|
1262
|
+
});
|
|
1263
|
+
} catch {
|
|
1264
|
+
}
|
|
1180
1265
|
}
|
|
1181
1266
|
async function disposeDatabase() {
|
|
1182
1267
|
if (_client) {
|
|
@@ -1190,6 +1275,7 @@ var init_database = __esm({
|
|
|
1190
1275
|
"src/lib/database.ts"() {
|
|
1191
1276
|
"use strict";
|
|
1192
1277
|
init_db_retry();
|
|
1278
|
+
init_employees();
|
|
1193
1279
|
_client = null;
|
|
1194
1280
|
_resilientClient = null;
|
|
1195
1281
|
initTurso = initDatabase;
|
|
@@ -1198,15 +1284,15 @@ var init_database = __esm({
|
|
|
1198
1284
|
});
|
|
1199
1285
|
|
|
1200
1286
|
// src/lib/keychain.ts
|
|
1201
|
-
import { readFile as
|
|
1202
|
-
import { existsSync as
|
|
1203
|
-
import
|
|
1204
|
-
import
|
|
1287
|
+
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
1288
|
+
import { existsSync as existsSync3 } from "fs";
|
|
1289
|
+
import path3 from "path";
|
|
1290
|
+
import os3 from "os";
|
|
1205
1291
|
function getKeyDir() {
|
|
1206
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
1292
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path3.join(os3.homedir(), ".exe-os");
|
|
1207
1293
|
}
|
|
1208
1294
|
function getKeyPath() {
|
|
1209
|
-
return
|
|
1295
|
+
return path3.join(getKeyDir(), "master.key");
|
|
1210
1296
|
}
|
|
1211
1297
|
async function tryKeytar() {
|
|
1212
1298
|
try {
|
|
@@ -1227,11 +1313,11 @@ async function getMasterKey() {
|
|
|
1227
1313
|
}
|
|
1228
1314
|
}
|
|
1229
1315
|
const keyPath = getKeyPath();
|
|
1230
|
-
if (!
|
|
1316
|
+
if (!existsSync3(keyPath)) {
|
|
1231
1317
|
return null;
|
|
1232
1318
|
}
|
|
1233
1319
|
try {
|
|
1234
|
-
const content = await
|
|
1320
|
+
const content = await readFile3(keyPath, "utf-8");
|
|
1235
1321
|
return Buffer.from(content.trim(), "base64");
|
|
1236
1322
|
} catch {
|
|
1237
1323
|
return null;
|
|
@@ -1314,12 +1400,12 @@ __export(shard_manager_exports, {
|
|
|
1314
1400
|
listShards: () => listShards,
|
|
1315
1401
|
shardExists: () => shardExists
|
|
1316
1402
|
});
|
|
1317
|
-
import
|
|
1318
|
-
import { existsSync as
|
|
1403
|
+
import path4 from "path";
|
|
1404
|
+
import { existsSync as existsSync4, mkdirSync, readdirSync } from "fs";
|
|
1319
1405
|
import { createClient as createClient2 } from "@libsql/client";
|
|
1320
1406
|
function initShardManager(encryptionKey) {
|
|
1321
1407
|
_encryptionKey = encryptionKey;
|
|
1322
|
-
if (!
|
|
1408
|
+
if (!existsSync4(SHARDS_DIR)) {
|
|
1323
1409
|
mkdirSync(SHARDS_DIR, { recursive: true });
|
|
1324
1410
|
}
|
|
1325
1411
|
_shardingEnabled = true;
|
|
@@ -1340,7 +1426,7 @@ function getShardClient(projectName) {
|
|
|
1340
1426
|
}
|
|
1341
1427
|
const cached = _shards.get(safeName);
|
|
1342
1428
|
if (cached) return cached;
|
|
1343
|
-
const dbPath =
|
|
1429
|
+
const dbPath = path4.join(SHARDS_DIR, `${safeName}.db`);
|
|
1344
1430
|
const client = createClient2({
|
|
1345
1431
|
url: `file:${dbPath}`,
|
|
1346
1432
|
encryptionKey: _encryptionKey
|
|
@@ -1350,10 +1436,10 @@ function getShardClient(projectName) {
|
|
|
1350
1436
|
}
|
|
1351
1437
|
function shardExists(projectName) {
|
|
1352
1438
|
const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
1353
|
-
return
|
|
1439
|
+
return existsSync4(path4.join(SHARDS_DIR, `${safeName}.db`));
|
|
1354
1440
|
}
|
|
1355
1441
|
function listShards() {
|
|
1356
|
-
if (!
|
|
1442
|
+
if (!existsSync4(SHARDS_DIR)) return [];
|
|
1357
1443
|
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
1358
1444
|
}
|
|
1359
1445
|
async function ensureShardSchema(client) {
|
|
@@ -1423,7 +1509,11 @@ async function ensureShardSchema(client) {
|
|
|
1423
1509
|
"ALTER TABLE memories ADD COLUMN source_path TEXT",
|
|
1424
1510
|
"ALTER TABLE memories ADD COLUMN source_type TEXT DEFAULT 'text'",
|
|
1425
1511
|
"ALTER TABLE memories ADD COLUMN tier INTEGER DEFAULT 3",
|
|
1426
|
-
"ALTER TABLE memories ADD COLUMN supersedes_id TEXT"
|
|
1512
|
+
"ALTER TABLE memories ADD COLUMN supersedes_id TEXT",
|
|
1513
|
+
// MS-11: draft staging, MS-6a: memory_type, MS-7: trajectory
|
|
1514
|
+
"ALTER TABLE memories ADD COLUMN draft INTEGER DEFAULT 0",
|
|
1515
|
+
"ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'",
|
|
1516
|
+
"ALTER TABLE memories ADD COLUMN trajectory TEXT"
|
|
1427
1517
|
]) {
|
|
1428
1518
|
try {
|
|
1429
1519
|
await client.execute(col);
|
|
@@ -1535,7 +1625,7 @@ var init_shard_manager = __esm({
|
|
|
1535
1625
|
"src/lib/shard-manager.ts"() {
|
|
1536
1626
|
"use strict";
|
|
1537
1627
|
init_config();
|
|
1538
|
-
SHARDS_DIR =
|
|
1628
|
+
SHARDS_DIR = path4.join(EXE_AI_DIR, "shards");
|
|
1539
1629
|
_shards = /* @__PURE__ */ new Map();
|
|
1540
1630
|
_encryptionKey = null;
|
|
1541
1631
|
_shardingEnabled = false;
|
|
@@ -1553,26 +1643,26 @@ var init_platform_procedures = __esm({
|
|
|
1553
1643
|
title: "What is exe-os \u2014 the operating model every agent must understand",
|
|
1554
1644
|
domain: "architecture",
|
|
1555
1645
|
priority: "p0",
|
|
1556
|
-
content: "Exe OS is an AI employee operating system. A founder runs 5-10 AI agents as a real org: COO
|
|
1646
|
+
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."
|
|
1557
1647
|
},
|
|
1558
1648
|
{
|
|
1559
1649
|
title: "Mode 1 \u2014 how exe-os runs inside Claude Code",
|
|
1560
1650
|
domain: "architecture",
|
|
1561
1651
|
priority: "p0",
|
|
1562
|
-
content: "Mode 1: exe-os runs AS hooks + MCP + skills inside Claude Code. The founder opens CC
|
|
1652
|
+
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."
|
|
1563
1653
|
},
|
|
1564
1654
|
{
|
|
1565
|
-
title: "Sessions explained \u2014
|
|
1655
|
+
title: "Sessions explained \u2014 coordinator session names and projects",
|
|
1566
1656
|
domain: "architecture",
|
|
1567
1657
|
priority: "p0",
|
|
1568
|
-
content: "Each
|
|
1658
|
+
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."
|
|
1569
1659
|
},
|
|
1570
1660
|
// --- Hierarchy and dispatch ---
|
|
1571
1661
|
{
|
|
1572
1662
|
title: "Chain of command \u2014 who talks to whom",
|
|
1573
1663
|
domain: "workflow",
|
|
1574
1664
|
priority: "p0",
|
|
1575
|
-
content: "Founder
|
|
1665
|
+
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."
|
|
1576
1666
|
},
|
|
1577
1667
|
{
|
|
1578
1668
|
title: "Single dispatch path \u2014 create_task only",
|
|
@@ -1582,30 +1672,30 @@ var init_platform_procedures = __esm({
|
|
|
1582
1672
|
},
|
|
1583
1673
|
// --- Session isolation ---
|
|
1584
1674
|
{
|
|
1585
|
-
title: "Session scoping \u2014 stay in your
|
|
1675
|
+
title: "Session scoping \u2014 stay in your coordinator boundary",
|
|
1586
1676
|
domain: "security",
|
|
1587
1677
|
priority: "p0",
|
|
1588
|
-
content: "Session scoping is mandatory. Managers dispatch to workers within their own
|
|
1678
|
+
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."
|
|
1589
1679
|
},
|
|
1590
1680
|
{
|
|
1591
1681
|
title: "Session isolation \u2014 never touch another session's work",
|
|
1592
1682
|
domain: "workflow",
|
|
1593
1683
|
priority: "p0",
|
|
1594
|
-
content:
|
|
1684
|
+
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."
|
|
1595
1685
|
},
|
|
1596
1686
|
// --- Engineering: session scoping in code ---
|
|
1597
1687
|
{
|
|
1598
1688
|
title: "Three-dimensional scoping \u2014 session, project, role \u2014 enforced in every query",
|
|
1599
1689
|
domain: "architecture",
|
|
1600
1690
|
priority: "p0",
|
|
1601
|
-
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
|
|
1691
|
+
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."
|
|
1602
1692
|
},
|
|
1603
1693
|
// --- Hard constraints ---
|
|
1604
1694
|
{
|
|
1605
1695
|
title: "What you CANNOT do in exe-os \u2014 hard constraints",
|
|
1606
1696
|
domain: "security",
|
|
1607
1697
|
priority: "p0",
|
|
1608
|
-
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
|
|
1698
|
+
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."
|
|
1609
1699
|
},
|
|
1610
1700
|
// --- Operations ---
|
|
1611
1701
|
{
|
|
@@ -1845,7 +1935,10 @@ async function writeMemory(record) {
|
|
|
1845
1935
|
source_path: record.source_path ?? null,
|
|
1846
1936
|
source_type: record.source_type ?? null,
|
|
1847
1937
|
tier: record.tier ?? classifyTier(record),
|
|
1848
|
-
supersedes_id: record.supersedes_id ?? null
|
|
1938
|
+
supersedes_id: record.supersedes_id ?? null,
|
|
1939
|
+
draft: record.draft ? 1 : 0,
|
|
1940
|
+
memory_type: record.memory_type ?? "raw",
|
|
1941
|
+
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null
|
|
1849
1942
|
};
|
|
1850
1943
|
_pendingRecords.push(dbRow);
|
|
1851
1944
|
orgBus.emit({
|
|
@@ -1900,6 +1993,9 @@ async function flushBatch() {
|
|
|
1900
1993
|
const sourceType = row.source_type ?? null;
|
|
1901
1994
|
const tier = row.tier ?? 3;
|
|
1902
1995
|
const supersedesId = row.supersedes_id ?? null;
|
|
1996
|
+
const draft = row.draft ? 1 : 0;
|
|
1997
|
+
const memoryType = row.memory_type ?? "raw";
|
|
1998
|
+
const trajectory = row.trajectory ?? null;
|
|
1903
1999
|
return {
|
|
1904
2000
|
sql: hasVector ? `INSERT OR IGNORE INTO memories
|
|
1905
2001
|
(id, agent_id, agent_role, session_id, timestamp,
|
|
@@ -1907,15 +2003,15 @@ async function flushBatch() {
|
|
|
1907
2003
|
has_error, raw_text, vector, version, task_id, importance, status,
|
|
1908
2004
|
confidence, last_accessed,
|
|
1909
2005
|
workspace_id, document_id, user_id, char_offset, page_number,
|
|
1910
|
-
source_path, source_type, tier, supersedes_id)
|
|
1911
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories
|
|
2006
|
+
source_path, source_type, tier, supersedes_id, draft, memory_type, trajectory)
|
|
2007
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories
|
|
1912
2008
|
(id, agent_id, agent_role, session_id, timestamp,
|
|
1913
2009
|
tool_name, project_name,
|
|
1914
2010
|
has_error, raw_text, vector, version, task_id, importance, status,
|
|
1915
2011
|
confidence, last_accessed,
|
|
1916
2012
|
workspace_id, document_id, user_id, char_offset, page_number,
|
|
1917
|
-
source_path, source_type, tier, supersedes_id)
|
|
1918
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
2013
|
+
source_path, source_type, tier, supersedes_id, draft, memory_type, trajectory)
|
|
2014
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
1919
2015
|
args: hasVector ? [
|
|
1920
2016
|
row.id,
|
|
1921
2017
|
row.agent_id,
|
|
@@ -1941,7 +2037,10 @@ async function flushBatch() {
|
|
|
1941
2037
|
sourcePath,
|
|
1942
2038
|
sourceType,
|
|
1943
2039
|
tier,
|
|
1944
|
-
supersedesId
|
|
2040
|
+
supersedesId,
|
|
2041
|
+
draft,
|
|
2042
|
+
memoryType,
|
|
2043
|
+
trajectory
|
|
1945
2044
|
] : [
|
|
1946
2045
|
row.id,
|
|
1947
2046
|
row.agent_id,
|
|
@@ -1966,7 +2065,10 @@ async function flushBatch() {
|
|
|
1966
2065
|
sourcePath,
|
|
1967
2066
|
sourceType,
|
|
1968
2067
|
tier,
|
|
1969
|
-
supersedesId
|
|
2068
|
+
supersedesId,
|
|
2069
|
+
draft,
|
|
2070
|
+
memoryType,
|
|
2071
|
+
trajectory
|
|
1970
2072
|
]
|
|
1971
2073
|
};
|
|
1972
2074
|
};
|
|
@@ -2035,6 +2137,8 @@ async function searchMemories(queryVector, agentId, options) {
|
|
|
2035
2137
|
const limit = options?.limit ?? 10;
|
|
2036
2138
|
const statusFilter = options?.includeArchived ? "" : `
|
|
2037
2139
|
AND COALESCE(status, 'active') = 'active'`;
|
|
2140
|
+
const draftFilter = options?.includeDrafts ? "" : `
|
|
2141
|
+
AND (draft = 0 OR draft IS NULL)`;
|
|
2038
2142
|
let sql = `SELECT id, agent_id, agent_role, session_id, timestamp,
|
|
2039
2143
|
tool_name, project_name,
|
|
2040
2144
|
has_error, raw_text, vector, importance, status,
|
|
@@ -2044,7 +2148,7 @@ async function searchMemories(queryVector, agentId, options) {
|
|
|
2044
2148
|
source_path, source_type
|
|
2045
2149
|
FROM memories
|
|
2046
2150
|
WHERE agent_id = ?
|
|
2047
|
-
AND vector IS NOT NULL${statusFilter}
|
|
2151
|
+
AND vector IS NOT NULL${statusFilter}${draftFilter}
|
|
2048
2152
|
AND COALESCE(confidence, 0.7) >= 0.3`;
|
|
2049
2153
|
const args = [agentId];
|
|
2050
2154
|
const scope = buildWikiScopeFilter(options, "");
|
|
@@ -2066,6 +2170,10 @@ async function searchMemories(queryVector, agentId, options) {
|
|
|
2066
2170
|
sql += ` AND timestamp >= ?`;
|
|
2067
2171
|
args.push(options.since);
|
|
2068
2172
|
}
|
|
2173
|
+
if (options?.memoryType) {
|
|
2174
|
+
sql += ` AND memory_type = ?`;
|
|
2175
|
+
args.push(options.memoryType);
|
|
2176
|
+
}
|
|
2069
2177
|
sql += ` ORDER BY vector_distance_cos(vector, vector32(?))`;
|
|
2070
2178
|
args.push(vectorToBlob(queryVector));
|
|
2071
2179
|
sql += ` LIMIT ?`;
|
|
@@ -2291,7 +2399,7 @@ var init_self_query_router = __esm({
|
|
|
2291
2399
|
},
|
|
2292
2400
|
is_broad_query: {
|
|
2293
2401
|
type: "boolean",
|
|
2294
|
-
description: "True if the query is exploratory/broad (e.g., 'what has
|
|
2402
|
+
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')."
|
|
2295
2403
|
}
|
|
2296
2404
|
},
|
|
2297
2405
|
required: ["semantic_query", "project_filter", "role_filter", "time_filter", "is_broad_query"]
|
|
@@ -2304,8 +2412,8 @@ var init_self_query_router = __esm({
|
|
|
2304
2412
|
import net from "net";
|
|
2305
2413
|
import { spawn } from "child_process";
|
|
2306
2414
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
2307
|
-
import { existsSync as
|
|
2308
|
-
import
|
|
2415
|
+
import { existsSync as existsSync5, unlinkSync as unlinkSync2, readFileSync as readFileSync3, openSync, closeSync, statSync } from "fs";
|
|
2416
|
+
import path5 from "path";
|
|
2309
2417
|
import { fileURLToPath } from "url";
|
|
2310
2418
|
function handleData(chunk) {
|
|
2311
2419
|
_buffer += chunk.toString();
|
|
@@ -2331,9 +2439,9 @@ function handleData(chunk) {
|
|
|
2331
2439
|
}
|
|
2332
2440
|
}
|
|
2333
2441
|
function cleanupStaleFiles() {
|
|
2334
|
-
if (
|
|
2442
|
+
if (existsSync5(PID_PATH)) {
|
|
2335
2443
|
try {
|
|
2336
|
-
const pid = parseInt(
|
|
2444
|
+
const pid = parseInt(readFileSync3(PID_PATH, "utf8").trim(), 10);
|
|
2337
2445
|
if (pid > 0) {
|
|
2338
2446
|
try {
|
|
2339
2447
|
process.kill(pid, 0);
|
|
@@ -2344,21 +2452,21 @@ function cleanupStaleFiles() {
|
|
|
2344
2452
|
} catch {
|
|
2345
2453
|
}
|
|
2346
2454
|
try {
|
|
2347
|
-
|
|
2455
|
+
unlinkSync2(PID_PATH);
|
|
2348
2456
|
} catch {
|
|
2349
2457
|
}
|
|
2350
2458
|
try {
|
|
2351
|
-
|
|
2459
|
+
unlinkSync2(SOCKET_PATH);
|
|
2352
2460
|
} catch {
|
|
2353
2461
|
}
|
|
2354
2462
|
}
|
|
2355
2463
|
}
|
|
2356
2464
|
function findPackageRoot() {
|
|
2357
|
-
let dir =
|
|
2358
|
-
const { root } =
|
|
2465
|
+
let dir = path5.dirname(fileURLToPath(import.meta.url));
|
|
2466
|
+
const { root } = path5.parse(dir);
|
|
2359
2467
|
while (dir !== root) {
|
|
2360
|
-
if (
|
|
2361
|
-
dir =
|
|
2468
|
+
if (existsSync5(path5.join(dir, "package.json"))) return dir;
|
|
2469
|
+
dir = path5.dirname(dir);
|
|
2362
2470
|
}
|
|
2363
2471
|
return null;
|
|
2364
2472
|
}
|
|
@@ -2368,8 +2476,8 @@ function spawnDaemon() {
|
|
|
2368
2476
|
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
2369
2477
|
return;
|
|
2370
2478
|
}
|
|
2371
|
-
const daemonPath =
|
|
2372
|
-
if (!
|
|
2479
|
+
const daemonPath = path5.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
2480
|
+
if (!existsSync5(daemonPath)) {
|
|
2373
2481
|
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
2374
2482
|
`);
|
|
2375
2483
|
return;
|
|
@@ -2377,7 +2485,7 @@ function spawnDaemon() {
|
|
|
2377
2485
|
const resolvedPath = daemonPath;
|
|
2378
2486
|
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
2379
2487
|
`);
|
|
2380
|
-
const logPath =
|
|
2488
|
+
const logPath = path5.join(path5.dirname(SOCKET_PATH), "exed.log");
|
|
2381
2489
|
let stderrFd = "ignore";
|
|
2382
2490
|
try {
|
|
2383
2491
|
stderrFd = openSync(logPath, "a");
|
|
@@ -2388,6 +2496,10 @@ function spawnDaemon() {
|
|
|
2388
2496
|
stdio: ["ignore", "ignore", stderrFd],
|
|
2389
2497
|
env: {
|
|
2390
2498
|
...process.env,
|
|
2499
|
+
TMUX: void 0,
|
|
2500
|
+
// Daemon is global — must not inherit session scope
|
|
2501
|
+
TMUX_PANE: void 0,
|
|
2502
|
+
// Prevents resolveExeSession() from scoping to one session
|
|
2391
2503
|
EXE_DAEMON_SOCK: SOCKET_PATH,
|
|
2392
2504
|
EXE_DAEMON_PID: PID_PATH
|
|
2393
2505
|
}
|
|
@@ -2410,7 +2522,7 @@ function acquireSpawnLock() {
|
|
|
2410
2522
|
const stat = statSync(SPAWN_LOCK_PATH);
|
|
2411
2523
|
if (Date.now() - stat.mtimeMs > SPAWN_LOCK_STALE_MS) {
|
|
2412
2524
|
try {
|
|
2413
|
-
|
|
2525
|
+
unlinkSync2(SPAWN_LOCK_PATH);
|
|
2414
2526
|
} catch {
|
|
2415
2527
|
}
|
|
2416
2528
|
try {
|
|
@@ -2427,7 +2539,7 @@ function acquireSpawnLock() {
|
|
|
2427
2539
|
}
|
|
2428
2540
|
function releaseSpawnLock() {
|
|
2429
2541
|
try {
|
|
2430
|
-
|
|
2542
|
+
unlinkSync2(SPAWN_LOCK_PATH);
|
|
2431
2543
|
} catch {
|
|
2432
2544
|
}
|
|
2433
2545
|
}
|
|
@@ -2539,9 +2651,9 @@ async function pingDaemon() {
|
|
|
2539
2651
|
}
|
|
2540
2652
|
function killAndRespawnDaemon() {
|
|
2541
2653
|
process.stderr.write("[exed-client] Killing daemon for restart...\n");
|
|
2542
|
-
if (
|
|
2654
|
+
if (existsSync5(PID_PATH)) {
|
|
2543
2655
|
try {
|
|
2544
|
-
const pid = parseInt(
|
|
2656
|
+
const pid = parseInt(readFileSync3(PID_PATH, "utf8").trim(), 10);
|
|
2545
2657
|
if (pid > 0) {
|
|
2546
2658
|
try {
|
|
2547
2659
|
process.kill(pid, "SIGKILL");
|
|
@@ -2558,11 +2670,11 @@ function killAndRespawnDaemon() {
|
|
|
2558
2670
|
_connected = false;
|
|
2559
2671
|
_buffer = "";
|
|
2560
2672
|
try {
|
|
2561
|
-
|
|
2673
|
+
unlinkSync2(PID_PATH);
|
|
2562
2674
|
} catch {
|
|
2563
2675
|
}
|
|
2564
2676
|
try {
|
|
2565
|
-
|
|
2677
|
+
unlinkSync2(SOCKET_PATH);
|
|
2566
2678
|
} catch {
|
|
2567
2679
|
}
|
|
2568
2680
|
spawnDaemon();
|
|
@@ -2625,9 +2737,9 @@ var init_exe_daemon_client = __esm({
|
|
|
2625
2737
|
"src/lib/exe-daemon-client.ts"() {
|
|
2626
2738
|
"use strict";
|
|
2627
2739
|
init_config();
|
|
2628
|
-
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ??
|
|
2629
|
-
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ??
|
|
2630
|
-
SPAWN_LOCK_PATH =
|
|
2740
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path5.join(EXE_AI_DIR, "exed.sock");
|
|
2741
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path5.join(EXE_AI_DIR, "exed.pid");
|
|
2742
|
+
SPAWN_LOCK_PATH = path5.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
2631
2743
|
SPAWN_LOCK_STALE_MS = 3e4;
|
|
2632
2744
|
CONNECT_TIMEOUT_MS = 15e3;
|
|
2633
2745
|
REQUEST_TIMEOUT_MS = 3e4;
|
|
@@ -2715,34 +2827,34 @@ __export(project_name_exports, {
|
|
|
2715
2827
|
_resetCache: () => _resetCache,
|
|
2716
2828
|
getProjectName: () => getProjectName
|
|
2717
2829
|
});
|
|
2718
|
-
import { execSync } from "child_process";
|
|
2719
|
-
import
|
|
2830
|
+
import { execSync as execSync2 } from "child_process";
|
|
2831
|
+
import path6 from "path";
|
|
2720
2832
|
function getProjectName(cwd) {
|
|
2721
2833
|
const dir = cwd ?? process.cwd();
|
|
2722
2834
|
if (_cached && _cachedCwd === dir) return _cached;
|
|
2723
2835
|
try {
|
|
2724
2836
|
let repoRoot;
|
|
2725
2837
|
try {
|
|
2726
|
-
const gitCommonDir =
|
|
2838
|
+
const gitCommonDir = execSync2("git rev-parse --path-format=absolute --git-common-dir", {
|
|
2727
2839
|
cwd: dir,
|
|
2728
2840
|
encoding: "utf8",
|
|
2729
2841
|
timeout: 2e3,
|
|
2730
2842
|
stdio: ["pipe", "pipe", "pipe"]
|
|
2731
2843
|
}).trim();
|
|
2732
|
-
repoRoot =
|
|
2844
|
+
repoRoot = path6.dirname(gitCommonDir);
|
|
2733
2845
|
} catch {
|
|
2734
|
-
repoRoot =
|
|
2846
|
+
repoRoot = execSync2("git rev-parse --show-toplevel", {
|
|
2735
2847
|
cwd: dir,
|
|
2736
2848
|
encoding: "utf8",
|
|
2737
2849
|
timeout: 2e3,
|
|
2738
2850
|
stdio: ["pipe", "pipe", "pipe"]
|
|
2739
2851
|
}).trim();
|
|
2740
2852
|
}
|
|
2741
|
-
_cached =
|
|
2853
|
+
_cached = path6.basename(repoRoot);
|
|
2742
2854
|
_cachedCwd = dir;
|
|
2743
2855
|
return _cached;
|
|
2744
2856
|
} catch {
|
|
2745
|
-
_cached =
|
|
2857
|
+
_cached = path6.basename(dir);
|
|
2746
2858
|
_cachedCwd = dir;
|
|
2747
2859
|
return _cached;
|
|
2748
2860
|
}
|
|
@@ -2765,14 +2877,14 @@ var file_grep_exports = {};
|
|
|
2765
2877
|
__export(file_grep_exports, {
|
|
2766
2878
|
grepProjectFiles: () => grepProjectFiles
|
|
2767
2879
|
});
|
|
2768
|
-
import { execSync as
|
|
2769
|
-
import { readFileSync as
|
|
2770
|
-
import
|
|
2880
|
+
import { execSync as execSync3 } from "child_process";
|
|
2881
|
+
import { readFileSync as readFileSync4, readdirSync as readdirSync2, statSync as statSync2, existsSync as existsSync6 } from "fs";
|
|
2882
|
+
import path7 from "path";
|
|
2771
2883
|
import crypto from "crypto";
|
|
2772
2884
|
function hasRipgrep() {
|
|
2773
2885
|
if (_hasRg === null) {
|
|
2774
2886
|
try {
|
|
2775
|
-
|
|
2887
|
+
execSync3("rg --version", { stdio: "ignore", timeout: 2e3 });
|
|
2776
2888
|
_hasRg = true;
|
|
2777
2889
|
} catch {
|
|
2778
2890
|
_hasRg = false;
|
|
@@ -2807,7 +2919,7 @@ async function grepProjectFiles(query, projectRoot, options) {
|
|
|
2807
2919
|
session_id: "file-grep",
|
|
2808
2920
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2809
2921
|
tool_name: "file_grep",
|
|
2810
|
-
project_name:
|
|
2922
|
+
project_name: path7.basename(projectRoot),
|
|
2811
2923
|
has_error: false,
|
|
2812
2924
|
raw_text: `${prefix} ${buildSnippet(hit, projectRoot)}`,
|
|
2813
2925
|
vector: null,
|
|
@@ -2819,7 +2931,7 @@ function getChunkContext(filePath, lineNumber) {
|
|
|
2819
2931
|
try {
|
|
2820
2932
|
const ext = filePath.split(".").pop()?.toLowerCase();
|
|
2821
2933
|
if (ext !== "ts" && ext !== "tsx" && ext !== "js" && ext !== "jsx") return "";
|
|
2822
|
-
const source =
|
|
2934
|
+
const source = readFileSync4(filePath, "utf8");
|
|
2823
2935
|
const lines = source.split("\n");
|
|
2824
2936
|
for (let i = Math.min(lineNumber - 1, lines.length - 1); i >= 0; i--) {
|
|
2825
2937
|
const line = lines[i];
|
|
@@ -2838,7 +2950,7 @@ function grepWithRipgrep(pattern, projectRoot, patterns) {
|
|
|
2838
2950
|
const globs = (patterns ?? DEFAULT_PATTERNS).map((p) => `--glob '${p}'`).join(" ");
|
|
2839
2951
|
const excludes = EXCLUDE_DIRS.map((d) => `--glob '!${d}'`).join(" ");
|
|
2840
2952
|
const cmd = `rg -i -c --hidden --no-config --no-ignore '${pattern.replace(/'/g, "\\'")}' . ${globs} ${excludes} --max-filesize ${MAX_FILE_SIZE} 2>/dev/null || true`;
|
|
2841
|
-
const output =
|
|
2953
|
+
const output = execSync3(cmd, {
|
|
2842
2954
|
cwd: projectRoot,
|
|
2843
2955
|
encoding: "utf8",
|
|
2844
2956
|
timeout: 3e3,
|
|
@@ -2853,12 +2965,12 @@ function grepWithRipgrep(pattern, projectRoot, patterns) {
|
|
|
2853
2965
|
const matchCount = parseInt(line.slice(colonIdx + 1));
|
|
2854
2966
|
if (isNaN(matchCount) || matchCount === 0) continue;
|
|
2855
2967
|
try {
|
|
2856
|
-
const firstMatch =
|
|
2968
|
+
const firstMatch = execSync3(
|
|
2857
2969
|
`rg -i -n --hidden '${pattern.replace(/'/g, "\\'")}' '${filePath}' --max-count 1 2>/dev/null | head -1`,
|
|
2858
2970
|
{ cwd: projectRoot, encoding: "utf8", timeout: 1e3 }
|
|
2859
2971
|
).trim();
|
|
2860
2972
|
const lineNum = parseInt(firstMatch.split(":")[0] ?? "1");
|
|
2861
|
-
const totalLines =
|
|
2973
|
+
const totalLines = execSync3(`wc -l < '${filePath}'`, {
|
|
2862
2974
|
cwd: projectRoot,
|
|
2863
2975
|
encoding: "utf8",
|
|
2864
2976
|
timeout: 1e3
|
|
@@ -2881,11 +2993,11 @@ function grepWithNodeFs(pattern, projectRoot, patterns) {
|
|
|
2881
2993
|
const files = collectFiles(projectRoot, patterns ?? DEFAULT_PATTERNS);
|
|
2882
2994
|
const hits = [];
|
|
2883
2995
|
for (const filePath of files.slice(0, MAX_FILES)) {
|
|
2884
|
-
const absPath =
|
|
2996
|
+
const absPath = path7.join(projectRoot, filePath);
|
|
2885
2997
|
try {
|
|
2886
2998
|
const stat = statSync2(absPath);
|
|
2887
2999
|
if (stat.size > MAX_FILE_SIZE) continue;
|
|
2888
|
-
const content =
|
|
3000
|
+
const content = readFileSync4(absPath, "utf8");
|
|
2889
3001
|
const lines = content.split("\n");
|
|
2890
3002
|
const matches = content.match(regex);
|
|
2891
3003
|
if (!matches || matches.length === 0) continue;
|
|
@@ -2908,15 +3020,15 @@ function collectFiles(root, patterns) {
|
|
|
2908
3020
|
const files = [];
|
|
2909
3021
|
function walk(dir, relative) {
|
|
2910
3022
|
if (files.length >= MAX_FILES) return;
|
|
2911
|
-
const basename =
|
|
3023
|
+
const basename = path7.basename(dir);
|
|
2912
3024
|
if (EXCLUDE_DIRS.includes(basename)) return;
|
|
2913
3025
|
try {
|
|
2914
3026
|
const entries = readdirSync2(dir, { withFileTypes: true });
|
|
2915
3027
|
for (const entry of entries) {
|
|
2916
3028
|
if (files.length >= MAX_FILES) return;
|
|
2917
|
-
const rel =
|
|
3029
|
+
const rel = path7.join(relative, entry.name);
|
|
2918
3030
|
if (entry.isDirectory()) {
|
|
2919
|
-
walk(
|
|
3031
|
+
walk(path7.join(dir, entry.name), rel);
|
|
2920
3032
|
} else if (entry.isFile()) {
|
|
2921
3033
|
for (const pat of patterns) {
|
|
2922
3034
|
if (matchGlob(rel, pat)) {
|
|
@@ -2948,7 +3060,7 @@ function matchGlob(filePath, pattern) {
|
|
|
2948
3060
|
if (slashIdx !== -1) {
|
|
2949
3061
|
const dir = pattern.slice(0, slashIdx);
|
|
2950
3062
|
const ext2 = pattern.slice(slashIdx + 1).replace("*", "");
|
|
2951
|
-
const fileDir =
|
|
3063
|
+
const fileDir = path7.dirname(filePath);
|
|
2952
3064
|
return fileDir === dir && filePath.endsWith(ext2);
|
|
2953
3065
|
}
|
|
2954
3066
|
const ext = pattern.replace("*", "");
|
|
@@ -2956,9 +3068,9 @@ function matchGlob(filePath, pattern) {
|
|
|
2956
3068
|
}
|
|
2957
3069
|
function buildSnippet(hit, projectRoot) {
|
|
2958
3070
|
try {
|
|
2959
|
-
const absPath =
|
|
2960
|
-
if (!
|
|
2961
|
-
const lines =
|
|
3071
|
+
const absPath = path7.join(projectRoot, hit.filePath);
|
|
3072
|
+
if (!existsSync6(absPath)) return hit.matchLine;
|
|
3073
|
+
const lines = readFileSync4(absPath, "utf8").split("\n");
|
|
2962
3074
|
const start = Math.max(0, hit.lineNumber - 3);
|
|
2963
3075
|
const end = Math.min(lines.length, hit.lineNumber + 2);
|
|
2964
3076
|
return lines.slice(start, end).join("\n").slice(0, 500);
|
|
@@ -2992,8 +3104,8 @@ __export(reranker_exports, {
|
|
|
2992
3104
|
rerank: () => rerank,
|
|
2993
3105
|
rerankWithScores: () => rerankWithScores
|
|
2994
3106
|
});
|
|
2995
|
-
import
|
|
2996
|
-
import { existsSync as
|
|
3107
|
+
import path8 from "path";
|
|
3108
|
+
import { existsSync as existsSync7 } from "fs";
|
|
2997
3109
|
function resetIdleTimer() {
|
|
2998
3110
|
if (_idleTimer) clearTimeout(_idleTimer);
|
|
2999
3111
|
_idleTimer = setTimeout(() => {
|
|
@@ -3004,18 +3116,18 @@ function resetIdleTimer() {
|
|
|
3004
3116
|
}
|
|
3005
3117
|
}
|
|
3006
3118
|
function isRerankerAvailable() {
|
|
3007
|
-
return
|
|
3119
|
+
return existsSync7(path8.join(MODELS_DIR, RERANKER_MODEL_FILE));
|
|
3008
3120
|
}
|
|
3009
3121
|
function getRerankerModelPath() {
|
|
3010
|
-
return
|
|
3122
|
+
return path8.join(MODELS_DIR, RERANKER_MODEL_FILE);
|
|
3011
3123
|
}
|
|
3012
3124
|
async function ensureLoaded() {
|
|
3013
3125
|
if (_rerankerContext) {
|
|
3014
3126
|
resetIdleTimer();
|
|
3015
3127
|
return;
|
|
3016
3128
|
}
|
|
3017
|
-
const modelPath =
|
|
3018
|
-
if (!
|
|
3129
|
+
const modelPath = path8.join(MODELS_DIR, RERANKER_MODEL_FILE);
|
|
3130
|
+
if (!existsSync7(modelPath)) {
|
|
3019
3131
|
throw new Error(
|
|
3020
3132
|
`Reranker model not found at ${modelPath}. Run /exe-setup to download it.`
|
|
3021
3133
|
);
|
|
@@ -3092,13 +3204,13 @@ var init_reranker = __esm({
|
|
|
3092
3204
|
});
|
|
3093
3205
|
|
|
3094
3206
|
// src/lib/session-key.ts
|
|
3095
|
-
import { execSync as
|
|
3207
|
+
import { execSync as execSync4 } from "child_process";
|
|
3096
3208
|
function getSessionKey() {
|
|
3097
3209
|
if (_cached2) return _cached2;
|
|
3098
3210
|
let pid = process.ppid;
|
|
3099
3211
|
for (let i = 0; i < 10; i++) {
|
|
3100
3212
|
try {
|
|
3101
|
-
const info =
|
|
3213
|
+
const info = execSync4(`ps -p ${pid} -o ppid=,comm=`, {
|
|
3102
3214
|
encoding: "utf8",
|
|
3103
3215
|
timeout: 2e3
|
|
3104
3216
|
}).trim();
|
|
@@ -3127,13 +3239,13 @@ var init_session_key = __esm({
|
|
|
3127
3239
|
});
|
|
3128
3240
|
|
|
3129
3241
|
// src/lib/session-registry.ts
|
|
3130
|
-
import
|
|
3131
|
-
import
|
|
3242
|
+
import path10 from "path";
|
|
3243
|
+
import os4 from "os";
|
|
3132
3244
|
var REGISTRY_PATH;
|
|
3133
3245
|
var init_session_registry = __esm({
|
|
3134
3246
|
"src/lib/session-registry.ts"() {
|
|
3135
3247
|
"use strict";
|
|
3136
|
-
REGISTRY_PATH =
|
|
3248
|
+
REGISTRY_PATH = path10.join(os4.homedir(), ".exe-os", "session-registry.json");
|
|
3137
3249
|
}
|
|
3138
3250
|
});
|
|
3139
3251
|
|
|
@@ -3253,7 +3365,7 @@ var init_transport = __esm({
|
|
|
3253
3365
|
});
|
|
3254
3366
|
|
|
3255
3367
|
// src/lib/cc-agent-support.ts
|
|
3256
|
-
import { execSync as
|
|
3368
|
+
import { execSync as execSync6 } from "child_process";
|
|
3257
3369
|
var init_cc_agent_support = __esm({
|
|
3258
3370
|
"src/lib/cc-agent-support.ts"() {
|
|
3259
3371
|
"use strict";
|
|
@@ -3289,17 +3401,17 @@ __export(intercom_queue_exports, {
|
|
|
3289
3401
|
queueIntercom: () => queueIntercom,
|
|
3290
3402
|
readQueue: () => readQueue
|
|
3291
3403
|
});
|
|
3292
|
-
import { readFileSync as
|
|
3293
|
-
import
|
|
3294
|
-
import
|
|
3404
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync3, renameSync as renameSync3, existsSync as existsSync8, mkdirSync as mkdirSync3 } from "fs";
|
|
3405
|
+
import path11 from "path";
|
|
3406
|
+
import os5 from "os";
|
|
3295
3407
|
function ensureDir() {
|
|
3296
|
-
const dir =
|
|
3297
|
-
if (!
|
|
3408
|
+
const dir = path11.dirname(QUEUE_PATH);
|
|
3409
|
+
if (!existsSync8(dir)) mkdirSync3(dir, { recursive: true });
|
|
3298
3410
|
}
|
|
3299
3411
|
function readQueue() {
|
|
3300
3412
|
try {
|
|
3301
|
-
if (!
|
|
3302
|
-
return JSON.parse(
|
|
3413
|
+
if (!existsSync8(QUEUE_PATH)) return [];
|
|
3414
|
+
return JSON.parse(readFileSync6(QUEUE_PATH, "utf8"));
|
|
3303
3415
|
} catch {
|
|
3304
3416
|
return [];
|
|
3305
3417
|
}
|
|
@@ -3307,8 +3419,8 @@ function readQueue() {
|
|
|
3307
3419
|
function writeQueue(queue) {
|
|
3308
3420
|
ensureDir();
|
|
3309
3421
|
const tmp = `${QUEUE_PATH}.tmp`;
|
|
3310
|
-
|
|
3311
|
-
|
|
3422
|
+
writeFileSync3(tmp, JSON.stringify(queue, null, 2));
|
|
3423
|
+
renameSync3(tmp, QUEUE_PATH);
|
|
3312
3424
|
}
|
|
3313
3425
|
function queueIntercom(targetSession, reason) {
|
|
3314
3426
|
const queue = readQueue();
|
|
@@ -3390,39 +3502,10 @@ var QUEUE_PATH, MAX_RETRIES2, TTL_MS, INTERCOM_LOG;
|
|
|
3390
3502
|
var init_intercom_queue = __esm({
|
|
3391
3503
|
"src/lib/intercom-queue.ts"() {
|
|
3392
3504
|
"use strict";
|
|
3393
|
-
QUEUE_PATH =
|
|
3505
|
+
QUEUE_PATH = path11.join(os5.homedir(), ".exe-os", "intercom-queue.json");
|
|
3394
3506
|
MAX_RETRIES2 = 5;
|
|
3395
3507
|
TTL_MS = 60 * 60 * 1e3;
|
|
3396
|
-
INTERCOM_LOG =
|
|
3397
|
-
}
|
|
3398
|
-
});
|
|
3399
|
-
|
|
3400
|
-
// src/lib/employees.ts
|
|
3401
|
-
import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
|
|
3402
|
-
import { existsSync as existsSync8, symlinkSync, readlinkSync, readFileSync as readFileSync6, renameSync as renameSync3, unlinkSync as unlinkSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
3403
|
-
import { execSync as execSync6 } from "child_process";
|
|
3404
|
-
import path11 from "path";
|
|
3405
|
-
import os5 from "os";
|
|
3406
|
-
async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
3407
|
-
if (!existsSync8(employeesPath)) {
|
|
3408
|
-
return [];
|
|
3409
|
-
}
|
|
3410
|
-
const raw = await readFile3(employeesPath, "utf-8");
|
|
3411
|
-
try {
|
|
3412
|
-
return JSON.parse(raw);
|
|
3413
|
-
} catch {
|
|
3414
|
-
return [];
|
|
3415
|
-
}
|
|
3416
|
-
}
|
|
3417
|
-
function getEmployee(employees, name) {
|
|
3418
|
-
return employees.find((e) => e.name.toLowerCase() === name.toLowerCase());
|
|
3419
|
-
}
|
|
3420
|
-
var EMPLOYEES_PATH;
|
|
3421
|
-
var init_employees = __esm({
|
|
3422
|
-
"src/lib/employees.ts"() {
|
|
3423
|
-
"use strict";
|
|
3424
|
-
init_config();
|
|
3425
|
-
EMPLOYEES_PATH = path11.join(EXE_AI_DIR, "exe-employees.json");
|
|
3508
|
+
INTERCOM_LOG = path11.join(os5.homedir(), ".exe-os", "intercom.log");
|
|
3426
3509
|
}
|
|
3427
3510
|
});
|
|
3428
3511
|
|
|
@@ -3479,7 +3562,7 @@ function employeeSessionName(employee, exeSession, instance) {
|
|
|
3479
3562
|
exeSession = root;
|
|
3480
3563
|
} else {
|
|
3481
3564
|
throw new Error(
|
|
3482
|
-
`Invalid
|
|
3565
|
+
`Invalid coordinator session "${exeSession}" \u2014 contains a dash but no recognizable root session. Pass a root session name.`
|
|
3483
3566
|
);
|
|
3484
3567
|
}
|
|
3485
3568
|
}
|
|
@@ -3493,8 +3576,10 @@ function employeeSessionName(employee, exeSession, instance) {
|
|
|
3493
3576
|
return name;
|
|
3494
3577
|
}
|
|
3495
3578
|
function extractRootExe(name) {
|
|
3496
|
-
|
|
3497
|
-
|
|
3579
|
+
if (!name) return null;
|
|
3580
|
+
if (!name.includes("-")) return name;
|
|
3581
|
+
const parts = name.split("-").filter(Boolean);
|
|
3582
|
+
return parts.length > 0 ? parts[parts.length - 1] : null;
|
|
3498
3583
|
}
|
|
3499
3584
|
function getParentExe(sessionKey) {
|
|
3500
3585
|
try {
|
|
@@ -3577,12 +3662,14 @@ function getSessionState(sessionName) {
|
|
|
3577
3662
|
}
|
|
3578
3663
|
}
|
|
3579
3664
|
function isExeSession(sessionName) {
|
|
3580
|
-
|
|
3665
|
+
const matchesBaseWithInstance = (baseName) => sessionName === baseName || sessionName.startsWith(baseName) && /^\d+$/.test(sessionName.slice(baseName.length));
|
|
3666
|
+
const coordinatorName = getCoordinatorName();
|
|
3667
|
+
return matchesBaseWithInstance(coordinatorName) || matchesBaseWithInstance("exe");
|
|
3581
3668
|
}
|
|
3582
3669
|
function sendIntercom(targetSession) {
|
|
3583
3670
|
const transport = getTransport();
|
|
3584
3671
|
if (isExeSession(targetSession)) {
|
|
3585
|
-
logIntercom(`
|
|
3672
|
+
logIntercom(`SKIP_COORDINATOR \u2192 ${targetSession} (coordinator sessions use prompt-submit hook)`);
|
|
3586
3673
|
return "skipped_exe";
|
|
3587
3674
|
}
|
|
3588
3675
|
if (isDebounced(targetSession)) {
|
|
@@ -3633,6 +3720,7 @@ var init_tmux_routing = __esm({
|
|
|
3633
3720
|
init_provider_table();
|
|
3634
3721
|
init_intercom_queue();
|
|
3635
3722
|
init_plan_limits();
|
|
3723
|
+
init_employees();
|
|
3636
3724
|
SPAWN_LOCK_DIR = path14.join(os6.homedir(), ".exe-os", "spawn-locks");
|
|
3637
3725
|
SESSION_CACHE = path14.join(os6.homedir(), ".exe-os", "session-cache");
|
|
3638
3726
|
VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
|
|
@@ -3768,7 +3856,7 @@ async function deliverLocalMessage(messageId) {
|
|
|
3768
3856
|
try {
|
|
3769
3857
|
const exeSession = resolveExeSession();
|
|
3770
3858
|
if (!exeSession) {
|
|
3771
|
-
throw new Error("No
|
|
3859
|
+
throw new Error("No coordinator session found");
|
|
3772
3860
|
}
|
|
3773
3861
|
const sessionName = employeeSessionName(targetAgent, exeSession);
|
|
3774
3862
|
if (!isEmployeeAlive(sessionName)) {
|
|
@@ -4088,21 +4176,23 @@ function getReviewChecklist(role, agent, taskSlug) {
|
|
|
4088
4176
|
}
|
|
4089
4177
|
async function createReviewForCompletedTask(row, result, _baseDir, now) {
|
|
4090
4178
|
const taskFile = String(row.task_file);
|
|
4091
|
-
|
|
4179
|
+
const employees = await loadEmployees();
|
|
4180
|
+
const coordinatorName = getCoordinatorName(employees);
|
|
4181
|
+
if (String(row.assigned_to) === "exe" || isCoordinatorName(String(row.assigned_to), employees)) return;
|
|
4092
4182
|
if (String(row.title).startsWith("Review:")) return;
|
|
4093
4183
|
const fileName = taskFile.split("/").pop() ?? "";
|
|
4094
4184
|
if (fileName.startsWith("review-") && String(row.assigned_by) === "system") return;
|
|
4095
4185
|
if (fileName.startsWith("review-") && String(row.assigned_to) === "system") return;
|
|
4096
4186
|
const client = getClient();
|
|
4097
4187
|
const agent = String(row.assigned_to);
|
|
4098
|
-
const
|
|
4188
|
+
const rawReviewer = row.reviewer || row.assigned_by;
|
|
4189
|
+
const reviewer = rawReviewer ? String(rawReviewer) : coordinatorName;
|
|
4099
4190
|
const currentStatus = String(row.status ?? "");
|
|
4100
4191
|
if (currentStatus === "done") return;
|
|
4101
4192
|
const existingResult = String(row.result ?? "");
|
|
4102
4193
|
if (existingResult.includes("## Review notes")) return;
|
|
4103
4194
|
let reviewerRole = "unknown";
|
|
4104
4195
|
try {
|
|
4105
|
-
const employees = await loadEmployees();
|
|
4106
4196
|
const emp = getEmployee(employees, reviewer);
|
|
4107
4197
|
if (emp) reviewerRole = emp.role;
|
|
4108
4198
|
} catch {
|
|
@@ -4147,7 +4237,7 @@ async function createReviewForCompletedTask(row, result, _baseDir, now) {
|
|
|
4147
4237
|
agentRole: String(row.assigned_to),
|
|
4148
4238
|
event: "task_complete",
|
|
4149
4239
|
project: String(row.project_name),
|
|
4150
|
-
summary: `completed "${taskTitle}" \u2014
|
|
4240
|
+
summary: `completed "${taskTitle}" \u2014 ready for review`,
|
|
4151
4241
|
taskFile
|
|
4152
4242
|
});
|
|
4153
4243
|
const originalPriority = String(row.priority).toLowerCase();
|
|
@@ -4261,6 +4351,8 @@ async function hybridSearch(queryText, agentId, options) {
|
|
|
4261
4351
|
return lightweightSearch(queryText, agentId, options);
|
|
4262
4352
|
}
|
|
4263
4353
|
const limit = options?.limit ?? 10;
|
|
4354
|
+
const trajectoryResults = await trajectoryBypass(queryText, agentId, options, limit);
|
|
4355
|
+
if (trajectoryResults !== null) return trajectoryResults;
|
|
4264
4356
|
let effectiveQuery = queryText;
|
|
4265
4357
|
let effectiveOptions = { ...options };
|
|
4266
4358
|
let _isBroadQuery = false;
|
|
@@ -4484,6 +4576,8 @@ async function lightweightSearch(queryText, agentId, options) {
|
|
|
4484
4576
|
async function ftsQuery(client, matchExpr, agentId, options, limit) {
|
|
4485
4577
|
const statusFilter = options?.includeArchived ? "" : `
|
|
4486
4578
|
AND COALESCE(m.status, 'active') = 'active'`;
|
|
4579
|
+
const draftFilter = options?.includeDrafts ? "" : `
|
|
4580
|
+
AND (m.draft = 0 OR m.draft IS NULL)`;
|
|
4487
4581
|
let sql = `SELECT m.id, m.agent_id, m.agent_role, m.session_id, m.timestamp,
|
|
4488
4582
|
m.tool_name, m.project_name,
|
|
4489
4583
|
m.has_error, m.raw_text, m.vector, m.task_id,
|
|
@@ -4494,7 +4588,7 @@ async function ftsQuery(client, matchExpr, agentId, options, limit) {
|
|
|
4494
4588
|
FROM memories m
|
|
4495
4589
|
JOIN memories_fts fts ON m.rowid = fts.rowid
|
|
4496
4590
|
WHERE memories_fts MATCH ?
|
|
4497
|
-
AND m.agent_id = ?${statusFilter}
|
|
4591
|
+
AND m.agent_id = ?${statusFilter}${draftFilter}
|
|
4498
4592
|
AND COALESCE(m.confidence, 0.7) >= 0.3`;
|
|
4499
4593
|
const args = [matchExpr, agentId];
|
|
4500
4594
|
const scope = buildWikiScopeFilter(options, "m.");
|
|
@@ -4516,6 +4610,10 @@ async function ftsQuery(client, matchExpr, agentId, options, limit) {
|
|
|
4516
4610
|
sql += ` AND m.timestamp >= ?`;
|
|
4517
4611
|
args.push(options.since);
|
|
4518
4612
|
}
|
|
4613
|
+
if (options?.memoryType) {
|
|
4614
|
+
sql += ` AND m.memory_type = ?`;
|
|
4615
|
+
args.push(options.memoryType);
|
|
4616
|
+
}
|
|
4519
4617
|
sql += ` ORDER BY rank LIMIT ?`;
|
|
4520
4618
|
args.push(limit);
|
|
4521
4619
|
const result = await client.execute({ sql, args });
|
|
@@ -4548,6 +4646,8 @@ async function recentRecords(agentId, options, limit) {
|
|
|
4548
4646
|
const client = getClient();
|
|
4549
4647
|
const statusFilter = options?.includeArchived ? "" : `
|
|
4550
4648
|
AND COALESCE(status, 'active') = 'active'`;
|
|
4649
|
+
const draftFilter = options?.includeDrafts ? "" : `
|
|
4650
|
+
AND (draft = 0 OR draft IS NULL)`;
|
|
4551
4651
|
let sql = `SELECT id, agent_id, agent_role, session_id, timestamp,
|
|
4552
4652
|
tool_name, project_name,
|
|
4553
4653
|
has_error, raw_text, vector, task_id,
|
|
@@ -4556,7 +4656,7 @@ async function recentRecords(agentId, options, limit) {
|
|
|
4556
4656
|
char_offset, page_number,
|
|
4557
4657
|
source_path, source_type
|
|
4558
4658
|
FROM memories
|
|
4559
|
-
WHERE agent_id = ?${statusFilter}
|
|
4659
|
+
WHERE agent_id = ?${statusFilter}${draftFilter}
|
|
4560
4660
|
AND COALESCE(confidence, 0.7) >= 0.3`;
|
|
4561
4661
|
const args = [agentId];
|
|
4562
4662
|
const scope = buildWikiScopeFilter(options, "");
|
|
@@ -4578,6 +4678,10 @@ async function recentRecords(agentId, options, limit) {
|
|
|
4578
4678
|
sql += ` AND timestamp >= ?`;
|
|
4579
4679
|
args.push(options.since);
|
|
4580
4680
|
}
|
|
4681
|
+
if (options?.memoryType) {
|
|
4682
|
+
sql += ` AND memory_type = ?`;
|
|
4683
|
+
args.push(options.memoryType);
|
|
4684
|
+
}
|
|
4581
4685
|
sql += ` ORDER BY timestamp DESC LIMIT ?`;
|
|
4582
4686
|
args.push(limit);
|
|
4583
4687
|
const result = await client.execute({ sql, args });
|
|
@@ -4606,33 +4710,158 @@ async function recentRecords(agentId, options, limit) {
|
|
|
4606
4710
|
source_type: row.source_type ?? null
|
|
4607
4711
|
}));
|
|
4608
4712
|
}
|
|
4713
|
+
var KNOWN_TOOLS = /* @__PURE__ */ new Set([
|
|
4714
|
+
"Bash",
|
|
4715
|
+
"Read",
|
|
4716
|
+
"Write",
|
|
4717
|
+
"Edit",
|
|
4718
|
+
"Grep",
|
|
4719
|
+
"Glob",
|
|
4720
|
+
"Agent",
|
|
4721
|
+
"Skill",
|
|
4722
|
+
"WebSearch",
|
|
4723
|
+
"WebFetch"
|
|
4724
|
+
]);
|
|
4725
|
+
function detectToolQuery(query) {
|
|
4726
|
+
const lower = query.toLowerCase().trim();
|
|
4727
|
+
for (const tool of KNOWN_TOOLS) {
|
|
4728
|
+
if (lower === tool.toLowerCase()) return tool;
|
|
4729
|
+
if (lower.startsWith(tool.toLowerCase() + " ")) return tool;
|
|
4730
|
+
if (lower.includes(`tool:${tool.toLowerCase()}`)) return tool;
|
|
4731
|
+
}
|
|
4732
|
+
return null;
|
|
4733
|
+
}
|
|
4734
|
+
async function trajectoryBypass(queryText, agentId, options, limit) {
|
|
4735
|
+
const toolName = detectToolQuery(queryText);
|
|
4736
|
+
if (!toolName) return null;
|
|
4737
|
+
try {
|
|
4738
|
+
const client = getClient();
|
|
4739
|
+
const statusFilter = options?.includeArchived ? "" : `
|
|
4740
|
+
AND COALESCE(status, 'active') = 'active'`;
|
|
4741
|
+
const draftFilter = options?.includeDrafts ? "" : `
|
|
4742
|
+
AND (draft = 0 OR draft IS NULL)`;
|
|
4743
|
+
let sql = `SELECT id, agent_id, agent_role, session_id, timestamp,
|
|
4744
|
+
tool_name, project_name,
|
|
4745
|
+
has_error, raw_text, vector, task_id,
|
|
4746
|
+
importance, status, confidence, last_accessed,
|
|
4747
|
+
workspace_id, document_id, user_id,
|
|
4748
|
+
char_offset, page_number,
|
|
4749
|
+
source_path, source_type
|
|
4750
|
+
FROM memories
|
|
4751
|
+
WHERE trajectory IS NOT NULL
|
|
4752
|
+
AND json_extract(trajectory, '$.tool') = ?
|
|
4753
|
+
AND agent_id = ?${statusFilter}${draftFilter}`;
|
|
4754
|
+
const args = [toolName, agentId];
|
|
4755
|
+
if (options?.projectName) {
|
|
4756
|
+
sql += ` AND project_name = ?`;
|
|
4757
|
+
args.push(options.projectName);
|
|
4758
|
+
}
|
|
4759
|
+
if (options?.since) {
|
|
4760
|
+
sql += ` AND timestamp >= ?`;
|
|
4761
|
+
args.push(options.since);
|
|
4762
|
+
}
|
|
4763
|
+
sql += ` ORDER BY timestamp DESC LIMIT ?`;
|
|
4764
|
+
args.push(limit);
|
|
4765
|
+
const result = await client.execute({ sql, args });
|
|
4766
|
+
if (result.rows.length < 3) return null;
|
|
4767
|
+
return result.rows.map((row) => ({
|
|
4768
|
+
id: row.id,
|
|
4769
|
+
agent_id: row.agent_id,
|
|
4770
|
+
agent_role: row.agent_role,
|
|
4771
|
+
session_id: row.session_id,
|
|
4772
|
+
timestamp: row.timestamp,
|
|
4773
|
+
tool_name: row.tool_name,
|
|
4774
|
+
project_name: row.project_name,
|
|
4775
|
+
has_error: row.has_error === 1,
|
|
4776
|
+
raw_text: row.raw_text,
|
|
4777
|
+
vector: row.vector == null ? [] : Array.isArray(row.vector) ? row.vector : Array.from(row.vector),
|
|
4778
|
+
task_id: row.task_id ?? null,
|
|
4779
|
+
importance: row.importance ?? 5,
|
|
4780
|
+
status: row.status ?? "active",
|
|
4781
|
+
confidence: row.confidence ?? 0.7,
|
|
4782
|
+
last_accessed: row.last_accessed ?? row.timestamp,
|
|
4783
|
+
workspace_id: row.workspace_id ?? null,
|
|
4784
|
+
document_id: row.document_id ?? null,
|
|
4785
|
+
user_id: row.user_id ?? null,
|
|
4786
|
+
char_offset: row.char_offset ?? null,
|
|
4787
|
+
page_number: row.page_number ?? null,
|
|
4788
|
+
source_path: row.source_path ?? null,
|
|
4789
|
+
source_type: row.source_type ?? null
|
|
4790
|
+
}));
|
|
4791
|
+
} catch {
|
|
4792
|
+
return null;
|
|
4793
|
+
}
|
|
4794
|
+
}
|
|
4609
4795
|
|
|
4610
4796
|
// src/adapters/claude/active-agent.ts
|
|
4611
4797
|
init_config();
|
|
4612
|
-
import { readFileSync as
|
|
4613
|
-
import { execSync as
|
|
4614
|
-
import
|
|
4798
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, unlinkSync as unlinkSync3, readdirSync as readdirSync3 } from "fs";
|
|
4799
|
+
import { execSync as execSync5 } from "child_process";
|
|
4800
|
+
import path9 from "path";
|
|
4615
4801
|
|
|
4616
4802
|
// src/adapters/claude/session-key.ts
|
|
4617
4803
|
init_session_key();
|
|
4618
4804
|
|
|
4619
4805
|
// src/adapters/claude/active-agent.ts
|
|
4620
|
-
|
|
4806
|
+
init_employees();
|
|
4807
|
+
var CACHE_DIR = path9.join(EXE_AI_DIR, "session-cache");
|
|
4621
4808
|
var STALE_MS = 24 * 60 * 60 * 1e3;
|
|
4809
|
+
function isNameWithOptionalInstance(candidate, baseName) {
|
|
4810
|
+
if (candidate === baseName) return true;
|
|
4811
|
+
if (!candidate.startsWith(baseName)) return false;
|
|
4812
|
+
return /^\d+$/.test(candidate.slice(baseName.length));
|
|
4813
|
+
}
|
|
4814
|
+
function resolveEmployeeFromSessionPrefix(prefix, employees) {
|
|
4815
|
+
const sorted = [...employees].sort((a, b) => b.name.length - a.name.length);
|
|
4816
|
+
for (const employee of sorted) {
|
|
4817
|
+
if (isNameWithOptionalInstance(prefix, employee.name)) {
|
|
4818
|
+
return { agentId: employee.name, agentRole: employee.role };
|
|
4819
|
+
}
|
|
4820
|
+
}
|
|
4821
|
+
return null;
|
|
4822
|
+
}
|
|
4823
|
+
function resolveActiveAgentFromTmuxSession(sessionName) {
|
|
4824
|
+
const employees = loadEmployeesSync();
|
|
4825
|
+
const coordinator = getCoordinatorEmployee(employees);
|
|
4826
|
+
const coordinatorName = coordinator?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME;
|
|
4827
|
+
if (isNameWithOptionalInstance(sessionName, coordinatorName)) {
|
|
4828
|
+
return {
|
|
4829
|
+
agentId: coordinatorName,
|
|
4830
|
+
agentRole: coordinator?.role ?? "COO"
|
|
4831
|
+
};
|
|
4832
|
+
}
|
|
4833
|
+
if (isNameWithOptionalInstance(sessionName, DEFAULT_COORDINATOR_TEMPLATE_NAME)) {
|
|
4834
|
+
return {
|
|
4835
|
+
agentId: coordinator?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME,
|
|
4836
|
+
agentRole: coordinator?.role ?? "COO"
|
|
4837
|
+
};
|
|
4838
|
+
}
|
|
4839
|
+
if (sessionName.includes("-")) {
|
|
4840
|
+
const prefix = sessionName.split("-")[0] ?? "";
|
|
4841
|
+
const employee = resolveEmployeeFromSessionPrefix(prefix, employees);
|
|
4842
|
+
if (employee) return employee;
|
|
4843
|
+
const legacy = prefix.match(/^([a-zA-Z]+)\d*$/);
|
|
4844
|
+
if (legacy?.[1] && legacy[1] !== DEFAULT_COORDINATOR_TEMPLATE_NAME) {
|
|
4845
|
+
const emp = getEmployee(employees, legacy[1]);
|
|
4846
|
+
return { agentId: emp?.name ?? legacy[1], agentRole: emp?.role ?? "employee" };
|
|
4847
|
+
}
|
|
4848
|
+
}
|
|
4849
|
+
return null;
|
|
4850
|
+
}
|
|
4622
4851
|
function getMarkerPath() {
|
|
4623
|
-
return
|
|
4852
|
+
return path9.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
|
|
4624
4853
|
}
|
|
4625
4854
|
function getActiveAgent() {
|
|
4626
4855
|
try {
|
|
4627
4856
|
const markerPath = getMarkerPath();
|
|
4628
|
-
const raw =
|
|
4857
|
+
const raw = readFileSync5(markerPath, "utf8");
|
|
4629
4858
|
const data = JSON.parse(raw);
|
|
4630
4859
|
if (data.agentId) {
|
|
4631
4860
|
if (data.startedAt) {
|
|
4632
4861
|
const age = Date.now() - new Date(data.startedAt).getTime();
|
|
4633
4862
|
if (age > STALE_MS) {
|
|
4634
4863
|
try {
|
|
4635
|
-
|
|
4864
|
+
unlinkSync3(markerPath);
|
|
4636
4865
|
} catch {
|
|
4637
4866
|
}
|
|
4638
4867
|
} else {
|
|
@@ -4651,17 +4880,12 @@ function getActiveAgent() {
|
|
|
4651
4880
|
} catch {
|
|
4652
4881
|
}
|
|
4653
4882
|
try {
|
|
4654
|
-
const sessionName =
|
|
4883
|
+
const sessionName = execSync5(
|
|
4655
4884
|
"tmux display-message -p '#{session_name}' 2>/dev/null",
|
|
4656
4885
|
{ encoding: "utf8", timeout: 2e3 }
|
|
4657
4886
|
).trim();
|
|
4658
|
-
const
|
|
4659
|
-
if (
|
|
4660
|
-
return { agentId: empMatch[1], agentRole: "employee" };
|
|
4661
|
-
}
|
|
4662
|
-
if (/^exe\d+$/.test(sessionName)) {
|
|
4663
|
-
return { agentId: "exe", agentRole: "COO" };
|
|
4664
|
-
}
|
|
4887
|
+
const resolved = resolveActiveAgentFromTmuxSession(sessionName);
|
|
4888
|
+
if (resolved) return resolved;
|
|
4665
4889
|
} catch {
|
|
4666
4890
|
}
|
|
4667
4891
|
return {
|
|
@@ -4671,6 +4895,8 @@ function getActiveAgent() {
|
|
|
4671
4895
|
}
|
|
4672
4896
|
|
|
4673
4897
|
// src/adapters/claude/hooks/prompt-submit.ts
|
|
4898
|
+
init_employees();
|
|
4899
|
+
init_tmux_routing();
|
|
4674
4900
|
if (!process.env.AGENT_ID) {
|
|
4675
4901
|
process.env.AGENT_ID = "default";
|
|
4676
4902
|
process.env.AGENT_ROLE = "employee";
|
|
@@ -4727,7 +4953,7 @@ process.stdin.on("end", async () => {
|
|
|
4727
4953
|
process.exit(0);
|
|
4728
4954
|
}
|
|
4729
4955
|
await initStore();
|
|
4730
|
-
if (agent.agentId
|
|
4956
|
+
if (!canCoordinate(agent.agentId, agent.agentRole)) {
|
|
4731
4957
|
try {
|
|
4732
4958
|
const { getReadMessages: getReadMessages2, markAcknowledged: markAcknowledged2 } = await Promise.resolve().then(() => (init_messaging(), messaging_exports));
|
|
4733
4959
|
const readMsgs = await getReadMessages2(agent.agentId);
|
|
@@ -4778,7 +5004,7 @@ ${fresh.map(
|
|
|
4778
5004
|
}
|
|
4779
5005
|
}
|
|
4780
5006
|
let reviewContext = "";
|
|
4781
|
-
if (agent.agentId
|
|
5007
|
+
if (canCoordinate(agent.agentId, agent.agentRole)) {
|
|
4782
5008
|
try {
|
|
4783
5009
|
const { countPendingReviews: countPendingReviews2, countNewPendingReviewsSince: countNewPendingReviewsSince2 } = await Promise.resolve().then(() => (init_tasks_review(), tasks_review_exports));
|
|
4784
5010
|
const sessionKey = getSessionKey();
|
|
@@ -4787,7 +5013,7 @@ ${fresh.map(
|
|
|
4787
5013
|
try {
|
|
4788
5014
|
const { execSync: execSync7 } = await import("child_process");
|
|
4789
5015
|
const tmuxSession = execSync7("tmux display-message -p '#{session_name}'", { encoding: "utf8", timeout: 2e3 }).trim();
|
|
4790
|
-
if (
|
|
5016
|
+
if (isExeSession(tmuxSession)) sessionScope = tmuxSession;
|
|
4791
5017
|
} catch {
|
|
4792
5018
|
}
|
|
4793
5019
|
let lastCheckedAt = "";
|