@okrlinkhub/agent-factory 3.0.1 → 3.0.3
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 +185 -31
- package/dist/client/index.d.ts +11 -6
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +16 -0
- package/dist/client/index.js.map +1 -1
- package/dist/component/_generated/api.d.ts +2 -0
- package/dist/component/_generated/api.d.ts.map +1 -1
- package/dist/component/_generated/api.js.map +1 -1
- package/dist/component/_generated/component.d.ts +60 -0
- package/dist/component/_generated/component.d.ts.map +1 -1
- package/dist/component/flyCleanup.d.ts +32 -0
- package/dist/component/flyCleanup.d.ts.map +1 -0
- package/dist/component/flyCleanup.js +272 -0
- package/dist/component/flyCleanup.js.map +1 -0
- package/dist/component/lib.d.ts +1 -0
- package/dist/component/lib.d.ts.map +1 -1
- package/dist/component/lib.js +1 -0
- package/dist/component/lib.js.map +1 -1
- package/dist/component/providers/fly.d.ts +23 -2
- package/dist/component/providers/fly.d.ts.map +1 -1
- package/dist/component/providers/fly.js +15 -3
- package/dist/component/providers/fly.js.map +1 -1
- package/dist/component/pushing.d.ts +4 -4
- package/dist/component/queue.d.ts +32 -16
- package/dist/component/queue.d.ts.map +1 -1
- package/dist/component/queue.js +44 -2
- package/dist/component/queue.js.map +1 -1
- package/dist/component/scheduler.d.ts +8 -8
- package/dist/component/scheduler.d.ts.map +1 -1
- package/dist/component/scheduler.js +59 -12
- package/dist/component/scheduler.js.map +1 -1
- package/dist/component/schema.d.ts +30 -28
- package/dist/component/schema.d.ts.map +1 -1
- package/dist/component/schema.js +1 -0
- package/dist/component/schema.js.map +1 -1
- package/package.json +1 -1
- package/src/client/index.ts +16 -0
- package/src/component/_generated/api.ts +2 -0
- package/src/component/_generated/component.ts +72 -0
- package/src/component/flyCleanup.ts +386 -0
- package/src/component/lib.test.ts +280 -4
- package/src/component/lib.ts +1 -0
- package/src/component/providers/fly.ts +39 -5
- package/src/component/queue.ts +55 -2
- package/src/component/scheduler.ts +100 -16
- package/src/component/schema.ts +1 -0
|
@@ -73,6 +73,12 @@ type SchedulerWorkerRow = {
|
|
|
73
73
|
machineId: string | null;
|
|
74
74
|
appName: string | null;
|
|
75
75
|
region: string | null;
|
|
76
|
+
volumeId: string | null;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
type SchedulerConversationTarget = {
|
|
80
|
+
conversationId: string;
|
|
81
|
+
agentKey: string;
|
|
76
82
|
};
|
|
77
83
|
|
|
78
84
|
const PROVIDER_RECONCILE_GRACE_MS = 90_000;
|
|
@@ -182,11 +188,11 @@ async function runReconcileWorkerPool(
|
|
|
182
188
|
}
|
|
183
189
|
const workspaceId = args.workspaceId ?? "default";
|
|
184
190
|
const provider = resolveProvider(providerConfig.kind, flyApiToken);
|
|
185
|
-
const
|
|
186
|
-
(internal.queue as any).
|
|
191
|
+
const activeConversations: Array<SchedulerConversationTarget> = await ctx.runQuery(
|
|
192
|
+
(internal.queue as any).getActiveConversationsForScheduler,
|
|
187
193
|
{ nowMs, limit: 1000 },
|
|
188
194
|
);
|
|
189
|
-
const activeConversationCount =
|
|
195
|
+
const activeConversationCount = activeConversations.length;
|
|
190
196
|
const cycle = await runWorkerLifecycleCycle(ctx, {
|
|
191
197
|
nowMs,
|
|
192
198
|
provider,
|
|
@@ -195,7 +201,7 @@ async function runReconcileWorkerPool(
|
|
|
195
201
|
allowSpawn: true,
|
|
196
202
|
convexUrl,
|
|
197
203
|
workspaceId,
|
|
198
|
-
|
|
204
|
+
activeConversations,
|
|
199
205
|
desiredActiveWorkers: clamp(activeConversationCount, 0, scaling.maxWorkers),
|
|
200
206
|
});
|
|
201
207
|
if (activeConversationCount > 0 || cycle.pending > 0) {
|
|
@@ -258,7 +264,7 @@ async function runEnforceIdleShutdowns(
|
|
|
258
264
|
scaling: DEFAULT_CONFIG.scaling,
|
|
259
265
|
allowSpawn: false,
|
|
260
266
|
desiredActiveWorkers: 0,
|
|
261
|
-
|
|
267
|
+
activeConversations: [],
|
|
262
268
|
});
|
|
263
269
|
|
|
264
270
|
if (cycle.pending > 0) {
|
|
@@ -282,7 +288,7 @@ async function runWorkerLifecycleCycle(
|
|
|
282
288
|
scaling: typeof DEFAULT_CONFIG.scaling;
|
|
283
289
|
allowSpawn: boolean;
|
|
284
290
|
desiredActiveWorkers: number;
|
|
285
|
-
|
|
291
|
+
activeConversations: Array<SchedulerConversationTarget>;
|
|
286
292
|
convexUrl?: string;
|
|
287
293
|
workspaceId?: string;
|
|
288
294
|
},
|
|
@@ -338,7 +344,7 @@ async function runWorkerLifecycleCycle(
|
|
|
338
344
|
if (input.allowSpawn && input.desiredActiveWorkers > 0) {
|
|
339
345
|
const claimableWorkers = countWorkersAvailableForActiveConversations(
|
|
340
346
|
filterScopedWorkers(workerRows, input.providerConfig.appName),
|
|
341
|
-
input.
|
|
347
|
+
input.activeConversations,
|
|
342
348
|
staleHeartbeatCutoff,
|
|
343
349
|
);
|
|
344
350
|
if (input.desiredActiveWorkers > claimableWorkers) {
|
|
@@ -350,8 +356,30 @@ async function runWorkerLifecycleCycle(
|
|
|
350
356
|
input.scaling.spawnStep,
|
|
351
357
|
input.desiredActiveWorkers - claimableWorkers,
|
|
352
358
|
);
|
|
359
|
+
const spawnTargets = selectSpawnTargetsForActiveConversations(
|
|
360
|
+
filterScopedWorkers(workerRows, input.providerConfig.appName),
|
|
361
|
+
input.activeConversations,
|
|
362
|
+
staleHeartbeatCutoff,
|
|
363
|
+
toSpawn,
|
|
364
|
+
);
|
|
353
365
|
for (let index = 0; index < toSpawn; index += 1) {
|
|
354
366
|
const workerId = `afw-${input.nowMs}-${index}`;
|
|
367
|
+
const target = spawnTargets[index];
|
|
368
|
+
const assignment = target
|
|
369
|
+
? {
|
|
370
|
+
conversationId: target.conversationId,
|
|
371
|
+
agentKey: target.agentKey,
|
|
372
|
+
leaseId: `spawn:${workerId}`,
|
|
373
|
+
assignedAt: input.nowMs,
|
|
374
|
+
}
|
|
375
|
+
: undefined;
|
|
376
|
+
const workerVolume = await input.provider.ensureWorkerVolume({
|
|
377
|
+
appName: input.providerConfig.appName,
|
|
378
|
+
workerId,
|
|
379
|
+
region: input.providerConfig.region,
|
|
380
|
+
volumeName: input.providerConfig.volumeName,
|
|
381
|
+
volumeSizeGb: input.providerConfig.volumeSizeGb,
|
|
382
|
+
});
|
|
355
383
|
await ctx.runMutation(internal.queue.upsertWorkerState, {
|
|
356
384
|
workerId,
|
|
357
385
|
provider: input.providerConfig.kind,
|
|
@@ -359,6 +387,8 @@ async function runWorkerLifecycleCycle(
|
|
|
359
387
|
load: 0,
|
|
360
388
|
nowMs: input.nowMs,
|
|
361
389
|
scheduledShutdownAt: input.nowMs + input.scaling.idleTimeoutMs,
|
|
390
|
+
volumeId: workerVolume.volumeId,
|
|
391
|
+
assignment,
|
|
362
392
|
});
|
|
363
393
|
let created;
|
|
364
394
|
try {
|
|
@@ -367,9 +397,8 @@ async function runWorkerLifecycleCycle(
|
|
|
367
397
|
appName: input.providerConfig.appName,
|
|
368
398
|
image: input.providerConfig.image,
|
|
369
399
|
region: input.providerConfig.region,
|
|
370
|
-
|
|
400
|
+
volumeId: workerVolume.volumeId,
|
|
371
401
|
volumePath: input.providerConfig.volumePath,
|
|
372
|
-
volumeSizeGb: input.providerConfig.volumeSizeGb,
|
|
373
402
|
env: compactEnv({
|
|
374
403
|
...DEFAULT_WORKER_RUNTIME_ENV,
|
|
375
404
|
...forwardedOpenClawEnv,
|
|
@@ -377,6 +406,8 @@ async function runWorkerLifecycleCycle(
|
|
|
377
406
|
WORKSPACE_ID: input.workspaceId ?? "default",
|
|
378
407
|
WORKER_ID: workerId,
|
|
379
408
|
WORKER_IDLE_TIMEOUT_MS: String(input.scaling.idleTimeoutMs),
|
|
409
|
+
OPENCLAW_AGENT_KEY: target?.agentKey,
|
|
410
|
+
OPENCLAW_CONVERSATION_ID: target?.conversationId,
|
|
380
411
|
}),
|
|
381
412
|
});
|
|
382
413
|
} catch (error) {
|
|
@@ -385,6 +416,13 @@ async function runWorkerLifecycleCycle(
|
|
|
385
416
|
error instanceof Error ? error.message : String(error)
|
|
386
417
|
}`,
|
|
387
418
|
);
|
|
419
|
+
await input.provider.cleanupWorkerStorage({
|
|
420
|
+
appName: input.providerConfig.appName,
|
|
421
|
+
workerId,
|
|
422
|
+
region: input.providerConfig.region,
|
|
423
|
+
volumeName: input.providerConfig.volumeName,
|
|
424
|
+
volumeId: workerVolume.volumeId,
|
|
425
|
+
});
|
|
388
426
|
await transitionWorkerToDraining(
|
|
389
427
|
ctx,
|
|
390
428
|
{
|
|
@@ -400,6 +438,7 @@ async function runWorkerLifecycleCycle(
|
|
|
400
438
|
machineId: null,
|
|
401
439
|
appName: input.providerConfig.appName,
|
|
402
440
|
region: input.providerConfig.region,
|
|
441
|
+
volumeId: workerVolume.volumeId,
|
|
403
442
|
},
|
|
404
443
|
input.providerConfig,
|
|
405
444
|
input.nowMs,
|
|
@@ -420,6 +459,7 @@ async function runWorkerLifecycleCycle(
|
|
|
420
459
|
machineId: null,
|
|
421
460
|
appName: input.providerConfig.appName,
|
|
422
461
|
region: input.providerConfig.region,
|
|
462
|
+
volumeId: workerVolume.volumeId,
|
|
423
463
|
},
|
|
424
464
|
input.providerConfig,
|
|
425
465
|
input.nowMs,
|
|
@@ -440,6 +480,7 @@ async function runWorkerLifecycleCycle(
|
|
|
440
480
|
machineId: null,
|
|
441
481
|
appName: input.providerConfig.appName,
|
|
442
482
|
region: input.providerConfig.region,
|
|
483
|
+
volumeId: workerVolume.volumeId,
|
|
443
484
|
},
|
|
444
485
|
input.providerConfig,
|
|
445
486
|
input.nowMs,
|
|
@@ -456,6 +497,8 @@ async function runWorkerLifecycleCycle(
|
|
|
456
497
|
machineId: created.machineId,
|
|
457
498
|
appName: input.providerConfig.appName,
|
|
458
499
|
region: created.region,
|
|
500
|
+
volumeId: created.volumeId ?? workerVolume.volumeId,
|
|
501
|
+
assignment,
|
|
459
502
|
});
|
|
460
503
|
await scheduleIdleShutdownWatch(
|
|
461
504
|
ctx,
|
|
@@ -655,6 +698,7 @@ async function transitionWorkerToStopped(
|
|
|
655
698
|
scheduledShutdownAt: worker.scheduledShutdownAt ?? nowMs,
|
|
656
699
|
stoppedAt: worker.stoppedAt ?? nowMs,
|
|
657
700
|
clearMachineRef: true,
|
|
701
|
+
clearVolumeId: true,
|
|
658
702
|
});
|
|
659
703
|
}
|
|
660
704
|
|
|
@@ -682,6 +726,7 @@ async function finalizeWorkerTeardown(input: {
|
|
|
682
726
|
machineId,
|
|
683
727
|
region: input.worker.region ?? input.providerConfig.region,
|
|
684
728
|
volumeName: input.providerConfig.volumeName,
|
|
729
|
+
volumeId: input.worker.volumeId,
|
|
685
730
|
});
|
|
686
731
|
return true;
|
|
687
732
|
}
|
|
@@ -862,10 +907,46 @@ function filterScopedWorkers(workerRows: Array<SchedulerWorkerRow>, appName: str
|
|
|
862
907
|
|
|
863
908
|
function countWorkersAvailableForActiveConversations(
|
|
864
909
|
workerRows: Array<SchedulerWorkerRow>,
|
|
865
|
-
|
|
910
|
+
activeConversations: Array<SchedulerConversationTarget>,
|
|
911
|
+
staleHeartbeatCutoff: number,
|
|
912
|
+
) {
|
|
913
|
+
const coverage = summarizeWorkerConversationCoverage(
|
|
914
|
+
workerRows,
|
|
915
|
+
activeConversations,
|
|
916
|
+
staleHeartbeatCutoff,
|
|
917
|
+
);
|
|
918
|
+
return coverage.unassignedWorkers + coverage.assignedConversationKeys.size;
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
function selectSpawnTargetsForActiveConversations(
|
|
922
|
+
workerRows: Array<SchedulerWorkerRow>,
|
|
923
|
+
activeConversations: Array<SchedulerConversationTarget>,
|
|
866
924
|
staleHeartbeatCutoff: number,
|
|
925
|
+
limit: number,
|
|
867
926
|
) {
|
|
868
|
-
const
|
|
927
|
+
const coverage = summarizeWorkerConversationCoverage(
|
|
928
|
+
workerRows,
|
|
929
|
+
activeConversations,
|
|
930
|
+
staleHeartbeatCutoff,
|
|
931
|
+
);
|
|
932
|
+
const uncoveredConversations = activeConversations.filter(
|
|
933
|
+
(conversation) =>
|
|
934
|
+
!coverage.assignedConversationKeys.has(getConversationTargetKey(conversation)),
|
|
935
|
+
);
|
|
936
|
+
return uncoveredConversations.slice(
|
|
937
|
+
coverage.unassignedWorkers,
|
|
938
|
+
coverage.unassignedWorkers + Math.max(0, limit),
|
|
939
|
+
);
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
function summarizeWorkerConversationCoverage(
|
|
943
|
+
workerRows: Array<SchedulerWorkerRow>,
|
|
944
|
+
activeConversations: Array<SchedulerConversationTarget>,
|
|
945
|
+
staleHeartbeatCutoff: number,
|
|
946
|
+
) {
|
|
947
|
+
const activeConversationSet = new Set(
|
|
948
|
+
activeConversations.map((conversation) => getConversationTargetKey(conversation)),
|
|
949
|
+
);
|
|
869
950
|
const assignedConversationKeys = new Set<string>();
|
|
870
951
|
let unassignedWorkers = 0;
|
|
871
952
|
for (const worker of workerRows) {
|
|
@@ -876,13 +957,16 @@ function countWorkersAvailableForActiveConversations(
|
|
|
876
957
|
unassignedWorkers += 1;
|
|
877
958
|
continue;
|
|
878
959
|
}
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
);
|
|
960
|
+
const assignmentKey = getConversationTargetKey(worker.assignment);
|
|
961
|
+
if (activeConversationSet.has(assignmentKey)) {
|
|
962
|
+
assignedConversationKeys.add(assignmentKey);
|
|
883
963
|
}
|
|
884
964
|
}
|
|
885
|
-
return unassignedWorkers
|
|
965
|
+
return { assignedConversationKeys, unassignedWorkers };
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
function getConversationTargetKey(input: { conversationId: string; agentKey: string }) {
|
|
969
|
+
return `${input.agentKey}::${input.conversationId}`;
|
|
886
970
|
}
|
|
887
971
|
|
|
888
972
|
function deriveScheduledShutdownAt(
|
package/src/component/schema.ts
CHANGED