@desplega.ai/agent-swarm 1.114.0 → 1.116.0

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.
Files changed (117) hide show
  1. package/dist/{actions-bm8vdxys.js → actions-brtj223f.js} +6 -6
  2. package/dist/{app-bhwdxmvp.js → app-00sngzz7.js} +4 -4
  3. package/dist/{assistant-15mp2apr.js → assistant-j2ser3vg.js} +9 -9
  4. package/dist/{boot-reembed-b9sbk87z.js → boot-reembed-fv3r1kqg.js} +3 -3
  5. package/dist/{boot-reembed-awr2179r.js → boot-reembed-v4n0jphx.js} +4 -4
  6. package/dist/{boot-scrub-logs-c1zsaw9m.js → boot-scrub-logs-72sy5z1r.js} +2 -2
  7. package/dist/{claude-adapter-2k45y7p7.js → claude-adapter-3csn5bks.js} +2 -2
  8. package/dist/{cli-hq39e630.js → cli-0h9yzvhe.js} +95 -10
  9. package/dist/{cli-49j095n9.js → cli-0pwtr9f0.js} +1 -1
  10. package/dist/{cli-zmmp2r79.js → cli-2rxckty2.js} +13 -2
  11. package/dist/{cli-w3nq88yc.js → cli-3ys55x7x.js} +2 -2
  12. package/dist/{cli-7rgh0w0k.js → cli-50czs4qy.js} +10 -3
  13. package/dist/{cli-f9n2jg31.js → cli-5g3hx9cc.js} +2 -2
  14. package/dist/{cli-0kgcv878.js → cli-86fdnerz.js} +63 -2
  15. package/dist/{cli-a6tzw6vq.js → cli-96nd7vgs.js} +6 -6
  16. package/dist/{cli-2t4e4ayk.js → cli-9ys4kdd4.js} +46 -5
  17. package/dist/{cli-hvg05qwm.js → cli-b4hf0g0x.js} +1 -1
  18. package/dist/{cli-a0t7vapf.js → cli-bc89my0z.js} +1 -1
  19. package/dist/{cli-q4e2x252.js → cli-cjmz7pp9.js} +73 -24
  20. package/dist/{cli-tkmm5nf9.js → cli-e61fs2hp.js} +59 -2
  21. package/dist/{cli-5ktc9fd4.js → cli-et9ab56s.js} +1 -1
  22. package/dist/{cli-jzwtn8rm.js → cli-k4yp6w9w.js} +188 -18
  23. package/dist/{cli-6xd0bvf8.js → cli-ma04faw5.js} +1 -1
  24. package/dist/{cli-ggy8c7tk.js → cli-mnbgv3w7.js} +1 -1
  25. package/dist/{cli-312v3c41.js → cli-n9kkkmtm.js} +1 -1
  26. package/dist/{cli-f5pzfvwb.js → cli-qnnbe59j.js} +2 -2
  27. package/dist/{cli-113wmegp.js → cli-rmyjzd8s.js} +1 -1
  28. package/dist/{cli-k6h2zkw3.js → cli-tw7xjzk7.js} +1 -1
  29. package/dist/{cli-r3mtrrzp.js → cli-ty7qe0vp.js} +12 -12
  30. package/dist/{cli-w4wk3kx6.js → cli-vb7g0zgk.js} +2 -2
  31. package/dist/{cli-5afkmcfj.js → cli-vggz87j8.js} +6 -6
  32. package/dist/{cli-byjcad11.js → cli-vnvv6hvm.js} +1 -1
  33. package/dist/cli-wh91zby6.js +187222 -0
  34. package/dist/{cli-q1z5d5ss.js → cli-x4trwrcr.js} +4 -4
  35. package/dist/{cli-pnfyyp6h.js → cli-x9q6bghe.js} +6 -3
  36. package/dist/{cli-e2xbxmhd.js → cli-xj6zntcq.js} +90 -32
  37. package/dist/cli.js +11 -11
  38. package/dist/{codex-adapter-4z8m9mb5.js → codex-adapter-de3w0nch.js} +2 -2
  39. package/dist/{codex-session-runner-rbct46dg.js → codex-session-runner-bnw8cysk.js} +2 -2
  40. package/dist/{commands-zk4d6esf.js → commands-4jt6hvc9.js} +2 -2
  41. package/dist/{db-stn16jp2.js → db-1p5e9zj8.js} +12 -2
  42. package/dist/{handlers-gshprvpf.js → handlers-5n9gq3ek.js} +9 -9
  43. package/dist/{hook-amzx9tap.js → hook-ny4kv3r0.js} +1 -1
  44. package/dist/{http-6g0hfzax.js → http-bb3pva4d.js} +182 -65
  45. package/dist/{index-f03rnnrs.js → index-ajkj0aaf.js} +9 -9
  46. package/dist/{index-df8fmphk.js → index-dpcmhqy0.js} +331 -30
  47. package/dist/{index-jbm7qcqk.js → index-mheeg078.js} +8 -8
  48. package/dist/{index-ty42rxw9.js → index-nzghbv06.js} +10 -10
  49. package/dist/{keepalive-pm6g2wpw.js → keepalive-vbgjzp3y.js} +4 -4
  50. package/dist/{lead-h79fzpzw.js → lead-98bg3kfz.js} +24 -24
  51. package/dist/{maintenance-433zndy1.js → maintenance-1fp5fz08.js} +4 -4
  52. package/dist/{oauth-refresh-sweep-mjsbmsk2.js → oauth-refresh-sweep-91s6j3dq.js} +3 -3
  53. package/dist/{onboard-vdr9hc3f.js → onboard-n32hf2t4.js} +2 -2
  54. package/dist/{opencode-adapter-mqsxr170.js → opencode-adapter-p6bxnbm4.js} +2 -2
  55. package/dist/{otel-impl-yswd0fty.js → otel-impl-j42cnqry.js} +1 -1
  56. package/dist/{pi-mono-adapter-vbrvg1p8.js → pi-mono-adapter-05sck1ds.js} +1 -1
  57. package/dist/{pricing-refresh-2kk2ap9b.js → pricing-refresh-56gkeqaw.js} +4 -4
  58. package/dist/{rbac-roles-2wgh9b5t.js → rbac-roles-c1ky22v2.js} +11 -4
  59. package/dist/{rbac-roles-xdc5208b.js → rbac-roles-xwt8319h.js} +4 -4
  60. package/dist/{seed-pricing-m9h69d2t.js → seed-pricing-qagjzjqj.js} +3 -3
  61. package/dist/{setup-eynkvafa.js → setup-1kk84v0w.js} +2 -2
  62. package/dist/{worker-136q8dc2.js → worker-bmjqg3c6.js} +24 -24
  63. package/openapi.json +8 -4
  64. package/package.json +2 -2
  65. package/src/be/db.ts +253 -20
  66. package/src/be/migrations/113_task_routing_affinity.sql +9 -0
  67. package/src/be/migrations/114_backfill_gpt_5_6_pricing.sql +15 -0
  68. package/src/be/modelsdev-cache.json +135 -0
  69. package/src/be/rbac-audit.ts +19 -8
  70. package/src/be/rbac-roles.ts +8 -1
  71. package/src/be/script-connections.ts +4 -4
  72. package/src/be/scripts/typecheck.ts +11 -0
  73. package/src/be/seed-scripts/catalog/catalog-report.inline.ts +325 -24
  74. package/src/be/seed-scripts/catalog/catalog-report.ts +325 -24
  75. package/src/heartbeat/heartbeat.ts +143 -21
  76. package/src/http/favorites.ts +1 -5
  77. package/src/http/mcp-servers.ts +38 -0
  78. package/src/http/poll.ts +7 -2
  79. package/src/http/scripts.ts +6 -0
  80. package/src/http/skills.ts +11 -0
  81. package/src/http/tasks.ts +21 -9
  82. package/src/providers/codex-models.ts +100 -30
  83. package/src/providers/modelsdev-reasoning.json +51 -0
  84. package/src/providers/reasoning-effort.ts +11 -5
  85. package/src/rbac/admission.ts +26 -2
  86. package/src/rbac/index.ts +2 -2
  87. package/src/rbac/legacy-policy.ts +9 -0
  88. package/src/rbac/permissions.ts +36 -0
  89. package/src/scripts-runtime/api-client.ts +82 -6
  90. package/src/scripts-runtime/types/swarm-sdk.d.ts +11 -0
  91. package/src/server-user.ts +114 -59
  92. package/src/slack/responses.ts +91 -1
  93. package/src/slack/watcher.ts +9 -6
  94. package/src/tasks/worker-follow-up.ts +100 -3
  95. package/src/tests/codex-adapter.test.ts +46 -23
  96. package/src/tests/heartbeat.test.ts +48 -3
  97. package/src/tests/model-control.test.ts +8 -2
  98. package/src/tests/pool-affinity.test.ts +401 -0
  99. package/src/tests/rbac-admission-e2e.test.ts +152 -7
  100. package/src/tests/rbac-admission.test.ts +60 -1
  101. package/src/tests/rbac-engine.test.ts +9 -0
  102. package/src/tests/rbac-mcp-admission-e2e.test.ts +362 -0
  103. package/src/tests/rbac-roles.test.ts +2 -0
  104. package/src/tests/reasoning-effort.test.ts +15 -2
  105. package/src/tests/script-apis-mcp.test.ts +37 -13
  106. package/src/tests/script-connections.test.ts +27 -1
  107. package/src/tests/scripts-runtime.test.ts +187 -0
  108. package/src/tests/seed-scripts.test.ts +36 -0
  109. package/src/tests/slack-inline-output.test.ts +65 -0
  110. package/src/tests/user-token-rest-auth.test.ts +110 -0
  111. package/src/tools/script-apis.ts +62 -3
  112. package/src/tools/send-task.ts +15 -1
  113. package/src/tools/task-action.ts +34 -2
  114. package/src/tools/templates.ts +48 -0
  115. package/src/tools/utils.ts +2 -0
  116. package/src/types.ts +27 -5
  117. package/dist/cli-46b6kc7f.js +0 -1698
@@ -2,6 +2,7 @@ import {
2
2
  taskAttachmentDisplayUrl
3
3
  } from "./cli-wspgs9bt.js";
4
4
  import {
5
+ buildRoutingAffinityFromAgent,
5
6
  createTaskExtended,
6
7
  getActiveTaskCount,
7
8
  getAgentById,
@@ -17,7 +18,7 @@ import {
17
18
  init_templates1 as init_templates,
18
19
  normalizeDateRequired,
19
20
  resolveTemplate
20
- } from "./cli-jzwtn8rm.js";
21
+ } from "./cli-k4yp6w9w.js";
21
22
 
22
23
  // src/be/db-queries/tracker.ts
23
24
  init_date_utils();
@@ -147,6 +148,13 @@ function isGracefulResumePinEnabled() {
147
148
  var RESUME_GENERATION_TAG_PREFIX = "resume-generation:";
148
149
  var CRASH_RECOVERY_PIN_TAG = "crash-recovery-pin";
149
150
  var GRACEFUL_SHUTDOWN_PIN_TAG = "graceful-shutdown-pin";
151
+ var REBOOT_RETRY_PIN_TAG = "reboot-retry-pin";
152
+ function getPinCandidateAgent(agentId) {
153
+ const candidate = getAgentById(agentId);
154
+ if (!candidate || candidate.status === "offline")
155
+ return null;
156
+ return candidate;
157
+ }
150
158
  function getResumeGeneration(task) {
151
159
  const tag = task.tags.find((value) => value.startsWith(RESUME_GENERATION_TAG_PREFIX));
152
160
  if (!tag)
@@ -247,8 +255,8 @@ function createResumeFollowUp(args) {
247
255
  }
248
256
  let preferredAgentId;
249
257
  if (parent.agentId) {
250
- const candidate = getAgentById(parent.agentId);
251
- if (candidate && candidate.status !== "offline") {
258
+ const candidate = getPinCandidateAgent(parent.agentId);
259
+ if (candidate) {
252
260
  const lastActivity = candidate.lastActivityAt ? Date.parse(candidate.lastActivityAt) : 0;
253
261
  const fresh = Number.isFinite(lastActivity) && Date.now() - lastActivity < WORKER_LIVENESS_WINDOW_SECONDS * 1000;
254
262
  const activeCount = getActiveTaskCount(candidate.id);
@@ -289,6 +297,7 @@ function createResumeFollowUp(args) {
289
297
  if (args.reason === "graceful_shutdown" && preferredAgentId !== undefined) {
290
298
  tags.push(GRACEFUL_SHUTDOWN_PIN_TAG);
291
299
  }
300
+ const routingAffinity = parent.agentId ? buildRoutingAffinityFromAgent(parent.agentId) ?? undefined : undefined;
292
301
  const created = createTaskExtended(followUpDescription, {
293
302
  agentId: preferredAgentId,
294
303
  creatorAgentId: parent.creatorAgentId,
@@ -296,7 +305,8 @@ function createResumeFollowUp(args) {
296
305
  taskType: "resume",
297
306
  tags,
298
307
  priority,
299
- parentTaskId: parent.id
308
+ parentTaskId: parent.id,
309
+ routingAffinity
300
310
  });
301
311
  const repointed = repointTrackerSyncBySwarmId(parent.id, created.id);
302
312
  if (repointed > 0) {
@@ -338,5 +348,36 @@ function createRerouteDecisionTask(args) {
338
348
  });
339
349
  return { kind: "created", task: created };
340
350
  }
351
+ function createPoolStarvationDecisionTask(args) {
352
+ const { original } = args;
353
+ const leadAgent = getLeadAgent();
354
+ if (!leadAgent)
355
+ return { kind: "skipped", reason: "lead_not_found" };
356
+ if (hasNonTerminalRerouteDecisionChild(original.id)) {
357
+ return { kind: "skipped", reason: "duplicate_exists" };
358
+ }
359
+ const affinity = original.routingAffinity;
360
+ const requiredRole = affinity?.role ?? "(none declared)";
361
+ const requiredCapabilities = affinity?.capabilities && affinity.capabilities.length > 0 ? affinity.capabilities.join(", ") : "(none declared)";
362
+ const attachmentsBlock = formatAttachmentsBlock(getTaskAttachments(original.id));
363
+ const decision = resolveTemplate("task.pool.starved.decision", {
364
+ original_task_id: original.id,
365
+ task_desc: original.task.slice(0, 200),
366
+ required_role: requiredRole,
367
+ required_capabilities: requiredCapabilities,
368
+ artifacts_block: attachmentsBlock
369
+ });
370
+ const created = createTaskExtended(decision.text, {
371
+ agentId: leadAgent.id,
372
+ creatorAgentId: original.creatorAgentId,
373
+ source: "system",
374
+ taskType: "reroute-decision",
375
+ tags: ["reroute-decision", "pool-starvation"],
376
+ priority: Math.min(100, (original.priority ?? 50) + 10),
377
+ parentTaskId: original.id,
378
+ inheritParentOutputSchema: false
379
+ });
380
+ return { kind: "created", task: created };
381
+ }
341
382
 
342
- export { getTrackerSync, getTrackerSyncByExternalId, createTrackerSyncIfAbsent, updateTrackerSyncSwarmId, repointTrackerSyncBySwarmId, createTrackerSync, updateTrackerSync, deleteTrackerSync, hasTrackerDelivery, markTrackerDelivery, getAllTrackerSyncs, createTrackerAgentMapping, getResumeGeneration, getNextResumeGeneration, createWorkerTaskFollowUp, createResumeFollowUp, createRerouteDecisionTask };
383
+ export { getTrackerSync, getTrackerSyncByExternalId, createTrackerSyncIfAbsent, updateTrackerSyncSwarmId, repointTrackerSyncBySwarmId, createTrackerSync, updateTrackerSync, deleteTrackerSync, hasTrackerDelivery, markTrackerDelivery, getAllTrackerSyncs, createTrackerAgentMapping, REBOOT_RETRY_PIN_TAG, getPinCandidateAgent, getResumeGeneration, getNextResumeGeneration, createWorkerTaskFollowUp, createResumeFollowUp, createRerouteDecisionTask, createPoolStarvationDecisionTask };
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  init_package,
3
3
  package_default
4
- } from "./cli-f5pzfvwb.js";
4
+ } from "./cli-qnnbe59j.js";
5
5
 
6
6
  // src/commands/shared/client-config.ts
7
7
  init_package();
@@ -20,7 +20,7 @@ import {
20
20
  } from "./cli-tghre7g6.js";
21
21
  import {
22
22
  applyReasoningEffort
23
- } from "./cli-tkmm5nf9.js";
23
+ } from "./cli-e61fs2hp.js";
24
24
  import {
25
25
  init_credentials,
26
26
  validateOpencodeCredentials
@@ -1,18 +1,22 @@
1
1
  import {
2
+ REBOOT_RETRY_PIN_TAG,
3
+ createPoolStarvationDecisionTask,
2
4
  createRerouteDecisionTask,
3
5
  createResumeFollowUp,
4
6
  getNextResumeGeneration,
7
+ getPinCandidateAgent,
5
8
  getResumeGeneration,
6
9
  repointTrackerSyncBySwarmId
7
- } from "./cli-2t4e4ayk.js";
10
+ } from "./cli-9ys4kdd4.js";
8
11
  import {
9
12
  getExecutorRegistry,
10
13
  recoverIncompleteRuns
11
- } from "./cli-a6tzw6vq.js";
14
+ } from "./cli-96nd7vgs.js";
12
15
  import {
13
16
  MAX_EMPTY_POLLS,
14
17
  assignUnassignedTaskPending,
15
18
  backfillSupersedeTaskResumeTaskId,
19
+ buildRoutingAffinityFromAgent,
16
20
  cleanupStaleSessions,
17
21
  createTaskExtended,
18
22
  deleteActiveSession,
@@ -28,6 +32,7 @@ import {
28
32
  getRecentFailedCount,
29
33
  getRecentFailedTasks,
30
34
  getStalePinnedResumes,
35
+ getStaleUnassignedAffinityTasks,
31
36
  getStalledInProgressTasks,
32
37
  getTaskById,
33
38
  getTaskStats,
@@ -37,6 +42,8 @@ import {
37
42
  init_db,
38
43
  init_registry,
39
44
  init_resolver,
45
+ isAgentEligibleForTask,
46
+ isPoolAffinityEnforcementEnabled,
40
47
  registerTemplate,
41
48
  releaseStaleMentionProcessing,
42
49
  releaseStaleProcessingInbox,
@@ -44,7 +51,7 @@ import {
44
51
  resolveTemplate,
45
52
  supersedeTask,
46
53
  updateAgentStatus
47
- } from "./cli-jzwtn8rm.js";
54
+ } from "./cli-k4yp6w9w.js";
48
55
 
49
56
  // src/heartbeat/heartbeat.ts
50
57
  init_db();
@@ -155,6 +162,8 @@ var STALL_THRESHOLD_NO_SESSION_MIN = Number(process.env.HEARTBEAT_STALL_NO_SESSI
155
162
  var STALL_THRESHOLD_STALE_HEARTBEAT_MIN = Number(process.env.HEARTBEAT_STALL_STALE_HB_MIN) || 15;
156
163
  var STALE_CLEANUP_THRESHOLD_MINUTES = Number(process.env.HEARTBEAT_STALE_CLEANUP_MIN) || 30;
157
164
  var MAX_AUTO_ASSIGN_PER_SWEEP = Number(process.env.HEARTBEAT_MAX_AUTO_ASSIGN) || 5;
165
+ var POOL_SCAN_BATCH_SIZE = Number(process.env.HEARTBEAT_POOL_SCAN_BATCH_SIZE) || 50;
166
+ var POOL_SCAN_CAP = Number(process.env.HEARTBEAT_POOL_SCAN_CAP) || 500;
158
167
  var MAX_RESUME_GENERATIONS = Number(process.env.HEARTBEAT_MAX_RESUME_GENERATIONS) || 3;
159
168
  var RESUME_BUDGET_EXHAUSTED_REASON = "resume_budget_exhausted";
160
169
  var HEARTBEAT_RESUME_PIN_GRACE_MIN = (() => {
@@ -164,6 +173,7 @@ var HEARTBEAT_RESUME_PIN_GRACE_MIN = (() => {
164
173
  const parsed = Number(raw);
165
174
  return Number.isFinite(parsed) ? parsed : 10;
166
175
  })();
176
+ var POOL_AFFINITY_ESCALATION_MIN = Number(process.env.POOL_AFFINITY_ESCALATION_MIN) || 15;
167
177
  var HEARTBEAT_CHECKLIST_INTERVAL_MS = Number(process.env.HEARTBEAT_CHECKLIST_INTERVAL_MS) || 30 * 60 * 1000;
168
178
  var HEARTBEAT_CHECKLIST_DISABLE = Boolean(process.env.HEARTBEAT_CHECKLIST_DISABLE);
169
179
  var heartbeatInterval = null;
@@ -412,12 +422,28 @@ async function runRebootSweep() {
412
422
  LIMIT 1`).get(task.id);
413
423
  if (!existingRetry) {
414
424
  try {
425
+ let preferredAgentId;
426
+ const candidate = getPinCandidateAgent(task.agentId);
427
+ if (candidate) {
428
+ const activeCount = getActiveTaskCount(candidate.id);
429
+ const hasCap = activeCount < (candidate.maxTasks ?? 1);
430
+ if (hasCap) {
431
+ preferredAgentId = candidate.id;
432
+ } else {
433
+ console.warn(`[Heartbeat] Reboot retry for task ${task.id.slice(0, 8)} NOT pinned: agent ${candidate.id.slice(0, 8)} at capacity (${activeCount}/${candidate.maxTasks ?? 1}); falling back to affinity-gated pool`);
434
+ }
435
+ }
436
+ const tags = ["reboot-retry", "auto-generated"];
437
+ if (preferredAgentId !== undefined)
438
+ tags.push(REBOOT_RETRY_PIN_TAG);
415
439
  const retryTask = createTaskExtended(task.task, {
416
440
  parentTaskId: task.id,
417
- tags: ["reboot-retry", "auto-generated"],
441
+ agentId: preferredAgentId,
442
+ tags,
418
443
  priority: task.priority,
419
444
  source: task.source,
420
- taskType: task.taskType ?? undefined
445
+ taskType: task.taskType ?? undefined,
446
+ routingAffinity: buildRoutingAffinityFromAgent(task.agentId) ?? undefined
421
447
  });
422
448
  retryTaskId = retryTask.id;
423
449
  console.log(`[Heartbeat] Reboot retry created: ${retryTaskId} (parent: ${task.id})`);
@@ -461,10 +487,6 @@ function autoAssignPoolTasks(findings) {
461
487
  const idleWorkers = getIdleWorkersWithCapacity().filter((w) => (w.emptyPollCount ?? 0) < MAX_EMPTY_POLLS);
462
488
  if (idleWorkers.length === 0)
463
489
  return;
464
- const poolTasks = getUnassignedPoolTasks(MAX_AUTO_ASSIGN_PER_SWEEP);
465
- if (poolTasks.length === 0)
466
- return;
467
- let workerIndex = 0;
468
490
  const reservedByWorker = new Map;
469
491
  const reservedForWorker = (agentId) => {
470
492
  const cached = reservedByWorker.get(agentId);
@@ -475,24 +497,28 @@ function autoAssignPoolTasks(findings) {
475
497
  reservedByWorker.set(agentId, reserved);
476
498
  return reserved;
477
499
  };
478
- for (const task of poolTasks) {
479
- if (workerIndex >= idleWorkers.length)
500
+ let assignedCount = 0;
501
+ let offset = 0;
502
+ while (assignedCount < MAX_AUTO_ASSIGN_PER_SWEEP && offset < POOL_SCAN_CAP) {
503
+ const batch = getUnassignedPoolTasks(POOL_SCAN_BATCH_SIZE, offset);
504
+ if (batch.length === 0)
480
505
  break;
481
- const worker = idleWorkers[workerIndex];
482
- const maxTasks = worker.maxTasks ?? 1;
483
- if (reservedForWorker(worker.id) >= maxTasks) {
484
- workerIndex++;
485
- continue;
486
- }
487
- const assigned = assignUnassignedTaskPending(task.id, worker.id);
488
- if (assigned) {
489
- findings.autoAssigned.push({ taskId: task.id, agentId: worker.id });
490
- reservedByWorker.set(worker.id, reservedForWorker(worker.id) + 1);
491
- const remaining = maxTasks - reservedForWorker(worker.id);
492
- if (remaining <= 0) {
493
- workerIndex++;
506
+ for (const task of batch) {
507
+ if (assignedCount >= MAX_AUTO_ASSIGN_PER_SWEEP)
508
+ break;
509
+ const worker = idleWorkers.find((w) => reservedForWorker(w.id) < (w.maxTasks ?? 1) && isAgentEligibleForTask(w, task));
510
+ if (!worker)
511
+ continue;
512
+ const assigned = assignUnassignedTaskPending(task.id, worker.id);
513
+ if (assigned) {
514
+ findings.autoAssigned.push({ taskId: task.id, agentId: worker.id });
515
+ reservedByWorker.set(worker.id, reservedForWorker(worker.id) + 1);
516
+ assignedCount++;
494
517
  }
495
518
  }
519
+ offset += batch.length;
520
+ if (batch.length < POOL_SCAN_BATCH_SIZE)
521
+ break;
496
522
  }
497
523
  })();
498
524
  }
@@ -550,12 +576,35 @@ function escalateUnreclaimedResumes(findings) {
550
576
  console.log(`[Heartbeat] Escalated unreclaimed pinned resume ${resume.id.slice(0, 8)} → Lead reroute-decision ${escalation.decisionTaskId.slice(0, 8)} (original ${original.id.slice(0, 8)})`);
551
577
  }
552
578
  }
579
+ function escalateStarvedPoolTasks(findings) {
580
+ if (!isPoolAffinityEnforcementEnabled())
581
+ return;
582
+ const cutoff = new Date(Date.now() - POOL_AFFINITY_ESCALATION_MIN * 60 * 1000).toISOString();
583
+ const candidates = getStaleUnassignedAffinityTasks(cutoff);
584
+ if (candidates.length === 0)
585
+ return;
586
+ const registeredAgents = getAllAgents().filter((a) => !a.isLead);
587
+ for (const task of candidates) {
588
+ const hasEligibleAgent = registeredAgents.some((agent) => isAgentEligibleForTask(agent, task));
589
+ if (hasEligibleAgent)
590
+ continue;
591
+ const decision = createPoolStarvationDecisionTask({ original: task });
592
+ if (decision.kind === "created") {
593
+ findings.escalatedReroutes.push({
594
+ originalTaskId: task.id,
595
+ decisionTaskId: decision.task.id
596
+ });
597
+ console.log(`[Heartbeat] Escalated starved pool task ${task.id.slice(0, 8)} → Lead reroute-decision ${decision.task.id.slice(0, 8)} (zero eligible agents)`);
598
+ }
599
+ }
600
+ }
553
601
  async function cleanupStaleResources(findings) {
554
602
  findings.staleCleanup.sessions = cleanupStaleSessions(STALE_CLEANUP_THRESHOLD_MINUTES);
555
603
  findings.staleCleanup.reviewingTasks = releaseStaleReviewingTasks(STALE_CLEANUP_THRESHOLD_MINUTES);
556
604
  findings.staleCleanup.mentionProcessing = releaseStaleMentionProcessing(STALE_CLEANUP_THRESHOLD_MINUTES);
557
605
  findings.staleCleanup.inboxProcessing = releaseStaleProcessingInbox(STALE_CLEANUP_THRESHOLD_MINUTES);
558
606
  escalateUnreclaimedResumes(findings);
607
+ escalateStarvedPoolTasks(findings);
559
608
  try {
560
609
  findings.staleCleanup.workflowRuns = await recoverIncompleteRuns(getExecutorRegistry());
561
610
  } catch {
@@ -781,6 +781,57 @@ var modelsdev_reasoning_default = {
781
781
  ]
782
782
  }
783
783
  ]
784
+ },
785
+ "gpt-5.6-sol": {
786
+ id: "gpt-5.6-sol",
787
+ reasoning: true,
788
+ reasoningOptions: [
789
+ {
790
+ type: "effort",
791
+ values: [
792
+ "none",
793
+ "low",
794
+ "medium",
795
+ "high",
796
+ "xhigh",
797
+ "max"
798
+ ]
799
+ }
800
+ ]
801
+ },
802
+ "gpt-5.6-terra": {
803
+ id: "gpt-5.6-terra",
804
+ reasoning: true,
805
+ reasoningOptions: [
806
+ {
807
+ type: "effort",
808
+ values: [
809
+ "none",
810
+ "low",
811
+ "medium",
812
+ "high",
813
+ "xhigh",
814
+ "max"
815
+ ]
816
+ }
817
+ ]
818
+ },
819
+ "gpt-5.6-luna": {
820
+ id: "gpt-5.6-luna",
821
+ reasoning: true,
822
+ reasoningOptions: [
823
+ {
824
+ type: "effort",
825
+ values: [
826
+ "none",
827
+ "low",
828
+ "medium",
829
+ "high",
830
+ "xhigh",
831
+ "max"
832
+ ]
833
+ }
834
+ ]
784
835
  }
785
836
  },
786
837
  openrouter: {
@@ -4035,7 +4086,7 @@ var modelsdev_reasoning_default = {
4035
4086
  };
4036
4087
 
4037
4088
  // src/providers/reasoning-effort.ts
4038
- var REASONING_EFFORT_LEVELS = ["off", "low", "medium", "high", "xhigh"];
4089
+ var REASONING_EFFORT_LEVELS = ["off", "low", "medium", "high", "xhigh", "max"];
4039
4090
  var SNAPSHOT = modelsdev_reasoning_default;
4040
4091
  var FALLBACK_LEVELS = ["low", "medium", "high"];
4041
4092
  var EFFORT_VALUE_MAP = {
@@ -4043,7 +4094,8 @@ var EFFORT_VALUE_MAP = {
4043
4094
  low: "low",
4044
4095
  medium: "medium",
4045
4096
  high: "high",
4046
- xhigh: "xhigh"
4097
+ xhigh: "xhigh",
4098
+ max: "max"
4047
4099
  };
4048
4100
  function splitProviderModel(model) {
4049
4101
  const slash = model.indexOf("/");
@@ -4080,6 +4132,9 @@ function applyHarnessOverrides(harness, model, entry, levels) {
4080
4132
  result = ["off", ...result];
4081
4133
  }
4082
4134
  }
4135
+ if (harness !== "codex") {
4136
+ result = result.filter((l) => l !== "max");
4137
+ }
4083
4138
  if (harness === "codex") {
4084
4139
  const isCodexMax = /-codex-max$/.test(model);
4085
4140
  const isCodexNonMax = /-codex$/.test(model) && !isCodexMax;
@@ -4133,6 +4188,8 @@ var ANTHROPIC_BUDGET_TOKENS_BY_LEVEL = {
4133
4188
  };
4134
4189
  function buildOpencodeReasoningOptions(providerId, level) {
4135
4190
  if (providerId === "anthropic") {
4191
+ if (level === "max")
4192
+ return { reasoningEffort: level };
4136
4193
  return { thinking: { type: "enabled", budgetTokens: ANTHROPIC_BUDGET_TOKENS_BY_LEVEL[level] } };
4137
4194
  }
4138
4195
  if (providerId === "openrouter") {
@@ -3,7 +3,7 @@ import {
3
3
  getAgentWorkingOnThread,
4
4
  getAllAgents,
5
5
  init_db
6
- } from "./cli-jzwtn8rm.js";
6
+ } from "./cli-k4yp6w9w.js";
7
7
 
8
8
  // src/slack/router.ts
9
9
  init_db();