@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.
@@ -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
- try {
5660
- const rawAgent = targetSession.split("-")[0] ?? targetSession;
5661
- const agent = baseAgentName(rawAgent);
5662
- const markerPath = path18.join(SESSION_CACHE, `current-task-${agent}.json`);
5663
- if (existsSync14(markerPath)) {
5664
- logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker \u2014 will auto-chain)`);
5665
- return "debounced";
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 isStale(updatedAt, staleMinutes) {
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 (!isStale(task.updatedAt, staleMinutes)) {
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
- try {
6040
- const rawAgent = targetSession.split("-")[0] ?? targetSession;
6041
- const agent = baseAgentName(rawAgent);
6042
- const markerPath = path18.join(SESSION_CACHE, `current-task-${agent}.json`);
6043
- if (existsSync14(markerPath)) {
6044
- logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker \u2014 will auto-chain)`);
6045
- return "debounced";
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 }
@@ -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', ?)",
@@ -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
- try {
6188
- const rawAgent = targetSession.split("-")[0] ?? targetSession;
6189
- const agent = baseAgentName(rawAgent);
6190
- const markerPath = path17.join(SESSION_CACHE, `current-task-${agent}.json`);
6191
- if (existsSync14(markerPath)) {
6192
- logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker \u2014 will auto-chain)`);
6193
- return "debounced";
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 }
@@ -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
- try {
777
- const rawAgent = targetSession.split("-")[0] ?? targetSession;
778
- const agent = baseAgentName(rawAgent);
779
- const markerPath = path9.join(SESSION_CACHE, `current-task-${agent}.json`);
780
- if (existsSync8(markerPath)) {
781
- logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker \u2014 will auto-chain)`);
782
- return "debounced";
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;