@askexenow/exe-os 0.9.10 → 0.9.11
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/cli.js +150 -8
- package/dist/bin/exe-boot.js +150 -8
- package/dist/bin/exe-dispatch.js +155 -8
- package/dist/bin/exe-gateway.js +531 -166
- package/dist/bin/exe-link.js +6 -0
- package/dist/bin/exe-session-cleanup.js +28 -8
- package/dist/bin/git-sweep.js +157 -10
- package/dist/bin/scan-tasks.js +155 -8
- package/dist/bin/setup.js +6 -0
- package/dist/gateway/index.js +144 -8
- package/dist/hooks/bug-report-worker.js +394 -258
- package/dist/hooks/codex-stop-task-finalizer.js +10 -8
- package/dist/hooks/commit-complete.js +155 -8
- package/dist/hooks/ingest-worker.js +155 -8
- package/dist/hooks/pre-compact.js +155 -8
- package/dist/hooks/prompt-submit.js +28 -8
- package/dist/hooks/session-end.js +157 -10
- package/dist/hooks/summary-worker.js +6 -0
- package/dist/index.js +144 -8
- package/dist/lib/cloud-sync.js +6 -0
- package/dist/lib/exe-daemon.js +28 -8
- package/dist/lib/messaging.js +10 -8
- package/dist/lib/tasks.js +460 -313
- package/dist/lib/tmux-routing.js +155 -8
- package/dist/mcp/server.js +396 -254
- package/dist/mcp/tools/create-task.js +449 -313
- package/dist/mcp/tools/send-message.js +10 -8
- package/dist/mcp/tools/update-task.js +460 -313
- package/dist/runtime/index.js +155 -8
- package/dist/tui/App.js +144 -8
- package/package.json +1 -1
|
@@ -341,6 +341,17 @@ function isCoordinatorName(agentName, employees = loadEmployeesSync()) {
|
|
|
341
341
|
function canCoordinate(agentName, agentRole, employees = loadEmployeesSync()) {
|
|
342
342
|
return agentName === "default" || isCoordinatorRole(agentRole) || isCoordinatorName(agentName, employees);
|
|
343
343
|
}
|
|
344
|
+
async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
345
|
+
if (!existsSync3(employeesPath)) {
|
|
346
|
+
return [];
|
|
347
|
+
}
|
|
348
|
+
const raw = await readFile2(employeesPath, "utf-8");
|
|
349
|
+
try {
|
|
350
|
+
return JSON.parse(raw);
|
|
351
|
+
} catch {
|
|
352
|
+
return [];
|
|
353
|
+
}
|
|
354
|
+
}
|
|
344
355
|
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
345
356
|
if (!existsSync3(employeesPath)) return [];
|
|
346
357
|
try {
|
|
@@ -4171,8 +4182,35 @@ var init_tasks_crud = __esm({
|
|
|
4171
4182
|
});
|
|
4172
4183
|
|
|
4173
4184
|
// src/lib/tasks-review.ts
|
|
4185
|
+
var tasks_review_exports = {};
|
|
4186
|
+
__export(tasks_review_exports, {
|
|
4187
|
+
cleanupOrphanedReviews: () => cleanupOrphanedReviews,
|
|
4188
|
+
cleanupReviewFile: () => cleanupReviewFile,
|
|
4189
|
+
countNewPendingReviewsSince: () => countNewPendingReviewsSince,
|
|
4190
|
+
countPendingReviews: () => countPendingReviews,
|
|
4191
|
+
createReviewForCompletedTask: () => createReviewForCompletedTask,
|
|
4192
|
+
formatAge: () => formatAge,
|
|
4193
|
+
getReviewChecklist: () => getReviewChecklist,
|
|
4194
|
+
isStale: () => isStale,
|
|
4195
|
+
listPendingReviews: () => listPendingReviews
|
|
4196
|
+
});
|
|
4174
4197
|
import path15 from "path";
|
|
4175
4198
|
import { existsSync as existsSync13, readdirSync as readdirSync3, unlinkSync as unlinkSync5 } from "fs";
|
|
4199
|
+
function formatAge(isoTimestamp) {
|
|
4200
|
+
if (!isoTimestamp) return "";
|
|
4201
|
+
const ms = Date.now() - new Date(isoTimestamp).getTime();
|
|
4202
|
+
if (ms < 0) return "just now";
|
|
4203
|
+
const minutes = Math.floor(ms / 6e4);
|
|
4204
|
+
if (minutes < 60) return `${minutes}m ago`;
|
|
4205
|
+
const hours = Math.floor(minutes / 60);
|
|
4206
|
+
if (hours < 24) return `${hours}h ago`;
|
|
4207
|
+
const days = Math.floor(hours / 24);
|
|
4208
|
+
return `${days}d ago`;
|
|
4209
|
+
}
|
|
4210
|
+
function isStale(isoTimestamp) {
|
|
4211
|
+
if (!isoTimestamp) return false;
|
|
4212
|
+
return Date.now() - new Date(isoTimestamp).getTime() > 24 * 60 * 60 * 1e3;
|
|
4213
|
+
}
|
|
4176
4214
|
async function countPendingReviews(sessionScope) {
|
|
4177
4215
|
const client = getClient();
|
|
4178
4216
|
const scope = strictSessionScopeFilter(
|
|
@@ -4293,6 +4331,95 @@ function getReviewChecklist(role, agent, taskSlug) {
|
|
|
4293
4331
|
]
|
|
4294
4332
|
};
|
|
4295
4333
|
}
|
|
4334
|
+
async function createReviewForCompletedTask(row, result, _baseDir, now) {
|
|
4335
|
+
const taskFile = String(row.task_file);
|
|
4336
|
+
const employees = await loadEmployees();
|
|
4337
|
+
const coordinatorName = getCoordinatorName(employees);
|
|
4338
|
+
if (isCoordinatorName(String(row.assigned_to), employees)) return;
|
|
4339
|
+
if (String(row.title).startsWith("Review:")) return;
|
|
4340
|
+
const fileName = taskFile.split("/").pop() ?? "";
|
|
4341
|
+
if (fileName.startsWith("review-") && String(row.assigned_by) === "system") return;
|
|
4342
|
+
if (fileName.startsWith("review-") && String(row.assigned_to) === "system") return;
|
|
4343
|
+
const client = getClient();
|
|
4344
|
+
const agent = String(row.assigned_to);
|
|
4345
|
+
const rawReviewer = row.reviewer || row.assigned_by;
|
|
4346
|
+
const reviewer = rawReviewer ? String(rawReviewer) : coordinatorName;
|
|
4347
|
+
const currentStatus = String(row.status ?? "");
|
|
4348
|
+
if (currentStatus === "done") return;
|
|
4349
|
+
const existingResult = String(row.result ?? "");
|
|
4350
|
+
if (existingResult.includes("## Review notes")) return;
|
|
4351
|
+
let reviewerRole = "unknown";
|
|
4352
|
+
try {
|
|
4353
|
+
const emp = getEmployee(employees, reviewer);
|
|
4354
|
+
if (emp) reviewerRole = emp.role;
|
|
4355
|
+
} catch {
|
|
4356
|
+
}
|
|
4357
|
+
const taskTitle = String(row.title);
|
|
4358
|
+
const taskSlug = taskFile.split("/").pop()?.replace(".md", "") ?? "unknown";
|
|
4359
|
+
const { lens, checklist } = getReviewChecklist(reviewerRole, agent, taskSlug);
|
|
4360
|
+
process.stderr.write(
|
|
4361
|
+
`[review] Annotating "${taskTitle}" for review by ${reviewer} (${reviewerRole})
|
|
4362
|
+
`
|
|
4363
|
+
);
|
|
4364
|
+
const reviewNotes = [
|
|
4365
|
+
`
|
|
4366
|
+
---
|
|
4367
|
+
## Review notes`,
|
|
4368
|
+
`Review lens: ${lens}`,
|
|
4369
|
+
`Reviewer: **${reviewer}** (${reviewerRole})`,
|
|
4370
|
+
"",
|
|
4371
|
+
"### Checklist",
|
|
4372
|
+
...checklist,
|
|
4373
|
+
"",
|
|
4374
|
+
"### Verdict",
|
|
4375
|
+
"- **Approved:** mark this task as done",
|
|
4376
|
+
"- **Needs work:** re-open with notes"
|
|
4377
|
+
].join("\n");
|
|
4378
|
+
const originalTaskId = String(row.id);
|
|
4379
|
+
const updatedResult = (result ?? "No result summary provided") + reviewNotes;
|
|
4380
|
+
await client.execute({
|
|
4381
|
+
sql: `UPDATE tasks SET result = ?, status = 'needs_review', updated_at = ?
|
|
4382
|
+
WHERE id = ?`,
|
|
4383
|
+
args: [updatedResult, now, originalTaskId]
|
|
4384
|
+
});
|
|
4385
|
+
orgBus.emit({
|
|
4386
|
+
type: "review_created",
|
|
4387
|
+
reviewId: originalTaskId,
|
|
4388
|
+
employee: agent,
|
|
4389
|
+
reviewer,
|
|
4390
|
+
timestamp: now
|
|
4391
|
+
});
|
|
4392
|
+
await writeNotification({
|
|
4393
|
+
agentId: agent,
|
|
4394
|
+
agentRole: String(row.assigned_to),
|
|
4395
|
+
event: "task_complete",
|
|
4396
|
+
project: String(row.project_name),
|
|
4397
|
+
summary: `completed "${taskTitle}" \u2014 ready for review`,
|
|
4398
|
+
taskFile
|
|
4399
|
+
});
|
|
4400
|
+
const originalPriority = String(row.priority).toLowerCase();
|
|
4401
|
+
const autoApprove = originalPriority === "p2" && result?.toLowerCase().includes("tests pass");
|
|
4402
|
+
if (!autoApprove) {
|
|
4403
|
+
try {
|
|
4404
|
+
const key = getSessionKey();
|
|
4405
|
+
const exeSession = getParentExe(key);
|
|
4406
|
+
if (exeSession) {
|
|
4407
|
+
sendIntercom(exeSession);
|
|
4408
|
+
}
|
|
4409
|
+
} catch {
|
|
4410
|
+
}
|
|
4411
|
+
}
|
|
4412
|
+
if (autoApprove) {
|
|
4413
|
+
process.stderr.write(
|
|
4414
|
+
`[review] Auto-approving "${taskTitle}" (P2 + tests pass)
|
|
4415
|
+
`
|
|
4416
|
+
);
|
|
4417
|
+
await client.execute({
|
|
4418
|
+
sql: "UPDATE tasks SET status = 'done', updated_at = ? WHERE id = ?",
|
|
4419
|
+
args: [now, originalTaskId]
|
|
4420
|
+
});
|
|
4421
|
+
}
|
|
4422
|
+
}
|
|
4296
4423
|
async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
4297
4424
|
if (String(row.assigned_by) !== "system" || !taskFile.includes("review-")) return;
|
|
4298
4425
|
try {
|
|
@@ -5656,15 +5783,17 @@ function sendIntercom(targetSession) {
|
|
|
5656
5783
|
logIntercom(`QUEUED \u2192 ${targetSession} (session busy)${batched2 > 0 ? ` [${batched2} batched]` : ""}`);
|
|
5657
5784
|
return "queued";
|
|
5658
5785
|
}
|
|
5659
|
-
|
|
5660
|
-
|
|
5661
|
-
|
|
5662
|
-
|
|
5663
|
-
|
|
5664
|
-
|
|
5665
|
-
|
|
5786
|
+
if (sessionState !== "idle") {
|
|
5787
|
+
try {
|
|
5788
|
+
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
5789
|
+
const agent = baseAgentName(rawAgent);
|
|
5790
|
+
const markerPath = path18.join(SESSION_CACHE, `current-task-${agent}.json`);
|
|
5791
|
+
if (existsSync14(markerPath)) {
|
|
5792
|
+
logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker + not idle \u2014 will auto-chain)`);
|
|
5793
|
+
return "debounced";
|
|
5794
|
+
}
|
|
5795
|
+
} catch {
|
|
5666
5796
|
}
|
|
5667
|
-
} catch {
|
|
5668
5797
|
}
|
|
5669
5798
|
try {
|
|
5670
5799
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
@@ -5735,6 +5864,24 @@ function notifyCoordinatorTaskCompletion(coordinatorSession, agentName, taskTitl
|
|
|
5735
5864
|
try {
|
|
5736
5865
|
const sessions = transport.listSessions();
|
|
5737
5866
|
if (!sessions.includes(coordinatorSession)) return false;
|
|
5867
|
+
try {
|
|
5868
|
+
const { countPendingReviews: countPendingReviews2 } = (init_tasks_review(), __toCommonJS(tasks_review_exports));
|
|
5869
|
+
const pending = countPendingReviews2(coordinatorSession);
|
|
5870
|
+
if (pending instanceof Promise) {
|
|
5871
|
+
pending.then((count) => {
|
|
5872
|
+
if (count > 0) {
|
|
5873
|
+
execSync7(
|
|
5874
|
+
`tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
|
|
5875
|
+
{ timeout: 3e3 }
|
|
5876
|
+
);
|
|
5877
|
+
logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}", ${count} reviews pending)`);
|
|
5878
|
+
}
|
|
5879
|
+
}).catch(() => {
|
|
5880
|
+
});
|
|
5881
|
+
return true;
|
|
5882
|
+
}
|
|
5883
|
+
} catch {
|
|
5884
|
+
}
|
|
5738
5885
|
execSync7(
|
|
5739
5886
|
`tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
|
|
5740
5887
|
{ timeout: 3e3 }
|
|
@@ -7353,7 +7500,7 @@ function getRecentCommits(limit = DEFAULT_COMMIT_LIMIT) {
|
|
|
7353
7500
|
return [];
|
|
7354
7501
|
}
|
|
7355
7502
|
}
|
|
7356
|
-
function
|
|
7503
|
+
function isStale2(updatedAt, staleMinutes) {
|
|
7357
7504
|
const updated = new Date(updatedAt).getTime();
|
|
7358
7505
|
const threshold = Date.now() - staleMinutes * 6e4;
|
|
7359
7506
|
return updated < threshold;
|
|
@@ -7418,7 +7565,7 @@ async function sweepTasks(projectName, options = {}) {
|
|
|
7418
7565
|
return result;
|
|
7419
7566
|
}
|
|
7420
7567
|
for (const task of tasks) {
|
|
7421
|
-
if (!
|
|
7568
|
+
if (!isStale2(task.updatedAt, staleMinutes)) {
|
|
7422
7569
|
result.unchanged++;
|
|
7423
7570
|
continue;
|
|
7424
7571
|
}
|
|
@@ -5307,6 +5307,12 @@ async function cloudSync(config) {
|
|
|
5307
5307
|
pulled = pullResult.records.length;
|
|
5308
5308
|
}
|
|
5309
5309
|
}
|
|
5310
|
+
if (pulled > 0) {
|
|
5311
|
+
try {
|
|
5312
|
+
await pushToPostgres(pullResult.records);
|
|
5313
|
+
} catch {
|
|
5314
|
+
}
|
|
5315
|
+
}
|
|
5310
5316
|
if (pullResult.maxVersion > lastPullVersion) {
|
|
5311
5317
|
await client.execute({
|
|
5312
5318
|
sql: "INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('last_cloud_pull_version', ?)",
|
package/dist/index.js
CHANGED
|
@@ -4489,8 +4489,35 @@ var init_tasks_crud = __esm({
|
|
|
4489
4489
|
});
|
|
4490
4490
|
|
|
4491
4491
|
// src/lib/tasks-review.ts
|
|
4492
|
+
var tasks_review_exports = {};
|
|
4493
|
+
__export(tasks_review_exports, {
|
|
4494
|
+
cleanupOrphanedReviews: () => cleanupOrphanedReviews,
|
|
4495
|
+
cleanupReviewFile: () => cleanupReviewFile,
|
|
4496
|
+
countNewPendingReviewsSince: () => countNewPendingReviewsSince,
|
|
4497
|
+
countPendingReviews: () => countPendingReviews,
|
|
4498
|
+
createReviewForCompletedTask: () => createReviewForCompletedTask,
|
|
4499
|
+
formatAge: () => formatAge,
|
|
4500
|
+
getReviewChecklist: () => getReviewChecklist,
|
|
4501
|
+
isStale: () => isStale,
|
|
4502
|
+
listPendingReviews: () => listPendingReviews
|
|
4503
|
+
});
|
|
4492
4504
|
import path15 from "path";
|
|
4493
4505
|
import { existsSync as existsSync13, readdirSync as readdirSync2, unlinkSync as unlinkSync4 } from "fs";
|
|
4506
|
+
function formatAge(isoTimestamp) {
|
|
4507
|
+
if (!isoTimestamp) return "";
|
|
4508
|
+
const ms = Date.now() - new Date(isoTimestamp).getTime();
|
|
4509
|
+
if (ms < 0) return "just now";
|
|
4510
|
+
const minutes = Math.floor(ms / 6e4);
|
|
4511
|
+
if (minutes < 60) return `${minutes}m ago`;
|
|
4512
|
+
const hours = Math.floor(minutes / 60);
|
|
4513
|
+
if (hours < 24) return `${hours}h ago`;
|
|
4514
|
+
const days = Math.floor(hours / 24);
|
|
4515
|
+
return `${days}d ago`;
|
|
4516
|
+
}
|
|
4517
|
+
function isStale(isoTimestamp) {
|
|
4518
|
+
if (!isoTimestamp) return false;
|
|
4519
|
+
return Date.now() - new Date(isoTimestamp).getTime() > 24 * 60 * 60 * 1e3;
|
|
4520
|
+
}
|
|
4494
4521
|
async function countPendingReviews(sessionScope) {
|
|
4495
4522
|
const client = getClient();
|
|
4496
4523
|
const scope = strictSessionScopeFilter(
|
|
@@ -4611,6 +4638,95 @@ function getReviewChecklist(role, agent, taskSlug) {
|
|
|
4611
4638
|
]
|
|
4612
4639
|
};
|
|
4613
4640
|
}
|
|
4641
|
+
async function createReviewForCompletedTask(row, result, _baseDir, now) {
|
|
4642
|
+
const taskFile = String(row.task_file);
|
|
4643
|
+
const employees = await loadEmployees();
|
|
4644
|
+
const coordinatorName = getCoordinatorName(employees);
|
|
4645
|
+
if (isCoordinatorName(String(row.assigned_to), employees)) return;
|
|
4646
|
+
if (String(row.title).startsWith("Review:")) return;
|
|
4647
|
+
const fileName = taskFile.split("/").pop() ?? "";
|
|
4648
|
+
if (fileName.startsWith("review-") && String(row.assigned_by) === "system") return;
|
|
4649
|
+
if (fileName.startsWith("review-") && String(row.assigned_to) === "system") return;
|
|
4650
|
+
const client = getClient();
|
|
4651
|
+
const agent = String(row.assigned_to);
|
|
4652
|
+
const rawReviewer = row.reviewer || row.assigned_by;
|
|
4653
|
+
const reviewer = rawReviewer ? String(rawReviewer) : coordinatorName;
|
|
4654
|
+
const currentStatus = String(row.status ?? "");
|
|
4655
|
+
if (currentStatus === "done") return;
|
|
4656
|
+
const existingResult = String(row.result ?? "");
|
|
4657
|
+
if (existingResult.includes("## Review notes")) return;
|
|
4658
|
+
let reviewerRole = "unknown";
|
|
4659
|
+
try {
|
|
4660
|
+
const emp = getEmployee(employees, reviewer);
|
|
4661
|
+
if (emp) reviewerRole = emp.role;
|
|
4662
|
+
} catch {
|
|
4663
|
+
}
|
|
4664
|
+
const taskTitle = String(row.title);
|
|
4665
|
+
const taskSlug = taskFile.split("/").pop()?.replace(".md", "") ?? "unknown";
|
|
4666
|
+
const { lens, checklist } = getReviewChecklist(reviewerRole, agent, taskSlug);
|
|
4667
|
+
process.stderr.write(
|
|
4668
|
+
`[review] Annotating "${taskTitle}" for review by ${reviewer} (${reviewerRole})
|
|
4669
|
+
`
|
|
4670
|
+
);
|
|
4671
|
+
const reviewNotes = [
|
|
4672
|
+
`
|
|
4673
|
+
---
|
|
4674
|
+
## Review notes`,
|
|
4675
|
+
`Review lens: ${lens}`,
|
|
4676
|
+
`Reviewer: **${reviewer}** (${reviewerRole})`,
|
|
4677
|
+
"",
|
|
4678
|
+
"### Checklist",
|
|
4679
|
+
...checklist,
|
|
4680
|
+
"",
|
|
4681
|
+
"### Verdict",
|
|
4682
|
+
"- **Approved:** mark this task as done",
|
|
4683
|
+
"- **Needs work:** re-open with notes"
|
|
4684
|
+
].join("\n");
|
|
4685
|
+
const originalTaskId = String(row.id);
|
|
4686
|
+
const updatedResult = (result ?? "No result summary provided") + reviewNotes;
|
|
4687
|
+
await client.execute({
|
|
4688
|
+
sql: `UPDATE tasks SET result = ?, status = 'needs_review', updated_at = ?
|
|
4689
|
+
WHERE id = ?`,
|
|
4690
|
+
args: [updatedResult, now, originalTaskId]
|
|
4691
|
+
});
|
|
4692
|
+
orgBus.emit({
|
|
4693
|
+
type: "review_created",
|
|
4694
|
+
reviewId: originalTaskId,
|
|
4695
|
+
employee: agent,
|
|
4696
|
+
reviewer,
|
|
4697
|
+
timestamp: now
|
|
4698
|
+
});
|
|
4699
|
+
await writeNotification({
|
|
4700
|
+
agentId: agent,
|
|
4701
|
+
agentRole: String(row.assigned_to),
|
|
4702
|
+
event: "task_complete",
|
|
4703
|
+
project: String(row.project_name),
|
|
4704
|
+
summary: `completed "${taskTitle}" \u2014 ready for review`,
|
|
4705
|
+
taskFile
|
|
4706
|
+
});
|
|
4707
|
+
const originalPriority = String(row.priority).toLowerCase();
|
|
4708
|
+
const autoApprove = originalPriority === "p2" && result?.toLowerCase().includes("tests pass");
|
|
4709
|
+
if (!autoApprove) {
|
|
4710
|
+
try {
|
|
4711
|
+
const key = getSessionKey();
|
|
4712
|
+
const exeSession = getParentExe(key);
|
|
4713
|
+
if (exeSession) {
|
|
4714
|
+
sendIntercom(exeSession);
|
|
4715
|
+
}
|
|
4716
|
+
} catch {
|
|
4717
|
+
}
|
|
4718
|
+
}
|
|
4719
|
+
if (autoApprove) {
|
|
4720
|
+
process.stderr.write(
|
|
4721
|
+
`[review] Auto-approving "${taskTitle}" (P2 + tests pass)
|
|
4722
|
+
`
|
|
4723
|
+
);
|
|
4724
|
+
await client.execute({
|
|
4725
|
+
sql: "UPDATE tasks SET status = 'done', updated_at = ? WHERE id = ?",
|
|
4726
|
+
args: [now, originalTaskId]
|
|
4727
|
+
});
|
|
4728
|
+
}
|
|
4729
|
+
}
|
|
4614
4730
|
async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
4615
4731
|
if (String(row.assigned_by) !== "system" || !taskFile.includes("review-")) return;
|
|
4616
4732
|
try {
|
|
@@ -6036,15 +6152,17 @@ function sendIntercom(targetSession) {
|
|
|
6036
6152
|
logIntercom(`QUEUED \u2192 ${targetSession} (session busy)${batched2 > 0 ? ` [${batched2} batched]` : ""}`);
|
|
6037
6153
|
return "queued";
|
|
6038
6154
|
}
|
|
6039
|
-
|
|
6040
|
-
|
|
6041
|
-
|
|
6042
|
-
|
|
6043
|
-
|
|
6044
|
-
|
|
6045
|
-
|
|
6155
|
+
if (sessionState !== "idle") {
|
|
6156
|
+
try {
|
|
6157
|
+
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
6158
|
+
const agent = baseAgentName(rawAgent);
|
|
6159
|
+
const markerPath = path18.join(SESSION_CACHE, `current-task-${agent}.json`);
|
|
6160
|
+
if (existsSync14(markerPath)) {
|
|
6161
|
+
logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker + not idle \u2014 will auto-chain)`);
|
|
6162
|
+
return "debounced";
|
|
6163
|
+
}
|
|
6164
|
+
} catch {
|
|
6046
6165
|
}
|
|
6047
|
-
} catch {
|
|
6048
6166
|
}
|
|
6049
6167
|
try {
|
|
6050
6168
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
@@ -6115,6 +6233,24 @@ function notifyCoordinatorTaskCompletion(coordinatorSession, agentName, taskTitl
|
|
|
6115
6233
|
try {
|
|
6116
6234
|
const sessions = transport.listSessions();
|
|
6117
6235
|
if (!sessions.includes(coordinatorSession)) return false;
|
|
6236
|
+
try {
|
|
6237
|
+
const { countPendingReviews: countPendingReviews2 } = (init_tasks_review(), __toCommonJS(tasks_review_exports));
|
|
6238
|
+
const pending = countPendingReviews2(coordinatorSession);
|
|
6239
|
+
if (pending instanceof Promise) {
|
|
6240
|
+
pending.then((count) => {
|
|
6241
|
+
if (count > 0) {
|
|
6242
|
+
execSync7(
|
|
6243
|
+
`tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
|
|
6244
|
+
{ timeout: 3e3 }
|
|
6245
|
+
);
|
|
6246
|
+
logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}", ${count} reviews pending)`);
|
|
6247
|
+
}
|
|
6248
|
+
}).catch(() => {
|
|
6249
|
+
});
|
|
6250
|
+
return true;
|
|
6251
|
+
}
|
|
6252
|
+
} catch {
|
|
6253
|
+
}
|
|
6118
6254
|
execSync7(
|
|
6119
6255
|
`tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
|
|
6120
6256
|
{ timeout: 3e3 }
|
package/dist/lib/cloud-sync.js
CHANGED
|
@@ -3223,6 +3223,12 @@ async function cloudSync(config) {
|
|
|
3223
3223
|
pulled = pullResult.records.length;
|
|
3224
3224
|
}
|
|
3225
3225
|
}
|
|
3226
|
+
if (pulled > 0) {
|
|
3227
|
+
try {
|
|
3228
|
+
await pushToPostgres(pullResult.records);
|
|
3229
|
+
} catch {
|
|
3230
|
+
}
|
|
3231
|
+
}
|
|
3226
3232
|
if (pullResult.maxVersion > lastPullVersion) {
|
|
3227
3233
|
await client.execute({
|
|
3228
3234
|
sql: "INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('last_cloud_pull_version', ?)",
|
package/dist/lib/exe-daemon.js
CHANGED
|
@@ -6184,15 +6184,17 @@ function sendIntercom(targetSession) {
|
|
|
6184
6184
|
logIntercom(`QUEUED \u2192 ${targetSession} (session busy)${batched2 > 0 ? ` [${batched2} batched]` : ""}`);
|
|
6185
6185
|
return "queued";
|
|
6186
6186
|
}
|
|
6187
|
-
|
|
6188
|
-
|
|
6189
|
-
|
|
6190
|
-
|
|
6191
|
-
|
|
6192
|
-
|
|
6193
|
-
|
|
6187
|
+
if (sessionState !== "idle") {
|
|
6188
|
+
try {
|
|
6189
|
+
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
6190
|
+
const agent = baseAgentName(rawAgent);
|
|
6191
|
+
const markerPath = path17.join(SESSION_CACHE, `current-task-${agent}.json`);
|
|
6192
|
+
if (existsSync14(markerPath)) {
|
|
6193
|
+
logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker + not idle \u2014 will auto-chain)`);
|
|
6194
|
+
return "debounced";
|
|
6195
|
+
}
|
|
6196
|
+
} catch {
|
|
6194
6197
|
}
|
|
6195
|
-
} catch {
|
|
6196
6198
|
}
|
|
6197
6199
|
try {
|
|
6198
6200
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
@@ -6263,6 +6265,24 @@ function notifyCoordinatorTaskCompletion(coordinatorSession, agentName, taskTitl
|
|
|
6263
6265
|
try {
|
|
6264
6266
|
const sessions = transport.listSessions();
|
|
6265
6267
|
if (!sessions.includes(coordinatorSession)) return false;
|
|
6268
|
+
try {
|
|
6269
|
+
const { countPendingReviews: countPendingReviews2 } = (init_tasks_review(), __toCommonJS(tasks_review_exports));
|
|
6270
|
+
const pending = countPendingReviews2(coordinatorSession);
|
|
6271
|
+
if (pending instanceof Promise) {
|
|
6272
|
+
pending.then((count) => {
|
|
6273
|
+
if (count > 0) {
|
|
6274
|
+
execSync7(
|
|
6275
|
+
`tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
|
|
6276
|
+
{ timeout: 3e3 }
|
|
6277
|
+
);
|
|
6278
|
+
logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}", ${count} reviews pending)`);
|
|
6279
|
+
}
|
|
6280
|
+
}).catch(() => {
|
|
6281
|
+
});
|
|
6282
|
+
return true;
|
|
6283
|
+
}
|
|
6284
|
+
} catch {
|
|
6285
|
+
}
|
|
6266
6286
|
execSync7(
|
|
6267
6287
|
`tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
|
|
6268
6288
|
{ timeout: 3e3 }
|
package/dist/lib/messaging.js
CHANGED
|
@@ -773,15 +773,17 @@ function sendIntercom(targetSession) {
|
|
|
773
773
|
logIntercom(`QUEUED \u2192 ${targetSession} (session busy)${batched2 > 0 ? ` [${batched2} batched]` : ""}`);
|
|
774
774
|
return "queued";
|
|
775
775
|
}
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
776
|
+
if (sessionState !== "idle") {
|
|
777
|
+
try {
|
|
778
|
+
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
779
|
+
const agent = baseAgentName(rawAgent);
|
|
780
|
+
const markerPath = path9.join(SESSION_CACHE, `current-task-${agent}.json`);
|
|
781
|
+
if (existsSync8(markerPath)) {
|
|
782
|
+
logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker + not idle \u2014 will auto-chain)`);
|
|
783
|
+
return "debounced";
|
|
784
|
+
}
|
|
785
|
+
} catch {
|
|
783
786
|
}
|
|
784
|
-
} catch {
|
|
785
787
|
}
|
|
786
788
|
try {
|
|
787
789
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|