@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,5 +1,5 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
import { hasRecentTurnId, rememberRecentTurnId,
|
|
2
|
+
import { hasRecentTurnId, rememberRecentTurnId, validateRecentTurnIds } from "../runtime/recentTurnIds.js";
|
|
3
3
|
import { effectiveLaunchSnapshotsCompatible, effectiveLaunchSnapshotsCompatibleForTaskSession, validateEffectiveLaunchSnapshot } from "./effectiveLaunch.js";
|
|
4
4
|
import { currentProviderActivation, endProviderActivation, settleProviderTurn, settleProviderTurnSubmission, validateProviderRuntimeBinding } from "../runtime/providerRuntimeIdentity.js";
|
|
5
5
|
import { builtinDriverIdForAdapter } from "../runtime/builtinAgentDrivers.js";
|
|
@@ -14,8 +14,7 @@ export function createRoleSessionSet(owner, activeAgentId, now) {
|
|
|
14
14
|
? { ...base, schemaVersion: 4 }
|
|
15
15
|
: {
|
|
16
16
|
...base,
|
|
17
|
-
schemaVersion:
|
|
18
|
-
inFlight: null,
|
|
17
|
+
schemaVersion: 11,
|
|
19
18
|
providerBinding: null
|
|
20
19
|
};
|
|
21
20
|
}
|
|
@@ -179,9 +178,6 @@ export function updateRoleAgentSessionStatus(set, agentId, status, now, endReaso
|
|
|
179
178
|
}
|
|
180
179
|
export function retireTaskRoleSessionsForWorkspace(set, now) {
|
|
181
180
|
validateRoleSessionSet(set);
|
|
182
|
-
if (set.inFlight !== null) {
|
|
183
|
-
throw new Error("Cannot retire a Task Role session with unsettled Run state.");
|
|
184
|
-
}
|
|
185
181
|
const live = Object.values(set.sessions).find(({ status }) => status === "active");
|
|
186
182
|
if (live !== undefined) {
|
|
187
183
|
throw new Error(`Task Role session must be stopped before workspace migration: ${live.agentId}.`);
|
|
@@ -205,9 +201,6 @@ export function retireTaskRoleSessionsForWorkspace(set, now) {
|
|
|
205
201
|
*/
|
|
206
202
|
export function retireConfirmedAbsentInactiveTaskRolePlaceholders(set, now) {
|
|
207
203
|
validateRoleSessionSet(set);
|
|
208
|
-
if (set.inFlight !== null) {
|
|
209
|
-
throw new Error("Cannot retire a Task Role placeholder with unsettled Run state.");
|
|
210
|
-
}
|
|
211
204
|
const timestamp = now.toISOString();
|
|
212
205
|
let changed = false;
|
|
213
206
|
const sessions = Object.fromEntries(Object.entries(set.sessions).map(([agentId, session]) => {
|
|
@@ -295,35 +288,6 @@ export function roleAgentSessionResumeMode(set, agentId, desired) {
|
|
|
295
288
|
throw new Error(`Role Agent session is incompatible with the next effective launch: ${agentId}. `
|
|
296
289
|
+ "Stop the existing native process before starting a fresh Session.");
|
|
297
290
|
}
|
|
298
|
-
export function bindTaskRoleRun(set, fence, preparedAt, mode) {
|
|
299
|
-
validateRoleSessionSet(set);
|
|
300
|
-
assertTaskRoleSessionSet(set);
|
|
301
|
-
const normalized = normalizeTaskRoleRunFence(fence);
|
|
302
|
-
if (normalized.agentId !== set.activeAgentId) {
|
|
303
|
-
throw new Error("Task Role Run Agent does not match the active Agent.");
|
|
304
|
-
}
|
|
305
|
-
if (set.inFlight !== null) {
|
|
306
|
-
if (sameRunFence(set.inFlight, normalized))
|
|
307
|
-
return set;
|
|
308
|
-
throw new Error("Task Role session set already has an in-flight Run.");
|
|
309
|
-
}
|
|
310
|
-
const timestamp = requireDate(preparedAt, "Task Role Run preparedAt");
|
|
311
|
-
// A fresh Session follows physical cleanup and does not inherit runtime
|
|
312
|
-
// evidence from the disposable old Conversation. A resumed Session keeps
|
|
313
|
-
// that evidence unchanged here: claiming a durable Run is a workflow
|
|
314
|
-
// mutation, not a Provider operation. AgentHost binds the Conversation to
|
|
315
|
-
// this Run only when it can actually submit the next serialized Turn.
|
|
316
|
-
const providerBinding = mode === "new"
|
|
317
|
-
? null
|
|
318
|
-
: set.providerBinding;
|
|
319
|
-
const updated = {
|
|
320
|
-
...set,
|
|
321
|
-
inFlight: { ...normalized, preparedAt: timestamp },
|
|
322
|
-
providerBinding,
|
|
323
|
-
updatedAt: timestamp
|
|
324
|
-
};
|
|
325
|
-
return validateRoleSessionSet(updated);
|
|
326
|
-
}
|
|
327
291
|
export function bindTaskRoleProviderRuntime(set, binding, updatedAt) {
|
|
328
292
|
validateRoleSessionSet(set);
|
|
329
293
|
const normalized = validateProviderRuntimeBinding(binding);
|
|
@@ -382,9 +346,9 @@ export function detachRoleAgentSessionHost(set, now) {
|
|
|
382
346
|
let binding = taskSet.providerBinding;
|
|
383
347
|
const turn = binding?.turn;
|
|
384
348
|
if (binding !== null && binding !== undefined && turn !== null && turn !== undefined
|
|
385
|
-
&&
|
|
349
|
+
&& turn.status === "accepted") {
|
|
386
350
|
binding = settleProviderTurn(binding, {
|
|
387
|
-
|
|
351
|
+
nativeTurnId: turn.nativeTurnId,
|
|
388
352
|
status: "cancelled",
|
|
389
353
|
settledAt: timestamp,
|
|
390
354
|
reason: "runtime-physical-exit"
|
|
@@ -413,51 +377,13 @@ export function detachRoleAgentSessionHost(set, now) {
|
|
|
413
377
|
}
|
|
414
378
|
return taskSet;
|
|
415
379
|
}
|
|
416
|
-
export function markTaskRoleRunPushed(set, fence, pushedAt) {
|
|
417
|
-
validateRoleSessionSet(set);
|
|
418
|
-
assertTaskRoleSessionSet(set);
|
|
419
|
-
const inFlight = requireMatchingInFlight(set, fence);
|
|
420
|
-
if (inFlight.pushedAt !== undefined)
|
|
421
|
-
return set;
|
|
422
|
-
const timestamp = requireDate(pushedAt, "Task Role Run pushedAt");
|
|
423
|
-
if (Date.parse(timestamp) < Date.parse(inFlight.preparedAt)) {
|
|
424
|
-
throw new Error("Task Role Run pushedAt must not be earlier than preparedAt.");
|
|
425
|
-
}
|
|
426
|
-
const updated = {
|
|
427
|
-
...set,
|
|
428
|
-
inFlight: { ...inFlight, pushedAt: timestamp },
|
|
429
|
-
updatedAt: timestamp
|
|
430
|
-
};
|
|
431
|
-
return validateRoleSessionSet(updated);
|
|
432
|
-
}
|
|
433
|
-
export function markTaskRoleRunDelivered(set, fence, deliveredAt) {
|
|
434
|
-
validateRoleSessionSet(set);
|
|
435
|
-
assertTaskRoleSessionSet(set);
|
|
436
|
-
const inFlight = requireMatchingInFlight(set, fence);
|
|
437
|
-
if (inFlight.deliveredAt !== undefined)
|
|
438
|
-
return set;
|
|
439
|
-
const timestamp = requireDate(deliveredAt, "Task Role Run deliveredAt");
|
|
440
|
-
if (Date.parse(timestamp) < Date.parse(inFlight.preparedAt)) {
|
|
441
|
-
throw new Error("Task Role Run deliveredAt must not be earlier than preparedAt.");
|
|
442
|
-
}
|
|
443
|
-
const updated = {
|
|
444
|
-
...set,
|
|
445
|
-
inFlight: {
|
|
446
|
-
...inFlight,
|
|
447
|
-
...(inFlight.pushedAt === undefined ? { pushedAt: timestamp } : {}),
|
|
448
|
-
deliveredAt: timestamp
|
|
449
|
-
},
|
|
450
|
-
updatedAt: timestamp
|
|
451
|
-
};
|
|
452
|
-
return validateRoleSessionSet(updated);
|
|
453
|
-
}
|
|
454
380
|
/**
|
|
455
|
-
* Records a
|
|
381
|
+
* Records a Provider activity boundary without changing the durable Yui Turn.
|
|
456
382
|
*
|
|
457
383
|
* A native session may finish one foreground Turn while provider-owned
|
|
458
384
|
* subagents, mailbox work, or later user corrections still belong to the same
|
|
459
|
-
* application-level
|
|
460
|
-
*
|
|
385
|
+
* application-level Turn. Only the native Provider terminal may clear the
|
|
386
|
+
* Turn fence; this transition merely makes the native Session available for a
|
|
461
387
|
* subsequent input and remembers the provider Turn idempotently.
|
|
462
388
|
*/
|
|
463
389
|
export function recordTaskRoleTurnBoundary(set, input, completedAt) {
|
|
@@ -472,9 +398,6 @@ export function recordTaskRoleTurnBoundary(set, input, completedAt) {
|
|
|
472
398
|
}
|
|
473
399
|
if (session.recentCompletedTurnIds.includes(turnId))
|
|
474
400
|
return set;
|
|
475
|
-
if (set.inFlight !== null && set.inFlight.agentId !== agentId) {
|
|
476
|
-
throw new Error("Completed Turn Agent does not match the in-flight Run.");
|
|
477
|
-
}
|
|
478
401
|
const timestamp = requireDate(completedAt, "Turn completedAt");
|
|
479
402
|
return validateRoleSessionSet({
|
|
480
403
|
...set,
|
|
@@ -489,94 +412,19 @@ export function recordTaskRoleTurnBoundary(set, input, completedAt) {
|
|
|
489
412
|
updatedAt: timestamp
|
|
490
413
|
});
|
|
491
414
|
}
|
|
492
|
-
export function recordObservedTaskRoleCompletion(set, completion) {
|
|
493
|
-
const observed = validatePendingTurnCompletion(completion);
|
|
494
|
-
return recordTaskRoleTurnBoundary(set, {
|
|
495
|
-
agentId: observed.agentId,
|
|
496
|
-
nativeSessionId: observed.nativeSessionId,
|
|
497
|
-
turnId: observed.turnId
|
|
498
|
-
}, new Date(observed.observedAt));
|
|
499
|
-
}
|
|
500
|
-
/**
|
|
501
|
-
* Rebinds the same active Run to a bounded control-input receipt while keeping
|
|
502
|
-
* its observed native Turn completion unsettled. No new Run fence is created.
|
|
503
|
-
*/
|
|
504
|
-
export function rebindTaskRoleRunControlReceipt(set, input, now) {
|
|
505
|
-
validateRoleSessionSet(set);
|
|
506
|
-
assertTaskRoleSessionSet(set);
|
|
507
|
-
const inFlight = set.inFlight;
|
|
508
|
-
if (inFlight === null
|
|
509
|
-
|| inFlight.agentId !== input.agentId
|
|
510
|
-
|| inFlight.runId !== input.runId) {
|
|
511
|
-
throw new Error("Workflow outcome control request does not match the unsettled Run.");
|
|
512
|
-
}
|
|
513
|
-
const timestamp = requireDate(now, "Workflow outcome control receipt timestamp");
|
|
514
|
-
return validateRoleSessionSet({
|
|
515
|
-
...set,
|
|
516
|
-
inFlight: {
|
|
517
|
-
...inFlight,
|
|
518
|
-
receiptId: requireSafeIdentity(input.receiptId, "Workflow outcome control receipt id")
|
|
519
|
-
},
|
|
520
|
-
updatedAt: timestamp
|
|
521
|
-
});
|
|
522
|
-
}
|
|
523
|
-
export function clearTaskRoleRun(set, fence, clearedAt) {
|
|
524
|
-
validateRoleSessionSet(set);
|
|
525
|
-
assertTaskRoleSessionSet(set);
|
|
526
|
-
const inFlight = requireMatchingInFlight(set, fence);
|
|
527
|
-
const timestamp = requireDate(clearedAt, "Task Role Run clearedAt");
|
|
528
|
-
if (Date.parse(timestamp) < Date.parse(inFlight.preparedAt)) {
|
|
529
|
-
throw new Error("Task Role Run clearedAt must not be earlier than preparedAt.");
|
|
530
|
-
}
|
|
531
|
-
const updated = {
|
|
532
|
-
...set,
|
|
533
|
-
inFlight: null,
|
|
534
|
-
updatedAt: timestamp
|
|
535
|
-
};
|
|
536
|
-
return validateRoleSessionSet(updated);
|
|
537
|
-
}
|
|
538
|
-
/**
|
|
539
|
-
* Applies an authoritative application-level Run terminal fact to its exact
|
|
540
|
-
* delivery receipt. Provider lifecycle remains independent: only the matching
|
|
541
|
-
* native Turn boundary may move the Session from running to ready. AgentHost
|
|
542
|
-
* serializes a retained next delivery while that Turn is still active.
|
|
543
|
-
*/
|
|
544
|
-
export function terminalizeTaskRoleRunSession(set, fence, terminalAt) {
|
|
545
|
-
validateRoleSessionSet(set);
|
|
546
|
-
const inFlight = set.inFlight;
|
|
547
|
-
return inFlight === null ? set : clearTaskRoleRun(set, fence, terminalAt);
|
|
548
|
-
}
|
|
549
|
-
export function settleTaskRoleCompletion(set, expected, settledAt) {
|
|
550
|
-
validateRoleSessionSet(set);
|
|
551
|
-
assertTaskRoleSessionSet(set);
|
|
552
|
-
const agentId = requireSafeIdentity(expected.agentId, "Agent id");
|
|
553
|
-
const runId = requireSafeIdentity(expected.runId, "Run id");
|
|
554
|
-
const turnId = requireText(expected.turnId, "Turn id");
|
|
555
|
-
const inFlight = set.inFlight;
|
|
556
|
-
if (inFlight === null || inFlight.agentId !== agentId || inFlight.runId !== runId) {
|
|
557
|
-
throw new Error("Pending Turn completion does not match the in-flight Run.");
|
|
558
|
-
}
|
|
559
|
-
const session = set.sessions[agentId];
|
|
560
|
-
if (session === undefined || !session.recentCompletedTurnIds.includes(turnId)) {
|
|
561
|
-
throw new Error("Turn completion has not been observed for the Role Agent session.");
|
|
562
|
-
}
|
|
563
|
-
const timestamp = requireDate(settledAt, "Turn settledAt");
|
|
564
|
-
const updated = {
|
|
565
|
-
...set,
|
|
566
|
-
sessions: {
|
|
567
|
-
...set.sessions,
|
|
568
|
-
[agentId]: {
|
|
569
|
-
...session,
|
|
570
|
-
recentCompletedTurnIds: session.recentCompletedTurnIds,
|
|
571
|
-
updatedAt: timestamp
|
|
572
|
-
}
|
|
573
|
-
},
|
|
574
|
-
inFlight: null,
|
|
575
|
-
updatedAt: timestamp
|
|
576
|
-
};
|
|
577
|
-
return validateRoleSessionSet(updated);
|
|
578
|
-
}
|
|
579
415
|
export function validateRoleSessionSet(set) {
|
|
416
|
+
const ownerScope = set.owner?.scope;
|
|
417
|
+
rejectUnknownFields(set, ownerScope === "global"
|
|
418
|
+
? ["schemaVersion", "owner", "activeAgentId", "sessions", "updatedAt", "history"]
|
|
419
|
+
: [
|
|
420
|
+
"schemaVersion",
|
|
421
|
+
"owner",
|
|
422
|
+
"activeAgentId",
|
|
423
|
+
"sessions",
|
|
424
|
+
"updatedAt",
|
|
425
|
+
"history",
|
|
426
|
+
"providerBinding"
|
|
427
|
+
], "Role session set");
|
|
580
428
|
normalizeOwner(set.owner);
|
|
581
429
|
requireSafeIdentity(set.activeAgentId, "Active Agent id");
|
|
582
430
|
for (const [agentId, session] of Object.entries(set.sessions)) {
|
|
@@ -586,8 +434,7 @@ export function validateRoleSessionSet(set) {
|
|
|
586
434
|
if (set.schemaVersion !== 4) {
|
|
587
435
|
throw new Error("Global Role session set schema version is invalid.");
|
|
588
436
|
}
|
|
589
|
-
if (Object.hasOwn(set, "
|
|
590
|
-
|| Object.hasOwn(set, "providerBinding")) {
|
|
437
|
+
if (Object.hasOwn(set, "providerBinding")) {
|
|
591
438
|
throw new Error("Global Role session set must not contain Task Role lifecycle fields.");
|
|
592
439
|
}
|
|
593
440
|
const history = set.history;
|
|
@@ -602,12 +449,11 @@ export function validateRoleSessionSet(set) {
|
|
|
602
449
|
}
|
|
603
450
|
}
|
|
604
451
|
else {
|
|
605
|
-
if (set.schemaVersion !==
|
|
452
|
+
if (set.schemaVersion !== 11) {
|
|
606
453
|
throw new Error("Task Role session set schema version is invalid.");
|
|
607
454
|
}
|
|
608
|
-
if (!Object.hasOwn(set, "
|
|
609
|
-
|
|
610
|
-
throw new Error("Task Role session set must contain its Turn fence.");
|
|
455
|
+
if (!Object.hasOwn(set, "providerBinding")) {
|
|
456
|
+
throw new Error("Task Role session set must contain its Provider Runtime Binding.");
|
|
611
457
|
}
|
|
612
458
|
const taskSet = set;
|
|
613
459
|
if (taskSet.history !== undefined) {
|
|
@@ -632,17 +478,11 @@ export function validateRoleSessionSet(set) {
|
|
|
632
478
|
}
|
|
633
479
|
}
|
|
634
480
|
}
|
|
635
|
-
const inFlight = taskSet.inFlight === null
|
|
636
|
-
? null
|
|
637
|
-
: validateTaskRoleInFlight(taskSet.inFlight);
|
|
638
481
|
const providerBinding = taskSet.providerBinding === null
|
|
639
482
|
? null
|
|
640
483
|
: validateProviderRuntimeBinding(taskSet.providerBinding);
|
|
641
|
-
if (inFlight !== null && inFlight.agentId !== set.activeAgentId) {
|
|
642
|
-
throw new Error("Task Role in-flight Run Agent must be active.");
|
|
643
|
-
}
|
|
644
484
|
if (providerBinding !== null) {
|
|
645
|
-
const session = taskSet.sessions[
|
|
485
|
+
const session = taskSet.sessions[set.activeAgentId];
|
|
646
486
|
if (session === undefined) {
|
|
647
487
|
throw new Error("Provider Runtime Binding has no active Role Agent session.");
|
|
648
488
|
}
|
|
@@ -732,93 +572,12 @@ function assertTaskRoleSessionSet(set) {
|
|
|
732
572
|
throw new Error("Turn fences require a Task Role session set.");
|
|
733
573
|
}
|
|
734
574
|
}
|
|
735
|
-
function normalizeTaskRoleRunFence(fence) {
|
|
736
|
-
return {
|
|
737
|
-
agentId: requireSafeIdentity(fence.agentId, "Agent id"),
|
|
738
|
-
runId: requireSafeIdentity(fence.runId, "Run id"),
|
|
739
|
-
receiptId: requireText(fence.receiptId, "Delivery receipt id")
|
|
740
|
-
};
|
|
741
|
-
}
|
|
742
|
-
function requireMatchingInFlight(set, fence) {
|
|
743
|
-
const normalized = normalizeTaskRoleRunFence(fence);
|
|
744
|
-
const inFlight = set.inFlight;
|
|
745
|
-
if (inFlight === null)
|
|
746
|
-
throw new Error("Task Role session set has no in-flight Run.");
|
|
747
|
-
if (inFlight.agentId !== normalized.agentId) {
|
|
748
|
-
throw new Error("Task Role Run Agent does not match the in-flight Run.");
|
|
749
|
-
}
|
|
750
|
-
if (inFlight.runId !== normalized.runId) {
|
|
751
|
-
throw new Error("Task Role Run does not match the in-flight Run.");
|
|
752
|
-
}
|
|
753
|
-
if (inFlight.receiptId !== normalized.receiptId) {
|
|
754
|
-
throw new Error("Task Role Run receipt does not match the in-flight Run.");
|
|
755
|
-
}
|
|
756
|
-
return inFlight;
|
|
757
|
-
}
|
|
758
|
-
function validateTaskRoleInFlight(value) {
|
|
759
|
-
const fields = Object.keys(value);
|
|
760
|
-
const unknown = fields.find((field) => !["agentId", "runId", "receiptId", "preparedAt", "pushedAt", "deliveredAt"].includes(field));
|
|
761
|
-
if (unknown !== undefined) {
|
|
762
|
-
throw new Error(`Task Role in-flight Run has unknown field: ${unknown}.`);
|
|
763
|
-
}
|
|
764
|
-
for (const required of ["agentId", "runId", "receiptId", "preparedAt"]) {
|
|
765
|
-
if (!Object.hasOwn(value, required)) {
|
|
766
|
-
throw new Error(`Task Role in-flight Run is missing field: ${required}.`);
|
|
767
|
-
}
|
|
768
|
-
}
|
|
769
|
-
const preparedAt = requirePersistedTimestamp(value.preparedAt, "Task Role Run preparedAt");
|
|
770
|
-
if (value.pushedAt !== undefined) {
|
|
771
|
-
const pushedAt = requirePersistedTimestamp(value.pushedAt, "Task Role Run pushedAt");
|
|
772
|
-
if (Date.parse(pushedAt) < Date.parse(preparedAt)) {
|
|
773
|
-
throw new Error("Task Role Run pushedAt must not be earlier than preparedAt.");
|
|
774
|
-
}
|
|
775
|
-
}
|
|
776
|
-
if (value.deliveredAt !== undefined) {
|
|
777
|
-
const deliveredAt = requirePersistedTimestamp(value.deliveredAt, "Task Role Run deliveredAt");
|
|
778
|
-
if (Date.parse(deliveredAt) < Date.parse(preparedAt)) {
|
|
779
|
-
throw new Error("Task Role Run deliveredAt must not be earlier than preparedAt.");
|
|
780
|
-
}
|
|
781
|
-
if (value.pushedAt === undefined) {
|
|
782
|
-
throw new Error("Task Role Run deliveredAt requires a prior pushedAt.");
|
|
783
|
-
}
|
|
784
|
-
}
|
|
785
|
-
requireSafeIdentity(value.agentId, "Agent id");
|
|
786
|
-
requireSafeIdentity(value.runId, "Run id");
|
|
787
|
-
requireText(value.receiptId, "Delivery receipt id");
|
|
788
|
-
return value;
|
|
789
|
-
}
|
|
790
|
-
function assertCompletionOwner(set, completion) {
|
|
791
|
-
if (completion.taskId !== set.owner.taskId) {
|
|
792
|
-
throw new Error("Observed Turn Task does not match the Task Role session set.");
|
|
793
|
-
}
|
|
794
|
-
if (completion.roleName !== set.owner.roleName) {
|
|
795
|
-
throw new Error("Observed Turn Role does not match the Task Role session set.");
|
|
796
|
-
}
|
|
797
|
-
if (completion.agentId !== set.activeAgentId) {
|
|
798
|
-
throw new Error("Observed Turn Agent does not match the active Agent.");
|
|
799
|
-
}
|
|
800
|
-
}
|
|
801
|
-
function sameRunFence(inFlight, fence) {
|
|
802
|
-
return inFlight.agentId === fence.agentId
|
|
803
|
-
&& inFlight.runId === fence.runId
|
|
804
|
-
&& inFlight.receiptId === fence.receiptId;
|
|
805
|
-
}
|
|
806
|
-
function samePendingTurnCompletion(left, right) {
|
|
807
|
-
return JSON.stringify(left) === JSON.stringify(right);
|
|
808
|
-
}
|
|
809
575
|
function requireDate(value, label) {
|
|
810
576
|
if (!(value instanceof Date) || !Number.isFinite(value.getTime())) {
|
|
811
577
|
throw new Error(`${label} must be a valid date.`);
|
|
812
578
|
}
|
|
813
579
|
return value.toISOString();
|
|
814
580
|
}
|
|
815
|
-
function requirePersistedTimestamp(value, label) {
|
|
816
|
-
const timestamp = Date.parse(value);
|
|
817
|
-
if (!Number.isFinite(timestamp) || new Date(timestamp).toISOString() !== value) {
|
|
818
|
-
throw new Error(`${label} must be a canonical timestamp.`);
|
|
819
|
-
}
|
|
820
|
-
return value;
|
|
821
|
-
}
|
|
822
581
|
function normalizeOwner(owner) {
|
|
823
582
|
const roleName = requireSafeIdentity(owner.roleName, "Role name");
|
|
824
583
|
return owner.scope === "global"
|
|
@@ -840,6 +599,12 @@ function requireSafeIdentity(value, label) {
|
|
|
840
599
|
}
|
|
841
600
|
return normalized;
|
|
842
601
|
}
|
|
602
|
+
function rejectUnknownFields(value, fields, label) {
|
|
603
|
+
const allowed = new Set(fields);
|
|
604
|
+
const unknown = Object.keys(value).find((field) => !allowed.has(field));
|
|
605
|
+
if (unknown !== undefined)
|
|
606
|
+
throw new Error(`${label} has unknown field: ${unknown}.`);
|
|
607
|
+
}
|
|
843
608
|
function requireText(value, label) {
|
|
844
609
|
if (typeof value !== "string" || value.includes("\0"))
|
|
845
610
|
throw new Error(`${label} is invalid.`);
|
|
@@ -79,7 +79,7 @@ export function effectiveLaunchSnapshotsCompatible(existing, desired) {
|
|
|
79
79
|
return isDeepStrictEqual(withoutDesiredRevision(existing), withoutDesiredRevision(desired));
|
|
80
80
|
}
|
|
81
81
|
/**
|
|
82
|
-
* Task Role Sessions keep one physical workspace while
|
|
82
|
+
* Task Role Sessions keep one physical workspace while Turn-scoped facts move.
|
|
83
83
|
* Candidate commits, ReviewRound identity and desired-revision bookkeeping do
|
|
84
84
|
* not define a native Session. Agent, adapter, permission, model, sandbox,
|
|
85
85
|
* manifest, Role context and physical workspace identity still do.
|
|
@@ -99,7 +99,7 @@ export function effectiveLaunchWithTaskMainWorkspace(existing, workspace) {
|
|
|
99
99
|
validateEffectiveLaunchSnapshot(existing);
|
|
100
100
|
validateManagedWorkspace(workspace);
|
|
101
101
|
if (workspace.owner.type !== "task") {
|
|
102
|
-
throw new Error("Only a Task-owned main workspace may refresh fixed Session
|
|
102
|
+
throw new Error("Only a Task-owned main workspace may refresh fixed Session Turn evidence.");
|
|
103
103
|
}
|
|
104
104
|
return validateEffectiveLaunchSnapshot({
|
|
105
105
|
...existing,
|
|
@@ -120,8 +120,8 @@ function taskSessionCompatibleSnapshot(snapshot) {
|
|
|
120
120
|
};
|
|
121
121
|
}
|
|
122
122
|
export function validateEffectiveLaunchSnapshot(snapshot) {
|
|
123
|
-
if (snapshot.schemaVersion !==
|
|
124
|
-
throw new Error("Effective launch snapshot must use schemaVersion
|
|
123
|
+
if (snapshot.schemaVersion !== 3) {
|
|
124
|
+
throw new Error("Effective launch snapshot must use schemaVersion 3.");
|
|
125
125
|
}
|
|
126
126
|
positiveInteger(snapshot.sourceDesiredRevision, "Source desired revision");
|
|
127
127
|
identity(snapshot.agentId, "Effective Agent id");
|
|
@@ -148,18 +148,12 @@ export function validateEffectiveLaunchSnapshot(snapshot) {
|
|
|
148
148
|
identity(snapshot.reviewRoundId, "Effective ReviewRound id");
|
|
149
149
|
commit(snapshot.reviewBaseCommit, "Effective review base commit");
|
|
150
150
|
}
|
|
151
|
-
if (snapshot.contextProtocolVersion !==
|
|
152
|
-
&& snapshot.contextProtocolVersion !== SESSION_BOOTSTRAP_MANIFEST_SCHEMA_VERSION) {
|
|
151
|
+
if (snapshot.contextProtocolVersion !== SESSION_BOOTSTRAP_MANIFEST_SCHEMA_VERSION) {
|
|
153
152
|
throw new Error("Effective launch context protocol version is unsupported.");
|
|
154
153
|
}
|
|
155
|
-
if (snapshot.sessionManifestCompatibilityDigest
|
|
156
|
-
&& !/^[a-f0-9]{64}$/u.test(snapshot.sessionManifestCompatibilityDigest)) {
|
|
154
|
+
if (!/^[a-f0-9]{64}$/u.test(snapshot.sessionManifestCompatibilityDigest)) {
|
|
157
155
|
throw new Error("Effective launch Session Manifest compatibility digest is invalid.");
|
|
158
156
|
}
|
|
159
|
-
if ((snapshot.contextProtocolVersion === undefined)
|
|
160
|
-
!== (snapshot.sessionManifestCompatibilityDigest === undefined)) {
|
|
161
|
-
throw new Error("Effective launch Context protocol compatibility identity is incomplete.");
|
|
162
|
-
}
|
|
163
157
|
validateWorkspace(snapshot.workspace);
|
|
164
158
|
cloneContext(snapshot.context);
|
|
165
159
|
const config = effectiveLaunchConfigUnchecked(snapshot);
|
|
@@ -198,7 +192,7 @@ function snapshotFromConfig(input) {
|
|
|
198
192
|
reviewBaseCommit: commit(input.reviewBaseCommit ?? "", "Review base commit")
|
|
199
193
|
};
|
|
200
194
|
const common = {
|
|
201
|
-
schemaVersion:
|
|
195
|
+
schemaVersion: 3,
|
|
202
196
|
sourceDesiredRevision: positiveInteger(input.sourceDesiredRevision, "Source desired revision"),
|
|
203
197
|
agentId: identity(input.agentId, "Effective Agent id"),
|
|
204
198
|
profileAccess: input.profileAccess,
|
|
@@ -212,12 +206,8 @@ function snapshotFromConfig(input) {
|
|
|
212
206
|
writeProjectIds: [...input.writeProjectIds],
|
|
213
207
|
workspace: cloneWorkspace(input.workspace),
|
|
214
208
|
context: cloneContext(input.context),
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
: { contextProtocolVersion: input.contextProtocolVersion }),
|
|
218
|
-
...(input.sessionManifestCompatibilityDigest === undefined
|
|
219
|
-
? {}
|
|
220
|
-
: { sessionManifestCompatibilityDigest: input.sessionManifestCompatibilityDigest }),
|
|
209
|
+
contextProtocolVersion: input.contextProtocolVersion,
|
|
210
|
+
sessionManifestCompatibilityDigest: input.sessionManifestCompatibilityDigest,
|
|
221
211
|
...review
|
|
222
212
|
};
|
|
223
213
|
const snapshot = config.adapterId === "codex"
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { createPromptEnvelope, createSessionLaunchRequest } from "../runtime/index.js";
|
|
3
|
-
import {
|
|
3
|
+
import { formatTurnReceiptId } from "../task/taskRecordReference.js";
|
|
4
4
|
/**
|
|
5
5
|
* rr13/test: Test-only liveness seam. Integration tests that spawn a real
|
|
6
6
|
* Controller subprocess cannot inject a fake TmuxDeliveryPort, and a saved
|
|
7
|
-
* active Leader
|
|
7
|
+
* active Leader Turn would be reaped by the startup liveness pass without a
|
|
8
8
|
* real tmux role. When this env var is "1", every role reads "present"
|
|
9
9
|
* without probing tmux. The Controller subprocess inherits it from the
|
|
10
10
|
* test's CLI env. Never set in production.
|
|
@@ -12,7 +12,7 @@ import { formatAgentRunReceiptId } from "../task/taskRecordReference.js";
|
|
|
12
12
|
const TEST_ROLE_LIVENESS_PRESENT = process.env.YUI_TEST_ROLE_LIVENESS_PRESENT === "1";
|
|
13
13
|
/**
|
|
14
14
|
* Scheduler-to-tmux adapter. It retains only in-process prepared launch data;
|
|
15
|
-
* durable session identity remains owned by
|
|
15
|
+
* durable session identity remains owned by TaskStore.
|
|
16
16
|
*/
|
|
17
17
|
export class ExecutorRegistry {
|
|
18
18
|
planner;
|
|
@@ -38,7 +38,7 @@ export class ExecutorRegistry {
|
|
|
38
38
|
agentId: input.agentId,
|
|
39
39
|
adapterId: input.adapterId,
|
|
40
40
|
mode: input.mode,
|
|
41
|
-
...(input.
|
|
41
|
+
...(input.turnId === undefined ? {} : { turnId: input.turnId })
|
|
42
42
|
};
|
|
43
43
|
const cached = this.#prepared.get(deliveryBase.deliveryId);
|
|
44
44
|
if (cached !== undefined)
|
|
@@ -74,7 +74,7 @@ export class ExecutorRegistry {
|
|
|
74
74
|
...(input.runtimePolicy === undefined
|
|
75
75
|
? {}
|
|
76
76
|
: { runtimePolicy: input.runtimePolicy }),
|
|
77
|
-
...(input.
|
|
77
|
+
...(input.turnId === undefined ? {} : { turnId: input.turnId })
|
|
78
78
|
};
|
|
79
79
|
if (this.runtimePorts.launchCoordinator !== undefined) {
|
|
80
80
|
binding = await this.runtimePorts.launchCoordinator.prepare(input.mode === "new"
|
|
@@ -141,13 +141,13 @@ export class ExecutorRegistry {
|
|
|
141
141
|
async sendOnce(input) {
|
|
142
142
|
const prepared = this.requirePrepared(input.delivery.prepared);
|
|
143
143
|
if (prepared.binding !== undefined && this.runtimePorts !== undefined) {
|
|
144
|
-
const
|
|
145
|
-
if (
|
|
146
|
-
throw new Error("Runtime prompt delivery requires a Task-local
|
|
144
|
+
const turnId = input.delivery.prepared.turnId;
|
|
145
|
+
if (turnId === undefined) {
|
|
146
|
+
throw new Error("Runtime prompt delivery requires a Task-local Turn id.");
|
|
147
147
|
}
|
|
148
148
|
// A reused native process retains the stable descriptor path from its
|
|
149
149
|
// original control plane. Publish only the current-control source after
|
|
150
|
-
// the
|
|
150
|
+
// the Turn/Session fence is durable and immediately before Provider input;
|
|
151
151
|
// the reused Hook self-refreshes its own source before the volatile
|
|
152
152
|
// fence instead of the Controller scanning history to keep it fresh.
|
|
153
153
|
if (prepared.binding.hostCreated === false
|
|
@@ -158,7 +158,7 @@ export class ExecutorRegistry {
|
|
|
158
158
|
this.planner.refreshTaskRuntimeDescriptor({
|
|
159
159
|
taskId: input.delivery.prepared.taskId,
|
|
160
160
|
roleName: input.delivery.prepared.roleName,
|
|
161
|
-
|
|
161
|
+
turnId,
|
|
162
162
|
launchId: prepared.binding.launchId,
|
|
163
163
|
nativeSessionId: prepared.binding.nativeSessionId,
|
|
164
164
|
agentId: prepared.binding.agentId,
|
|
@@ -171,9 +171,9 @@ export class ExecutorRegistry {
|
|
|
171
171
|
envelope: createPromptEnvelope({
|
|
172
172
|
id: input.receiptId,
|
|
173
173
|
source: {
|
|
174
|
-
kind: input.receiptId ===
|
|
174
|
+
kind: input.receiptId === formatTurnReceiptId(input.delivery.prepared.taskId, turnId) ? "turn" : "turn-input",
|
|
175
175
|
taskId: input.delivery.prepared.taskId,
|
|
176
|
-
localId:
|
|
176
|
+
localId: turnId
|
|
177
177
|
},
|
|
178
178
|
text: input.text,
|
|
179
179
|
createdAt: new Date()
|
|
@@ -190,6 +190,26 @@ export class ExecutorRegistry {
|
|
|
190
190
|
}
|
|
191
191
|
return outcome;
|
|
192
192
|
}
|
|
193
|
+
async steerOnce(input) {
|
|
194
|
+
if (this.runtimePorts === undefined)
|
|
195
|
+
return "unavailable";
|
|
196
|
+
const outcome = await this.runtimePorts.promptPush.trySteer({
|
|
197
|
+
owner: { scope: "task", taskId: input.taskId, roleName: input.roleName },
|
|
198
|
+
launchId: input.launchId,
|
|
199
|
+
agentId: input.agentId,
|
|
200
|
+
adapterId: input.adapterId,
|
|
201
|
+
nativeSessionId: input.nativeSessionId,
|
|
202
|
+
nativeTurnId: input.nativeTurnId,
|
|
203
|
+
providerAuthority: input.authority,
|
|
204
|
+
envelope: createPromptEnvelope({
|
|
205
|
+
id: input.receiptId,
|
|
206
|
+
source: { kind: "turn-input", taskId: input.taskId, localId: activeTurnId(input.receiptId) },
|
|
207
|
+
text: input.text,
|
|
208
|
+
createdAt: new Date()
|
|
209
|
+
})
|
|
210
|
+
});
|
|
211
|
+
return outcome === "delivered" ? "sent" : outcome;
|
|
212
|
+
}
|
|
193
213
|
async notifyOperatorInputOnce(input) {
|
|
194
214
|
const probe = this.readiness(input.adapterId, "operator");
|
|
195
215
|
return this.tmux.sendRoleInputOnceIfReadyAsync === undefined
|
|
@@ -201,8 +221,8 @@ export class ExecutorRegistry {
|
|
|
201
221
|
const delivery = prepared.delivery;
|
|
202
222
|
if (delivery.taskId !== input.taskId
|
|
203
223
|
|| delivery.roleName !== input.roleName
|
|
204
|
-
|| (input.
|
|
205
|
-
&& delivery.
|
|
224
|
+
|| (input.turnId !== undefined
|
|
225
|
+
&& delivery.turnId !== input.turnId)
|
|
206
226
|
|| (input.launchId !== undefined
|
|
207
227
|
&& delivery.launchId !== input.launchId)) {
|
|
208
228
|
continue;
|
|
@@ -261,7 +281,7 @@ export class ExecutorRegistry {
|
|
|
261
281
|
const requested = resourceInputs ?? inputs.map((input) => ({
|
|
262
282
|
taskId: input.taskId,
|
|
263
283
|
roleName: input.roleName,
|
|
264
|
-
...(input.
|
|
284
|
+
...(input.turnId === undefined ? {} : { turnId: input.turnId }),
|
|
265
285
|
agentId: input.agentId,
|
|
266
286
|
adapterId: input.adapterId,
|
|
267
287
|
...(input.nativeSessionId === undefined
|
|
@@ -329,11 +349,17 @@ export class ExecutorRegistry {
|
|
|
329
349
|
return planned;
|
|
330
350
|
}
|
|
331
351
|
}
|
|
352
|
+
function activeTurnId(receiptId) {
|
|
353
|
+
const match = /^turn-input:[^/]+\/([^/]+)\/[1-9]\d*$/u.exec(receiptId);
|
|
354
|
+
if (match === null)
|
|
355
|
+
throw new Error("Turn steer receipt is invalid.");
|
|
356
|
+
return decodeURIComponent(match[1]);
|
|
357
|
+
}
|
|
332
358
|
export function agentProcessReadinessProbe(adapterId, _surface = "role") {
|
|
333
359
|
if (adapterId !== "codex" && adapterId !== "claude") {
|
|
334
360
|
throw new Error(`No tmux readiness probe is registered for Agent adapter: ${adapterId}.`);
|
|
335
361
|
}
|
|
336
|
-
//
|
|
362
|
+
// Turn state and receipt fences decide whether delivery is allowed. Provider
|
|
337
363
|
// terminal contents are display-only evidence and never lifecycle input.
|
|
338
364
|
return livePane;
|
|
339
365
|
}
|
|
@@ -348,7 +374,7 @@ function preparedDeliveryId(input) {
|
|
|
348
374
|
input.adapterId,
|
|
349
375
|
input.effective,
|
|
350
376
|
input.mode,
|
|
351
|
-
input.
|
|
377
|
+
input.turnId ?? null,
|
|
352
378
|
input.nativeSessionId ?? null,
|
|
353
379
|
input.hostActivationId ?? null
|
|
354
380
|
])).digest("hex");
|