@zq-silk/yui 0.14.0 → 0.14.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.
- package/ARCHITECTURE.md +64 -35
- package/README.md +258 -125
- package/dist/agent/argumentPolicy.js +2 -1
- package/dist/agent/managedRuntimeEnvironment.js +5 -8
- package/dist/cli/commandCatalog.js +74 -109
- package/dist/cli/helpRenderer.js +1 -2
- package/dist/cli/interactionCandidates.js +10 -0
- package/dist/cli/interactionPolicy.js +76 -37
- package/dist/cli/roleWizard.js +5 -6
- package/dist/cli/updateOrchestrator.js +10 -8
- package/dist/cli/updatePorts.js +17 -13
- package/dist/cli/upgradeCommand.js +2 -5
- package/dist/cli.js +268 -231
- package/dist/commands/agentCommands.js +13 -0
- package/dist/commands/configCommands.js +7 -19
- package/dist/commands/configOverview.js +3 -2
- package/dist/commands/durableJobCommands.js +1 -1
- package/dist/commands/executionAuditCommands.js +4 -9
- package/dist/commands/globalRoleCommands.js +17 -0
- package/dist/commands/profileCommands.js +200 -30
- package/dist/commands/sessionCommands.js +1 -1
- package/dist/commands/taskActor.js +29 -127
- package/dist/commands/taskChangeSetCommands.js +1 -3
- package/dist/commands/taskCommands.js +1771 -2107
- package/dist/commands/taskCompletionGate.js +27 -103
- package/dist/commands/taskContextCommand.js +47 -80
- package/dist/commands/taskExecutionCommands.js +9 -27
- package/dist/commands/taskInputCommands.js +10 -10
- package/dist/commands/taskIntegrationCommands.js +69 -61
- package/dist/commands/taskIntegrationQueueCommands.js +1 -1
- package/dist/commands/taskNextActionCommand.js +8 -29
- package/dist/commands/taskOverviewCommand.js +25 -3
- package/dist/commands/taskPublicationCommands.js +151 -85
- package/dist/commands/taskPublicationVerifyCommand.js +181 -0
- package/dist/commands/taskRemoteDeliveryCommand.js +134 -0
- package/dist/commands/taskRoleRuntimeStatus.js +5 -6
- package/dist/commands/taskUpstreamCommands.js +79 -95
- package/dist/config/configCatalog.js +1 -1
- package/dist/context/contextSnapshot.js +17 -2
- package/dist/context/sessionBootstrapManifest.js +18 -1
- package/dist/context/sourceTurnContext.js +30 -0
- package/dist/context/turnContextPack.js +107 -48
- package/dist/context/turnInputContract.js +11 -4
- package/dist/context/wakeNotification.js +84 -22
- package/dist/controller/agentRuntimeObserver.js +9 -12
- package/dist/controller/clientRuntime.js +13 -13
- package/dist/controller/controller.js +11 -11
- package/dist/controller/fileSchedulerStoreAdapter.js +262 -219
- package/dist/controller/jobControl.js +11 -10
- package/dist/controller/resourceInventory.js +6 -6
- package/dist/controller/resourceInventoryLinux.js +2 -2
- package/dist/controller/runtime.js +18 -61
- package/dist/controller/runtimeEventInbox.js +57 -34
- package/dist/controller/runtimeEventProcessor.js +36 -96
- package/dist/controller/runtimeHookTurnFence.js +33 -57
- package/dist/controller/runtimeLaunchCoordinator.js +44 -40
- package/dist/controller/runtimeObservationHook.js +18 -12
- package/dist/controller/sessionNotify.js +43 -28
- package/dist/controller/sessionOwnerReconciliation.js +21 -21
- package/dist/controller/structuredProviderObservation.js +13 -16
- package/dist/coordination/workMailboxQueue.js +132 -0
- package/dist/core/controllerServer.js +1 -0
- package/dist/domain/agentResultTransport.js +60 -0
- package/dist/execution/executionHealth.js +111 -118
- package/dist/execution/reviewMainTurn.js +161 -0
- package/dist/execution/workItemExecution.js +454 -0
- package/dist/execution/workItemExecutionProjection.js +309 -0
- package/dist/execution/workItemMainTurn.js +164 -0
- package/dist/executor/agentExecutor.js +19 -19
- package/dist/executor/executorRegistry.js +7 -28
- package/dist/executor/fileRoleLaunchPlanner.js +51 -83
- package/dist/external/githubPublicationVerifier.js +114 -0
- package/dist/external/gitlabPublicationVerifier.js +158 -0
- package/dist/external/pinnedCommandRunner.js +73 -0
- package/dist/integration/changeSetManifest.js +0 -3
- package/dist/integration/deliveryObligation.js +43 -6
- package/dist/integration/gitIntegrationService.js +284 -101
- package/dist/integration/integrationAttempt.js +64 -14
- package/dist/integration/integrationQueueEntry.js +5 -35
- package/dist/integration/integrationQueueService.js +65 -194
- package/dist/lifecycle/canonicalLifecycleEvent.js +16 -15
- package/dist/lifecycle/exactTurnTerminalization.js +100 -97
- package/dist/message/message.js +16 -0
- package/dist/observability/executionAudit.js +4 -25
- package/dist/observability/faultClassification.js +58 -70
- package/dist/observability/orchestrationMetrics.js +8 -50
- package/dist/profile/agentProfile.js +60 -15
- package/dist/profile/agentProfileRuntime.js +100 -0
- package/dist/release/releaseWorkflowPorts.js +5 -68
- package/dist/repository/executionLaneGitSnapshot.js +80 -0
- package/dist/repository/gitWorkspace.js +9 -67
- package/dist/repository/taskBaseFreshness.js +3 -1
- package/dist/repository/taskWorkspaceCoordinator.js +32 -4
- package/dist/repository/taskWorkspacePreparer.js +252 -411
- package/dist/resources/liveReferences.js +6 -55
- package/dist/resources/resourceDiscovery.js +1 -30
- package/dist/resources/resourceRegistrar.js +0 -23
- package/dist/review/deltaRecheck.js +8 -65
- package/dist/review/reviewAcceptance.js +22 -19
- package/dist/review/reviewConfig.js +0 -10
- package/dist/review/reviewDecision.js +2 -2
- package/dist/review/reviewRound.js +147 -273
- package/dist/review/reviewerAvailability.js +13 -23
- package/dist/role/role.js +3 -0
- package/dist/runtime/agentDriverObservation.js +1 -1
- package/dist/runtime/agentHost.js +51 -51
- package/dist/runtime/builtinAgentDrivers.js +9 -2
- package/dist/runtime/codexAppServerRuntime.js +23 -5
- package/dist/runtime/exactControlPlane.js +2 -267
- package/dist/runtime/firstProgressAdvisory.js +1 -1
- package/dist/runtime/index.js +1 -1
- package/dist/runtime/launchBroker.js +11 -11
- package/dist/runtime/lifecycleReservation.js +2 -2
- package/dist/runtime/managedCaller.js +111 -0
- package/dist/runtime/ports.js +16 -3
- package/dist/runtime/preallocatedNativeSession.js +2 -2
- package/dist/runtime/processExitObservation.js +3 -2
- package/dist/runtime/providerContinuationReconciliationService.js +1 -1
- package/dist/runtime/runtimeBinding.js +1 -1
- package/dist/runtime/runtimeObservation.js +33 -16
- package/dist/runtime/runtimeSessionCandidate.js +2 -2
- package/dist/runtime/runtimeStopReceipt.js +11 -11
- package/dist/runtime/sessionLaunchRequest.js +2 -2
- package/dist/runtime/sessionOwnerIdentity.js +9 -9
- package/dist/runtime/sessionOwnerRegistry.js +13 -13
- package/dist/runtime/sessionReconciliation.js +4 -4
- package/dist/runtime/sessionTerminationGuard.js +9 -9
- package/dist/runtime/sessionTokenMetrics.js +5 -7
- package/dist/runtime/structuredProviderHost.js +5 -5
- package/dist/runtime/taskRuntimeIsolation.js +24 -24
- package/dist/runtime/tmuxAdapters.js +32 -30
- package/dist/scheduler/activeRoleTurnDelivery.js +37 -15
- package/dist/scheduler/leaderWakeupProcessor.js +78 -55
- package/dist/scheduler/roleTurnLiveness.js +6 -6
- package/dist/scheduler/roleTurnStall.js +8 -9
- package/dist/scheduler/taskExecutionProjection.js +19 -41
- package/dist/scheduler/taskObservabilityProjection.js +75 -122
- package/dist/storage/sqliteSchema.js +1 -21
- package/dist/storage/sqliteStore.js +62 -47
- package/dist/storage/storageSchema.js +1 -8
- package/dist/storage/storageVersions.js +7 -4
- package/dist/storage/taskStore.js +8 -8
- package/dist/storage/upgrade/upgradeOrchestrator.js +112 -83
- package/dist/task/completionReadiness.js +17 -98
- package/dist/task/deliveryGuard.js +9 -11
- package/dist/task/draftPlan.js +142 -0
- package/dist/task/nextAction.js +179 -477
- package/dist/task/publicationVerification.js +1 -0
- package/dist/task/remoteDelivery.js +233 -0
- package/dist/task/task.js +108 -4
- package/dist/task/taskRecordReference.js +0 -1
- package/dist/turn/turn.js +109 -16
- package/dist/verification/verificationGateService.js +0 -26
- package/dist/web/assets/client/components.js +99 -121
- package/dist/web/assets/client/i18n.js +136 -58
- package/dist/web/assets/client/view.js +44 -0
- package/dist/web/assets/styles/cards.js +3 -15
- package/dist/web/assets/styles/widgets.js +4 -0
- package/dist/web/webSnapshot.js +29 -14
- package/dist/workItem/dependencyGate.js +34 -0
- package/dist/workItem/workItem.js +99 -266
- package/dist/workspace/workItemChangeSetManager.js +15 -59
- package/docs/task-local-identity.md +1 -1
- package/i18n/README.zh-CN.md +165 -59
- package/package.json +1 -1
- package/skills/yui-leader/SKILL.md +272 -782
- package/skills/yui-operator/SKILL.md +190 -365
- package/skills/yui-reviewer/SKILL.md +40 -33
- package/skills/yui-runtime/SKILL.md +27 -6
- package/skills/yui-worker/SKILL.md +19 -6
- package/dist/execution/candidateConvergence.js +0 -623
- package/dist/execution/executionGroup.js +0 -854
- package/dist/execution/resourceBroker.js +0 -426
- package/dist/executor/launchPlan.js +0 -16
- package/dist/review/reviewFinding.js +0 -240
- package/dist/review/reviewFindingLedger.js +0 -550
- package/dist/review/reviewOutcomeClassifier.js +0 -255
- package/dist/scheduler/resourceQueueProjection.js +0 -71
- package/dist/task/repairWave.js +0 -150
|
@@ -141,7 +141,7 @@ export class FileTaskRuntimeIsolation {
|
|
|
141
141
|
}
|
|
142
142
|
cleanupTaskLaunch(input) {
|
|
143
143
|
const taskId = requireIdentity(input.taskId, "Task id");
|
|
144
|
-
const
|
|
144
|
+
const runtimeGenerationId = requireIdentity(input.runtimeGenerationId, "Runtime generation id");
|
|
145
145
|
requireCleanupReason(input.reason);
|
|
146
146
|
const taskRoot = taskRuntimeInventoryRoot(this.#runtimeRoot, taskId, this.#pathLayout);
|
|
147
147
|
let entries;
|
|
@@ -157,7 +157,7 @@ export class FileTaskRuntimeIsolation {
|
|
|
157
157
|
return "absent";
|
|
158
158
|
throw error;
|
|
159
159
|
}
|
|
160
|
-
const launchDigest = taskRuntimeLaunchDigest(taskId,
|
|
160
|
+
const launchDigest = taskRuntimeLaunchDigest(taskId, runtimeGenerationId, this.#pathLayout);
|
|
161
161
|
const matches = [];
|
|
162
162
|
for (const entry of entries) {
|
|
163
163
|
if (!entry.isDirectory() || entry.isSymbolicLink()) {
|
|
@@ -181,7 +181,7 @@ export class FileTaskRuntimeIsolation {
|
|
|
181
181
|
marker = parseMarker(readFileSync(join(generationRoot, MARKER_FILE), "utf8"));
|
|
182
182
|
}
|
|
183
183
|
catch (error) {
|
|
184
|
-
throw new Error("Task runtime
|
|
184
|
+
throw new Error("Task runtime generation is unmarked or invalid.", {
|
|
185
185
|
cause: error
|
|
186
186
|
});
|
|
187
187
|
}
|
|
@@ -189,9 +189,9 @@ export class FileTaskRuntimeIsolation {
|
|
|
189
189
|
const fingerprint = taskRuntimeIsolationFingerprint(descriptor);
|
|
190
190
|
if (marker.fingerprint !== fingerprint
|
|
191
191
|
|| descriptor.taskId !== taskId
|
|
192
|
-
|| descriptor.generation.
|
|
192
|
+
|| descriptor.generation.runtimeGenerationId !== runtimeGenerationId
|
|
193
193
|
|| descriptor.roots.generation !== generationRoot) {
|
|
194
|
-
throw new Error("Task runtime
|
|
194
|
+
throw new Error("Task runtime generation ownership is mismatched.");
|
|
195
195
|
}
|
|
196
196
|
matches.push({
|
|
197
197
|
descriptor,
|
|
@@ -202,7 +202,7 @@ export class FileTaskRuntimeIsolation {
|
|
|
202
202
|
if (matches.length === 0)
|
|
203
203
|
return "absent";
|
|
204
204
|
if (matches.length !== 1) {
|
|
205
|
-
throw new Error("Task runtime
|
|
205
|
+
throw new Error("Task runtime generation ownership is ambiguous.");
|
|
206
206
|
}
|
|
207
207
|
this.cleanup(matches[0], input.reason);
|
|
208
208
|
return "cleaned";
|
|
@@ -212,17 +212,17 @@ export function createTaskRuntimeIsolationDescriptor(input) {
|
|
|
212
212
|
const workspace = validateManagedWorkspace(input.workspace);
|
|
213
213
|
const owner = taskRuntimeWorkspaceOwner(workspace.owner);
|
|
214
214
|
const taskId = requireIdentity(owner.taskId, "Task id");
|
|
215
|
-
const
|
|
215
|
+
const runtimeGenerationId = requireIdentity(input.runtimeGenerationId, "Runtime generation id");
|
|
216
216
|
const generationId = requireIdentity(input.generationId, "Generation id");
|
|
217
217
|
const runtimeRoot = canonicalPath(input.runtimeRoot, "Task runtime root");
|
|
218
218
|
const pathLayout = taskRuntimePathLayout(input.pathLayout);
|
|
219
|
-
const generation = taskRuntimeGenerationRoot(runtimeRoot, taskId, owner,
|
|
219
|
+
const generation = taskRuntimeGenerationRoot(runtimeRoot, taskId, owner, runtimeGenerationId, pathLayout);
|
|
220
220
|
const declared = capabilities(input.policy?.declaredExternalCapabilities ?? [], "Declared external capability");
|
|
221
221
|
const requested = capabilities(input.policy?.requestedExternalCapabilities ?? [], "Requested external capability");
|
|
222
222
|
const portPreference = ports(input.policy?.portPreference ?? [], "Port preference");
|
|
223
223
|
const portAllocations = allocations(input.policy?.portAllocations ?? []);
|
|
224
224
|
return Object.freeze({
|
|
225
|
-
schemaVersion:
|
|
225
|
+
schemaVersion: 2,
|
|
226
226
|
kind: "yui-task-runtime-isolation",
|
|
227
227
|
taskId,
|
|
228
228
|
workspace: Object.freeze({ owner, root: canonicalPath(workspace.root, "Workspace root") }),
|
|
@@ -232,12 +232,12 @@ export function createTaskRuntimeIsolationDescriptor(input) {
|
|
|
232
232
|
cache: join(generation, "cache"),
|
|
233
233
|
temporary: join(generation, "tmp")
|
|
234
234
|
}),
|
|
235
|
-
serviceNamespace: taskRuntimeServiceNamespace(taskId, owner,
|
|
235
|
+
serviceNamespace: taskRuntimeServiceNamespace(taskId, owner, runtimeGenerationId, generationId),
|
|
236
236
|
portPreference,
|
|
237
237
|
portAllocations,
|
|
238
238
|
externalCapabilities: Object.freeze({ declared, requested }),
|
|
239
239
|
generation: Object.freeze({
|
|
240
|
-
|
|
240
|
+
runtimeGenerationId,
|
|
241
241
|
generationId
|
|
242
242
|
})
|
|
243
243
|
});
|
|
@@ -253,7 +253,7 @@ export function parseTaskRuntimeIsolationDescriptor(serialized) {
|
|
|
253
253
|
if (!isRecord(value) || value.kind !== "yui-task-runtime-isolation") {
|
|
254
254
|
throw new Error(`Expected yui-task-runtime-isolation descriptor; found ${isRecord(value) && typeof value.kind === "string" ? value.kind : "unknown"}.`);
|
|
255
255
|
}
|
|
256
|
-
if (value.schemaVersion !==
|
|
256
|
+
if (value.schemaVersion !== 2) {
|
|
257
257
|
throw new Error("Task runtime isolation descriptor schema version is invalid.");
|
|
258
258
|
}
|
|
259
259
|
const workspace = requireRecord(value.workspace, "Task runtime workspace");
|
|
@@ -262,7 +262,7 @@ export function parseTaskRuntimeIsolationDescriptor(serialized) {
|
|
|
262
262
|
const external = requireRecord(value.externalCapabilities, "Task runtime external capabilities");
|
|
263
263
|
const owner = taskRuntimeWorkspaceOwner(requireRecord(workspace.owner, "Task runtime workspace owner"));
|
|
264
264
|
const descriptor = {
|
|
265
|
-
schemaVersion:
|
|
265
|
+
schemaVersion: 2,
|
|
266
266
|
kind: "yui-task-runtime-isolation",
|
|
267
267
|
taskId: requireIdentity(value.taskId, "Task id"),
|
|
268
268
|
workspace: {
|
|
@@ -283,7 +283,7 @@ export function parseTaskRuntimeIsolationDescriptor(serialized) {
|
|
|
283
283
|
requested: capabilities(external.requested, "Requested external capability")
|
|
284
284
|
},
|
|
285
285
|
generation: {
|
|
286
|
-
|
|
286
|
+
runtimeGenerationId: requireIdentity(generation.runtimeGenerationId, "Runtime generation id"),
|
|
287
287
|
generationId: requireIdentity(generation.generationId, "Generation id")
|
|
288
288
|
}
|
|
289
289
|
};
|
|
@@ -315,15 +315,15 @@ export function assertTaskRuntimeIsolationPreflight(input) {
|
|
|
315
315
|
}
|
|
316
316
|
const runtimeRoot = canonicalPath(input.runtimeRoot, "Task runtime root");
|
|
317
317
|
const pathLayout = taskRuntimePathLayout(input.pathLayout);
|
|
318
|
-
const expectedGeneration = taskRuntimeGenerationRoot(runtimeRoot, descriptor.taskId, expectedOwner, descriptor.generation.
|
|
318
|
+
const expectedGeneration = taskRuntimeGenerationRoot(runtimeRoot, descriptor.taskId, expectedOwner, descriptor.generation.runtimeGenerationId, pathLayout);
|
|
319
319
|
if (descriptor.roots.generation !== expectedGeneration
|
|
320
320
|
|| descriptor.roots.data !== join(expectedGeneration, "data")
|
|
321
321
|
|| descriptor.roots.cache !== join(expectedGeneration, "cache")
|
|
322
322
|
|| descriptor.roots.temporary !== join(expectedGeneration, "tmp")) {
|
|
323
|
-
throw new Error("Task runtime roots do not match the exact Task, owner, and
|
|
323
|
+
throw new Error("Task runtime roots do not match the exact Task, owner, and runtime generation identity.");
|
|
324
324
|
}
|
|
325
|
-
if (descriptor.serviceNamespace !== taskRuntimeServiceNamespace(descriptor.taskId, expectedOwner, descriptor.generation.
|
|
326
|
-
throw new Error("Task runtime service namespace does not match its exact Task
|
|
325
|
+
if (descriptor.serviceNamespace !== taskRuntimeServiceNamespace(descriptor.taskId, expectedOwner, descriptor.generation.runtimeGenerationId, descriptor.generation.generationId)) {
|
|
326
|
+
throw new Error("Task runtime service namespace does not match its exact Task runtime generation.");
|
|
327
327
|
}
|
|
328
328
|
if (!isWithin(runtimeRoot, descriptor.roots.generation)) {
|
|
329
329
|
throw new Error("Task runtime generation is outside its runtime root.");
|
|
@@ -494,8 +494,8 @@ function validatePreparation(preparation) {
|
|
|
494
494
|
function taskRuntimeWorkspaceOwner(owner) {
|
|
495
495
|
return owner;
|
|
496
496
|
}
|
|
497
|
-
function taskRuntimeGenerationRoot(runtimeRoot, taskId, owner,
|
|
498
|
-
return join(taskRuntimeInventoryRoot(runtimeRoot, taskId, pathLayout), taskRuntimeOwnerDigest(taskId, owner, pathLayout), taskRuntimeLaunchDigest(taskId,
|
|
497
|
+
function taskRuntimeGenerationRoot(runtimeRoot, taskId, owner, runtimeGenerationId, pathLayout = "hierarchical") {
|
|
498
|
+
return join(taskRuntimeInventoryRoot(runtimeRoot, taskId, pathLayout), taskRuntimeOwnerDigest(taskId, owner, pathLayout), taskRuntimeLaunchDigest(taskId, runtimeGenerationId, pathLayout));
|
|
499
499
|
}
|
|
500
500
|
function taskRuntimeInventoryRoot(runtimeRoot, taskId, pathLayout) {
|
|
501
501
|
return pathLayout === "compact" ? runtimeRoot : join(runtimeRoot, taskId);
|
|
@@ -505,8 +505,8 @@ function taskRuntimeOwnerDigest(taskId, owner, pathLayout) {
|
|
|
505
505
|
? digest(JSON.stringify([taskId, owner])).slice(0, 20)
|
|
506
506
|
: digest(JSON.stringify(owner)).slice(0, 24);
|
|
507
507
|
}
|
|
508
|
-
function taskRuntimeLaunchDigest(taskId,
|
|
509
|
-
return digest(JSON.stringify([taskId,
|
|
508
|
+
function taskRuntimeLaunchDigest(taskId, runtimeGenerationId, pathLayout) {
|
|
509
|
+
return digest(JSON.stringify([taskId, runtimeGenerationId])).slice(0, pathLayout === "compact" ? 20 : 24);
|
|
510
510
|
}
|
|
511
511
|
function taskRuntimePathLayout(value) {
|
|
512
512
|
const pathLayout = value ?? "hierarchical";
|
|
@@ -515,11 +515,11 @@ function taskRuntimePathLayout(value) {
|
|
|
515
515
|
}
|
|
516
516
|
return pathLayout;
|
|
517
517
|
}
|
|
518
|
-
function taskRuntimeServiceNamespace(taskId, owner,
|
|
518
|
+
function taskRuntimeServiceNamespace(taskId, owner, runtimeGenerationId, generationId) {
|
|
519
519
|
return `yui-task-${digest(JSON.stringify([
|
|
520
520
|
taskId,
|
|
521
521
|
owner,
|
|
522
|
-
|
|
522
|
+
runtimeGenerationId,
|
|
523
523
|
generationId
|
|
524
524
|
])).slice(0, 24)}`;
|
|
525
525
|
}
|
|
@@ -3,10 +3,10 @@ import { fileURLToPath } from "node:url";
|
|
|
3
3
|
import { resolve } from "node:path";
|
|
4
4
|
import { createRuntimeBinding } from "./runtimeBinding.js";
|
|
5
5
|
import { normalizeRuntimeOwner } from "./runtimeOwner.js";
|
|
6
|
-
import { RuntimeHostContentionError } from "./ports.js";
|
|
6
|
+
import { RuntimeGenerationMismatchError, RuntimeHostContentionError } from "./ports.js";
|
|
7
7
|
import { toRuntimeLaunchFailure } from "./launchDiagnostics.js";
|
|
8
8
|
import { requireSafeIdentity } from "./validation.js";
|
|
9
|
-
import { YUI_CONTROL_PLANE_DESCRIPTOR
|
|
9
|
+
import { YUI_CONTROL_PLANE_DESCRIPTOR } from "./exactControlPlane.js";
|
|
10
10
|
import { launchBrokerForHome } from "./launchBroker.js";
|
|
11
11
|
import { AGENT_HOST_CONTROL_PROTOCOL, sendAgentHostLaunchControl, sendAgentHostTurnControl, sendAgentHostSteerControl, waitForAgentHostLaunchAck } from "./agentHost.js";
|
|
12
12
|
const DEFAULT_INACTIVITY_TIMEOUT_MS = 300_000;
|
|
@@ -172,7 +172,7 @@ export class TmuxSessionHost {
|
|
|
172
172
|
agentId: request.agentId,
|
|
173
173
|
adapterId: request.adapterId,
|
|
174
174
|
effective: request.effective,
|
|
175
|
-
|
|
175
|
+
runtimeGenerationId: request.runtimeGenerationId,
|
|
176
176
|
mode: request.mode,
|
|
177
177
|
...(request.turnId === undefined ? {} : { turnId: request.turnId }),
|
|
178
178
|
...(request.runtimeIsolation === undefined
|
|
@@ -220,7 +220,7 @@ export class TmuxSessionHost {
|
|
|
220
220
|
?? (request.mode === "resume" ? request.nativeSessionId : undefined);
|
|
221
221
|
beforeHostStart?.({
|
|
222
222
|
owner: request.owner,
|
|
223
|
-
|
|
223
|
+
runtimeGenerationId: request.runtimeGenerationId,
|
|
224
224
|
...(request.turnId === undefined ? {} : { turnId: request.turnId }),
|
|
225
225
|
agentId: request.agentId,
|
|
226
226
|
adapterId: request.adapterId,
|
|
@@ -251,7 +251,7 @@ export class TmuxSessionHost {
|
|
|
251
251
|
}
|
|
252
252
|
let binding = createRuntimeBinding({
|
|
253
253
|
id: bindingId,
|
|
254
|
-
|
|
254
|
+
runtimeGenerationId: request.runtimeGenerationId,
|
|
255
255
|
owner: request.owner,
|
|
256
256
|
agentId: request.agentId,
|
|
257
257
|
adapterId: request.adapterId,
|
|
@@ -285,16 +285,14 @@ export class TmuxSessionHost {
|
|
|
285
285
|
const broker = launchBrokerForHome(yuiHome);
|
|
286
286
|
const sessionManifest = planned.launch.env.YUI_SESSION_MANIFEST;
|
|
287
287
|
const frozenControlPlane = planned.launch.env[YUI_CONTROL_PLANE_DESCRIPTOR];
|
|
288
|
-
const frozenTaskRuntime = planned.launch.env[YUI_TASK_RUNTIME_DESCRIPTOR];
|
|
289
288
|
if (request.owner.scope === "task" && request.turnId !== undefined
|
|
290
289
|
&& (sessionManifest === undefined
|
|
291
|
-
|| frozenControlPlane === undefined
|
|
292
|
-
|
|
293
|
-
throw new Error("Managed Task Agent Host launch is missing its Session Manifest or frozen control descriptors.");
|
|
290
|
+
|| frozenControlPlane === undefined)) {
|
|
291
|
+
throw new Error("Managed Task Agent Host launch is missing its Session Manifest or frozen control descriptor.");
|
|
294
292
|
}
|
|
295
293
|
const reservation = broker.reserve(Object.freeze({
|
|
296
|
-
schemaVersion:
|
|
297
|
-
|
|
294
|
+
schemaVersion: 2,
|
|
295
|
+
runtimeGenerationId: request.runtimeGenerationId,
|
|
298
296
|
command: planned.launch.command,
|
|
299
297
|
args: [...planned.launch.args],
|
|
300
298
|
environment: { ...planned.launch.env },
|
|
@@ -311,7 +309,7 @@ export class TmuxSessionHost {
|
|
|
311
309
|
fileURLToPath(new URL("../cli.js", import.meta.url)),
|
|
312
310
|
"internal",
|
|
313
311
|
"agent-host",
|
|
314
|
-
reservation.
|
|
312
|
+
reservation.runtimeGenerationId,
|
|
315
313
|
reservation.ticket
|
|
316
314
|
],
|
|
317
315
|
env: {
|
|
@@ -319,7 +317,7 @@ export class TmuxSessionHost {
|
|
|
319
317
|
YUI_SESSION_SCOPE: request.owner.scope,
|
|
320
318
|
...(request.owner.scope === "task" ? { YUI_TASK_ID: request.owner.taskId } : {}),
|
|
321
319
|
YUI_ROLE: request.owner.roleName,
|
|
322
|
-
|
|
320
|
+
YUI_RUNTIME_GENERATION_ID: request.runtimeGenerationId,
|
|
323
321
|
...(planned.launch.env.YUI_AGENT_ID === undefined
|
|
324
322
|
? {}
|
|
325
323
|
: { YUI_AGENT_ID: planned.launch.env.YUI_AGENT_ID }),
|
|
@@ -334,10 +332,7 @@ export class TmuxSessionHost {
|
|
|
334
332
|
: { YUI_SESSION_MANIFEST: sessionManifest }),
|
|
335
333
|
...(frozenControlPlane === undefined
|
|
336
334
|
? {}
|
|
337
|
-
: { [YUI_CONTROL_PLANE_DESCRIPTOR]: frozenControlPlane })
|
|
338
|
-
...(frozenTaskRuntime === undefined
|
|
339
|
-
? {}
|
|
340
|
-
: { [YUI_TASK_RUNTIME_DESCRIPTOR]: frozenTaskRuntime })
|
|
335
|
+
: { [YUI_CONTROL_PLANE_DESCRIPTOR]: frozenControlPlane })
|
|
341
336
|
}
|
|
342
337
|
};
|
|
343
338
|
let hostCreated = false;
|
|
@@ -351,7 +346,7 @@ export class TmuxSessionHost {
|
|
|
351
346
|
scope: request.owner.scope,
|
|
352
347
|
...(request.owner.scope === "task" ? { taskId: request.owner.taskId } : {}),
|
|
353
348
|
roleName: request.owner.roleName,
|
|
354
|
-
|
|
349
|
+
runtimeGenerationId: reservation.runtimeGenerationId,
|
|
355
350
|
requireTurnAck: false
|
|
356
351
|
});
|
|
357
352
|
providerDispatchObserved = true;
|
|
@@ -365,29 +360,36 @@ export class TmuxSessionHost {
|
|
|
365
360
|
control: {
|
|
366
361
|
protocol: AGENT_HOST_CONTROL_PROTOCOL,
|
|
367
362
|
type: "launch",
|
|
368
|
-
|
|
363
|
+
runtimeGenerationId: reservation.runtimeGenerationId,
|
|
369
364
|
ticket: reservation.ticket
|
|
370
365
|
}
|
|
371
366
|
});
|
|
372
|
-
if (controlResult.outcome === "active-other-
|
|
373
|
-
broker.revoke(request.
|
|
367
|
+
if (controlResult.outcome === "active-other-generation") {
|
|
368
|
+
broker.revoke(request.runtimeGenerationId);
|
|
374
369
|
throw new RuntimeHostContentionError("provider-child-active", `The persistent Agent Host for ${request.owner.roleName} still owns another Provider Turn.`);
|
|
375
370
|
}
|
|
376
|
-
if (controlResult.outcome === "active-same-
|
|
377
|
-
broker.revoke(request.
|
|
371
|
+
if (controlResult.outcome === "active-same-generation") {
|
|
372
|
+
broker.revoke(request.runtimeGenerationId);
|
|
378
373
|
}
|
|
379
374
|
const acceptableState = ["idle", "ready", "busy"].includes(controlResult.snapshot.state);
|
|
380
375
|
if (!acceptableState
|
|
381
|
-
|| controlResult.snapshot.
|
|
382
|
-
broker.revoke(request.
|
|
383
|
-
|
|
376
|
+
|| controlResult.snapshot.runtimeGenerationId !== reservation.runtimeGenerationId) {
|
|
377
|
+
broker.revoke(request.runtimeGenerationId);
|
|
378
|
+
try {
|
|
379
|
+
await stopExactRole(this.tmux, hostId, request.owner.roleName);
|
|
380
|
+
}
|
|
381
|
+
catch {
|
|
382
|
+
// The coordinator will enqueue durable owner cleanup.
|
|
383
|
+
}
|
|
384
|
+
throw new RuntimeGenerationMismatchError(reservation.runtimeGenerationId, controlResult.snapshot.runtimeGenerationId, controlResult.snapshot.state, `Agent Host acknowledgement generation mismatch for ${reservation.runtimeGenerationId}; observed=${controlResult.snapshot.runtimeGenerationId ?? "none"}; `
|
|
385
|
+
+ `state=${controlResult.snapshot.state}.`);
|
|
384
386
|
}
|
|
385
387
|
providerSnapshot = controlResult.snapshot;
|
|
386
388
|
providerDispatchObserved = true;
|
|
387
389
|
}
|
|
388
390
|
}
|
|
389
391
|
catch (error) {
|
|
390
|
-
broker.revoke(request.
|
|
392
|
+
broker.revoke(request.runtimeGenerationId);
|
|
391
393
|
if (hostCreated && !providerDispatchObserved) {
|
|
392
394
|
try {
|
|
393
395
|
await stopExactRole(this.tmux, hostId, request.owner.roleName);
|
|
@@ -415,7 +417,7 @@ export class TmuxSessionHost {
|
|
|
415
417
|
}
|
|
416
418
|
const binding = createRuntimeBinding({
|
|
417
419
|
id: bindingId,
|
|
418
|
-
|
|
420
|
+
runtimeGenerationId: request.runtimeGenerationId,
|
|
419
421
|
owner: request.owner,
|
|
420
422
|
agentId: request.agentId,
|
|
421
423
|
adapterId: request.adapterId,
|
|
@@ -529,7 +531,7 @@ export class AgentHostPromptPushAdapter {
|
|
|
529
531
|
control: {
|
|
530
532
|
protocol: AGENT_HOST_CONTROL_PROTOCOL,
|
|
531
533
|
type: "submit-turn",
|
|
532
|
-
|
|
534
|
+
runtimeGenerationId: request.binding.runtimeGenerationId,
|
|
533
535
|
nativeSessionId: request.binding.nativeSessionId,
|
|
534
536
|
turnId: request.envelope.source.localId,
|
|
535
537
|
authority: request.binding.providerAuthority,
|
|
@@ -573,7 +575,7 @@ export class AgentHostPromptPushAdapter {
|
|
|
573
575
|
control: {
|
|
574
576
|
protocol: AGENT_HOST_CONTROL_PROTOCOL,
|
|
575
577
|
type: "steer-turn",
|
|
576
|
-
|
|
578
|
+
runtimeGenerationId: request.runtimeGenerationId,
|
|
577
579
|
nativeSessionId: request.nativeSessionId,
|
|
578
580
|
nativeTurnId: request.nativeTurnId,
|
|
579
581
|
authority: request.providerAuthority,
|
|
@@ -7,6 +7,7 @@ import { RuntimeLaunchFailure } from "../runtime/launchDiagnostics.js";
|
|
|
7
7
|
import { RuntimeLaunchError } from "../runtime/ports.js";
|
|
8
8
|
import { formatTurnReceiptId } from "../task/taskRecordReference.js";
|
|
9
9
|
import { turnInputEnvelope } from "../turn/turn.js";
|
|
10
|
+
import { captureRoleTurnDispatch } from "../coordination/workMailboxQueue.js";
|
|
10
11
|
import { isSchedulerTaskWorkspaceReady, selectedActiveSchedulerTasks, selectedSchedulerRoles } from "./ports.js";
|
|
11
12
|
/**
|
|
12
13
|
* Sole managed Provider write path. A Turn is durable workflow intent;
|
|
@@ -31,6 +32,15 @@ async function deliverActiveTurn(store, delivery, task, role, turn, now) {
|
|
|
31
32
|
if (!isSchedulerTaskWorkspaceReady(task, store.getTaskWorkspace(task.id))) {
|
|
32
33
|
return { ...base, status: "skipped", reason: "workspace-not-ready" };
|
|
33
34
|
}
|
|
35
|
+
const dispatchToken = captureRoleTurnDispatch(store.getWorkMailbox({
|
|
36
|
+
kind: "role",
|
|
37
|
+
taskId: task.id,
|
|
38
|
+
roleName: role.name
|
|
39
|
+
}), {
|
|
40
|
+
taskId: task.id,
|
|
41
|
+
roleName: role.name,
|
|
42
|
+
turnId: turn.id
|
|
43
|
+
});
|
|
34
44
|
const sessionSet = store.getTaskRoleSessionSet?.(task.id, role.name) ?? null;
|
|
35
45
|
const binding = sessionSet?.providerBinding ?? null;
|
|
36
46
|
const observedTurn = binding?.turn ?? null;
|
|
@@ -40,8 +50,11 @@ async function deliverActiveTurn(store, delivery, task, role, turn, now) {
|
|
|
40
50
|
|| binding?.authority.owner === "unknown") {
|
|
41
51
|
return { ...base, status: "skipped", reason: "writer-attached" };
|
|
42
52
|
}
|
|
43
|
-
if (currentProviderTurn
|
|
44
|
-
|
|
53
|
+
if (currentProviderTurn?.status === "accepted") {
|
|
54
|
+
settleAcceptedRoleTurnDispatch(store, turn, dispatchToken);
|
|
55
|
+
return { ...base, status: "skipped", reason: "not-ready" };
|
|
56
|
+
}
|
|
57
|
+
if (currentProviderTurn?.status === "submitting") {
|
|
45
58
|
return { ...base, status: "skipped", reason: "not-ready" };
|
|
46
59
|
}
|
|
47
60
|
if (currentProviderTurn?.status === "delivery-unknown") {
|
|
@@ -72,9 +85,9 @@ async function deliverActiveTurn(store, delivery, task, role, turn, now) {
|
|
|
72
85
|
mode,
|
|
73
86
|
turnId: turn.id,
|
|
74
87
|
...(nativeSessionId === undefined ? {} : { nativeSessionId }),
|
|
75
|
-
...(mode !== "resume" || existingSession?.
|
|
88
|
+
...(mode !== "resume" || existingSession?.runtimeGenerationId === undefined
|
|
76
89
|
? {}
|
|
77
|
-
: { hostActivationId: existingSession.
|
|
90
|
+
: { hostActivationId: existingSession.runtimeGenerationId }),
|
|
78
91
|
beforeHostStart: (preflight) => persistPreStartSession(store, task, role, turn, existingSession, mode, preflight, now)
|
|
79
92
|
});
|
|
80
93
|
const preparedSession = prepared.session === undefined
|
|
@@ -85,7 +98,7 @@ async function deliverActiveTurn(store, delivery, task, role, turn, now) {
|
|
|
85
98
|
role,
|
|
86
99
|
turn,
|
|
87
100
|
session: preparedSession,
|
|
88
|
-
...(prepared.
|
|
101
|
+
...(prepared.runtimeGenerationId === undefined ? {} : { runtimeGenerationId: prepared.runtimeGenerationId }),
|
|
89
102
|
now
|
|
90
103
|
});
|
|
91
104
|
const ready = await delivery.waitUntilReady(prepared);
|
|
@@ -95,7 +108,7 @@ async function deliverActiveTurn(store, delivery, task, role, turn, now) {
|
|
|
95
108
|
role,
|
|
96
109
|
turn,
|
|
97
110
|
session: readySession,
|
|
98
|
-
...(ready.prepared.
|
|
111
|
+
...(ready.prepared.runtimeGenerationId === undefined ? {} : { runtimeGenerationId: ready.prepared.runtimeGenerationId }),
|
|
99
112
|
now
|
|
100
113
|
});
|
|
101
114
|
submitted = true;
|
|
@@ -105,7 +118,7 @@ async function deliverActiveTurn(store, delivery, task, role, turn, now) {
|
|
|
105
118
|
text: serializeTurnInputEnvelope(turnInputEnvelope(turn))
|
|
106
119
|
});
|
|
107
120
|
if (outcome === "busy" || outcome === "unavailable") {
|
|
108
|
-
forget(delivery, task.id, role.name, turn.id, ready.prepared.
|
|
121
|
+
forget(delivery, task.id, role.name, turn.id, ready.prepared.runtimeGenerationId);
|
|
109
122
|
return {
|
|
110
123
|
...base,
|
|
111
124
|
status: "skipped",
|
|
@@ -113,12 +126,13 @@ async function deliverActiveTurn(store, delivery, task, role, turn, now) {
|
|
|
113
126
|
};
|
|
114
127
|
}
|
|
115
128
|
if (outcome === "rejected" || outcome === "delivery-unknown") {
|
|
116
|
-
forget(delivery, task.id, role.name, turn.id, ready.prepared.
|
|
129
|
+
forget(delivery, task.id, role.name, turn.id, ready.prepared.runtimeGenerationId);
|
|
117
130
|
return failTurnDelivery(store, turn, now, outcome === "delivery-unknown" ? "delivery-unknown" : "runtime-failed", outcome === "delivery-unknown"
|
|
118
131
|
? "Provider Turn delivery is ambiguous; Yui will not replay it automatically."
|
|
119
132
|
: "Provider rejected the managed Turn.");
|
|
120
133
|
}
|
|
121
|
-
forget(delivery, task.id, role.name, turn.id, ready.prepared.
|
|
134
|
+
forget(delivery, task.id, role.name, turn.id, ready.prepared.runtimeGenerationId);
|
|
135
|
+
settleAcceptedRoleTurnDispatch(store, turn, dispatchToken);
|
|
122
136
|
return {
|
|
123
137
|
...base,
|
|
124
138
|
status: outcome === "sent" ? "delivered" : "already-delivered"
|
|
@@ -147,7 +161,7 @@ async function deliverActiveTurn(store, delivery, task, role, turn, now) {
|
|
|
147
161
|
error: message
|
|
148
162
|
};
|
|
149
163
|
}
|
|
150
|
-
forget(delivery, task.id, role.name, turn.id, prepared?.
|
|
164
|
+
forget(delivery, task.id, role.name, turn.id, prepared?.runtimeGenerationId);
|
|
151
165
|
return failTurnDelivery(store, turn, now, submitted ? "delivery-unknown" : "startup-failed", message);
|
|
152
166
|
}
|
|
153
167
|
}
|
|
@@ -172,19 +186,27 @@ function failTurnDelivery(store, turn, now, failureReason, summary) {
|
|
|
172
186
|
...(disposition === "failed" ? { terminalized: true } : {})
|
|
173
187
|
};
|
|
174
188
|
}
|
|
189
|
+
function settleAcceptedRoleTurnDispatch(store, turn, expected) {
|
|
190
|
+
store.settleRoleTurnDispatch({
|
|
191
|
+
taskId: turn.taskId,
|
|
192
|
+
roleName: turn.roleName,
|
|
193
|
+
turnId: turn.id,
|
|
194
|
+
...(expected === undefined ? {} : { expected })
|
|
195
|
+
});
|
|
196
|
+
}
|
|
175
197
|
function persistPreStartSession(store, task, role, turn, existing, mode, preflight, now) {
|
|
176
198
|
if (preflight.owner.scope !== "task"
|
|
177
199
|
|| preflight.owner.taskId !== task.id
|
|
178
200
|
|| preflight.owner.roleName !== role.name
|
|
179
201
|
|| preflight.turnId !== turn.id
|
|
180
|
-
|| preflight.
|
|
202
|
+
|| preflight.runtimeGenerationId.trim().length === 0) {
|
|
181
203
|
throw new Error(`Pre-start launch fence changed the active Role Turn: ${task.id}/${role.name}.`);
|
|
182
204
|
}
|
|
183
205
|
const session = preflight.nativeSessionId === undefined ? null : {
|
|
184
206
|
agentId: preflight.agentId,
|
|
185
207
|
adapterId: preflight.adapterId,
|
|
186
208
|
nativeSessionId: preflight.nativeSessionId,
|
|
187
|
-
|
|
209
|
+
runtimeGenerationId: preflight.runtimeGenerationId,
|
|
188
210
|
...(preflight.sessionTitle === undefined ? {} : { title: preflight.sessionTitle }),
|
|
189
211
|
status: "active",
|
|
190
212
|
effective: preflight.effective
|
|
@@ -194,7 +216,7 @@ function persistPreStartSession(store, task, role, turn, existing, mode, preflig
|
|
|
194
216
|
role,
|
|
195
217
|
turn,
|
|
196
218
|
session: validateRoleSession(role, turn, existing, mode, session),
|
|
197
|
-
|
|
219
|
+
runtimeGenerationId: preflight.runtimeGenerationId,
|
|
198
220
|
now
|
|
199
221
|
});
|
|
200
222
|
}
|
|
@@ -232,12 +254,12 @@ function requireResumeSession(role, turn, session) {
|
|
|
232
254
|
}
|
|
233
255
|
return session.nativeSessionId;
|
|
234
256
|
}
|
|
235
|
-
function forget(delivery, taskId, roleName, turnId,
|
|
257
|
+
function forget(delivery, taskId, roleName, turnId, runtimeGenerationId) {
|
|
236
258
|
delivery.forgetPrepared?.({
|
|
237
259
|
taskId,
|
|
238
260
|
roleName,
|
|
239
261
|
turnId,
|
|
240
|
-
...(
|
|
262
|
+
...(runtimeGenerationId === undefined ? {} : { runtimeGenerationId })
|
|
241
263
|
});
|
|
242
264
|
}
|
|
243
265
|
function hasText(value) {
|
|
@@ -81,15 +81,18 @@ export async function processLeaderWakeups(store, delivery, now, selection) {
|
|
|
81
81
|
: "new"
|
|
82
82
|
: roleAgentSessionResumeMode(sessionSet, role.effective.agentId, role.effective);
|
|
83
83
|
const turnId = store.peekNextTurnId(task.id);
|
|
84
|
+
const envelope = store.getTaskWakeEnvelope?.(task.id) ?? null;
|
|
84
85
|
const contextSnapshot = store.freezeLeaderContextSnapshot?.(task.id, role.name, now);
|
|
85
86
|
const input = createTurnInput({
|
|
86
87
|
source: leaderWakeInputSource(wakeup.reasons),
|
|
87
88
|
directive: [
|
|
88
89
|
`Wake reasons: ${wakeup.reasons.join(", ")}.`,
|
|
90
|
+
...(envelope === null ? [] : [envelope.text.trim()]),
|
|
89
91
|
`Load exact context for ${task.id}/${turnId}.`,
|
|
92
|
+
"Read every referenced completed Worker or Reviewer Turn in full before deciding its disposition.",
|
|
90
93
|
"Before ending this Turn, judge the affected WorkItems and Task; persist any lifecycle or result changes you establish.",
|
|
91
94
|
"The final Turn response is evidence only and never updates WorkItem or Task state by itself."
|
|
92
|
-
].join("
|
|
95
|
+
].join("\n"),
|
|
93
96
|
...(contextSnapshot === undefined ? {} : { contextSnapshotRef: contextSnapshot.ref }),
|
|
94
97
|
deltaRefIds: contextSnapshot?.deltaRefIds ?? []
|
|
95
98
|
});
|
|
@@ -97,7 +100,6 @@ export async function processLeaderWakeups(store, delivery, now, selection) {
|
|
|
97
100
|
...(role.managedWorkspace === undefined ? {} : { workspace: role.managedWorkspace }),
|
|
98
101
|
effective: role.effective
|
|
99
102
|
});
|
|
100
|
-
const envelope = store.getTaskWakeEnvelope?.(task.id) ?? null;
|
|
101
103
|
const claim = store.saveLeaderDispatch({
|
|
102
104
|
task,
|
|
103
105
|
role,
|
|
@@ -155,65 +157,86 @@ async function forceLeaderSteer(store, delivery, taskId, roleName, active, now)
|
|
|
155
157
|
if (processing.batchId !== batchId || processing.owner !== owner) {
|
|
156
158
|
return { taskId, turnId: active.id, status: "skipped", reason: "busy" };
|
|
157
159
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
return { taskId, status: "failed", reason: "not-ready", error: "Active Leader Turn has no steerable Provider fence." };
|
|
170
|
-
}
|
|
171
|
-
const batch = processing.batch;
|
|
172
|
-
const directive = [
|
|
173
|
-
`Aggregated Leader events: ${batch.reasons.join(", ")}.`,
|
|
174
|
-
`The first event arrived at ${batch.firstQueuedAt} and has waited at least 10 minutes while this Leader Turn remained active.`,
|
|
175
|
-
"Process these events now and update durable Task or WorkItem facts when needed.",
|
|
176
|
-
"After the events are handled, continue the work you were doing before this interruption.",
|
|
177
|
-
`Load the current exact context for ${taskId}/${active.id}; the event batch may have grown while this input was delivered.`
|
|
178
|
-
].join(" ");
|
|
179
|
-
const input = createTurnInput({
|
|
180
|
-
source: { type: "yui", channel: "leader-forced-wakeup" },
|
|
181
|
-
directive,
|
|
182
|
-
deltaRefIds: []
|
|
183
|
-
});
|
|
184
|
-
const outcome = await delivery.steerOnce({
|
|
185
|
-
taskId,
|
|
186
|
-
roleName,
|
|
187
|
-
agentId: active.effective.agentId,
|
|
188
|
-
adapterId: active.effective.adapterId,
|
|
189
|
-
launchId: session.launchId,
|
|
190
|
-
nativeSessionId: session.nativeSessionId,
|
|
191
|
-
nativeTurnId: providerTurn.nativeTurnId,
|
|
192
|
-
authority: {
|
|
193
|
-
epoch: authority.epoch,
|
|
194
|
-
owner: "controller",
|
|
195
|
-
holderId: authority.holderId
|
|
196
|
-
},
|
|
197
|
-
receiptId: `turn-input:${taskId}/${active.id}/${batchId}`,
|
|
198
|
-
text: directive
|
|
199
|
-
});
|
|
200
|
-
if (outcome !== "sent" && outcome !== "already-sent") {
|
|
201
|
-
if (outcome !== "delivery-unknown")
|
|
160
|
+
try {
|
|
161
|
+
const sessions = store.getTaskRoleSessionSet?.(taskId, roleName) ?? null;
|
|
162
|
+
const session = sessions?.sessions[active.effective.agentId];
|
|
163
|
+
const binding = sessions?.providerBinding;
|
|
164
|
+
const providerTurn = binding?.turn;
|
|
165
|
+
const authority = binding?.authority;
|
|
166
|
+
if (session?.runtimeGenerationId === undefined || session.nativeSessionId === undefined
|
|
167
|
+
|| providerTurn === null || providerTurn === undefined
|
|
168
|
+
|| providerTurn.turnId !== active.id || providerTurn.nativeTurnId === undefined
|
|
169
|
+
|| providerTurn.status !== "accepted"
|
|
170
|
+
|| authority?.owner !== "controller" || authority.holderId === undefined) {
|
|
202
171
|
store.releaseWorkMailbox(target, batchId);
|
|
172
|
+
return { taskId, status: "failed", reason: "not-ready", error: "Active Leader Turn has no steerable Provider fence." };
|
|
173
|
+
}
|
|
174
|
+
const batch = processing.batch;
|
|
175
|
+
const envelope = store.getTaskWakeEnvelope?.(taskId) ?? null;
|
|
176
|
+
const referencedTurnIds = envelope?.referencedTurnIds ?? [];
|
|
177
|
+
const displayedTurnIds = referencedTurnIds.slice(0, 4);
|
|
178
|
+
const directive = [
|
|
179
|
+
`Aggregated Leader events: ${batch.reasons.join(", ")}.`,
|
|
180
|
+
`The first event arrived at ${batch.firstQueuedAt} and has waited at least 10 minutes while this Leader Turn remained active.`,
|
|
181
|
+
...displayedTurnIds.map((turnId) => `Read the complete result: yui task turn show ${taskId}/${turnId}.`),
|
|
182
|
+
...(referencedTurnIds.length > displayedTurnIds.length
|
|
183
|
+
? [
|
|
184
|
+
`${referencedTurnIds.length - displayedTurnIds.length} additional result Turns are listed in yui task wake show ${taskId} ${envelope.wakeId}.`
|
|
185
|
+
]
|
|
186
|
+
: []),
|
|
187
|
+
"Process these events now and update durable Task or WorkItem facts when needed.",
|
|
188
|
+
"After the events are handled, continue the work you were doing before this interruption.",
|
|
189
|
+
`Load the current exact context for ${taskId}/${active.id}; the event batch may have grown while this input was delivered.`
|
|
190
|
+
].join("\n");
|
|
191
|
+
const input = createTurnInput({
|
|
192
|
+
source: { type: "yui", channel: "leader-forced-wakeup" },
|
|
193
|
+
directive,
|
|
194
|
+
deltaRefIds: []
|
|
195
|
+
});
|
|
196
|
+
const outcome = await delivery.steerOnce({
|
|
197
|
+
taskId,
|
|
198
|
+
roleName,
|
|
199
|
+
agentId: active.effective.agentId,
|
|
200
|
+
adapterId: active.effective.adapterId,
|
|
201
|
+
runtimeGenerationId: session.runtimeGenerationId,
|
|
202
|
+
nativeSessionId: session.nativeSessionId,
|
|
203
|
+
nativeTurnId: providerTurn.nativeTurnId,
|
|
204
|
+
authority: {
|
|
205
|
+
epoch: authority.epoch,
|
|
206
|
+
owner: "controller",
|
|
207
|
+
holderId: authority.holderId
|
|
208
|
+
},
|
|
209
|
+
receiptId: `turn-input:${taskId}/${active.id}/${batchId}`,
|
|
210
|
+
text: directive
|
|
211
|
+
});
|
|
212
|
+
if (outcome !== "sent" && outcome !== "already-sent") {
|
|
213
|
+
if (outcome !== "delivery-unknown")
|
|
214
|
+
store.releaseWorkMailbox(target, batchId);
|
|
215
|
+
return {
|
|
216
|
+
taskId,
|
|
217
|
+
turnId: active.id,
|
|
218
|
+
status: outcome === "busy" ? "skipped" : "failed",
|
|
219
|
+
reason: outcome === "busy" ? "busy" : "not-ready",
|
|
220
|
+
error: outcome
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
const saved = store.saveLeaderSteer({ taskId, turnId: active.id, batchId, input, now });
|
|
224
|
+
if (saved !== "claimed")
|
|
225
|
+
store.releaseWorkMailbox(target, batchId);
|
|
226
|
+
return saved === "claimed"
|
|
227
|
+
? { taskId, turnId: active.id, status: "steered" }
|
|
228
|
+
: { taskId, turnId: active.id, status: "skipped", reason: saved };
|
|
229
|
+
}
|
|
230
|
+
catch (error) {
|
|
231
|
+
store.releaseWorkMailbox(target, batchId);
|
|
203
232
|
return {
|
|
204
233
|
taskId,
|
|
205
234
|
turnId: active.id,
|
|
206
|
-
status:
|
|
207
|
-
reason:
|
|
208
|
-
error:
|
|
235
|
+
status: "failed",
|
|
236
|
+
reason: "not-ready",
|
|
237
|
+
error: error instanceof Error ? error.message : String(error)
|
|
209
238
|
};
|
|
210
239
|
}
|
|
211
|
-
const saved = store.saveLeaderSteer({ taskId, turnId: active.id, batchId, input, now });
|
|
212
|
-
if (saved !== "claimed")
|
|
213
|
-
store.releaseWorkMailbox(target, batchId);
|
|
214
|
-
return saved === "claimed"
|
|
215
|
-
? { taskId, turnId: active.id, status: "steered" }
|
|
216
|
-
: { taskId, turnId: active.id, status: "skipped", reason: saved };
|
|
217
240
|
}
|
|
218
241
|
function hasNativeSession(session) {
|
|
219
242
|
return session !== null
|