@zq-silk/yui 0.13.9 → 0.14.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.
- package/ARCHITECTURE.md +69 -428
- package/README.md +108 -298
- package/dist/agent/managedRuntimeEnvironment.js +2 -2
- package/dist/cli/commandCatalog.js +39 -77
- package/dist/cli/interactionCandidates.js +4 -4
- package/dist/cli/interactionPolicy.js +7 -10
- package/dist/cli/invocationRouter.js +1 -1
- package/dist/cli/updateCommand.js +6 -27
- package/dist/cli/updateOrchestrator.js +63 -484
- package/dist/cli/updatePorts.js +15 -333
- package/dist/cli/upgradeCommand.js +8 -65
- package/dist/cli.js +57 -242
- package/dist/commands/configCommands.js +11 -62
- package/dist/commands/executionAuditCommands.js +23 -23
- package/dist/commands/globalRoleCommands.js +2 -7
- package/dist/commands/jobCommands.js +1 -4
- package/dist/commands/projectCommands.js +5 -5
- package/dist/commands/releaseCommands.js +3 -10
- package/dist/commands/resourcesCommands.js +2 -2
- package/dist/commands/sessionCommands.js +2 -2
- package/dist/commands/taskActor.js +31 -39
- package/dist/commands/taskChangeSetCommands.js +5 -12
- package/dist/commands/taskCommands.js +464 -1249
- package/dist/commands/taskContextCommand.js +35 -36
- package/dist/commands/taskExecutionCommands.js +33 -46
- package/dist/commands/taskInputCommands.js +20 -42
- package/dist/commands/taskNextActionCommand.js +9 -9
- package/dist/commands/taskOverviewCommand.js +56 -95
- package/dist/commands/taskRoleRuntimeStatus.js +77 -107
- package/dist/commands/taskUpstreamCommands.js +3 -3
- package/dist/commands/taskWorkspaceCommands.js +7 -79
- package/dist/commands/telemetryCommands.js +32 -124
- package/dist/commands/workflowCommands.js +3 -3
- package/dist/completion/fileCompletionManager.js +1 -1
- package/dist/config/configCatalog.js +4 -6
- package/dist/config/yuiConfig.js +6 -43
- package/dist/context/dispatchContext.js +14 -30
- package/dist/context/roleSessionContext.js +2 -2
- package/dist/context/sessionBootstrapManifest.js +2 -2
- package/dist/context/{runContextPack.js → turnContextPack.js} +58 -73
- package/dist/context/turnInputContract.js +204 -0
- package/dist/context/wakeNotification.js +2 -2
- package/dist/controller/agentRuntimeObserver.js +18 -18
- package/dist/controller/clientRuntime.js +13 -20
- package/dist/controller/controller.js +89 -116
- package/dist/controller/fileSchedulerStoreAdapter.js +511 -742
- package/dist/controller/jobControl.js +17 -17
- package/dist/controller/resourceInventory.js +6 -6
- package/dist/controller/resourceInventoryLinux.js +38 -59
- package/dist/controller/resourceInventoryRpc.js +3 -3
- package/dist/controller/resourceInventoryWorker.js +1 -1
- package/dist/controller/runtime.js +46 -53
- package/dist/controller/runtimeEventInbox.js +23 -31
- package/dist/controller/runtimeEventProcessor.js +15 -15
- package/dist/controller/{runtimeHookRunFence.js → runtimeHookTurnFence.js} +87 -81
- package/dist/controller/runtimeLaunchCoordinator.js +3 -3
- package/dist/controller/runtimeObservationHook.js +11 -8
- package/dist/controller/sessionNotify.js +11 -11
- package/dist/controller/structuredProviderObservation.js +89 -16
- package/dist/coordination/workMailbox.js +309 -579
- package/dist/coordination/workMailboxQueue.js +5 -6
- package/dist/core/controllerClient.js +1 -91
- package/dist/core/controllerServer.js +3 -6
- package/dist/doctor/doctor.js +51 -314
- package/dist/execution/candidateConvergence.js +3 -3
- package/dist/execution/executionGroup.js +32 -33
- package/dist/execution/executionHealth.js +36 -38
- package/dist/execution/resourceBroker.js +12 -12
- package/dist/executor/agentExecutor.js +30 -265
- package/dist/executor/effectiveLaunch.js +9 -19
- package/dist/executor/executorRegistry.js +43 -17
- package/dist/executor/fileRoleLaunchPlanner.js +56 -56
- package/dist/executor/launchPlan.js +3 -3
- package/dist/executor/workspacePreflightClassification.js +16 -16
- package/dist/grant/capabilityGrant.js +20 -25
- package/dist/input/inputRequest.js +12 -12
- package/dist/integration/changeSet.js +9 -13
- package/dist/integration/changeSetManifest.js +1 -1
- package/dist/integration/gitIntegrationService.js +2 -2
- package/dist/integration/integrationQueueService.js +5 -21
- package/dist/integration/overlapDiagnostics.js +2 -2
- package/dist/interaction/operatorPresentation.js +1 -1
- package/dist/job/jobRunner.js +1 -1
- package/dist/lifecycle/canonicalLifecycleEvent.js +25 -25
- package/dist/lifecycle/{exactRunTerminalization.js → exactTurnTerminalization.js} +116 -232
- package/dist/message/message.js +6 -6
- package/dist/observability/executionAudit.js +74 -86
- package/dist/observability/faultClassification.js +7 -24
- package/dist/observability/orchestrationMetrics.js +5 -5
- package/dist/observability/runtimeIdentity.js +34 -101
- package/dist/profile/agentProfile.js +1 -1
- package/dist/release/cliHomeReleaseFence.js +3 -3
- package/dist/release/releaseWorkflowEngine.js +6 -10
- package/dist/release/releaseWorkflowPorts.js +2 -2
- package/dist/release/workflowFileLock.js +1 -1
- package/dist/repository/checkoutSwap.js +1 -1
- package/dist/repository/gitWorkspace.js +0 -13
- package/dist/repository/project.js +1 -1
- package/dist/repository/projectMaintenanceLock.js +1 -2
- package/dist/repository/taskBaseFreshness.js +13 -13
- package/dist/repository/taskWorkspaceCoordinator.js +21 -25
- package/dist/repository/taskWorkspaceIdentity.js +8 -21
- package/dist/repository/taskWorkspacePreparer.js +63 -620
- package/dist/resources/autoResourceGc.js +3 -3
- package/dist/resources/liveReferences.js +1 -1
- package/dist/resources/resourceGc.js +1 -1
- package/dist/resources/sqliteResourceRegistry.js +1 -1
- package/dist/review/reviewDecision.js +7 -7
- package/dist/review/reviewFindingLedger.js +1 -1
- package/dist/review/reviewOutcomeClassifier.js +35 -50
- package/dist/review/reviewRound.js +14 -25
- package/dist/review/reviewerAvailability.js +4 -4
- package/dist/review/taskFinalReviewContractResolution.js +41 -0
- package/dist/role/role.js +6 -3
- package/dist/runtime/agentDriver.js +4 -0
- package/dist/runtime/agentHost.js +151 -36
- package/dist/runtime/builtinAgentDrivers.js +79 -2
- package/dist/runtime/codexAppServerRuntime.js +122 -0
- package/dist/runtime/continuationManager.js +3 -5
- package/dist/runtime/exactControlPlane.js +17 -42
- package/dist/runtime/firstProgressAdvisory.js +1 -1
- package/dist/runtime/index.js +2 -2
- package/dist/runtime/launchDiagnostics.js +2 -2
- package/dist/runtime/lifecycleReservation.js +4 -4
- package/dist/runtime/processExitObservation.js +1 -1
- package/dist/runtime/processExitOutbox.js +0 -32
- package/dist/runtime/promptEnvelope.js +9 -9
- package/dist/runtime/providerContinuation.js +2 -2
- package/dist/runtime/providerContinuationReconciliationService.js +2 -2
- package/dist/runtime/providerRuntimeIdentity.js +76 -24
- package/dist/runtime/recentTurnIds.js +38 -0
- package/dist/runtime/runtimeContinuationProjection.js +2 -2
- package/dist/runtime/runtimeHealthPolicy.js +1 -1
- package/dist/runtime/runtimeObservation.js +58 -21
- package/dist/runtime/runtimeProjection.js +19 -27
- package/dist/runtime/sessionLaunchRequest.js +2 -2
- package/dist/runtime/structuredProviderHost.js +177 -12
- package/dist/runtime/tmuxAdapters.js +46 -11
- package/dist/scheduler/activeRoleTurnDelivery.js +245 -0
- package/dist/scheduler/leaderWakeupProcessor.js +147 -445
- package/dist/scheduler/operatorEvent.js +4 -6
- package/dist/scheduler/resourceQueueProjection.js +71 -0
- package/dist/scheduler/{roleRunLiveness.js → roleTurnLiveness.js} +24 -28
- package/dist/scheduler/{roleRunStall.js → roleTurnStall.js} +162 -247
- package/dist/scheduler/taskExecutionProjection.js +99 -99
- package/dist/scheduler/taskObservabilityProjection.js +10 -10
- package/dist/scheduler/taskWake.js +4 -4
- package/dist/scheduler/wakeReason.js +6 -10
- package/dist/scheduler/wakeupQueue.js +0 -9
- package/dist/setup/setupCommand.js +2 -2
- package/dist/storage/currentTaskStore.js +46 -0
- package/dist/storage/persistenceWorker.js +1 -1
- package/dist/storage/sqliteSchema.js +81 -485
- package/dist/storage/sqliteStore.js +206 -365
- package/dist/storage/storageSchema.js +1 -15
- package/dist/storage/storageVersions.js +8 -9
- package/dist/storage/storeRpc.js +10 -34
- package/dist/storage/taskStore.js +63 -3415
- package/dist/storage/upgrade/recordVersions.js +55 -149
- package/dist/storage/upgrade/upgradeOrchestrator.js +89 -1752
- package/dist/task/completionReadiness.js +5 -5
- package/dist/task/deliveryGuard.js +10 -10
- package/dist/task/nextAction.js +74 -77
- package/dist/task/task.js +2 -4
- package/dist/task/taskRecordReference.js +3 -3
- package/dist/task/taskRecordRetirement.js +1 -1
- package/dist/telemetry/sqliteTelemetryStore.js +53 -53
- package/dist/telemetry/telemetryConfig.js +14 -14
- package/dist/telemetry/telemetryWiring.js +8 -14
- package/dist/turn/turn.js +356 -0
- package/dist/{run/runIdentity.js → turn/turnIdentity.js} +4 -4
- package/dist/verification/gateArtifactStore.js +2 -2
- package/dist/web/assets/client/app.js +1 -1
- package/dist/web/assets/client/components.js +29 -54
- package/dist/web/assets/client/i18n.js +17 -37
- package/dist/web/assets/client/view.js +37 -37
- package/dist/web/assets/shell.js +1 -1
- package/dist/web/assets/styles/cards.js +5 -5
- package/dist/web/assets/styles/widgets.js +1 -2
- package/dist/web/webSnapshot.js +65 -60
- package/dist/workItem/workItem.js +11 -11
- package/dist/workspace/workItemChangeSetManager.js +1 -1
- package/docs/task-local-identity.md +5 -5
- package/i18n/README.zh-CN.md +41 -54
- package/package.json +1 -1
- package/skills/yui-leader/SKILL.md +85 -83
- package/skills/yui-operator/SKILL.md +16 -16
- package/skills/yui-reviewer/SKILL.md +10 -10
- package/skills/yui-runtime/SKILL.md +17 -18
- package/skills/yui-worker/SKILL.md +16 -18
- package/dist/context/runContextContract.js +0 -162
- package/dist/executor/turnCompletion.js +0 -126
- package/dist/review/taskFinalReviewContractEvent.js +0 -1
- package/dist/review/taskFinalReviewContractRebind.js +0 -367
- package/dist/run/agentRun.js +0 -356
- package/dist/run/rejectedYieldAttempt.js +0 -221
- package/dist/run/runControlRequest.js +0 -50
- package/dist/run/yieldReceipt.js +0 -65
- package/dist/runtime/turnCompletion.js +0 -3
- package/dist/scheduler/actionability.js +0 -323
- package/dist/scheduler/activeRoleRunDelivery.js +0 -868
- package/dist/scheduler/activeTaskProgress.js +0 -126
- package/dist/storage/compatibleTaskStore.js +0 -248
- package/dist/storage/migration/baseline.js +0 -78
- package/dist/storage/migration/classifier.js +0 -51
- package/dist/storage/migration/compatibleCodec.js +0 -53
- package/dist/storage/migration/engine.js +0 -147
- package/dist/storage/migration/index.js +0 -33
- package/dist/storage/migration/planner.js +0 -154
- package/dist/storage/migration/productionRegistry.js +0 -3931
- package/dist/storage/migration/registry.js +0 -169
- package/dist/storage/migration/report.js +0 -54
- package/dist/storage/migration/types.js +0 -31
- package/dist/storage/upgrade/homeClassification.js +0 -353
- package/dist/storage/upgrade/homeMigrationTarget.js +0 -595
- package/dist/storage/upgrade/migrationReceipt.js +0 -67
- package/dist/storage/upgrade/offlineUpgradeInventory.js +0 -453
- package/dist/storage/upgrade/productionMigrationRegistry.js +0 -6
- package/dist/storage/upgrade/pseudoLayoutRepair.js +0 -241
- package/dist/storage/upgrade/recordVersionScan.js +0 -176
- package/dist/storage/upgrade/sqliteFileSet.js +0 -50
- package/dist/storage/upgrade/sqliteMigrationTarget.js +0 -375
- package/dist/storage/upgrade/sqliteRecordMigrationTarget.js +0 -305
- package/dist/storage/upgrade/sqliteStateMigration.js +0 -918
- package/dist/storage/upgrade/switchProgress.js +0 -80
- package/dist/storage/upgrade/upgradeReceipt.js +0 -161
- package/dist/storage/upgradeCoordination.js +0 -187
- package/dist/storage/upgradeFence.js +0 -366
- package/dist/telemetry/telemetryCompaction.js +0 -252
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
2
|
import { fileURLToPath } from "node:url";
|
|
3
|
-
import { callController, ControllerClientError, readControllerDiscovery, stopOrphanedFileTaskController
|
|
3
|
+
import { callController, ControllerClientError, readControllerDiscovery, stopOrphanedFileTaskController } from "../core/controllerClient.js";
|
|
4
4
|
import { controllerSocketPath } from "../core/controllerEndpoint.js";
|
|
5
5
|
import { FILE_TASK_CONTROLLER_PROTOCOL_VERSION } from "../core/protocol.js";
|
|
6
6
|
import { AGENT_OPERATIONAL_ENVIRONMENT_NAMES, nativeAgentEnvironmentNames, operationalAgentEnvironment, selectEnvironment, YUI_MANAGED_RUNTIME_ENVIRONMENT_NAMES } from "../agent/launchEnvironment.js";
|
|
7
|
-
import {
|
|
7
|
+
import { openCurrentTaskStore } from "../storage/currentTaskStore.js";
|
|
8
8
|
import { yuiTmuxServerName } from "../tmux/tmuxManager.js";
|
|
9
9
|
import { hasRuntimeLifecycleWork } from "../runtime/lifecycleReservation.js";
|
|
10
10
|
import { assertControllerStatusIdentity } from "../runtime/exactControlPlane.js";
|
|
@@ -21,19 +21,15 @@ const ENVIRONMENT_REFRESH_TIMEOUT_MS = 500;
|
|
|
21
21
|
const CONFIGURATION_REFRESH_TIMEOUT_MS = 500;
|
|
22
22
|
const CONTROLLER_OPERATIONAL_ENVIRONMENT = [
|
|
23
23
|
...AGENT_OPERATIONAL_ENVIRONMENT_NAMES,
|
|
24
|
-
//
|
|
25
|
-
// explicit recovery commands. A Controller spawned by such a command must
|
|
26
|
-
// inherit the forced backend; otherwise a layout-7 test Home silently opens
|
|
27
|
-
// SQLite, creates yui.db, and diverges from the file store the test writes.
|
|
28
|
-
"YUI_STORE_BACKEND",
|
|
24
|
+
// The worker toggle changes process placement, not storage authority.
|
|
29
25
|
"YUI_STORE_WORKER",
|
|
30
26
|
// rr13/test: Forward the liveness seam so an integration test's Controller
|
|
31
|
-
// subprocess does not reap a saved active Leader
|
|
27
|
+
// subprocess does not reap a saved active Leader Turn without a real tmux role.
|
|
32
28
|
"YUI_TEST_ROLE_LIVENESS_PRESENT",
|
|
33
29
|
...EPHEMERAL_DOMAIN_ENVIRONMENT_NAMES
|
|
34
30
|
];
|
|
35
31
|
/**
|
|
36
|
-
* Direct
|
|
32
|
+
* Direct TaskStore writers must not extend state while an older Controller
|
|
37
33
|
* is still reading the same YUI_HOME.
|
|
38
34
|
*/
|
|
39
35
|
export async function assertFileTaskControllerStorageCompatible(home, options = {}) {
|
|
@@ -241,11 +237,8 @@ export async function restartFileTaskController(home, options = {}) {
|
|
|
241
237
|
catch (error) {
|
|
242
238
|
if (options.call !== undefined || !isInvalidDiscovery(error))
|
|
243
239
|
throw error;
|
|
244
|
-
//
|
|
245
|
-
//
|
|
246
|
-
// no ordinary request can fall back to it.
|
|
247
|
-
const previous = await stopPreviousFileTaskController(home, shutdownTimeoutMs);
|
|
248
|
-
previousPid = previous.pid;
|
|
240
|
+
// A malformed current discovery record cannot authorize a protocol call.
|
|
241
|
+
// Exact process identity below may still fence and stop an orphaned owner.
|
|
249
242
|
}
|
|
250
243
|
if (!controllerRunning(current) && options.call === undefined) {
|
|
251
244
|
const orphan = await stopOrphanedFileTaskController(home, shutdownTimeoutMs);
|
|
@@ -278,7 +271,7 @@ export async function restartFileTaskController(home, options = {}) {
|
|
|
278
271
|
function controllerSpawnEnvironment(home, source) {
|
|
279
272
|
const allowed = new Set(CONTROLLER_OPERATIONAL_ENVIRONMENT);
|
|
280
273
|
try {
|
|
281
|
-
for (const agent of
|
|
274
|
+
for (const agent of openCurrentTaskStore(home).listConfiguredAgents()) {
|
|
282
275
|
for (const name of nativeAgentEnvironmentNames(agent.adapterId))
|
|
283
276
|
allowed.add(name);
|
|
284
277
|
for (const binding of agent.environment)
|
|
@@ -291,7 +284,7 @@ function controllerSpawnEnvironment(home, source) {
|
|
|
291
284
|
}
|
|
292
285
|
// Merge with process.env so a partial source (e.g. a managed-Session env
|
|
293
286
|
// that only carries YUI_SESSION_SCOPE/YUI_TASK_ID) still forwards
|
|
294
|
-
// operational vars like
|
|
287
|
+
// operational vars like YUI_STORE_WORKER and PATH. The source wins on
|
|
295
288
|
// conflict; the whitelist still gates every forwarded name.
|
|
296
289
|
const effectiveSource = { ...process.env, ...source };
|
|
297
290
|
const environment = {};
|
|
@@ -440,8 +433,8 @@ export class FileTaskWorkflowRuntime {
|
|
|
440
433
|
async stopTaskRoleSessions(taskId, roleNames) {
|
|
441
434
|
const targets = [];
|
|
442
435
|
for (const roleName of [...new Set(roleNames)]) {
|
|
443
|
-
if (this.store.
|
|
444
|
-
throw new Error(`Role has an active
|
|
436
|
+
if (this.store.getActiveTurn(taskId, roleName) !== null) {
|
|
437
|
+
throw new Error(`Role has an active Turn: ${taskId}/${roleName}.`);
|
|
445
438
|
}
|
|
446
439
|
const target = this.schedulerStore.enqueueRuntimeCleanup({
|
|
447
440
|
scope: "task",
|
|
@@ -473,8 +466,8 @@ export class FileTaskWorkflowRuntime {
|
|
|
473
466
|
}
|
|
474
467
|
/** Stops only the exact idle Session observed by an Agent command. */
|
|
475
468
|
async stopExactTaskRoleSession(input) {
|
|
476
|
-
if (this.store.
|
|
477
|
-
throw new Error(`Role has an active
|
|
469
|
+
if (this.store.getActiveTurn(input.taskId, input.roleName) !== null) {
|
|
470
|
+
throw new Error(`Role has an active Turn: ${input.taskId}/${input.roleName}.`);
|
|
478
471
|
}
|
|
479
472
|
const owner = {
|
|
480
473
|
scope: "task",
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { reconciliationIntervalMilliseconds } from "../config/yuiConfig.js";
|
|
2
|
-
import { processLeaderWakeups } from "../scheduler/leaderWakeupProcessor.js";
|
|
2
|
+
import { LEADER_WAKE_AGGREGATION_MS, LEADER_WAKE_FORCE_MS, processLeaderWakeups } from "../scheduler/leaderWakeupProcessor.js";
|
|
3
3
|
import { pendingWakeupsMatch } from "../scheduler/pendingWakeup.js";
|
|
4
|
-
import {
|
|
5
|
-
import { selectedActiveSchedulerTasks
|
|
6
|
-
import {
|
|
7
|
-
import { DEFAULT_STALL_WINDOW_MS, DEFAULT_WORKFLOW_STALL_CANDIDATE_AGE_MS,
|
|
8
|
-
import { repairOrphanedActiveTasks } from "../scheduler/activeTaskProgress.js";
|
|
4
|
+
import { processActiveRoleTurnDeliveries } from "../scheduler/activeRoleTurnDelivery.js";
|
|
5
|
+
import { selectedActiveSchedulerTasks } from "../scheduler/ports.js";
|
|
6
|
+
import { reconcileExitedRoleTurns } from "../scheduler/roleTurnLiveness.js";
|
|
7
|
+
import { DEFAULT_STALL_WINDOW_MS, DEFAULT_WORKFLOW_STALL_CANDIDATE_AGE_MS, reconcileStalledRoleTurns } from "../scheduler/roleTurnStall.js";
|
|
9
8
|
import { processOperatorInputNotifications } from "../scheduler/operatorInputNotificationProcessor.js";
|
|
10
9
|
import { startControllerServer } from "../core/controllerServer.js";
|
|
11
10
|
import { monotonicMilliseconds } from "../core/controllerTelemetry.js";
|
|
@@ -15,7 +14,7 @@ import { KeyedWorkQueue } from "../coordination/keyedWorkQueue.js";
|
|
|
15
14
|
import { MailboxScheduler } from "../coordination/mailboxScheduler.js";
|
|
16
15
|
import { nearestDeadlineBatch } from "../coordination/deadlineScheduler.js";
|
|
17
16
|
import { mailboxHasWork, nextPendingBatch } from "../coordination/workMailbox.js";
|
|
18
|
-
import { hasRuntimeCleanupObligation, isRuntimeLaunchReservation
|
|
17
|
+
import { hasRuntimeCleanupObligation, isRuntimeLaunchReservation } from "../runtime/lifecycleReservation.js";
|
|
19
18
|
import { formatTaskRecordReference } from "../task/taskRecordReference.js";
|
|
20
19
|
import { parseDurableJobAcknowledgeParams, parseDurableJobCancelParams, parseDurableJobRefParams, parseDurableJobStartParams } from "./jobControl.js";
|
|
21
20
|
const DEFAULT_RECONCILIATION_INTERVAL_MS = reconciliationIntervalMilliseconds();
|
|
@@ -46,19 +45,18 @@ const ZERO_DRAIN_METRICS = Object.freeze({
|
|
|
46
45
|
* Runs one lean scheduler pass. Due native Turn completions are folded before
|
|
47
46
|
* liveness, so a valid Hook boundary fences destructive process reconciliation.
|
|
48
47
|
*/
|
|
49
|
-
export async function runControllerSchedulerPass(store, delivery, now, workspacePreparer, scope = { kind: "full" }, includeOperator = true, runtimeCleanupOutcomes = [], lifecycleHost, stallWindowMs = DEFAULT_STALL_WINDOW_MS, maintenanceFence, onMaintenanceFenceDefer, blockedTaskIds = new Set(),
|
|
48
|
+
export async function runControllerSchedulerPass(store, delivery, now, workspacePreparer, scope = { kind: "full" }, includeOperator = true, runtimeCleanupOutcomes = [], lifecycleHost, stallWindowMs = DEFAULT_STALL_WINDOW_MS, maintenanceFence, onMaintenanceFenceDefer, blockedTaskIds = new Set(), diagnosticAfterMs = DEFAULT_WORKFLOW_STALL_CANDIDATE_AGE_MS, leaderWakeFence) {
|
|
50
49
|
const compiledSelection = compileReconcileSelection(scope);
|
|
51
50
|
const selection = includeOperator
|
|
52
51
|
? { ...compiledSelection, blockedTaskIds }
|
|
53
52
|
: { ...compiledSelection, operator: false, blockedTaskIds };
|
|
54
|
-
queueSelectedCompletedTaskRuntimeCleanups(store, selection, now);
|
|
55
53
|
// A full-state Task projection can be individually bounded yet still starve
|
|
56
54
|
// control sockets when several scheduler phases repeat it in one native
|
|
57
55
|
// event-loop turn. Give already-written requests a poll boundary before the
|
|
58
56
|
// next durable phase; later phases retain their existing CAS fences.
|
|
59
57
|
await controlEventLoopTurn();
|
|
60
58
|
// A dormant Session may still name a Host that disappeared after its last
|
|
61
|
-
// Provider Turn. Detach that disposable Host before claiming the next
|
|
59
|
+
// Provider Turn. Detach that disposable Host before claiming the next Turn,
|
|
62
60
|
// so retained Agent intent restores the same Session instead of first
|
|
63
61
|
// connecting to a known-dead control socket.
|
|
64
62
|
await reconcileDormantRuntimeOwners(store, delivery, lifecycleHost, scope, now, blockedTaskIds);
|
|
@@ -67,8 +65,6 @@ export async function runControllerSchedulerPass(store, delivery, now, workspace
|
|
|
67
65
|
const availableWakeupSelection = () => (leaderWakeFence?.() === true
|
|
68
66
|
? exactTaskSelection(new Set())
|
|
69
67
|
: selectionWithoutFailedLeaderCleanupTasks(store, selection, failedCleanupRoles));
|
|
70
|
-
if (selection.full)
|
|
71
|
-
repairOrphanedActiveTasks(store, now, selection);
|
|
72
68
|
const claimedTaskMailboxes = claimSelectedTaskMailboxes(store, selection, now);
|
|
73
69
|
try {
|
|
74
70
|
// Durable Leader work that already has a ready Task workspace belongs to
|
|
@@ -81,13 +77,10 @@ export async function runControllerSchedulerPass(store, delivery, now, workspace
|
|
|
81
77
|
await controlEventLoopTurn();
|
|
82
78
|
const workspacePreparation = await prepareActiveWorkspaces(store, workspacePreparer, selection, maintenanceFence, onMaintenanceFenceDefer);
|
|
83
79
|
await controlEventLoopTurn();
|
|
84
|
-
|
|
85
|
-
// Sessions before delivery, so the existing delivery path re-pushes the
|
|
86
|
-
// exact same input in this same pass.
|
|
87
|
-
const activeRunDeliveries = await processActiveRoleRunDeliveries(store, delivery, now, roleSelection, inputDeliveryRecoveryCutoff);
|
|
80
|
+
const activeTurnDeliveries = await processActiveRoleTurnDeliveries(store, delivery, now, roleSelection);
|
|
88
81
|
await controlEventLoopTurn();
|
|
89
|
-
const
|
|
90
|
-
? [formatTaskRecordReference(result.taskId, result.
|
|
82
|
+
const unsettledTurnRefs = new Set(activeTurnDeliveries.flatMap((result) => (result.reason === "delivery-uncertain"
|
|
83
|
+
? [formatTaskRecordReference(result.taskId, result.turnId, "turn")]
|
|
91
84
|
: [])));
|
|
92
85
|
// Every successful Task has completed the targeted phases owned by its
|
|
93
86
|
// mailbox at this boundary. Settle that exact claim before advisory
|
|
@@ -99,16 +92,16 @@ export async function runControllerSchedulerPass(store, delivery, now, workspace
|
|
|
99
92
|
}
|
|
100
93
|
}
|
|
101
94
|
const newlyIdleBusyTaskIds = new Set(initialWakeupResults.flatMap((result) => (result.reason === "busy"
|
|
102
|
-
&& store.
|
|
95
|
+
&& store.getActiveTurn(result.taskId, "leader") === null
|
|
103
96
|
? [result.taskId]
|
|
104
97
|
: [])));
|
|
105
|
-
// Phase-one Leader
|
|
106
|
-
// Keep every newly claimed
|
|
98
|
+
// Phase-one Leader Turns did not exist at the pass's liveness boundary.
|
|
99
|
+
// Keep every newly claimed Turn outside destructive absence decisions until
|
|
107
100
|
// a later pass can observe its stable provider/session generation.
|
|
108
101
|
for (const result of initialWakeupResults) {
|
|
109
|
-
if (result.
|
|
110
|
-
&& store.
|
|
111
|
-
|
|
102
|
+
if (result.turnId !== undefined
|
|
103
|
+
&& store.getActiveTurn(result.taskId, "leader")?.id === result.turnId) {
|
|
104
|
+
unsettledTurnRefs.add(formatTaskRecordReference(result.taskId, result.turnId, "turn"));
|
|
112
105
|
}
|
|
113
106
|
}
|
|
114
107
|
// Let socket callbacks queued during the bounded scheduler phases run
|
|
@@ -116,9 +109,9 @@ export async function runControllerSchedulerPass(store, delivery, now, workspace
|
|
|
116
109
|
await controlEventLoopTurn();
|
|
117
110
|
const liveStatuses = new Map();
|
|
118
111
|
const resourceEvidence = new Map();
|
|
119
|
-
const
|
|
112
|
+
const failedTurnRefs = await reconcileExitedRoleTurns(store, delivery, now, roleSelection, unsettledTurnRefs, liveStatuses, resourceEvidence, scope.kind === "dirty");
|
|
120
113
|
await controlEventLoopTurn();
|
|
121
|
-
await
|
|
114
|
+
await reconcileStalledRoleTurns(store, delivery, now, roleSelection, stallWindowMs, liveStatuses, resourceEvidence, diagnosticAfterMs);
|
|
122
115
|
await controlEventLoopTurn();
|
|
123
116
|
await reconcileDormantRuntimeOwners(store, delivery, lifecycleHost, scope, now, selection.blockedTaskIds);
|
|
124
117
|
const selectedInputTaskIds = selectedTaskIdsForBoundedPass(store, selection);
|
|
@@ -155,8 +148,8 @@ export async function runControllerSchedulerPass(store, delivery, now, workspace
|
|
|
155
148
|
? await processOperatorInputNotifications(store, delivery, selection, now)
|
|
156
149
|
: [];
|
|
157
150
|
return {
|
|
158
|
-
|
|
159
|
-
|
|
151
|
+
activeTurnDeliveries,
|
|
152
|
+
failedTurnRefs,
|
|
160
153
|
wakeups: mergeWakeupPhaseResults(initialWakeupResults, laterWakeups),
|
|
161
154
|
inputNotifications,
|
|
162
155
|
autoResolvedInputs
|
|
@@ -208,60 +201,6 @@ function mergeWakeupPhaseResults(initial, later) {
|
|
|
208
201
|
}
|
|
209
202
|
return merged;
|
|
210
203
|
}
|
|
211
|
-
function queueSelectedCompletedTaskRuntimeCleanups(store, selection, now) {
|
|
212
|
-
if (store.enqueueRuntimeCleanup === undefined)
|
|
213
|
-
return;
|
|
214
|
-
if (selection.full && store.listRuntimeSessionCandidates !== undefined) {
|
|
215
|
-
for (const candidate of store.listRuntimeSessionCandidates({
|
|
216
|
-
cleanupRequiredOnly: true
|
|
217
|
-
})) {
|
|
218
|
-
if (candidate.owner.scope !== "task"
|
|
219
|
-
|| !candidate.cleanupRequired
|
|
220
|
-
|| selection.blockedTaskIds?.has(candidate.owner.taskId)) {
|
|
221
|
-
continue;
|
|
222
|
-
}
|
|
223
|
-
const { taskId, roleName } = candidate.owner;
|
|
224
|
-
if (store.getTask(taskId)?.status !== "completed")
|
|
225
|
-
continue;
|
|
226
|
-
if (store.getActiveAgentRun(taskId, roleName) !== null)
|
|
227
|
-
continue;
|
|
228
|
-
const target = runtimeLifecycleTarget(candidate.owner);
|
|
229
|
-
if (hasRuntimeCleanupObligation(store.getWorkMailbox(target)))
|
|
230
|
-
continue;
|
|
231
|
-
store.enqueueRuntimeCleanup(candidate.owner, now);
|
|
232
|
-
}
|
|
233
|
-
return;
|
|
234
|
-
}
|
|
235
|
-
for (const task of selectedSchedulerTasks(store, selection)) {
|
|
236
|
-
if (task.status !== "completed")
|
|
237
|
-
continue;
|
|
238
|
-
for (const role of selectedSchedulerRoles(store, task.id, selection)) {
|
|
239
|
-
if (store.getActiveAgentRun(task.id, role.name) !== null)
|
|
240
|
-
continue;
|
|
241
|
-
const session = store.getRoleSession(task.id, role.name);
|
|
242
|
-
if (!schedulerSessionRequiresRuntimeCleanup(session))
|
|
243
|
-
continue;
|
|
244
|
-
const target = runtimeLifecycleTarget({
|
|
245
|
-
scope: "task",
|
|
246
|
-
taskId: task.id,
|
|
247
|
-
roleName: role.name
|
|
248
|
-
});
|
|
249
|
-
if (hasRuntimeCleanupObligation(store.getWorkMailbox(target)))
|
|
250
|
-
continue;
|
|
251
|
-
store.enqueueRuntimeCleanup({
|
|
252
|
-
scope: "task",
|
|
253
|
-
taskId: task.id,
|
|
254
|
-
roleName: role.name
|
|
255
|
-
}, now);
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
function schedulerSessionRequiresRuntimeCleanup(session) {
|
|
260
|
-
if (session === null || session.status === "ended") {
|
|
261
|
-
return false;
|
|
262
|
-
}
|
|
263
|
-
return session.launchId !== undefined;
|
|
264
|
-
}
|
|
265
204
|
async function processSelectedRoleRuntimeCleanups(store, delivery, lifecycleHost, scope, now, outcomes, blockedTaskIds = new Set()) {
|
|
266
205
|
const targets = selectedRuntimeLifecycleTargets(store, scope, blockedTaskIds);
|
|
267
206
|
const failedRoles = new Set();
|
|
@@ -750,8 +689,8 @@ function partitionDirtyScope(keys) {
|
|
|
750
689
|
}
|
|
751
690
|
function emptyControllerSchedulerResult() {
|
|
752
691
|
return {
|
|
753
|
-
|
|
754
|
-
|
|
692
|
+
activeTurnDeliveries: [],
|
|
693
|
+
failedTurnRefs: [],
|
|
755
694
|
wakeups: [],
|
|
756
695
|
inputNotifications: [],
|
|
757
696
|
autoResolvedInputs: []
|
|
@@ -768,15 +707,15 @@ function runtimeTaskFailureIds(result) {
|
|
|
768
707
|
}
|
|
769
708
|
function mergeControllerSchedulerResults(results) {
|
|
770
709
|
return {
|
|
771
|
-
|
|
772
|
-
|
|
710
|
+
activeTurnDeliveries: results.flatMap((result) => result.activeTurnDeliveries),
|
|
711
|
+
failedTurnRefs: results.flatMap((result) => result.failedTurnRefs),
|
|
773
712
|
wakeups: results.flatMap((result) => result.wakeups),
|
|
774
713
|
inputNotifications: results.flatMap((result) => result.inputNotifications),
|
|
775
714
|
autoResolvedInputs: results.flatMap((result) => result.autoResolvedInputs)
|
|
776
715
|
};
|
|
777
716
|
}
|
|
778
717
|
/**
|
|
779
|
-
* Single-owner periodic runtime for
|
|
718
|
+
* Single-owner periodic runtime for TaskStore-backed scheduling. Full pump
|
|
780
719
|
* requests remain exclusive, while dirty work is serialized per Task and may
|
|
781
720
|
* progress concurrently across different Tasks up to the configured bound.
|
|
782
721
|
*/
|
|
@@ -809,6 +748,7 @@ export class FileTaskController {
|
|
|
809
748
|
#passRetryAttempt = 0;
|
|
810
749
|
#timer;
|
|
811
750
|
#deadlineTimer;
|
|
751
|
+
#leaderWakeDeadlineTimer;
|
|
812
752
|
#runtimeObserverTimer;
|
|
813
753
|
#signalScheduler;
|
|
814
754
|
#operatorSignalScheduler;
|
|
@@ -851,8 +791,8 @@ export class FileTaskController {
|
|
|
851
791
|
: this.#deliveryRetryLimit;
|
|
852
792
|
this.#taskOrchestrationRetryLimit = positiveInteger(options.taskOrchestrationRetryLimit, DEFAULT_TASK_ORCHESTRATION_RETRY_LIMIT, "Controller Task orchestration retry limit");
|
|
853
793
|
this.#taskConcurrency = boundedPositiveInteger(options.taskConcurrency, DEFAULT_TASK_CONCURRENCY, MAX_TASK_CONCURRENCY, "Controller Task concurrency");
|
|
854
|
-
this.#stallWindowMs = positiveInteger(options.stallWindowMs, DEFAULT_STALL_WINDOW_MS, "Controller
|
|
855
|
-
this.#diagnosticAfterMs = positiveInteger(options.diagnosticAfterMs, DEFAULT_WORKFLOW_STALL_CANDIDATE_AGE_MS, "Controller
|
|
794
|
+
this.#stallWindowMs = positiveInteger(options.stallWindowMs, DEFAULT_STALL_WINDOW_MS, "Controller Turn stall window");
|
|
795
|
+
this.#diagnosticAfterMs = positiveInteger(options.diagnosticAfterMs, DEFAULT_WORKFLOW_STALL_CANDIDATE_AGE_MS, "Controller Turn diagnostic threshold");
|
|
856
796
|
this.#runtimeEventProcessor = options.runtimeEventProcessor;
|
|
857
797
|
this.#runtimeObserver = options.runtimeObserver;
|
|
858
798
|
this.#runtimeObserverIntervalMs = positiveInteger(options.runtimeObserverIntervalMs, DEFAULT_RUNTIME_OBSERVER_INTERVAL_MS, "Controller runtime observer interval");
|
|
@@ -969,6 +909,10 @@ export class FileTaskController {
|
|
|
969
909
|
clearTimeout(this.#deadlineTimer);
|
|
970
910
|
this.#deadlineTimer = undefined;
|
|
971
911
|
}
|
|
912
|
+
if (this.#leaderWakeDeadlineTimer !== undefined) {
|
|
913
|
+
clearTimeout(this.#leaderWakeDeadlineTimer);
|
|
914
|
+
this.#leaderWakeDeadlineTimer = undefined;
|
|
915
|
+
}
|
|
972
916
|
if (this.#timer !== undefined) {
|
|
973
917
|
clearInterval(this.#timer);
|
|
974
918
|
this.#timer = undefined;
|
|
@@ -1115,7 +1059,7 @@ export class FileTaskController {
|
|
|
1115
1059
|
// processes Leader wakeups.
|
|
1116
1060
|
this.#jobSupervisor?.reconcile(this.#now());
|
|
1117
1061
|
if (scope.kind === "full") {
|
|
1118
|
-
result = await runControllerSchedulerPass(this.store, this.delivery, this.#now(), this.#workspacePreparer, scope, false, runtimeCleanupOutcomes, this.#lifecycleHost, this.#stallWindowMs, this.#maintenanceFence, this.#onMaintenanceFenceDefer, runtimeFailedTaskIds, this.#
|
|
1062
|
+
result = await runControllerSchedulerPass(this.store, this.delivery, this.#now(), this.#workspacePreparer, scope, false, runtimeCleanupOutcomes, this.#lifecycleHost, this.#stallWindowMs, this.#maintenanceFence, this.#onMaintenanceFenceDefer, runtimeFailedTaskIds, this.#diagnosticAfterMs, this.#leaderWakeFence);
|
|
1119
1063
|
}
|
|
1120
1064
|
else {
|
|
1121
1065
|
const dirtyPass = await this.#runDirtySchedulerPass(scope, runtimeCleanupOutcomes, runtimeFailedTaskIds);
|
|
@@ -1207,6 +1151,7 @@ export class FileTaskController {
|
|
|
1207
1151
|
}
|
|
1208
1152
|
finally {
|
|
1209
1153
|
this.#scheduleNextInputDeadline();
|
|
1154
|
+
this.#scheduleNextLeaderWakeDeadline();
|
|
1210
1155
|
}
|
|
1211
1156
|
}
|
|
1212
1157
|
async #runDirtySchedulerPass(scope, runtimeCleanupOutcomes, blockedTaskIds = new Set()) {
|
|
@@ -1214,7 +1159,7 @@ export class FileTaskController {
|
|
|
1214
1159
|
const taskScopes = partition.taskScopes.filter((taskScope) => (!blockedTaskIds.has(taskScope.taskId)));
|
|
1215
1160
|
const orderedResults = [];
|
|
1216
1161
|
if (partition.globalKeys.length > 0) {
|
|
1217
|
-
orderedResults.push(await runControllerSchedulerPass(this.store, this.delivery, this.#now(), this.#workspacePreparer, { kind: "dirty", keys: partition.globalKeys }, false, runtimeCleanupOutcomes, this.#lifecycleHost, this.#stallWindowMs, this.#maintenanceFence, this.#onMaintenanceFenceDefer, blockedTaskIds, this.#
|
|
1162
|
+
orderedResults.push(await runControllerSchedulerPass(this.store, this.delivery, this.#now(), this.#workspacePreparer, { kind: "dirty", keys: partition.globalKeys }, false, runtimeCleanupOutcomes, this.#lifecycleHost, this.#stallWindowMs, this.#maintenanceFence, this.#onMaintenanceFenceDefer, blockedTaskIds, this.#diagnosticAfterMs, this.#leaderWakeFence));
|
|
1218
1163
|
}
|
|
1219
1164
|
if (taskScopes.length === 0
|
|
1220
1165
|
|| this.#stopped
|
|
@@ -1247,7 +1192,7 @@ export class FileTaskController {
|
|
|
1247
1192
|
if (this.#stopped || this.#pendingFull)
|
|
1248
1193
|
continue;
|
|
1249
1194
|
try {
|
|
1250
|
-
taskResults[selected.index] = await runControllerSchedulerPass(this.store, this.delivery, this.#now(), this.#workspacePreparer, { kind: "dirty", keys: selected.taskScope.keys }, false, taskCleanupOutcomes[selected.index], this.#lifecycleHost, this.#stallWindowMs, this.#maintenanceFence, this.#onMaintenanceFenceDefer, blockedTaskIds, this.#
|
|
1195
|
+
taskResults[selected.index] = await runControllerSchedulerPass(this.store, this.delivery, this.#now(), this.#workspacePreparer, { kind: "dirty", keys: selected.taskScope.keys }, false, taskCleanupOutcomes[selected.index], this.#lifecycleHost, this.#stallWindowMs, this.#maintenanceFence, this.#onMaintenanceFenceDefer, blockedTaskIds, this.#diagnosticAfterMs, this.#leaderWakeFence);
|
|
1251
1196
|
this.#clearTaskPassRetry(selected.taskScope.taskId);
|
|
1252
1197
|
}
|
|
1253
1198
|
catch (error) {
|
|
@@ -1302,6 +1247,7 @@ export class FileTaskController {
|
|
|
1302
1247
|
this.#scheduleDeliveryRetries(result);
|
|
1303
1248
|
this.#clearEmptyOperatorRetry();
|
|
1304
1249
|
this.#scheduleNextInputDeadline();
|
|
1250
|
+
this.#scheduleNextLeaderWakeDeadline();
|
|
1305
1251
|
}
|
|
1306
1252
|
#signalOperatorMailbox(retryReady = false) {
|
|
1307
1253
|
if (this.#stopped)
|
|
@@ -1322,7 +1268,7 @@ export class FileTaskController {
|
|
|
1322
1268
|
const processor = this.#runtimeEventProcessor;
|
|
1323
1269
|
if (processor === undefined)
|
|
1324
1270
|
return undefined;
|
|
1325
|
-
// The worker
|
|
1271
|
+
// The persistence worker exposes drainAsync; the in-process store stays sync.
|
|
1326
1272
|
const result = "drainAsync" in processor
|
|
1327
1273
|
? await processor.drainAsync(this.#now())
|
|
1328
1274
|
: processor.drain(this.#now());
|
|
@@ -1459,12 +1405,51 @@ export class FileTaskController {
|
|
|
1459
1405
|
}, delayMs);
|
|
1460
1406
|
this.#deadlineTimer.unref();
|
|
1461
1407
|
}
|
|
1408
|
+
#scheduleNextLeaderWakeDeadline() {
|
|
1409
|
+
if (this.#leaderWakeDeadlineTimer !== undefined) {
|
|
1410
|
+
clearTimeout(this.#leaderWakeDeadlineTimer);
|
|
1411
|
+
this.#leaderWakeDeadlineTimer = undefined;
|
|
1412
|
+
}
|
|
1413
|
+
if (this.#stopped)
|
|
1414
|
+
return;
|
|
1415
|
+
const now = this.#now().getTime();
|
|
1416
|
+
const deadlines = this.store.listPendingWakeups().flatMap((wakeup) => {
|
|
1417
|
+
const firstRequestedAt = Date.parse(wakeup.firstRequestedAt);
|
|
1418
|
+
if (!Number.isFinite(firstRequestedAt))
|
|
1419
|
+
return [];
|
|
1420
|
+
const aggregationAt = firstRequestedAt + LEADER_WAKE_AGGREGATION_MS;
|
|
1421
|
+
if (aggregationAt > now) {
|
|
1422
|
+
return [{
|
|
1423
|
+
key: `role:${encodeURIComponent(wakeup.taskId)}/leader`,
|
|
1424
|
+
at: aggregationAt
|
|
1425
|
+
}];
|
|
1426
|
+
}
|
|
1427
|
+
const forceAt = firstRequestedAt + LEADER_WAKE_FORCE_MS;
|
|
1428
|
+
return forceAt > now && this.store.getActiveTurn(wakeup.taskId, "leader") !== null
|
|
1429
|
+
? [{
|
|
1430
|
+
key: `role:${encodeURIComponent(wakeup.taskId)}/leader`,
|
|
1431
|
+
at: forceAt
|
|
1432
|
+
}]
|
|
1433
|
+
: [];
|
|
1434
|
+
});
|
|
1435
|
+
const nearest = nearestDeadlineBatch(deadlines);
|
|
1436
|
+
if (nearest === null)
|
|
1437
|
+
return;
|
|
1438
|
+
const delayMs = Math.min(MAX_TIMER_DELAY_MS, Math.max(0, nearest.at - now));
|
|
1439
|
+
this.#leaderWakeDeadlineTimer = setTimeout(() => {
|
|
1440
|
+
this.#leaderWakeDeadlineTimer = undefined;
|
|
1441
|
+
for (const key of nearest.keys)
|
|
1442
|
+
this.#signalScheduler.signal(key);
|
|
1443
|
+
void this.#signalScheduler.drain().catch(this.#onError);
|
|
1444
|
+
}, delayMs);
|
|
1445
|
+
this.#leaderWakeDeadlineTimer.unref();
|
|
1446
|
+
}
|
|
1462
1447
|
#scheduleDeliveryRetries(result) {
|
|
1463
1448
|
const retry = new Map();
|
|
1464
1449
|
const settled = new Set();
|
|
1465
1450
|
const writerBlocked = new Set();
|
|
1466
1451
|
const resignal = new Set();
|
|
1467
|
-
for (const delivery of result.
|
|
1452
|
+
for (const delivery of result.activeTurnDeliveries) {
|
|
1468
1453
|
const key = `role:${encodeURIComponent(delivery.taskId)}/${encodeURIComponent(delivery.roleName)}`;
|
|
1469
1454
|
if (delivery.terminalized === true) {
|
|
1470
1455
|
settled.add(key);
|
|
@@ -1474,28 +1459,16 @@ export class FileTaskController {
|
|
|
1474
1459
|
writerBlocked.add(key);
|
|
1475
1460
|
else if (delivery.reason === "not-ready"
|
|
1476
1461
|
|| delivery.reason === "runtime-unavailable") {
|
|
1477
|
-
retry.set(key, {
|
|
1478
|
-
identity: delivery.runId,
|
|
1479
|
-
...(delivery.terminalFailure === undefined
|
|
1480
|
-
? {}
|
|
1481
|
-
: { terminalFailure: delivery.terminalFailure })
|
|
1482
|
-
});
|
|
1462
|
+
retry.set(key, { identity: delivery.turnId });
|
|
1483
1463
|
}
|
|
1484
1464
|
else if (delivery.status === "delivered" || delivery.status === "already-delivered")
|
|
1485
1465
|
settled.add(key);
|
|
1486
1466
|
}
|
|
1487
1467
|
for (const wakeup of result.wakeups) {
|
|
1488
1468
|
const key = `role:${encodeURIComponent(wakeup.taskId)}/leader`;
|
|
1489
|
-
if (wakeup.
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
}
|
|
1493
|
-
else if (wakeup.reason === "writer-attached") {
|
|
1494
|
-
writerBlocked.add(key);
|
|
1495
|
-
}
|
|
1496
|
-
else if (wakeup.reason === "not-ready"
|
|
1497
|
-
|| (wakeup.reason === "busy" && wakeup.runId !== undefined)) {
|
|
1498
|
-
retry.set(key, { identity: wakeup.runId ?? key });
|
|
1469
|
+
if (wakeup.reason === "not-ready"
|
|
1470
|
+
|| (wakeup.reason === "busy" && wakeup.turnId !== undefined)) {
|
|
1471
|
+
retry.set(key, { identity: wakeup.turnId ?? key });
|
|
1499
1472
|
}
|
|
1500
1473
|
else if (wakeup.status === "dispatched")
|
|
1501
1474
|
settled.add(key);
|
|
@@ -1602,9 +1575,9 @@ export class FileTaskController {
|
|
|
1602
1575
|
timer.unref();
|
|
1603
1576
|
this.#deliveryRetryTimers.set(key, timer);
|
|
1604
1577
|
}
|
|
1605
|
-
#terminalizePreparedAfterRetryExhaustion(key,
|
|
1578
|
+
#terminalizePreparedAfterRetryExhaustion(key, turnId, failure) {
|
|
1606
1579
|
if (!key.startsWith("role:")
|
|
1607
|
-
||
|
|
1580
|
+
|| turnId.startsWith("runtime-cleanup:")
|
|
1608
1581
|
|| failure === undefined) {
|
|
1609
1582
|
return;
|
|
1610
1583
|
}
|
|
@@ -1613,10 +1586,10 @@ export class FileTaskController {
|
|
|
1613
1586
|
return;
|
|
1614
1587
|
if (target.taskId !== failure.taskId
|
|
1615
1588
|
|| target.roleName !== failure.roleName
|
|
1616
|
-
||
|
|
1617
|
-
throw new Error(`Role delivery retry identity changed: ${key}/${
|
|
1589
|
+
|| turnId !== failure.turnId) {
|
|
1590
|
+
throw new Error(`Role delivery retry identity changed: ${key}/${turnId}.`);
|
|
1618
1591
|
}
|
|
1619
|
-
const result = this.store.
|
|
1592
|
+
const result = this.store.saveRoleTurnDeliveryFailure({
|
|
1620
1593
|
...failure,
|
|
1621
1594
|
now: this.#now()
|
|
1622
1595
|
});
|
|
@@ -1626,7 +1599,7 @@ export class FileTaskController {
|
|
|
1626
1599
|
this.delivery.forgetPrepared?.({
|
|
1627
1600
|
taskId: failure.taskId,
|
|
1628
1601
|
roleName: failure.roleName,
|
|
1629
|
-
|
|
1602
|
+
turnId: failure.turnId,
|
|
1630
1603
|
...(failure.launchId === undefined
|
|
1631
1604
|
? {}
|
|
1632
1605
|
: { launchId: failure.launchId })
|