@askexenow/exe-os 0.8.0 → 0.8.1
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/README.md +178 -79
- package/dist/bin/backfill-responses.js +160 -8
- package/dist/bin/backfill-vectors.js +130 -1
- package/dist/bin/cleanup-stale-review-tasks.js +130 -1
- package/dist/bin/cli.js +10111 -7540
- package/dist/bin/exe-agent.js +159 -1
- package/dist/bin/exe-assign.js +235 -16
- package/dist/bin/exe-boot.js +344 -472
- package/dist/bin/exe-call.js +145 -1
- package/dist/bin/exe-cloud.js +11 -0
- package/dist/bin/exe-dispatch.js +37 -24
- package/dist/bin/exe-doctor.js +130 -1
- package/dist/bin/exe-export-behaviors.js +150 -7
- package/dist/bin/exe-forget.js +822 -665
- package/dist/bin/exe-gateway.js +470 -62
- package/dist/bin/exe-heartbeat.js +133 -2
- package/dist/bin/exe-kill.js +150 -7
- package/dist/bin/exe-launch-agent.js +150 -7
- package/dist/bin/exe-new-employee.js +756 -224
- package/dist/bin/exe-pending-messages.js +132 -2
- package/dist/bin/exe-pending-notifications.js +130 -1
- package/dist/bin/exe-pending-reviews.js +132 -2
- package/dist/bin/exe-review.js +160 -8
- package/dist/bin/exe-search.js +2473 -2008
- package/dist/bin/exe-session-cleanup.js +238 -51
- package/dist/bin/exe-settings.js +11 -0
- package/dist/bin/exe-status.js +130 -1
- package/dist/bin/exe-team.js +130 -1
- package/dist/bin/git-sweep.js +272 -16
- package/dist/bin/graph-backfill.js +150 -7
- package/dist/bin/graph-export.js +150 -7
- package/dist/bin/install.js +5 -0
- package/dist/bin/scan-tasks.js +238 -19
- package/dist/bin/setup.js +1776 -10
- package/dist/bin/shard-migrate.js +150 -7
- package/dist/bin/update.js +9 -6
- package/dist/bin/wiki-sync.js +150 -7
- package/dist/gateway/index.js +470 -62
- package/dist/hooks/bug-report-worker.js +195 -35
- package/dist/hooks/commit-complete.js +272 -16
- package/dist/hooks/error-recall.js +2313 -1847
- package/dist/hooks/exe-heartbeat-hook.js +5 -0
- package/dist/hooks/ingest-worker.js +330 -58
- package/dist/hooks/ingest.js +11 -0
- package/dist/hooks/instructions-loaded.js +199 -10
- package/dist/hooks/notification.js +199 -10
- package/dist/hooks/post-compact.js +199 -10
- package/dist/hooks/pre-compact.js +199 -10
- package/dist/hooks/pre-tool-use.js +199 -10
- package/dist/hooks/prompt-ingest-worker.js +179 -14
- package/dist/hooks/prompt-submit.js +781 -285
- package/dist/hooks/response-ingest-worker.js +1900 -1405
- package/dist/hooks/session-end.js +456 -12
- package/dist/hooks/session-start.js +2188 -1724
- package/dist/hooks/stop.js +200 -10
- package/dist/hooks/subagent-stop.js +199 -10
- package/dist/hooks/summary-worker.js +604 -334
- package/dist/index.js +554 -61
- package/dist/lib/cloud-sync.js +5 -0
- package/dist/lib/config.js +13 -0
- package/dist/lib/consolidation.js +5 -0
- package/dist/lib/database.js +104 -0
- package/dist/lib/device-registry.js +109 -0
- package/dist/lib/embedder.js +13 -0
- package/dist/lib/employee-templates.js +53 -26
- package/dist/lib/employees.js +5 -0
- package/dist/lib/exe-daemon-client.js +5 -0
- package/dist/lib/exe-daemon.js +493 -79
- package/dist/lib/file-grep.js +20 -4
- package/dist/lib/hybrid-search.js +1435 -190
- package/dist/lib/identity-templates.js +126 -5
- package/dist/lib/identity.js +5 -0
- package/dist/lib/license.js +5 -0
- package/dist/lib/messaging.js +37 -24
- package/dist/lib/schedules.js +130 -1
- package/dist/lib/skill-learning.js +11 -0
- package/dist/lib/status-brief.js +5 -0
- package/dist/lib/store.js +199 -10
- package/dist/lib/task-router.js +72 -6
- package/dist/lib/tasks.js +179 -50
- package/dist/lib/tmux-routing.js +179 -46
- package/dist/mcp/server.js +2129 -1855
- package/dist/mcp/tools/create-task.js +86 -36
- package/dist/mcp/tools/deactivate-behavior.js +5 -0
- package/dist/mcp/tools/list-tasks.js +39 -11
- package/dist/mcp/tools/send-message.js +37 -24
- package/dist/mcp/tools/update-task.js +153 -38
- package/dist/runtime/index.js +451 -59
- package/dist/tui/App.js +454 -59
- package/package.json +1 -1
package/dist/lib/tasks.js
CHANGED
|
@@ -101,6 +101,11 @@ function normalizeSessionLifecycle(raw) {
|
|
|
101
101
|
const userSL = raw.sessionLifecycle ?? {};
|
|
102
102
|
raw.sessionLifecycle = { ...defaultSL, ...userSL };
|
|
103
103
|
}
|
|
104
|
+
function normalizeAutoUpdate(raw) {
|
|
105
|
+
const defaultAU = DEFAULT_CONFIG.autoUpdate;
|
|
106
|
+
const userAU = raw.autoUpdate ?? {};
|
|
107
|
+
raw.autoUpdate = { ...defaultAU, ...userAU };
|
|
108
|
+
}
|
|
104
109
|
async function loadConfig() {
|
|
105
110
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
106
111
|
await mkdir(dir, { recursive: true });
|
|
@@ -123,6 +128,7 @@ async function loadConfig() {
|
|
|
123
128
|
}
|
|
124
129
|
normalizeScalingRoadmap(migratedCfg);
|
|
125
130
|
normalizeSessionLifecycle(migratedCfg);
|
|
131
|
+
normalizeAutoUpdate(migratedCfg);
|
|
126
132
|
const config = { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db"), ...migratedCfg };
|
|
127
133
|
if (config.dbPath.startsWith("~")) {
|
|
128
134
|
config.dbPath = config.dbPath.replace(/^~/, os.homedir());
|
|
@@ -198,6 +204,11 @@ var init_config = __esm({
|
|
|
198
204
|
idleKillTicksRequired: 3,
|
|
199
205
|
idleKillIntercomAckWindowMs: 1e4,
|
|
200
206
|
maxAutoInstances: 10
|
|
207
|
+
},
|
|
208
|
+
autoUpdate: {
|
|
209
|
+
checkOnBoot: true,
|
|
210
|
+
autoInstall: false,
|
|
211
|
+
checkIntervalMs: 24 * 60 * 60 * 1e3
|
|
201
212
|
}
|
|
202
213
|
};
|
|
203
214
|
CONFIG_MIGRATIONS = [
|
|
@@ -271,6 +282,36 @@ import path3 from "path";
|
|
|
271
282
|
import { execSync } from "child_process";
|
|
272
283
|
import { mkdir as mkdir2, writeFile as writeFile2, appendFile } from "fs/promises";
|
|
273
284
|
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
|
|
285
|
+
async function writeCheckpoint(input) {
|
|
286
|
+
const client = getClient();
|
|
287
|
+
const row = await resolveTask(client, input.taskId);
|
|
288
|
+
const taskId = String(row.id);
|
|
289
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
290
|
+
const blockedByIds = [];
|
|
291
|
+
if (row.blocked_by) {
|
|
292
|
+
blockedByIds.push(String(row.blocked_by));
|
|
293
|
+
}
|
|
294
|
+
const checkpoint = {
|
|
295
|
+
step: input.step,
|
|
296
|
+
context_summary: input.contextSummary,
|
|
297
|
+
files_touched: input.filesTouched ?? [],
|
|
298
|
+
blocked_by_ids: blockedByIds,
|
|
299
|
+
last_checkpoint_at: now
|
|
300
|
+
};
|
|
301
|
+
const result = await client.execute({
|
|
302
|
+
sql: `UPDATE tasks SET checkpoint = ?, checkpoint_count = checkpoint_count + 1, updated_at = ? WHERE id = ?`,
|
|
303
|
+
args: [JSON.stringify(checkpoint), now, taskId]
|
|
304
|
+
});
|
|
305
|
+
if (result.rowsAffected === 0) {
|
|
306
|
+
throw new Error(`Checkpoint write failed: task ${taskId} not found`);
|
|
307
|
+
}
|
|
308
|
+
const countResult = await client.execute({
|
|
309
|
+
sql: "SELECT checkpoint_count FROM tasks WHERE id = ?",
|
|
310
|
+
args: [taskId]
|
|
311
|
+
});
|
|
312
|
+
const checkpointCount = Number(countResult.rows[0]?.checkpoint_count ?? 1);
|
|
313
|
+
return { checkpointCount };
|
|
314
|
+
}
|
|
274
315
|
function extractParentFromContext(contextBody) {
|
|
275
316
|
if (!contextBody) return null;
|
|
276
317
|
const match = contextBody.match(
|
|
@@ -377,9 +418,10 @@ async function createTaskCore(input) {
|
|
|
377
418
|
} catch {
|
|
378
419
|
}
|
|
379
420
|
}
|
|
421
|
+
const complexity = input.complexity ?? "standard";
|
|
380
422
|
await client.execute({
|
|
381
|
-
sql: `INSERT INTO tasks (id, title, assigned_to, assigned_by, project_name, priority, status, task_file, blocked_by, parent_task_id, reviewer, context, created_at, updated_at)
|
|
382
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
423
|
+
sql: `INSERT INTO tasks (id, title, assigned_to, assigned_by, project_name, priority, status, task_file, blocked_by, parent_task_id, reviewer, context, complexity, budget_tokens, budget_fallback_model, tokens_used, tokens_warned_at, created_at, updated_at)
|
|
424
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
383
425
|
args: [
|
|
384
426
|
id,
|
|
385
427
|
input.title,
|
|
@@ -393,6 +435,11 @@ async function createTaskCore(input) {
|
|
|
393
435
|
parentTaskId,
|
|
394
436
|
input.reviewer ?? null,
|
|
395
437
|
input.context,
|
|
438
|
+
input.budgetTokens ?? null,
|
|
439
|
+
input.budgetFallbackModel ?? null,
|
|
440
|
+
0,
|
|
441
|
+
null,
|
|
442
|
+
complexity,
|
|
396
443
|
now,
|
|
397
444
|
now
|
|
398
445
|
]
|
|
@@ -408,7 +455,11 @@ async function createTaskCore(input) {
|
|
|
408
455
|
taskFile,
|
|
409
456
|
createdAt: now,
|
|
410
457
|
updatedAt: now,
|
|
411
|
-
warning
|
|
458
|
+
warning,
|
|
459
|
+
budgetTokens: input.budgetTokens ?? null,
|
|
460
|
+
budgetFallbackModel: input.budgetFallbackModel ?? null,
|
|
461
|
+
tokensUsed: 0,
|
|
462
|
+
tokensWarnedAt: null
|
|
412
463
|
};
|
|
413
464
|
}
|
|
414
465
|
async function listTasks(input) {
|
|
@@ -448,7 +499,12 @@ async function listTasks(input) {
|
|
|
448
499
|
status: String(r.status),
|
|
449
500
|
taskFile: String(r.task_file),
|
|
450
501
|
createdAt: String(r.created_at),
|
|
451
|
-
updatedAt: String(r.updated_at)
|
|
502
|
+
updatedAt: String(r.updated_at),
|
|
503
|
+
checkpointCount: Number(r.checkpoint_count ?? 0),
|
|
504
|
+
budgetTokens: r.budget_tokens !== null ? Number(r.budget_tokens) : null,
|
|
505
|
+
budgetFallbackModel: r.budget_fallback_model !== null ? String(r.budget_fallback_model) : null,
|
|
506
|
+
tokensUsed: Number(r.tokens_used ?? 0),
|
|
507
|
+
tokensWarnedAt: r.tokens_warned_at !== null ? Number(r.tokens_warned_at) : null
|
|
452
508
|
}));
|
|
453
509
|
}
|
|
454
510
|
function checkStaleCompletion(taskContext, taskCreatedAt) {
|
|
@@ -456,8 +512,13 @@ function checkStaleCompletion(taskContext, taskCreatedAt) {
|
|
|
456
512
|
if (!DELEGATION_KEYWORDS.test(taskContext)) return null;
|
|
457
513
|
try {
|
|
458
514
|
const since = new Date(taskCreatedAt).toISOString();
|
|
515
|
+
const branch = execSync(
|
|
516
|
+
"git rev-parse --abbrev-ref HEAD 2>/dev/null",
|
|
517
|
+
{ encoding: "utf8", timeout: 3e3 }
|
|
518
|
+
).trim();
|
|
519
|
+
const branchArg = branch && branch !== "HEAD" ? branch : "";
|
|
459
520
|
const commitCount = execSync(
|
|
460
|
-
`git log --oneline --since="${since}" 2>/dev/null | wc -l`,
|
|
521
|
+
`git log --oneline --since="${since}" ${branchArg} 2>/dev/null | wc -l`,
|
|
461
522
|
{ encoding: "utf8", timeout: 5e3 }
|
|
462
523
|
).trim();
|
|
463
524
|
const count = parseInt(commitCount, 10);
|
|
@@ -516,6 +577,14 @@ ${input.result}` : `\u26A0\uFE0F ${warning}`;
|
|
|
516
577
|
const claimedBy = cur?.assigned_tmux ? ` (claimed by ${cur.assigned_tmux})` : "";
|
|
517
578
|
throw new Error(`${TASK_ALREADY_CLAIMED_PREFIX}: task ${taskId} is ${status}${claimedBy}`);
|
|
518
579
|
}
|
|
580
|
+
try {
|
|
581
|
+
await writeCheckpoint({
|
|
582
|
+
taskId,
|
|
583
|
+
step: "claimed",
|
|
584
|
+
contextSummary: `Task claimed by session. Transitioning open \u2192 in_progress.`
|
|
585
|
+
});
|
|
586
|
+
} catch {
|
|
587
|
+
}
|
|
519
588
|
return { row, taskFile, now, taskId };
|
|
520
589
|
}
|
|
521
590
|
if (input.result) {
|
|
@@ -529,6 +598,14 @@ ${input.result}` : `\u26A0\uFE0F ${warning}`;
|
|
|
529
598
|
args: [input.status, now, taskId]
|
|
530
599
|
});
|
|
531
600
|
}
|
|
601
|
+
try {
|
|
602
|
+
await writeCheckpoint({
|
|
603
|
+
taskId,
|
|
604
|
+
step: `status_transition:${input.status}`,
|
|
605
|
+
contextSummary: input.result ? `Transitioned to ${input.status}. Result: ${input.result.slice(0, 500)}` : `Transitioned to ${input.status}.`
|
|
606
|
+
});
|
|
607
|
+
} catch {
|
|
608
|
+
}
|
|
532
609
|
return { row, taskFile, now, taskId };
|
|
533
610
|
}
|
|
534
611
|
async function deleteTaskCore(taskId, _baseDir) {
|
|
@@ -902,11 +979,12 @@ function queueIntercom(targetSession, reason) {
|
|
|
902
979
|
}
|
|
903
980
|
writeQueue(queue);
|
|
904
981
|
}
|
|
905
|
-
var QUEUE_PATH, INTERCOM_LOG;
|
|
982
|
+
var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
|
|
906
983
|
var init_intercom_queue = __esm({
|
|
907
984
|
"src/lib/intercom-queue.ts"() {
|
|
908
985
|
"use strict";
|
|
909
986
|
QUEUE_PATH = path6.join(os4.homedir(), ".exe-os", "intercom-queue.json");
|
|
987
|
+
TTL_MS = 60 * 60 * 1e3;
|
|
910
988
|
INTERCOM_LOG = path6.join(os4.homedir(), ".exe-os", "intercom.log");
|
|
911
989
|
}
|
|
912
990
|
});
|
|
@@ -1029,6 +1107,17 @@ function getGitRoot(dir) {
|
|
|
1029
1107
|
return null;
|
|
1030
1108
|
}
|
|
1031
1109
|
}
|
|
1110
|
+
function getMainRepoRoot(dir) {
|
|
1111
|
+
try {
|
|
1112
|
+
const commonDir = execSync5(
|
|
1113
|
+
"git rev-parse --path-format=absolute --git-common-dir",
|
|
1114
|
+
{ cwd: dir, encoding: "utf-8", timeout: GIT_TIMEOUT_MS, stdio: ["pipe", "pipe", "pipe"] }
|
|
1115
|
+
).trim();
|
|
1116
|
+
return realpath(path9.dirname(commonDir));
|
|
1117
|
+
} catch {
|
|
1118
|
+
return null;
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1032
1121
|
function worktreePath(repoRoot, employeeName, instance) {
|
|
1033
1122
|
const label = instanceLabel(employeeName, instance);
|
|
1034
1123
|
return path9.join(repoRoot, ".worktrees", label);
|
|
@@ -1265,6 +1354,11 @@ function getSessionState(sessionName) {
|
|
|
1265
1354
|
if (!transport.isAlive(sessionName)) return "offline";
|
|
1266
1355
|
try {
|
|
1267
1356
|
const pane = transport.capturePane(sessionName, 5);
|
|
1357
|
+
if (!pane.includes("\u276F") && !pane.includes("Claude Code") && !BUSY_PATTERN.test(pane) && !/Running…/.test(pane)) {
|
|
1358
|
+
if (/\$\s*$/.test(pane) || /% $/.test(pane.trimEnd())) {
|
|
1359
|
+
return "no_claude";
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1268
1362
|
if (/Running…/.test(pane)) return "tool";
|
|
1269
1363
|
if (BUSY_PATTERN.test(pane)) return "thinking";
|
|
1270
1364
|
return "idle";
|
|
@@ -1272,10 +1366,6 @@ function getSessionState(sessionName) {
|
|
|
1272
1366
|
return "offline";
|
|
1273
1367
|
}
|
|
1274
1368
|
}
|
|
1275
|
-
function isSessionBusy(sessionName) {
|
|
1276
|
-
const state = getSessionState(sessionName);
|
|
1277
|
-
return state === "thinking" || state === "tool";
|
|
1278
|
-
}
|
|
1279
1369
|
function isExeSession(sessionName) {
|
|
1280
1370
|
return /^exe\d*$/.test(sessionName);
|
|
1281
1371
|
}
|
|
@@ -1295,7 +1385,14 @@ function sendIntercom(targetSession) {
|
|
|
1295
1385
|
logIntercom(`SKIP \u2192 ${targetSession} (session not found)`);
|
|
1296
1386
|
return "failed";
|
|
1297
1387
|
}
|
|
1298
|
-
|
|
1388
|
+
const sessionState = getSessionState(targetSession);
|
|
1389
|
+
if (sessionState === "no_claude") {
|
|
1390
|
+
queueIntercom(targetSession, "claude not running in session");
|
|
1391
|
+
recordDebounce(targetSession);
|
|
1392
|
+
logIntercom(`QUEUED \u2192 ${targetSession} (no claude process \u2014 raw shell detected)`);
|
|
1393
|
+
return "queued";
|
|
1394
|
+
}
|
|
1395
|
+
if (sessionState === "thinking" || sessionState === "tool") {
|
|
1299
1396
|
queueIntercom(targetSession, "session busy at send time");
|
|
1300
1397
|
recordDebounce(targetSession);
|
|
1301
1398
|
logIntercom(`QUEUED \u2192 ${targetSession} (session busy, will retry from queue)`);
|
|
@@ -1307,18 +1404,7 @@ function sendIntercom(targetSession) {
|
|
|
1307
1404
|
}
|
|
1308
1405
|
transport.sendKeys(targetSession, "/exe-intercom");
|
|
1309
1406
|
recordDebounce(targetSession);
|
|
1310
|
-
|
|
1311
|
-
try {
|
|
1312
|
-
execSync6(`sleep ${INTERCOM_POLL_INTERVAL_S}`);
|
|
1313
|
-
} catch {
|
|
1314
|
-
}
|
|
1315
|
-
const state = getSessionState(targetSession);
|
|
1316
|
-
if (state === "thinking" || state === "tool") {
|
|
1317
|
-
logIntercom(`ACKNOWLEDGED \u2192 ${targetSession} (state=${state}, poll=${i + 1})`);
|
|
1318
|
-
return "acknowledged";
|
|
1319
|
-
}
|
|
1320
|
-
}
|
|
1321
|
-
logIntercom(`DELIVERED \u2192 ${targetSession} (no state transition after ${INTERCOM_POLL_MAX_ATTEMPTS}s)`);
|
|
1407
|
+
logIntercom(`DELIVERED \u2192 ${targetSession} (fire-and-forget)`);
|
|
1322
1408
|
return "delivered";
|
|
1323
1409
|
} catch {
|
|
1324
1410
|
logIntercom(`FAIL \u2192 ${targetSession}`);
|
|
@@ -1335,7 +1421,17 @@ function notifyParentExe(sessionKey) {
|
|
|
1335
1421
|
process.stderr.write(`[intercom] notifyParentExe \u2192 ${target}
|
|
1336
1422
|
`);
|
|
1337
1423
|
const result = sendIntercom(target);
|
|
1338
|
-
|
|
1424
|
+
if (result === "failed") {
|
|
1425
|
+
const rootExe = resolveExeSession();
|
|
1426
|
+
if (rootExe && rootExe !== target) {
|
|
1427
|
+
process.stderr.write(`[intercom] notifyParentExe: dispatcher ${target} dead, falling back to root exe ${rootExe}
|
|
1428
|
+
`);
|
|
1429
|
+
const fallback = sendIntercom(rootExe);
|
|
1430
|
+
return fallback !== "failed";
|
|
1431
|
+
}
|
|
1432
|
+
return false;
|
|
1433
|
+
}
|
|
1434
|
+
return true;
|
|
1339
1435
|
}
|
|
1340
1436
|
function ensureEmployee(employeeName, exeSession, projectDir, opts) {
|
|
1341
1437
|
if (employeeName === "exe") {
|
|
@@ -1384,7 +1480,8 @@ function ensureEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
1384
1480
|
return { status: "failed", sessionName, error: "intercom delivery failed" };
|
|
1385
1481
|
}
|
|
1386
1482
|
const spawnOpts = { ...opts, instance: effectiveInstance };
|
|
1387
|
-
const
|
|
1483
|
+
const mainRoot = getMainRepoRoot(projectDir) ?? projectDir;
|
|
1484
|
+
const wtPath = ensureWorktree(mainRoot, employeeName, effectiveInstance);
|
|
1388
1485
|
if (wtPath) {
|
|
1389
1486
|
spawnOpts.cwd = wtPath;
|
|
1390
1487
|
}
|
|
@@ -1565,7 +1662,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
1565
1662
|
let booted = false;
|
|
1566
1663
|
for (let i = 0; i < 30; i++) {
|
|
1567
1664
|
try {
|
|
1568
|
-
execSync6("sleep
|
|
1665
|
+
execSync6("sleep 0.5");
|
|
1569
1666
|
} catch {
|
|
1570
1667
|
}
|
|
1571
1668
|
try {
|
|
@@ -1585,7 +1682,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
1585
1682
|
}
|
|
1586
1683
|
}
|
|
1587
1684
|
if (!booted) {
|
|
1588
|
-
return { sessionName, error: `${useExeAgent ? "exe-agent" : "claude"} did not boot within
|
|
1685
|
+
return { sessionName, error: `${useExeAgent ? "exe-agent" : "claude"} did not boot within 15s` };
|
|
1589
1686
|
}
|
|
1590
1687
|
if (!useExeAgent) {
|
|
1591
1688
|
try {
|
|
@@ -1603,7 +1700,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
1603
1700
|
});
|
|
1604
1701
|
return { sessionName };
|
|
1605
1702
|
}
|
|
1606
|
-
var SESSION_CACHE, BEHAVIORS_EXPORT_TIMEOUT_MS, INTERCOM_DEBOUNCE_MS, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS, BUSY_PATTERN
|
|
1703
|
+
var SESSION_CACHE, BEHAVIORS_EXPORT_TIMEOUT_MS, INTERCOM_DEBOUNCE_MS, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS, BUSY_PATTERN;
|
|
1607
1704
|
var init_tmux_routing = __esm({
|
|
1608
1705
|
"src/lib/tmux-routing.ts"() {
|
|
1609
1706
|
"use strict";
|
|
@@ -1623,8 +1720,6 @@ var init_tmux_routing = __esm({
|
|
|
1623
1720
|
DEBOUNCE_FILE = path10.join(SESSION_CACHE, "intercom-debounce.json");
|
|
1624
1721
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
1625
1722
|
BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
|
|
1626
|
-
INTERCOM_POLL_INTERVAL_S = 1;
|
|
1627
|
-
INTERCOM_POLL_MAX_ATTEMPTS = 8;
|
|
1628
1723
|
}
|
|
1629
1724
|
});
|
|
1630
1725
|
|
|
@@ -1710,23 +1805,38 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
1710
1805
|
if (String(row.assigned_by) !== "system" || !taskFile.includes("review-")) return;
|
|
1711
1806
|
try {
|
|
1712
1807
|
const client = getClient();
|
|
1713
|
-
const
|
|
1714
|
-
const
|
|
1715
|
-
|
|
1716
|
-
if (parts.length >= 3 && parts[0] === "review") {
|
|
1717
|
-
const agent = parts[1];
|
|
1718
|
-
const slug = parts.slice(2).join("-");
|
|
1719
|
-
const originalTaskFile = `exe/${agent}/${slug}.md`;
|
|
1808
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1809
|
+
const parentId = row.parent_task_id ? String(row.parent_task_id) : null;
|
|
1810
|
+
if (parentId) {
|
|
1720
1811
|
const result = await client.execute({
|
|
1721
|
-
sql: "UPDATE tasks SET status = 'done', updated_at = ? WHERE
|
|
1722
|
-
args: [
|
|
1812
|
+
sql: "UPDATE tasks SET status = 'done', updated_at = ? WHERE id = ? AND status = 'needs_review'",
|
|
1813
|
+
args: [now, parentId]
|
|
1723
1814
|
});
|
|
1724
1815
|
if (result.rowsAffected > 0) {
|
|
1725
1816
|
process.stderr.write(
|
|
1726
|
-
`[review-cleanup] Cascaded original task to done: ${
|
|
1817
|
+
`[review-cleanup] Cascaded original task to done via parent_task_id: ${parentId}
|
|
1727
1818
|
`
|
|
1728
1819
|
);
|
|
1729
1820
|
}
|
|
1821
|
+
} else {
|
|
1822
|
+
const fileName = taskFile.split("/").pop() ?? "";
|
|
1823
|
+
const reviewPrefix = fileName.replace(".md", "");
|
|
1824
|
+
const parts = reviewPrefix.split("-");
|
|
1825
|
+
if (parts.length >= 3 && parts[0] === "review") {
|
|
1826
|
+
const agent = parts[1];
|
|
1827
|
+
const slug = parts.slice(2).join("-");
|
|
1828
|
+
const originalTaskFile = `exe/${agent}/${slug}.md`;
|
|
1829
|
+
const result = await client.execute({
|
|
1830
|
+
sql: "UPDATE tasks SET status = 'done', updated_at = ? WHERE task_file = ? AND status = 'needs_review'",
|
|
1831
|
+
args: [now, originalTaskFile]
|
|
1832
|
+
});
|
|
1833
|
+
if (result.rowsAffected > 0) {
|
|
1834
|
+
process.stderr.write(
|
|
1835
|
+
`[review-cleanup] Cascaded original task to done (legacy path): ${originalTaskFile}
|
|
1836
|
+
`
|
|
1837
|
+
);
|
|
1838
|
+
}
|
|
1839
|
+
}
|
|
1730
1840
|
}
|
|
1731
1841
|
} catch (err) {
|
|
1732
1842
|
process.stderr.write(
|
|
@@ -1847,12 +1957,23 @@ function getProjectName(cwd) {
|
|
|
1847
1957
|
const dir = cwd ?? process.cwd();
|
|
1848
1958
|
if (_cached2 && _cachedCwd === dir) return _cached2;
|
|
1849
1959
|
try {
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1960
|
+
let repoRoot;
|
|
1961
|
+
try {
|
|
1962
|
+
const gitCommonDir = execSync7("git rev-parse --path-format=absolute --git-common-dir", {
|
|
1963
|
+
cwd: dir,
|
|
1964
|
+
encoding: "utf8",
|
|
1965
|
+
timeout: 2e3,
|
|
1966
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
1967
|
+
}).trim();
|
|
1968
|
+
repoRoot = path13.dirname(gitCommonDir);
|
|
1969
|
+
} catch {
|
|
1970
|
+
repoRoot = execSync7("git rev-parse --show-toplevel", {
|
|
1971
|
+
cwd: dir,
|
|
1972
|
+
encoding: "utf8",
|
|
1973
|
+
timeout: 2e3,
|
|
1974
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
1975
|
+
}).trim();
|
|
1976
|
+
}
|
|
1856
1977
|
_cached2 = path13.basename(repoRoot);
|
|
1857
1978
|
_cachedCwd = dir;
|
|
1858
1979
|
return _cached2;
|
|
@@ -1958,7 +2079,9 @@ async function dispatchTaskToEmployee(input) {
|
|
|
1958
2079
|
return { dispatched, session: sessionName, crossProject };
|
|
1959
2080
|
} else {
|
|
1960
2081
|
const projectDir = input.projectDir ?? process.cwd();
|
|
1961
|
-
const result = ensureEmployee(input.assignedTo, exeSession, projectDir
|
|
2082
|
+
const result = ensureEmployee(input.assignedTo, exeSession, projectDir, {
|
|
2083
|
+
autoInstance: input.assignedTo === "tom" || input.assignedTo === "sasha"
|
|
2084
|
+
});
|
|
1962
2085
|
if (result.status === "failed") {
|
|
1963
2086
|
process.stderr.write(
|
|
1964
2087
|
`[dispatch] Failed to spawn ${input.assignedTo}: ${result.error}
|
|
@@ -2346,10 +2469,11 @@ async function updateTask(input) {
|
|
|
2346
2469
|
try {
|
|
2347
2470
|
const client = getClient();
|
|
2348
2471
|
const taskTitle = String(row.title);
|
|
2472
|
+
const escaped = taskTitle.replace(/%/g, "\\%").replace(/_/g, "\\_");
|
|
2349
2473
|
await client.execute({
|
|
2350
2474
|
sql: `UPDATE tasks SET status = 'cancelled', updated_at = ?
|
|
2351
|
-
WHERE title LIKE ? AND status IN ('open', 'in_progress')`,
|
|
2352
|
-
args: [now, `%left
|
|
2475
|
+
WHERE title LIKE ? ESCAPE '\\' AND status IN ('open', 'in_progress')`,
|
|
2476
|
+
args: [now, `%left '${escaped}' as in\\_progress%`]
|
|
2353
2477
|
});
|
|
2354
2478
|
} catch {
|
|
2355
2479
|
}
|
|
@@ -2407,6 +2531,10 @@ async function updateTask(input) {
|
|
|
2407
2531
|
taskFile,
|
|
2408
2532
|
createdAt: String(row.created_at),
|
|
2409
2533
|
updatedAt: now,
|
|
2534
|
+
budgetTokens: row.budget_tokens !== void 0 && row.budget_tokens !== null ? Number(row.budget_tokens) : null,
|
|
2535
|
+
budgetFallbackModel: row.budget_fallback_model !== void 0 && row.budget_fallback_model !== null ? String(row.budget_fallback_model) : null,
|
|
2536
|
+
tokensUsed: Number(row.tokens_used ?? 0),
|
|
2537
|
+
tokensWarnedAt: row.tokens_warned_at !== void 0 && row.tokens_warned_at !== null ? Number(row.tokens_warned_at) : null,
|
|
2410
2538
|
nextTask
|
|
2411
2539
|
};
|
|
2412
2540
|
}
|
|
@@ -2452,5 +2580,6 @@ export {
|
|
|
2452
2580
|
resolveTask,
|
|
2453
2581
|
slugify,
|
|
2454
2582
|
updateTask,
|
|
2455
|
-
updateTaskStatus
|
|
2583
|
+
updateTaskStatus,
|
|
2584
|
+
writeCheckpoint
|
|
2456
2585
|
};
|