@okrlinkhub/agent-factory 1.0.1 → 1.0.2

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.
@@ -1041,6 +1041,7 @@ export const claimNextJob = mutation({
1041
1041
  returns: v.union(v.null(), claimedJobValidator),
1042
1042
  handler: async (ctx, args) => {
1043
1043
  const nowMs = args.nowMs ?? Date.now();
1044
+ const staleHeartbeatCutoff = nowMs - DEFAULT_CONFIG.lease.staleAfterMs;
1044
1045
  const worker = await ctx.db
1045
1046
  .query("workers")
1046
1047
  .withIndex("by_workerId", (q) => q.eq("workerId", args.workerId))
@@ -1055,6 +1056,7 @@ export const claimNextJob = mutation({
1055
1056
  ) {
1056
1057
  return null;
1057
1058
  }
1059
+ const workers = await ctx.db.query("workers").collect();
1058
1060
  const candidates = await ctx.db
1059
1061
  .query("messageQueue")
1060
1062
  .withIndex("by_status_and_scheduledFor", (q) =>
@@ -1093,6 +1095,15 @@ export const claimNextJob = mutation({
1093
1095
  ) {
1094
1096
  continue;
1095
1097
  }
1098
+ const existingOwner = findActiveAssignmentOwner(workers, {
1099
+ conversationId: candidate.conversationId,
1100
+ agentKey: candidate.agentKey,
1101
+ excludeWorkerId: args.workerId,
1102
+ staleHeartbeatCutoff,
1103
+ });
1104
+ if (existingOwner) {
1105
+ continue;
1106
+ }
1096
1107
 
1097
1108
  const lock = conversation.processingLock;
1098
1109
  if (lock && lock.leaseExpiresAt > nowMs) continue;
@@ -2292,7 +2303,7 @@ async function scheduleIdleShutdownWatchdog(
2292
2303
  }
2293
2304
  }
2294
2305
 
2295
- async function scheduleLeaseRecoveryWatchdog(ctx: any, nowMs: number) {
2306
+ async function scheduleLeaseRecoveryWatchdog(ctx: any, _nowMs: number) {
2296
2307
  const delayMs = DEFAULT_CONFIG.lease.leaseMs + 1_000;
2297
2308
  try {
2298
2309
  await ctx.scheduler.runAfter(delayMs, (internal.scheduler as any).reconcileWorkerPoolInternal, {
@@ -2376,6 +2387,35 @@ function clearAssignmentForMessage(
2376
2387
  return worker.assignment;
2377
2388
  }
2378
2389
 
2390
+ function findActiveAssignmentOwner(
2391
+ workers: Array<{
2392
+ workerId: string;
2393
+ status: "active" | "draining" | "stopping" | "stopped";
2394
+ heartbeatAt: number;
2395
+ assignment?: {
2396
+ conversationId: string;
2397
+ agentKey: string;
2398
+ leaseId: string;
2399
+ assignedAt: number;
2400
+ };
2401
+ }>,
2402
+ args: {
2403
+ conversationId: string;
2404
+ agentKey: string;
2405
+ excludeWorkerId: string;
2406
+ staleHeartbeatCutoff: number;
2407
+ },
2408
+ ) {
2409
+ return workers.find(
2410
+ (candidate) =>
2411
+ candidate.workerId !== args.excludeWorkerId &&
2412
+ isWorkerClaimable(candidate.status) &&
2413
+ candidate.heartbeatAt > args.staleHeartbeatCutoff &&
2414
+ candidate.assignment?.conversationId === args.conversationId &&
2415
+ candidate.assignment.agentKey === args.agentKey,
2416
+ );
2417
+ }
2418
+
2379
2419
  function dedupeMessagesById<T extends { _id: string }>(messages: Array<T>): Array<T> {
2380
2420
  const seen = new Set<string>();
2381
2421
  const deduped: Array<T> = [];
@@ -765,20 +765,23 @@ function countWorkersAvailableForActiveConversations(
765
765
  staleHeartbeatCutoff: number,
766
766
  ) {
767
767
  const activeConversationSet = new Set(activeConversationIds);
768
- let available = 0;
768
+ const assignedConversationKeys = new Set<string>();
769
+ let unassignedWorkers = 0;
769
770
  for (const worker of workerRows) {
770
771
  if (!isWorkerClaimable(worker.status) || worker.heartbeatAt <= staleHeartbeatCutoff) {
771
772
  continue;
772
773
  }
773
774
  if (!worker.assignment) {
774
- available += 1;
775
+ unassignedWorkers += 1;
775
776
  continue;
776
777
  }
777
778
  if (activeConversationSet.has(worker.assignment.conversationId)) {
778
- available += 1;
779
+ assignedConversationKeys.add(
780
+ `${worker.assignment.agentKey}::${worker.assignment.conversationId}`,
781
+ );
779
782
  }
780
783
  }
781
- return available;
784
+ return unassignedWorkers + assignedConversationKeys.size;
782
785
  }
783
786
 
784
787
  function deriveScheduledShutdownAt(