@zq-silk/yui 0.14.1 → 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 +254 -124
- 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/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 +16 -82
- 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 +164 -58
- 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 +22 -0
- 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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createHash, randomUUID } from "node:crypto";
|
|
2
2
|
import { runtimeLifecycleTarget } from "../runtime/lifecycleReservation.js";
|
|
3
|
-
import { createRuntimeBinding, RuntimeHostContentionError, RuntimeLaunchError } from "../runtime/index.js";
|
|
3
|
+
import { createRuntimeBinding, RuntimeGenerationMismatchError, RuntimeHostContentionError, RuntimeLaunchError } from "../runtime/index.js";
|
|
4
4
|
import { effectiveLaunchSnapshotsCompatible, validateEffectiveLaunchSnapshot } from "../executor/effectiveLaunch.js";
|
|
5
5
|
class RuntimeBindingContractError extends Error {
|
|
6
6
|
constructor(message, options) {
|
|
@@ -89,7 +89,7 @@ export class RuntimeLaunchCoordinator {
|
|
|
89
89
|
};
|
|
90
90
|
const generationPrefix = `runtime-${expectedFingerprint}:generation:`;
|
|
91
91
|
const proposedGenerationId = requireText(this.#createGenerationId(), "Launch generation id");
|
|
92
|
-
let
|
|
92
|
+
let proposedRuntimeGenerationId = `${generationPrefix}${proposedGenerationId}`;
|
|
93
93
|
let reusedConfirmedRunningHost = false;
|
|
94
94
|
if (request.mode === "resume" && request.hostActivationId !== undefined) {
|
|
95
95
|
if (!request.hostActivationId.startsWith(generationPrefix)) {
|
|
@@ -100,25 +100,25 @@ export class RuntimeLaunchCoordinator {
|
|
|
100
100
|
throw new RuntimeLaunchError(true, request.hostActivationId, `Host activation is temporarily ${inspection.state}: ${request.owner.roleName}.`);
|
|
101
101
|
}
|
|
102
102
|
if (inspection.state === "running") {
|
|
103
|
-
|
|
103
|
+
proposedRuntimeGenerationId = request.hostActivationId;
|
|
104
104
|
reusedConfirmedRunningHost = true;
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
107
|
let runtimeIsolation = reusedConfirmedRunningHost
|
|
108
108
|
? undefined
|
|
109
|
-
: this.#preflightRuntimeIsolation(request,
|
|
109
|
+
: this.#preflightRuntimeIsolation(request, proposedRuntimeGenerationId, proposedGenerationId, false);
|
|
110
110
|
let reservation = this.reservations.reserveRuntimeLaunch({
|
|
111
111
|
owner: request.owner,
|
|
112
|
-
|
|
112
|
+
runtimeGenerationId: proposedRuntimeGenerationId
|
|
113
113
|
}, assertLaunchCurrent, this.#now());
|
|
114
114
|
if (reservation.status === "existing") {
|
|
115
|
-
if (!reservation.
|
|
115
|
+
if (!reservation.runtimeGenerationId.startsWith(generationPrefix)) {
|
|
116
116
|
this.#requireCleanup(request.owner);
|
|
117
117
|
throw new Error(`Runtime launch reservation belongs to stale Role or Agent state: ${request.owner.roleName}.`);
|
|
118
118
|
}
|
|
119
119
|
const inspection = await this.host.inspectOwner(request.owner);
|
|
120
120
|
if (inspection.state === "unavailable" || inspection.state === "starting") {
|
|
121
|
-
throw new RuntimeLaunchError(true, reservation.
|
|
121
|
+
throw new RuntimeLaunchError(true, reservation.runtimeGenerationId, `Runtime is temporarily ${inspection.state}: ${request.owner.roleName}/${reservation.runtimeGenerationId}.`);
|
|
122
122
|
}
|
|
123
123
|
if (inspection.state === "stopped") {
|
|
124
124
|
const taskOwner = request.owner.scope === "task"
|
|
@@ -127,10 +127,10 @@ export class RuntimeLaunchCoordinator {
|
|
|
127
127
|
let exactCleanupAttempted = false;
|
|
128
128
|
let completed = false;
|
|
129
129
|
try {
|
|
130
|
-
completed = this.reservations.completeRuntimeLaunchReservation(request.owner, reservation.
|
|
130
|
+
completed = this.reservations.completeRuntimeLaunchReservation(request.owner, reservation.runtimeGenerationId, taskOwner !== undefined && this.#runtimeIsolation !== undefined
|
|
131
131
|
? () => {
|
|
132
132
|
exactCleanupAttempted = true;
|
|
133
|
-
this.#cleanupTaskLaunch(taskOwner, reservation.
|
|
133
|
+
this.#cleanupTaskLaunch(taskOwner, reservation.runtimeGenerationId);
|
|
134
134
|
}
|
|
135
135
|
: undefined);
|
|
136
136
|
}
|
|
@@ -146,7 +146,7 @@ export class RuntimeLaunchCoordinator {
|
|
|
146
146
|
}
|
|
147
147
|
reservation = this.reservations.reserveRuntimeLaunch({
|
|
148
148
|
owner: request.owner,
|
|
149
|
-
|
|
149
|
+
runtimeGenerationId: proposedRuntimeGenerationId
|
|
150
150
|
}, assertLaunchCurrent, this.#now());
|
|
151
151
|
if (reservation.status !== "reserved") {
|
|
152
152
|
throw new Error("Runtime launch reservation could not be renewed.");
|
|
@@ -158,13 +158,13 @@ export class RuntimeLaunchCoordinator {
|
|
|
158
158
|
assertLaunchCurrent();
|
|
159
159
|
}
|
|
160
160
|
}
|
|
161
|
-
const
|
|
161
|
+
const runtimeGenerationId = reservation.runtimeGenerationId;
|
|
162
162
|
let preflightObserved = beforeHostStart === undefined;
|
|
163
163
|
const observePreflight = (preflight) => {
|
|
164
164
|
if (preflightObserved) {
|
|
165
165
|
throw new Error("Runtime host reported its pre-start launch fence more than once.");
|
|
166
166
|
}
|
|
167
|
-
validateRuntimeLaunchPreflight(preflight, request,
|
|
167
|
+
validateRuntimeLaunchPreflight(preflight, request, runtimeGenerationId);
|
|
168
168
|
preflightObserved = true;
|
|
169
169
|
beforeHostStart?.(preflight);
|
|
170
170
|
};
|
|
@@ -179,7 +179,7 @@ export class RuntimeLaunchCoordinator {
|
|
|
179
179
|
const rawBinding = request.mode === "new"
|
|
180
180
|
? await this.host.start({
|
|
181
181
|
mode: "new",
|
|
182
|
-
|
|
182
|
+
runtimeGenerationId,
|
|
183
183
|
owner: request.owner,
|
|
184
184
|
agentId: request.agentId,
|
|
185
185
|
adapterId: request.adapterId,
|
|
@@ -195,7 +195,7 @@ export class RuntimeLaunchCoordinator {
|
|
|
195
195
|
}, beforeHostStart === undefined ? undefined : observePreflight)
|
|
196
196
|
: await this.host.restore({
|
|
197
197
|
mode: "resume",
|
|
198
|
-
|
|
198
|
+
runtimeGenerationId,
|
|
199
199
|
owner: request.owner,
|
|
200
200
|
agentId: request.agentId,
|
|
201
201
|
adapterId: request.adapterId,
|
|
@@ -210,24 +210,28 @@ export class RuntimeLaunchCoordinator {
|
|
|
210
210
|
: { environment: request.environment }),
|
|
211
211
|
nativeSessionId: requireText(request.nativeSessionId, "Native session id")
|
|
212
212
|
}, beforeHostStart === undefined ? undefined : observePreflight);
|
|
213
|
-
binding = requireMatchingRuntimeBinding(rawBinding, request,
|
|
213
|
+
binding = requireMatchingRuntimeBinding(rawBinding, request, runtimeGenerationId);
|
|
214
214
|
if (!preflightObserved) {
|
|
215
215
|
throw new Error("Runtime session host did not expose a pre-host-start launch fence.");
|
|
216
216
|
}
|
|
217
217
|
}
|
|
218
218
|
catch (error) {
|
|
219
|
+
if (error instanceof RuntimeGenerationMismatchError) {
|
|
220
|
+
await this.#settleFailedStart(request, runtimeGenerationId, reusedConfirmedRunningHost, runtimeIsolation, true);
|
|
221
|
+
throw new RuntimeLaunchError(false, runtimeGenerationId, error.message, "generation-mismatch");
|
|
222
|
+
}
|
|
219
223
|
if (error instanceof RuntimeHostContentionError && reusedConfirmedRunningHost) {
|
|
220
224
|
// The exact recovered generation remains authoritative. A late human
|
|
221
225
|
// writer is transient backpressure and must not settle, clean, or
|
|
222
226
|
// terminalize that reservation.
|
|
223
|
-
throw new RuntimeLaunchError(true,
|
|
227
|
+
throw new RuntimeLaunchError(true, runtimeGenerationId, error.message, error.reason);
|
|
224
228
|
}
|
|
225
229
|
if (error instanceof RuntimeHostContentionError
|
|
226
230
|
&& !reusedConfirmedRunningHost) {
|
|
227
231
|
let exactCleanupAttempted = false;
|
|
228
232
|
let completed = false;
|
|
229
233
|
try {
|
|
230
|
-
completed = this.reservations.completeRuntimeLaunchReservation(request.owner,
|
|
234
|
+
completed = this.reservations.completeRuntimeLaunchReservation(request.owner, runtimeGenerationId, runtimeIsolation === undefined
|
|
231
235
|
? undefined
|
|
232
236
|
: () => {
|
|
233
237
|
exactCleanupAttempted = true;
|
|
@@ -243,7 +247,7 @@ export class RuntimeLaunchCoordinator {
|
|
|
243
247
|
this.#requireCleanup(request.owner);
|
|
244
248
|
throw new Error("Busy managed runtime launch reservation changed during retry release.");
|
|
245
249
|
}
|
|
246
|
-
throw new RuntimeLaunchError(true,
|
|
250
|
+
throw new RuntimeLaunchError(true, runtimeGenerationId, error.message, error instanceof RuntimeHostContentionError
|
|
247
251
|
? error.reason
|
|
248
252
|
: "previous-process");
|
|
249
253
|
}
|
|
@@ -253,40 +257,40 @@ export class RuntimeLaunchCoordinator {
|
|
|
253
257
|
this.#requireCleanup(request.owner);
|
|
254
258
|
throw error;
|
|
255
259
|
}
|
|
256
|
-
await this.#settleFailedStart(request,
|
|
260
|
+
await this.#settleFailedStart(request, runtimeGenerationId, reusedConfirmedRunningHost, runtimeIsolation);
|
|
257
261
|
throw error;
|
|
258
262
|
}
|
|
259
263
|
if (reusedConfirmedRunningHost && binding.hostCreated === true) {
|
|
260
|
-
await this.#compensateStartedHost(request.owner, binding,
|
|
264
|
+
await this.#compensateStartedHost(request.owner, binding, runtimeGenerationId, runtimeIsolation, new Error(`Runtime host was recreated while recovering an existing generation: ${request.owner.roleName}.`));
|
|
261
265
|
}
|
|
262
266
|
try {
|
|
263
267
|
assertLaunchCurrent();
|
|
264
268
|
const reservationState = this.reservations.confirmRuntimeLaunchReservation({
|
|
265
269
|
owner: request.owner,
|
|
266
|
-
|
|
270
|
+
runtimeGenerationId
|
|
267
271
|
}, assertLaunchCurrent);
|
|
268
272
|
if (reservationState === "reserved") {
|
|
269
273
|
if (binding.nativeSessionId !== undefined) {
|
|
270
274
|
this.reservations.recordReservedRuntimeNativeSession({
|
|
271
275
|
owner: request.owner,
|
|
272
|
-
|
|
276
|
+
runtimeGenerationId,
|
|
273
277
|
agentId: request.agentId,
|
|
274
278
|
adapterId: request.adapterId,
|
|
275
279
|
nativeSessionId: binding.nativeSessionId,
|
|
276
280
|
effective: request.effective
|
|
277
281
|
}, assertLaunchCurrent, this.#now());
|
|
278
282
|
}
|
|
279
|
-
else if (!this.reservations.completeRuntimeLaunchReservation(request.owner,
|
|
283
|
+
else if (!this.reservations.completeRuntimeLaunchReservation(request.owner, runtimeGenerationId)) {
|
|
280
284
|
throw new Error("Runtime Host-start reservation changed before completion.");
|
|
281
285
|
}
|
|
282
286
|
}
|
|
283
287
|
}
|
|
284
288
|
catch (error) {
|
|
285
|
-
await this.#compensateStartedHost(request.owner, binding,
|
|
289
|
+
await this.#compensateStartedHost(request.owner, binding, runtimeGenerationId, runtimeIsolation, error);
|
|
286
290
|
}
|
|
287
291
|
return binding;
|
|
288
292
|
}
|
|
289
|
-
async #settleFailedStart(request,
|
|
293
|
+
async #settleFailedStart(request, runtimeGenerationId, reusedConfirmedRunningHost, runtimeIsolation, forceCleanup = false) {
|
|
290
294
|
let inspection;
|
|
291
295
|
try {
|
|
292
296
|
inspection = await this.host.inspectOwner(request.owner);
|
|
@@ -294,7 +298,7 @@ export class RuntimeLaunchCoordinator {
|
|
|
294
298
|
catch {
|
|
295
299
|
// A rebind/preflight failure did not create the already-confirmed host.
|
|
296
300
|
// Preserve its generation when the follow-up probe is merely unknown.
|
|
297
|
-
if (!reusedConfirmedRunningHost)
|
|
301
|
+
if (!reusedConfirmedRunningHost || forceCleanup)
|
|
298
302
|
this.#requireCleanup(request.owner);
|
|
299
303
|
return;
|
|
300
304
|
}
|
|
@@ -308,16 +312,16 @@ export class RuntimeLaunchCoordinator {
|
|
|
308
312
|
throw new Error(`Task runtime launch failed and exact resource cleanup also failed: ${cleanupError instanceof Error ? cleanupError.message : String(cleanupError)}`, { cause: cleanupError });
|
|
309
313
|
}
|
|
310
314
|
}
|
|
311
|
-
if (!this.#settleConfirmedStopped(request,
|
|
315
|
+
if (!this.#settleConfirmedStopped(request, runtimeGenerationId)) {
|
|
312
316
|
this.#requireCleanup(request.owner);
|
|
313
317
|
}
|
|
314
318
|
return;
|
|
315
319
|
}
|
|
316
|
-
if (reusedConfirmedRunningHost)
|
|
320
|
+
if (reusedConfirmedRunningHost && !forceCleanup)
|
|
317
321
|
return;
|
|
318
322
|
this.#requireCleanup(request.owner);
|
|
319
323
|
}
|
|
320
|
-
async #compensateStartedHost(owner, binding,
|
|
324
|
+
async #compensateStartedHost(owner, binding, runtimeGenerationId, runtimeIsolation, cause) {
|
|
321
325
|
try {
|
|
322
326
|
await this.host.stop(binding);
|
|
323
327
|
}
|
|
@@ -344,7 +348,7 @@ export class RuntimeLaunchCoordinator {
|
|
|
344
348
|
if (!stopped
|
|
345
349
|
|| !this.reservations.settleStoppedRuntimeLaunch({
|
|
346
350
|
owner,
|
|
347
|
-
|
|
351
|
+
runtimeGenerationId,
|
|
348
352
|
agentId: binding.agentId,
|
|
349
353
|
adapterId: binding.adapterId,
|
|
350
354
|
...(binding.nativeSessionId === undefined
|
|
@@ -356,33 +360,33 @@ export class RuntimeLaunchCoordinator {
|
|
|
356
360
|
const detail = cause instanceof Error ? cause.message : String(cause);
|
|
357
361
|
throw new RuntimeLaunchStateChangedError(`Runtime launch was compensated after state changed: ${detail}`, { cause });
|
|
358
362
|
}
|
|
359
|
-
#preflightRuntimeIsolation(request,
|
|
363
|
+
#preflightRuntimeIsolation(request, runtimeGenerationId, generationId, allowExactActive) {
|
|
360
364
|
if (this.#runtimeIsolation === undefined || request.owner.scope !== "task") {
|
|
361
365
|
return undefined;
|
|
362
366
|
}
|
|
363
367
|
return this.#runtimeIsolation.preflight({
|
|
364
368
|
workspace: request.managedWorkspace,
|
|
365
|
-
|
|
369
|
+
runtimeGenerationId,
|
|
366
370
|
generationId: requireText(generationId, "Launch generation id"),
|
|
367
371
|
...(request.runtimePolicy === undefined ? {} : { policy: request.runtimePolicy }),
|
|
368
372
|
...(allowExactActive ? { allowExactActive: true } : {})
|
|
369
373
|
});
|
|
370
374
|
}
|
|
371
|
-
#cleanupTaskLaunch(owner,
|
|
375
|
+
#cleanupTaskLaunch(owner, runtimeGenerationId) {
|
|
372
376
|
const cleanupTaskLaunch = this.#runtimeIsolation?.cleanupTaskLaunch;
|
|
373
377
|
if (cleanupTaskLaunch === undefined) {
|
|
374
378
|
throw new Error("Exact Task runtime launch cleanup is unavailable.");
|
|
375
379
|
}
|
|
376
380
|
cleanupTaskLaunch.call(this.#runtimeIsolation, {
|
|
377
381
|
taskId: owner.taskId,
|
|
378
|
-
|
|
382
|
+
runtimeGenerationId,
|
|
379
383
|
reason: "interruption"
|
|
380
384
|
});
|
|
381
385
|
}
|
|
382
|
-
#settleConfirmedStopped(request,
|
|
386
|
+
#settleConfirmedStopped(request, runtimeGenerationId) {
|
|
383
387
|
return this.reservations.settleStoppedRuntimeLaunch({
|
|
384
388
|
owner: request.owner,
|
|
385
|
-
|
|
389
|
+
runtimeGenerationId,
|
|
386
390
|
agentId: request.agentId,
|
|
387
391
|
adapterId: request.adapterId,
|
|
388
392
|
...(request.nativeSessionId === undefined
|
|
@@ -401,13 +405,13 @@ export class RuntimeLaunchCoordinator {
|
|
|
401
405
|
}
|
|
402
406
|
}
|
|
403
407
|
}
|
|
404
|
-
function validateRuntimeLaunchPreflight(preflight, request,
|
|
408
|
+
function validateRuntimeLaunchPreflight(preflight, request, runtimeGenerationId) {
|
|
405
409
|
const ownerMatches = preflight.owner.scope === request.owner.scope
|
|
406
410
|
&& preflight.owner.roleName === request.owner.roleName
|
|
407
411
|
&& (preflight.owner.scope === "global"
|
|
408
412
|
|| (request.owner.scope === "task"
|
|
409
413
|
&& preflight.owner.taskId === request.owner.taskId));
|
|
410
|
-
if (preflight.
|
|
414
|
+
if (preflight.runtimeGenerationId !== runtimeGenerationId
|
|
411
415
|
|| !ownerMatches
|
|
412
416
|
|| preflight.turnId !== request.turnId
|
|
413
417
|
|| preflight.agentId !== request.agentId
|
|
@@ -418,7 +422,7 @@ function validateRuntimeLaunchPreflight(preflight, request, launchId) {
|
|
|
418
422
|
throw new Error(`Session host pre-start launch fence does not match the requested runtime: ${request.owner.roleName}.`);
|
|
419
423
|
}
|
|
420
424
|
}
|
|
421
|
-
function requireMatchingRuntimeBinding(raw, request,
|
|
425
|
+
function requireMatchingRuntimeBinding(raw, request, runtimeGenerationId) {
|
|
422
426
|
let binding;
|
|
423
427
|
try {
|
|
424
428
|
binding = createRuntimeBinding(raw);
|
|
@@ -431,7 +435,7 @@ function requireMatchingRuntimeBinding(raw, request, launchId) {
|
|
|
431
435
|
&& (binding.owner.scope === "global"
|
|
432
436
|
|| (request.owner.scope === "task"
|
|
433
437
|
&& binding.owner.taskId === request.owner.taskId));
|
|
434
|
-
if (binding.
|
|
438
|
+
if (binding.runtimeGenerationId !== runtimeGenerationId
|
|
435
439
|
|| !ownerMatches
|
|
436
440
|
|| binding.agentId !== request.agentId
|
|
437
441
|
|| binding.adapterId !== request.adapterId
|
|
@@ -4,6 +4,7 @@ import { normalizeAgentDriverHookClassification } from "../runtime/agentDriver.j
|
|
|
4
4
|
import { mapAgentDriverHooks } from "../runtime/agentDriverObservation.js";
|
|
5
5
|
import { runtimeLifecycleSignalKey } from "../runtime/lifecycleReservation.js";
|
|
6
6
|
import { formatTurnReceiptId } from "../task/taskRecordReference.js";
|
|
7
|
+
import { boundedTurnFailureDiagnostic, transportAgentResult } from "../domain/agentResultTransport.js";
|
|
7
8
|
import { FileRuntimeEventInbox, MAX_RUNTIME_EVENT_FILE_BYTES } from "./runtimeEventInbox.js";
|
|
8
9
|
import { resolveRuntimeHookTurnFence } from "./runtimeHookTurnFence.js";
|
|
9
10
|
/**
|
|
@@ -48,28 +49,34 @@ async function runGlobalRuntimeTurnHook(stdinJson, environment, call, now, depen
|
|
|
48
49
|
}
|
|
49
50
|
const home = requireIdentity(environment.YUI_HOME, "YUI_HOME");
|
|
50
51
|
const roleName = requireIdentity(environment.YUI_ROLE, "Role name");
|
|
51
|
-
new FileRuntimeEventInbox(home, () => now).
|
|
52
|
+
new FileRuntimeEventInbox(home, () => now).enqueueTurnTerminal({
|
|
52
53
|
scope: "global",
|
|
53
54
|
roleName,
|
|
54
55
|
agentId: requireIdentity(environment.YUI_AGENT_ID, "Agent id"),
|
|
55
56
|
adapterId: "claude",
|
|
56
|
-
|
|
57
|
+
runtimeGenerationId: requireIdentity(environment.YUI_RUNTIME_GENERATION_ID, "Runtime generation id"),
|
|
57
58
|
nativeSessionId,
|
|
58
59
|
nativeTurnId: optionalIdentity(driver.runtime.nativeTurnId(nativeHook)) ?? occurrenceId,
|
|
59
60
|
...(environment.YUI_SESSION_TITLE === undefined
|
|
60
61
|
? {}
|
|
61
62
|
: { title: requireIdentity(environment.YUI_SESSION_TITLE, "Session title") }),
|
|
62
|
-
|
|
63
|
+
providerStatus: hookEventName === "Stop" ? "completed" : "failed",
|
|
64
|
+
outcome: globalHookOutcome(payload, hookEventName)
|
|
63
65
|
});
|
|
64
66
|
await call(home, "scheduler.signal", { key: runtimeLifecycleSignalKey({ scope: "global", roleName }) }, { timeoutMs: 100 }).catch(() => { });
|
|
65
67
|
}
|
|
66
|
-
function
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
if (typeof value === "string" && value.trim().length > 0)
|
|
70
|
-
return value.trim();
|
|
68
|
+
function globalHookOutcome(payload, hookEventName) {
|
|
69
|
+
if (hookEventName === "Stop") {
|
|
70
|
+
return transportAgentResult(payload.last_assistant_message);
|
|
71
71
|
}
|
|
72
|
-
|
|
72
|
+
const diagnostic = [payload.error, payload.error_details]
|
|
73
|
+
.filter((value) => typeof value === "string" && value.trim().length > 0)
|
|
74
|
+
.join("\n");
|
|
75
|
+
return {
|
|
76
|
+
status: "failed",
|
|
77
|
+
failureReason: "runtime-failed",
|
|
78
|
+
diagnostic: boundedTurnFailureDiagnostic(diagnostic)
|
|
79
|
+
};
|
|
73
80
|
}
|
|
74
81
|
export function parseRuntimeObservationHook(stdinJson, environment, now = new Date(), dependencies = {}) {
|
|
75
82
|
const payload = parseObject(stdinJson);
|
|
@@ -113,10 +120,9 @@ export function parseRuntimeObservationHook(stdinJson, environment, now = new Da
|
|
|
113
120
|
turnId: fence.turnId,
|
|
114
121
|
agentId: fence.agentId,
|
|
115
122
|
driverId,
|
|
116
|
-
|
|
117
|
-
sessionGenerationId: fence.launchId,
|
|
123
|
+
runtimeGenerationId: fence.runtimeGenerationId,
|
|
118
124
|
conversationId: fence.nativeSessionId,
|
|
119
|
-
activationId: fence.
|
|
125
|
+
activationId: fence.runtimeGenerationId,
|
|
120
126
|
nativeSessionId: fence.nativeSessionId,
|
|
121
127
|
nativeTurnId: nativeTurnId ?? fence.turnId,
|
|
122
128
|
receiptId: fence.receiptId ?? formatTurnReceiptId(fence.taskId, fence.turnId)
|
|
@@ -1,25 +1,30 @@
|
|
|
1
1
|
import { callController } from "../core/controllerClient.js";
|
|
2
2
|
import { FileRuntimeEventInbox } from "./runtimeEventInbox.js";
|
|
3
|
+
import { transportAgentResult } from "../domain/agentResultTransport.js";
|
|
3
4
|
import { runtimeLifecycleSignalKey } from "../runtime/lifecycleReservation.js";
|
|
4
5
|
import { setCodexThreadName } from "../execution/codexThreadNaming.js";
|
|
5
6
|
import { openCurrentTaskStore } from "../storage/currentTaskStore.js";
|
|
6
|
-
const NO_FINAL_ASSISTANT_MESSAGE_SUMMARY = "Native Turn completed without a final assistant message.";
|
|
7
7
|
/** Hidden CLI entrypoint used by Codex's structured notify hook. */
|
|
8
8
|
export async function runSessionNotifyCommand(payloadArgument, environment = process.env, call, setThreadName = setCodexThreadName) {
|
|
9
9
|
const params = parseCodexSessionNotification(payloadArgument, environment);
|
|
10
10
|
const home = requireText(environment.YUI_HOME, "YUI_HOME");
|
|
11
|
-
|
|
11
|
+
// A Codex process outlives its Turn, so the notify envelope cannot say which
|
|
12
|
+
// Turn or runtime generation is current. Durable Session state answers both;
|
|
13
|
+
// the envelope's runtime generation id is used only before a Session has been projected.
|
|
14
|
+
const current = currentNotifyGeneration(home, params);
|
|
15
|
+
const enqueued = new FileRuntimeEventInbox(home).enqueueTurnTerminal({
|
|
12
16
|
scope: params.scope,
|
|
13
17
|
...(params.scope === "task" ? { taskId: params.taskId } : {}),
|
|
14
18
|
roleName: params.roleName,
|
|
15
19
|
agentId: params.agentId,
|
|
16
20
|
adapterId: params.adapterId,
|
|
17
|
-
|
|
21
|
+
...(current.runtimeGenerationId === undefined ? {} : { runtimeGenerationId: current.runtimeGenerationId }),
|
|
18
22
|
nativeSessionId: params.nativeSessionId,
|
|
19
23
|
nativeTurnId: params.nativeTurnId,
|
|
20
|
-
...(
|
|
24
|
+
...(current.turnId === undefined ? {} : { turnId: current.turnId }),
|
|
21
25
|
...(params.title === undefined ? {} : { title: params.title }),
|
|
22
|
-
|
|
26
|
+
providerStatus: "completed",
|
|
27
|
+
outcome: params.outcome
|
|
23
28
|
});
|
|
24
29
|
// The durable write is authoritative. This short socket call is only a
|
|
25
30
|
// wake-up hint and never starts or waits for a Controller process.
|
|
@@ -35,12 +40,36 @@ export async function runSessionNotifyCommand(payloadArgument, environment = pro
|
|
|
35
40
|
roleName: params.roleName
|
|
36
41
|
})
|
|
37
42
|
}, { timeoutMs: 100 }).catch(() => { });
|
|
38
|
-
if (enqueued.created && shouldSetThreadName(home, params)) {
|
|
43
|
+
if (enqueued.created && shouldSetThreadName(home, params, current.turnId)) {
|
|
39
44
|
const request = threadNameRequest(params, environment);
|
|
40
45
|
if (request !== null)
|
|
41
46
|
await setThreadName(request).catch(() => { });
|
|
42
47
|
}
|
|
43
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* Reads the durable runtime generation and active Turn for the notifying
|
|
51
|
+
* Session. The envelope's runtime generation id only applies before Yui has projected a
|
|
52
|
+
* Session for this Role, which is the one moment durable state cannot answer.
|
|
53
|
+
*/
|
|
54
|
+
function currentNotifyGeneration(home, params) {
|
|
55
|
+
if (params.scope !== "task" || params.taskId === undefined) {
|
|
56
|
+
return params.runtimeGenerationId === undefined ? {} : { runtimeGenerationId: params.runtimeGenerationId };
|
|
57
|
+
}
|
|
58
|
+
try {
|
|
59
|
+
const store = openCurrentTaskStore(home);
|
|
60
|
+
const session = store.getTaskRoleSessionSet(params.taskId, params.roleName)
|
|
61
|
+
?.sessions[params.agentId];
|
|
62
|
+
const activeTurn = store.getActiveTurn(params.taskId, params.roleName);
|
|
63
|
+
const runtimeGenerationId = session?.runtimeGenerationId ?? params.runtimeGenerationId;
|
|
64
|
+
return {
|
|
65
|
+
...(runtimeGenerationId === undefined ? {} : { runtimeGenerationId }),
|
|
66
|
+
...(activeTurn === null ? {} : { turnId: activeTurn.id })
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return params.runtimeGenerationId === undefined ? {} : { runtimeGenerationId: params.runtimeGenerationId };
|
|
71
|
+
}
|
|
72
|
+
}
|
|
44
73
|
export function parseCodexSessionNotification(payloadArgument, environment) {
|
|
45
74
|
const payload = parseObject(payloadArgument, "Codex notify payload");
|
|
46
75
|
if (payload.type !== "agent-turn-complete") {
|
|
@@ -48,10 +77,7 @@ export function parseCodexSessionNotification(payloadArgument, environment) {
|
|
|
48
77
|
}
|
|
49
78
|
const nativeSessionId = requireText(payload["thread-id"], "Codex thread-id");
|
|
50
79
|
const nativeTurnId = requireText(payload["turn-id"], "Codex turn-id");
|
|
51
|
-
const
|
|
52
|
-
const turnId = environment.YUI_TURN_ID === undefined
|
|
53
|
-
? undefined
|
|
54
|
-
: requireText(environment.YUI_TURN_ID, "YUI_TURN_ID");
|
|
80
|
+
const outcome = transportAgentResult(payload["last-assistant-message"]);
|
|
55
81
|
const title = environment.YUI_SESSION_TITLE === undefined
|
|
56
82
|
? undefined
|
|
57
83
|
: requireText(environment.YUI_SESSION_TITLE, "YUI_SESSION_TITLE");
|
|
@@ -64,12 +90,13 @@ export function parseCodexSessionNotification(payloadArgument, environment) {
|
|
|
64
90
|
roleName: requireText(environment.YUI_ROLE, "YUI_ROLE"),
|
|
65
91
|
agentId: requireText(environment.YUI_AGENT_ID, "YUI_AGENT_ID"),
|
|
66
92
|
adapterId: requireCodexAdapter(environment.YUI_ADAPTER_ID),
|
|
67
|
-
|
|
93
|
+
...(environment.YUI_RUNTIME_GENERATION_ID === undefined
|
|
94
|
+
? {}
|
|
95
|
+
: { runtimeGenerationId: requireText(environment.YUI_RUNTIME_GENERATION_ID, "YUI_RUNTIME_GENERATION_ID") }),
|
|
68
96
|
nativeSessionId,
|
|
69
97
|
nativeTurnId,
|
|
70
|
-
...(turnId === undefined ? {} : { turnId }),
|
|
71
98
|
...(title === undefined ? {} : { title }),
|
|
72
|
-
|
|
99
|
+
outcome
|
|
73
100
|
};
|
|
74
101
|
return scope === "task"
|
|
75
102
|
? {
|
|
@@ -106,26 +133,14 @@ function requireText(value, label) {
|
|
|
106
133
|
throw new Error(`${label} is invalid.`);
|
|
107
134
|
return text;
|
|
108
135
|
}
|
|
109
|
-
function
|
|
110
|
-
if (value === null)
|
|
111
|
-
return NO_FINAL_ASSISTANT_MESSAGE_SUMMARY;
|
|
112
|
-
if (typeof value !== "string" || value.includes("\0")) {
|
|
113
|
-
throw new Error("Codex last assistant message is required.");
|
|
114
|
-
}
|
|
115
|
-
const text = value.trim();
|
|
116
|
-
if (text.length === 0 || Buffer.byteLength(text) > 524_288) {
|
|
117
|
-
throw new Error("Codex last assistant message is invalid.");
|
|
118
|
-
}
|
|
119
|
-
return text;
|
|
120
|
-
}
|
|
121
|
-
function shouldSetThreadName(home, params) {
|
|
136
|
+
function shouldSetThreadName(home, params, turnId) {
|
|
122
137
|
if (params.scope !== "task"
|
|
123
138
|
|| params.title === undefined
|
|
124
|
-
||
|
|
139
|
+
|| turnId === undefined)
|
|
125
140
|
return false;
|
|
126
141
|
try {
|
|
127
142
|
const store = openCurrentTaskStore(home);
|
|
128
|
-
return store.getTurn(params.taskId,
|
|
143
|
+
return store.getTurn(params.taskId, turnId)?.mode === "new";
|
|
129
144
|
}
|
|
130
145
|
catch {
|
|
131
146
|
return false;
|
|
@@ -26,11 +26,11 @@ export class SessionOwnerReconciliation {
|
|
|
26
26
|
/**
|
|
27
27
|
* Persists the exact physical owner identity after a host created a new
|
|
28
28
|
* Provider process. The Provider root is attributed by the exact
|
|
29
|
-
*
|
|
29
|
+
* YUI_RUNTIME_GENERATION_ID environment fence; the tmux pane PID is only a weaker
|
|
30
30
|
* fallback when the fence cannot be read.
|
|
31
31
|
*/
|
|
32
32
|
recordHostOwner(input) {
|
|
33
|
-
const discovered = discoverProviderRootByLaunchEnv(input.
|
|
33
|
+
const discovered = discoverProviderRootByLaunchEnv(input.runtimeGenerationId);
|
|
34
34
|
const fallback = discovered === undefined && input.panePid !== undefined
|
|
35
35
|
? readLinuxProcessIdentity(input.panePid)
|
|
36
36
|
: undefined;
|
|
@@ -38,7 +38,7 @@ export class SessionOwnerReconciliation {
|
|
|
38
38
|
? undefined
|
|
39
39
|
: { pid: fallback.pid, identity: fallback });
|
|
40
40
|
if (root === undefined) {
|
|
41
|
-
this.#onWarning(`Session owner identity could not attribute a Provider root for launch ${input.
|
|
41
|
+
this.#onWarning(`Session owner identity could not attribute a Provider root for launch ${input.runtimeGenerationId}; `
|
|
42
42
|
+ "no owner record was written.");
|
|
43
43
|
return;
|
|
44
44
|
}
|
|
@@ -52,7 +52,7 @@ export class SessionOwnerReconciliation {
|
|
|
52
52
|
},
|
|
53
53
|
agentId: input.agentId,
|
|
54
54
|
adapterId: input.adapterId,
|
|
55
|
-
|
|
55
|
+
runtimeGenerationId: input.runtimeGenerationId,
|
|
56
56
|
...(input.nativeSessionId === undefined
|
|
57
57
|
? {}
|
|
58
58
|
: { nativeSessionId: input.nativeSessionId }),
|
|
@@ -98,7 +98,7 @@ export class SessionOwnerReconciliation {
|
|
|
98
98
|
return undefined;
|
|
99
99
|
}
|
|
100
100
|
},
|
|
101
|
-
lastStopOutcome: (taskId, roleName,
|
|
101
|
+
lastStopOutcome: (taskId, roleName, runtimeGenerationId) => (lastSessionTerminationOutcome(this.#store, taskId, roleName, runtimeGenerationId)),
|
|
102
102
|
now: new Date()
|
|
103
103
|
});
|
|
104
104
|
}
|
|
@@ -130,7 +130,7 @@ export class SessionOwnerReconciliation {
|
|
|
130
130
|
return false;
|
|
131
131
|
}
|
|
132
132
|
},
|
|
133
|
-
listLaunchFencedProcesses: (
|
|
133
|
+
listLaunchFencedProcesses: (runtimeGenerationId) => listLaunchFencedProcesses(runtimeGenerationId),
|
|
134
134
|
signalProcess: (pid, signal) => process.kill(pid, signal),
|
|
135
135
|
signalProcessGroup: (processGroupId, signal) => {
|
|
136
136
|
try {
|
|
@@ -148,23 +148,23 @@ export class SessionOwnerReconciliation {
|
|
|
148
148
|
const result = await terminateSessionOwners(owner, records, ports, options);
|
|
149
149
|
if (result.outcome === "stop-confirmed") {
|
|
150
150
|
for (const record of result.confirmed) {
|
|
151
|
-
this.#store.removeSessionOwner(record.
|
|
152
|
-
removeRuntimeStopReceipt(this.#home, record.
|
|
151
|
+
this.#store.removeSessionOwner(record.runtimeGenerationId);
|
|
152
|
+
removeRuntimeStopReceipt(this.#home, record.runtimeGenerationId);
|
|
153
153
|
}
|
|
154
154
|
}
|
|
155
155
|
return result;
|
|
156
156
|
}
|
|
157
157
|
#recordTerminationEvent(event) {
|
|
158
158
|
const owner = event.owner;
|
|
159
|
-
if (event.stage === "stop-requested" && event.
|
|
160
|
-
writeRuntimeStopReceipt(this.#home, event.
|
|
159
|
+
if (event.stage === "stop-requested" && event.runtimeGenerationId !== undefined) {
|
|
160
|
+
writeRuntimeStopReceipt(this.#home, event.runtimeGenerationId, event.at);
|
|
161
161
|
}
|
|
162
162
|
if (owner.scope !== "task")
|
|
163
163
|
return;
|
|
164
164
|
try {
|
|
165
165
|
this.#store.saveEvent(owner.taskId, createTaskEvent(this.#store.nextEventId(owner.taskId), owner.taskId, "runtime.session-termination", {
|
|
166
166
|
roleName: owner.roleName,
|
|
167
|
-
|
|
167
|
+
runtimeGenerationId: event.runtimeGenerationId ?? "",
|
|
168
168
|
nativeSessionId: event.nativeSessionId ?? "",
|
|
169
169
|
outcome: event.stage,
|
|
170
170
|
...(event.detail === undefined ? {} : { detail: event.detail })
|
|
@@ -175,7 +175,7 @@ export class SessionOwnerReconciliation {
|
|
|
175
175
|
}
|
|
176
176
|
}
|
|
177
177
|
}
|
|
178
|
-
/** Projects every durable Role
|
|
178
|
+
/** Projects every durable Role runtime generation for reconciliation. */
|
|
179
179
|
export function durableSessionFacts(store) {
|
|
180
180
|
const facts = [];
|
|
181
181
|
for (const task of store.listTasks()) {
|
|
@@ -187,7 +187,7 @@ export function durableSessionFacts(store) {
|
|
|
187
187
|
roleName: set.owner.roleName,
|
|
188
188
|
agentId,
|
|
189
189
|
adapterId: session.adapterId,
|
|
190
|
-
...(session.
|
|
190
|
+
...(session.runtimeGenerationId === undefined ? {} : { runtimeGenerationId: session.runtimeGenerationId }),
|
|
191
191
|
...(session.nativeSessionId === undefined
|
|
192
192
|
? {}
|
|
193
193
|
: { nativeSessionId: session.nativeSessionId }),
|
|
@@ -202,7 +202,7 @@ export function durableSessionFacts(store) {
|
|
|
202
202
|
roleName: set.owner.roleName,
|
|
203
203
|
agentId: history.agentId,
|
|
204
204
|
adapterId: history.adapterId,
|
|
205
|
-
...(history.
|
|
205
|
+
...(history.runtimeGenerationId === undefined ? {} : { runtimeGenerationId: history.runtimeGenerationId }),
|
|
206
206
|
...(history.nativeSessionId === undefined
|
|
207
207
|
? {}
|
|
208
208
|
: { nativeSessionId: history.nativeSessionId }),
|
|
@@ -219,7 +219,7 @@ export function durableSessionFacts(store) {
|
|
|
219
219
|
roleName: set.owner.roleName,
|
|
220
220
|
agentId,
|
|
221
221
|
adapterId: session.adapterId,
|
|
222
|
-
...(session.
|
|
222
|
+
...(session.runtimeGenerationId === undefined ? {} : { runtimeGenerationId: session.runtimeGenerationId }),
|
|
223
223
|
...(session.nativeSessionId === undefined
|
|
224
224
|
? {}
|
|
225
225
|
: { nativeSessionId: session.nativeSessionId }),
|
|
@@ -234,7 +234,7 @@ export function durableSessionFacts(store) {
|
|
|
234
234
|
roleName: set.owner.roleName,
|
|
235
235
|
agentId: session.agentId,
|
|
236
236
|
adapterId: session.adapterId,
|
|
237
|
-
...(session.
|
|
237
|
+
...(session.runtimeGenerationId === undefined ? {} : { runtimeGenerationId: session.runtimeGenerationId }),
|
|
238
238
|
...(session.nativeSessionId === undefined
|
|
239
239
|
? {}
|
|
240
240
|
: { nativeSessionId: session.nativeSessionId }),
|
|
@@ -297,7 +297,7 @@ export function observeSessionOwnerPhysical(record) {
|
|
|
297
297
|
};
|
|
298
298
|
}
|
|
299
299
|
/** Reads the latest termination outcome recorded for one generation. */
|
|
300
|
-
export function lastSessionTerminationOutcome(store, taskId, roleName,
|
|
300
|
+
export function lastSessionTerminationOutcome(store, taskId, roleName, runtimeGenerationId) {
|
|
301
301
|
if (taskId === undefined)
|
|
302
302
|
return undefined;
|
|
303
303
|
let latest;
|
|
@@ -306,10 +306,10 @@ export function lastSessionTerminationOutcome(store, taskId, roleName, launchId)
|
|
|
306
306
|
continue;
|
|
307
307
|
if (event.payload.roleName !== roleName)
|
|
308
308
|
continue;
|
|
309
|
-
if (
|
|
310
|
-
&& event.payload.
|
|
311
|
-
&& event.payload.
|
|
312
|
-
&& event.payload.
|
|
309
|
+
if (runtimeGenerationId !== ""
|
|
310
|
+
&& event.payload.runtimeGenerationId !== undefined
|
|
311
|
+
&& event.payload.runtimeGenerationId !== ""
|
|
312
|
+
&& event.payload.runtimeGenerationId !== runtimeGenerationId) {
|
|
313
313
|
continue;
|
|
314
314
|
}
|
|
315
315
|
latest = event.payload.outcome;
|