@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
|
@@ -187,23 +187,23 @@ function validateStartParams(store, params) {
|
|
|
187
187
|
/**
|
|
188
188
|
* rr8/rr12: Bind the declared job owner to the caller's managed identity. The
|
|
189
189
|
* Controller validates the declared owner/workspace and verifies the caller
|
|
190
|
-
* against durable
|
|
190
|
+
* against durable Turn/Session state — a self-reported role or scope is never
|
|
191
191
|
* authority on its own. The rules:
|
|
192
192
|
*
|
|
193
193
|
* - `user` (non-managed): rejected because it has no managed Session identity.
|
|
194
194
|
* - `global`: full Task authority after current Role Session verification.
|
|
195
195
|
* - `task` + mismatched taskId: rejected.
|
|
196
|
-
* - `task` + missing `
|
|
197
|
-
* - `task` +
|
|
198
|
-
* claimed Role must be the real Role of an active
|
|
199
|
-
* - `task`: full authority inside the matching Task after active
|
|
196
|
+
* - `task` + missing `turnId`: rejected (a managed caller must bind to a Turn).
|
|
197
|
+
* - `task` + Turn not found / not active / `roleName !== role`: rejected — the
|
|
198
|
+
* claimed Role must be the real Role of an active Turn.
|
|
199
|
+
* - `task`: full authority inside the matching Task after active Turn and
|
|
200
200
|
* per-Session caller-key verification; Role does not narrow it.
|
|
201
201
|
*/
|
|
202
202
|
/**
|
|
203
203
|
* rr13: `job.start`/`job.cancel` caller authorization at the Controller
|
|
204
204
|
* boundary. Every identity claim the Controller can verify from durable state
|
|
205
|
-
* (role and
|
|
206
|
-
* same
|
|
205
|
+
* (role and turnId) is replayable by any client in the
|
|
206
|
+
* same authoritative Controller. The channel itself is therefore not
|
|
207
207
|
* authenticated by those claims alone. A non-replayable per-Session caller key
|
|
208
208
|
* closes the gap:
|
|
209
209
|
*
|
|
@@ -213,10 +213,10 @@ function validateStartParams(store, params) {
|
|
|
213
213
|
* - `task` scope: the caller must present `callerKey` — the
|
|
214
214
|
* `YUI_JOB_CALLER_KEY` injected at its native Session launch. The Controller
|
|
215
215
|
* hashes it (SHA-256) and compares against the durable `jobCallerKeyHashes`
|
|
216
|
-
* map for the caller's Role + Agent.
|
|
216
|
+
* map for the caller's Role + Agent. An absent hash or a
|
|
217
217
|
* mismatched key is UNAUTHORIZED.
|
|
218
218
|
*
|
|
219
|
-
* The existing
|
|
219
|
+
* The existing Turn binding checks run after the key check, so a forged
|
|
220
220
|
* identity that also lacks the key is rejected at the channel boundary.
|
|
221
221
|
*/
|
|
222
222
|
function assertCallerAuthorized(store, caller, taskId) {
|
|
@@ -257,16 +257,16 @@ function assertCallerAuthorized(store, caller, taskId) {
|
|
|
257
257
|
if (caller.taskId !== taskId) {
|
|
258
258
|
throw jobControlError("UNAUTHORIZED", "A managed Task Session may not start or cancel Jobs for a different Task.");
|
|
259
259
|
}
|
|
260
|
-
if (caller.
|
|
261
|
-
throw jobControlError("UNAUTHORIZED", "A managed Task Session must bind its request to a
|
|
260
|
+
if (caller.turnId === undefined) {
|
|
261
|
+
throw jobControlError("UNAUTHORIZED", "A managed Task Session must bind its request to a Turn.");
|
|
262
262
|
}
|
|
263
|
-
const run = store.
|
|
263
|
+
const run = store.getTurn(taskId, caller.turnId);
|
|
264
264
|
if (run === null || run.status !== "active" || run.roleName !== caller.role) {
|
|
265
|
-
throw jobControlError("UNAUTHORIZED", "A managed Task Session's Role is not bound to an active
|
|
265
|
+
throw jobControlError("UNAUTHORIZED", "A managed Task Session's Role is not bound to an active Turn.");
|
|
266
266
|
}
|
|
267
267
|
// rr13: Verify the non-replayable per-Session caller key. The key is injected
|
|
268
268
|
// at native Session launch and never persisted in plaintext; only its SHA-256
|
|
269
|
-
// hash is durable. A client
|
|
269
|
+
// hash is durable. A client with database read access can see the hash but cannot
|
|
270
270
|
// recover the key.
|
|
271
271
|
if (caller.callerKey === undefined) {
|
|
272
272
|
throw jobControlError("UNAUTHORIZED", "job.start/job.cancel requires a managed Session caller key.");
|
|
@@ -490,7 +490,7 @@ function parseCaller(value) {
|
|
|
490
490
|
const record = value;
|
|
491
491
|
const allowed = new Set([
|
|
492
492
|
"scope", "taskId", "role", "agentId", "adapterId", "launchId", "nativeSessionId",
|
|
493
|
-
"
|
|
493
|
+
"turnId", "callerKey"
|
|
494
494
|
]);
|
|
495
495
|
for (const key of Object.keys(record)) {
|
|
496
496
|
if (!allowed.has(key)) {
|
|
@@ -512,7 +512,7 @@ function parseCaller(value) {
|
|
|
512
512
|
const adapterId = optionalId("adapterId");
|
|
513
513
|
const launchId = optionalId("launchId");
|
|
514
514
|
const nativeSessionId = optionalId("nativeSessionId");
|
|
515
|
-
const
|
|
515
|
+
const turnId = optionalId("turnId");
|
|
516
516
|
const callerKey = record.callerKey === undefined
|
|
517
517
|
? undefined
|
|
518
518
|
: requiredId(record.callerKey, "job caller callerKey");
|
|
@@ -524,7 +524,7 @@ function parseCaller(value) {
|
|
|
524
524
|
...(adapterId === undefined ? {} : { adapterId }),
|
|
525
525
|
...(launchId === undefined ? {} : { launchId }),
|
|
526
526
|
...(nativeSessionId === undefined ? {} : { nativeSessionId }),
|
|
527
|
-
...(
|
|
527
|
+
...(turnId === undefined ? {} : { turnId }),
|
|
528
528
|
...(callerKey === undefined ? {} : { callerKey })
|
|
529
529
|
};
|
|
530
530
|
}
|
|
@@ -32,7 +32,7 @@ export function createRuntimeResourceActivityTracker() {
|
|
|
32
32
|
|| roleKey === undefined)
|
|
33
33
|
return reject();
|
|
34
34
|
const identityKey = JSON.stringify([
|
|
35
|
-
identity.
|
|
35
|
+
identity.turnId,
|
|
36
36
|
identity.agentId,
|
|
37
37
|
identity.adapterId,
|
|
38
38
|
identity.nativeSessionId ?? null,
|
|
@@ -262,7 +262,7 @@ function roleOwner(role) {
|
|
|
262
262
|
...(role.adapterId === undefined ? {} : { adapterId: role.adapterId }),
|
|
263
263
|
...(role.nativeSessionId === undefined ? {} : { nativeSessionId: role.nativeSessionId }),
|
|
264
264
|
...(role.launchId === undefined ? {} : { launchId: role.launchId }),
|
|
265
|
-
...(role.
|
|
265
|
+
...(role.turnId === undefined ? {} : { turnId: role.turnId })
|
|
266
266
|
};
|
|
267
267
|
}
|
|
268
268
|
return {
|
|
@@ -275,7 +275,7 @@ function roleOwner(role) {
|
|
|
275
275
|
...(role.adapterId === undefined ? {} : { adapterId: role.adapterId }),
|
|
276
276
|
...(role.nativeSessionId === undefined ? {} : { nativeSessionId: role.nativeSessionId }),
|
|
277
277
|
...(role.launchId === undefined ? {} : { launchId: role.launchId }),
|
|
278
|
-
...(role.
|
|
278
|
+
...(role.turnId === undefined ? {} : { turnId: role.turnId })
|
|
279
279
|
};
|
|
280
280
|
}
|
|
281
281
|
function processTree(processes, rootPid) {
|
|
@@ -437,8 +437,8 @@ function matchesResourceIdentity(identity, owner) {
|
|
|
437
437
|
|| identity.taskId.length === 0
|
|
438
438
|
|| typeof identity.roleName !== "string"
|
|
439
439
|
|| identity.roleName.length === 0
|
|
440
|
-
|| typeof identity.
|
|
441
|
-
|| identity.
|
|
440
|
+
|| typeof identity.turnId !== "string"
|
|
441
|
+
|| identity.turnId.length === 0
|
|
442
442
|
|| typeof identity.agentId !== "string"
|
|
443
443
|
|| identity.agentId.length === 0
|
|
444
444
|
|| typeof identity.adapterId !== "string"
|
|
@@ -449,7 +449,7 @@ function matchesResourceIdentity(identity, owner) {
|
|
|
449
449
|
if (owner.kind !== "task-role"
|
|
450
450
|
|| owner.taskId !== identity.taskId
|
|
451
451
|
|| owner.roleName !== identity.roleName
|
|
452
|
-
|| owner.
|
|
452
|
+
|| owner.turnId !== identity.turnId
|
|
453
453
|
|| owner.agentId !== identity.agentId
|
|
454
454
|
|| owner.adapterId !== identity.adapterId)
|
|
455
455
|
return false;
|
|
@@ -3,7 +3,7 @@ import { existsSync, lstatSync, readFileSync, readdirSync } from "node:fs";
|
|
|
3
3
|
import { basename, join, resolve } from "node:path";
|
|
4
4
|
import { activeLiveRoleAgentSession } from "../executor/agentExecutor.js";
|
|
5
5
|
import { inspectStorageSchema } from "../storage/storageSchema.js";
|
|
6
|
-
import {
|
|
6
|
+
import { openCurrentTaskStore } from "../storage/currentTaskStore.js";
|
|
7
7
|
import { NodeCommandExecutor } from "../tmux/commandExecutor.js";
|
|
8
8
|
import { TmuxManager, yuiTmuxServerName } from "../tmux/tmuxManager.js";
|
|
9
9
|
import { tmuxSocketDirectory, tmuxSocketEnvironment } from "../tmux/tmuxSocketEndpoint.js";
|
|
@@ -450,8 +450,7 @@ function inspectRuntimeDomain(home, processes, roles, storageStatus, now) {
|
|
|
450
450
|
// A current, ordinary YUI_HOME is a real control domain, not a disposable
|
|
451
451
|
// test domain. Keep it visible even when it is currently idle so a stale
|
|
452
452
|
// artifact cannot acquire ephemeral cleanup authority by omission. An
|
|
453
|
-
// uninitialized temporary directory with no runtime facts remains absent
|
|
454
|
-
// to preserve the legacy empty-home inventory behavior.
|
|
453
|
+
// uninitialized temporary directory with no runtime facts remains absent.
|
|
455
454
|
if (storageStatus !== "current"
|
|
456
455
|
&& processes.length === 0
|
|
457
456
|
&& roles.length === 0)
|
|
@@ -614,19 +613,14 @@ function inspectRuntimeDomain(home, processes, roles, storageStatus, now) {
|
|
|
614
613
|
}
|
|
615
614
|
function loadHomeState(home, warnings, options) {
|
|
616
615
|
const schema = (options.inspectStorage ?? inspectStorageSchema)(home);
|
|
617
|
-
|
|
618
|
-
&& schema.incompatibleComponent === "record"
|
|
619
|
-
&& schema.direction === "older"
|
|
620
|
-
&& schema.currentLayoutVersion === schema.latestLayoutVersion
|
|
621
|
-
&& schema.currentAggregateSchemaVersion === schema.latestAggregateSchemaVersion;
|
|
622
|
-
if (schema.status !== "current" && !recordOnlyOlder) {
|
|
616
|
+
if (schema.status !== "current") {
|
|
623
617
|
return {
|
|
624
618
|
storageStatus: schema.status,
|
|
625
619
|
roles: []
|
|
626
620
|
};
|
|
627
621
|
}
|
|
628
622
|
try {
|
|
629
|
-
const store = (options.
|
|
623
|
+
const store = (options.openCurrentStore ?? openCurrentTaskStore)(home);
|
|
630
624
|
const roles = [...store.listGlobalRoles()]
|
|
631
625
|
.sort((left, right) => numericCompare(left.name, right.name))
|
|
632
626
|
.map((role) => {
|
|
@@ -646,7 +640,7 @@ function loadHomeState(home, warnings, options) {
|
|
|
646
640
|
const appendTaskRole = (task, role) => {
|
|
647
641
|
const session = activeLiveRoleAgentSession(store.getRoleSessionSet(task.id, role.name));
|
|
648
642
|
const binding = role.agentBindings[role.activeAgentId];
|
|
649
|
-
const run = store.
|
|
643
|
+
const run = store.getActiveTurn(task.id, role.name);
|
|
650
644
|
const agentId = session?.effective.agentId ?? role.activeAgentId;
|
|
651
645
|
const adapterId = session?.effective.adapterId ?? binding?.adapterId;
|
|
652
646
|
roles.push({
|
|
@@ -659,57 +653,42 @@ function loadHomeState(home, warnings, options) {
|
|
|
659
653
|
...(adapterId === undefined ? {} : { adapterId }),
|
|
660
654
|
...(session === null ? {} : { nativeSessionId: session.nativeSessionId }),
|
|
661
655
|
...(session?.launchId === undefined ? {} : { launchId: session.launchId }),
|
|
662
|
-
...(run === null ? {} : {
|
|
656
|
+
...(run === null ? {} : { turnId: run.id })
|
|
663
657
|
});
|
|
664
658
|
};
|
|
665
|
-
//
|
|
666
|
-
// Use
|
|
667
|
-
// Task/Role/Session/
|
|
668
|
-
// fences.
|
|
669
|
-
|
|
670
|
-
const
|
|
671
|
-
const
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
}
|
|
679
|
-
}
|
|
659
|
+
// The current SQLite store owns bounded active-Task and current-Session
|
|
660
|
+
// indexes. Use them only as selectors, then point-read the authoritative
|
|
661
|
+
// Task/Role/Session/Turn records so an index row cannot bypass ownership
|
|
662
|
+
// fences.
|
|
663
|
+
const indexedTaskIds = [...new Set(store.listActiveTaskIds())].sort(numericCompare);
|
|
664
|
+
const sessionCandidates = store.listRuntimeSessionCandidates({ scope: "task" });
|
|
665
|
+
const candidateRoles = new Map();
|
|
666
|
+
for (const candidate of sessionCandidates) {
|
|
667
|
+
if (candidate.owner.scope !== "task")
|
|
668
|
+
continue;
|
|
669
|
+
const roleNames = candidateRoles.get(candidate.owner.taskId) ?? new Set();
|
|
670
|
+
roleNames.add(candidate.owner.roleName);
|
|
671
|
+
candidateRoles.set(candidate.owner.taskId, roleNames);
|
|
680
672
|
}
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
for (const
|
|
699
|
-
|
|
700
|
-
if (task === null)
|
|
701
|
-
continue;
|
|
702
|
-
const rolesForTask = activeTaskSet.has(taskId) && task.status === "active"
|
|
703
|
-
? store.listRoles(taskId)
|
|
704
|
-
: [...(candidateRoles.get(taskId) ?? [])]
|
|
705
|
-
.sort(numericCompare)
|
|
706
|
-
.flatMap((roleName) => {
|
|
707
|
-
const role = store.getRole(taskId, roleName);
|
|
708
|
-
return role === null ? [] : [role];
|
|
709
|
-
});
|
|
710
|
-
for (const role of [...rolesForTask].sort((left, right) => (numericCompare(left.name, right.name)))) {
|
|
711
|
-
appendTaskRole(task, role);
|
|
712
|
-
}
|
|
673
|
+
const activeTaskSet = new Set(indexedTaskIds);
|
|
674
|
+
const taskIds = [...new Set([
|
|
675
|
+
...indexedTaskIds,
|
|
676
|
+
...candidateRoles.keys()
|
|
677
|
+
])].sort(numericCompare);
|
|
678
|
+
for (const taskId of taskIds) {
|
|
679
|
+
const task = store.getTask(taskId);
|
|
680
|
+
if (task === null)
|
|
681
|
+
continue;
|
|
682
|
+
const rolesForTask = activeTaskSet.has(taskId) && task.status === "active"
|
|
683
|
+
? store.listRoles(taskId)
|
|
684
|
+
: [...(candidateRoles.get(taskId) ?? [])]
|
|
685
|
+
.sort(numericCompare)
|
|
686
|
+
.flatMap((roleName) => {
|
|
687
|
+
const role = store.getRole(taskId, roleName);
|
|
688
|
+
return role === null ? [] : [role];
|
|
689
|
+
});
|
|
690
|
+
for (const role of [...rolesForTask].sort((left, right) => (numericCompare(left.name, right.name)))) {
|
|
691
|
+
appendTaskRole(task, role);
|
|
713
692
|
}
|
|
714
693
|
}
|
|
715
694
|
return {
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*
|
|
5
5
|
* The `/proc` scanning in `resourceInventoryLinux` is blocking IO (hundreds of
|
|
6
6
|
* `readFileSync` calls per pass, `spawnSync`, tmux inspection). When the worker
|
|
7
|
-
*
|
|
7
|
+
* persistence worker is active (`YUI_STORE_WORKER=1`, the same
|
|
8
8
|
* flag as the persistence worker, §6), the Controller runtime and the ephemeral
|
|
9
9
|
* reaper scan through this client instead: the scan runs in the inventory
|
|
10
10
|
* worker (its own thread), the main event loop stays free, and the result is
|
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
* worker -> main: ready | result | error
|
|
18
18
|
*
|
|
19
19
|
* Only structured-cloneable scan options cross the port (the file-backed
|
|
20
|
-
* `inspectStorage` / `
|
|
21
|
-
* path
|
|
20
|
+
* `inspectStorage` / `openCurrentStore` / `now` seams stay on the direct
|
|
21
|
+
* path used without the worker). The inventory worker never touches
|
|
22
22
|
* the persistence worker's database connection (§3.3): it is a separate worker
|
|
23
23
|
* with a separate concern.
|
|
24
24
|
*/
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Runs the blocking `/proc` scanning of `resourceInventoryLinux` off the main
|
|
5
5
|
* thread. The main thread asks for a scan over a `MessageChannel` port; the
|
|
6
|
-
* worker runs the exact same `scanControllerResourceInventory` the
|
|
6
|
+
* worker runs the exact same `scanControllerResourceInventory` as the main thread
|
|
7
7
|
* calls directly and posts the inventory back. Because worker threads share the
|
|
8
8
|
* process, the scanner's self-exclusion (`process.pid`, its start identity)
|
|
9
9
|
* keeps excluding the Controller process exactly as on the main thread.
|
|
@@ -11,7 +11,7 @@ import { effectiveLaunchSnapshotsCompatible, effectiveLaunchSnapshotsCompatibleF
|
|
|
11
11
|
import { AgentConfigurationCatalogService, validateAgentLaunchConfiguration } from "../executor/agentConfigurationCatalog.js";
|
|
12
12
|
import { isTaskOwnedWorkspace, managedWorkspaceIdentity, sameManagedWorkspaceIdentity } from "../worktree/managedWorkspace.js";
|
|
13
13
|
import { FileRoleLaunchPlanner } from "../executor/fileRoleLaunchPlanner.js";
|
|
14
|
-
import {
|
|
14
|
+
import { openCurrentTaskStore } from "../storage/currentTaskStore.js";
|
|
15
15
|
import { SqliteTaskStore } from "../storage/sqliteStore.js";
|
|
16
16
|
import { AsyncTaskStoreClient, resolveStoreWorkerEnabledForHome } from "../storage/storeRpc.js";
|
|
17
17
|
import { FileTaskWorkspacePreparer } from "../repository/taskWorkspacePreparer.js";
|
|
@@ -45,13 +45,13 @@ import { createTaskEvent } from "../event/taskEvent.js";
|
|
|
45
45
|
export function refreshAppliedTaskRuntimeDescriptor(store, planner, input) {
|
|
46
46
|
if (input.launchId === undefined)
|
|
47
47
|
return;
|
|
48
|
-
const run = input.
|
|
48
|
+
const run = input.turnId === undefined
|
|
49
49
|
? null
|
|
50
|
-
: store.
|
|
51
|
-
if (input.
|
|
50
|
+
: store.getTurn(input.taskId, input.turnId);
|
|
51
|
+
if (input.turnId !== undefined && run === null) {
|
|
52
52
|
throw new Error("Prepared Task runtime generation is not current.");
|
|
53
53
|
}
|
|
54
|
-
// A terminal completion has already settled this exact
|
|
54
|
+
// A terminal completion has already settled this exact Turn and no later
|
|
55
55
|
// prompt can use its descriptor. Acknowledge the applied provider fact
|
|
56
56
|
// without republishing a dead generation.
|
|
57
57
|
if (run !== null && run.status !== "active")
|
|
@@ -76,22 +76,18 @@ export function refreshAppliedTaskRuntimeDescriptor(store, planner, input) {
|
|
|
76
76
|
workspace: effective.workspace.root
|
|
77
77
|
});
|
|
78
78
|
}
|
|
79
|
-
/** Production composition root for the
|
|
79
|
+
/** Production composition root for the current SQLite TaskStore + tmux Controller. */
|
|
80
80
|
export async function startFileTaskControllerRuntime(home, options = {}) {
|
|
81
|
-
// The Home
|
|
82
|
-
//
|
|
83
|
-
// in-process SQLite connection. The non-worker path opens the Home-decided
|
|
84
|
-
// backend through the compatibility opener (SQLite for layout 7, file store
|
|
85
|
-
// with normalization for older layouts).
|
|
81
|
+
// The current Home always uses SQLite. The persistence worker is enabled by
|
|
82
|
+
// default; YUI_STORE_WORKER=0/false keeps the same database in-process.
|
|
86
83
|
const useWorker = resolveStoreWorkerEnabledForHome(home, options.environment ?? process.env);
|
|
87
84
|
const store = options.store
|
|
88
85
|
?? (useWorker
|
|
89
|
-
//
|
|
90
|
-
//
|
|
91
|
-
//
|
|
92
|
-
// the same WAL db and serialize via BEGIN IMMEDIATE + busy_timeout.
|
|
86
|
+
// The worker owns the event-processing hot path while the scheduler uses
|
|
87
|
+
// the synchronous connection. Both point at the same WAL database and
|
|
88
|
+
// serialize via BEGIN IMMEDIATE + busy_timeout.
|
|
93
89
|
? new SqliteTaskStore(home)
|
|
94
|
-
:
|
|
90
|
+
: openCurrentTaskStore(home));
|
|
95
91
|
const homeId = store.getHomeIdentity().homeId;
|
|
96
92
|
const durableConfig = store.getConfig();
|
|
97
93
|
// When the worker backend is active, the db-touching observer folds run in
|
|
@@ -259,9 +255,8 @@ export async function startFileTaskControllerRuntime(home, options = {}) {
|
|
|
259
255
|
onCleanupRequired: signalRuntimeCleanup,
|
|
260
256
|
runtimeIsolation
|
|
261
257
|
});
|
|
262
|
-
// One inventory scan per scheduler pass. When the worker
|
|
263
|
-
// the blocking /proc scan runs
|
|
264
|
-
// the main thread (file backend, unchanged).
|
|
258
|
+
// One inventory scan per scheduler pass. When the inventory worker is active
|
|
259
|
+
// the blocking /proc scan runs there; otherwise it runs on the main thread.
|
|
265
260
|
const scanInventory = (panes) => inventoryClient !== undefined
|
|
266
261
|
? inventoryClient.scan({
|
|
267
262
|
currentHome: home,
|
|
@@ -297,7 +292,7 @@ export async function startFileTaskControllerRuntime(home, options = {}) {
|
|
|
297
292
|
&& candidate.roleName === owner.roleName));
|
|
298
293
|
if (input === undefined)
|
|
299
294
|
return [];
|
|
300
|
-
const identity = owner.
|
|
295
|
+
const identity = owner.turnId === undefined
|
|
301
296
|
|| owner.adapterId === undefined
|
|
302
297
|
|| (owner.nativeSessionId === undefined && owner.launchId === undefined)
|
|
303
298
|
|| (owner.nativeSessionId !== undefined && owner.nativeSessionId.trim().length === 0)
|
|
@@ -306,7 +301,7 @@ export async function startFileTaskControllerRuntime(home, options = {}) {
|
|
|
306
301
|
: {
|
|
307
302
|
taskId: owner.taskId,
|
|
308
303
|
roleName: owner.roleName,
|
|
309
|
-
|
|
304
|
+
turnId: owner.turnId,
|
|
310
305
|
agentId: owner.agentId,
|
|
311
306
|
adapterId: owner.adapterId,
|
|
312
307
|
...(owner.nativeSessionId === undefined
|
|
@@ -314,12 +309,12 @@ export async function startFileTaskControllerRuntime(home, options = {}) {
|
|
|
314
309
|
: { nativeSessionId: owner.nativeSessionId }),
|
|
315
310
|
...(owner.launchId === undefined ? {} : { launchId: owner.launchId })
|
|
316
311
|
};
|
|
317
|
-
const changed = !active || input.
|
|
312
|
+
const changed = !active || input.turnId === undefined
|
|
318
313
|
? false
|
|
319
314
|
: resourceActivity({
|
|
320
315
|
taskId: input.taskId,
|
|
321
316
|
roleName: input.roleName,
|
|
322
|
-
|
|
317
|
+
turnId: input.turnId,
|
|
323
318
|
agentId: input.agentId,
|
|
324
319
|
adapterId: input.adapterId,
|
|
325
320
|
...(input.nativeSessionId === undefined
|
|
@@ -570,7 +565,7 @@ export function createRuntimeLifecycleDispatcher(store, schedulerStore, sessionH
|
|
|
570
565
|
schedulerStore.beginAgentHostProviderTurn({
|
|
571
566
|
taskId: value.taskId,
|
|
572
567
|
roleName: value.roleName,
|
|
573
|
-
|
|
568
|
+
...(value.turnId === undefined ? {} : { turnId: value.turnId }),
|
|
574
569
|
agentId: value.agentId,
|
|
575
570
|
launchId: value.launchId,
|
|
576
571
|
nativeSessionId: value.nativeSessionId,
|
|
@@ -602,7 +597,7 @@ export function createRuntimeLifecycleDispatcher(store, schedulerStore, sessionH
|
|
|
602
597
|
schedulerStore.resolveAgentHostProviderTurnSubmission({
|
|
603
598
|
taskId: value.taskId,
|
|
604
599
|
roleName: value.roleName,
|
|
605
|
-
|
|
600
|
+
...(value.turnId === undefined ? {} : { turnId: value.turnId }),
|
|
606
601
|
attemptId: value.attemptId,
|
|
607
602
|
status,
|
|
608
603
|
reason,
|
|
@@ -613,9 +608,9 @@ export function createRuntimeLifecycleDispatcher(store, schedulerStore, sessionH
|
|
|
613
608
|
}
|
|
614
609
|
if (method === "runtime.process-exit-observe") {
|
|
615
610
|
const observation = validateRuntimeProcessExitObservation(params);
|
|
616
|
-
const run = observation.taskId === undefined || observation.
|
|
611
|
+
const run = observation.taskId === undefined || observation.turnId === undefined
|
|
617
612
|
? null
|
|
618
|
-
: store.
|
|
613
|
+
: store.getTurn(observation.taskId, observation.turnId);
|
|
619
614
|
const globalRole = observation.taskId === undefined
|
|
620
615
|
? store.getGlobalRole(observation.roleName)
|
|
621
616
|
: null;
|
|
@@ -625,20 +620,20 @@ export function createRuntimeLifecycleDispatcher(store, schedulerStore, sessionH
|
|
|
625
620
|
? null
|
|
626
621
|
: builtinAgentDriverRegistry().findByAdapterId(adapterId);
|
|
627
622
|
const turnTerminalObserved = observation.taskId !== undefined
|
|
628
|
-
&& observation.
|
|
623
|
+
&& observation.turnId !== undefined
|
|
629
624
|
&& store.listEvents(observation.taskId).some((event) => {
|
|
630
625
|
const runtime = runtimeObservationFromTaskEvent(event);
|
|
631
626
|
return runtime !== null
|
|
632
|
-
&& runtime.fence.
|
|
627
|
+
&& runtime.fence.turnId === observation.turnId
|
|
633
628
|
&& ["turn.completed", "turn.failed", "turn.cancelled"].includes(runtime.kind)
|
|
634
629
|
&& Date.parse(runtime.receivedAt) <= Date.parse(observation.observedAt);
|
|
635
630
|
});
|
|
636
631
|
const turnFailureObserved = observation.taskId !== undefined
|
|
637
|
-
&& observation.
|
|
632
|
+
&& observation.turnId !== undefined
|
|
638
633
|
&& store.listEvents(observation.taskId).some((event) => {
|
|
639
634
|
const runtime = runtimeObservationFromTaskEvent(event);
|
|
640
635
|
return runtime !== null
|
|
641
|
-
&& runtime.fence.
|
|
636
|
+
&& runtime.fence.turnId === observation.turnId
|
|
642
637
|
&& runtime.fence.launchId === observation.launchId
|
|
643
638
|
&& runtime.kind === "turn.failed"
|
|
644
639
|
&& Date.parse(runtime.receivedAt) <= Date.parse(observation.observedAt);
|
|
@@ -750,20 +745,20 @@ export function createRuntimeLifecycleDispatcher(store, schedulerStore, sessionH
|
|
|
750
745
|
? `Role not found: ${request.taskId}/${request.roleName}.`
|
|
751
746
|
: `Global Role not found: ${request.roleName}.`);
|
|
752
747
|
}
|
|
753
|
-
const
|
|
754
|
-
? store.
|
|
748
|
+
const activeTurn = request.scope === "task"
|
|
749
|
+
? store.getActiveTurn(request.taskId, request.roleName)
|
|
755
750
|
: null;
|
|
756
|
-
if (request.scope === "task" &&
|
|
757
|
-
throw applicationError("INVALID_PARAMS", "Task Role runtime attachment requires an admitted active
|
|
751
|
+
if (request.scope === "task" && activeTurn === null) {
|
|
752
|
+
throw applicationError("INVALID_PARAMS", "Task Role runtime attachment requires an admitted active Turn; it cannot create an empty Provider Conversation.");
|
|
758
753
|
}
|
|
759
754
|
const managedWorkspace = request.scope === "task"
|
|
760
|
-
?
|
|
755
|
+
? activeTurn?.workspace
|
|
761
756
|
?? currentDesiredManagedWorkspace(store, request.taskId, request.roleName)
|
|
762
757
|
: undefined;
|
|
763
758
|
const sessions = request.scope === "task"
|
|
764
759
|
? store.getTaskRoleSessionSet(request.taskId, request.roleName)
|
|
765
760
|
: store.getGlobalRoleSessionSet(request.roleName);
|
|
766
|
-
const effective =
|
|
761
|
+
const effective = activeTurn?.effective
|
|
767
762
|
?? activeLiveRoleAgentSession(sessions)?.effective
|
|
768
763
|
?? resolveRuntimeDesiredEffective(store, request, role);
|
|
769
764
|
const binding = role.agentBindings[effective.agentId];
|
|
@@ -776,14 +771,12 @@ export function createRuntimeLifecycleDispatcher(store, schedulerStore, sessionH
|
|
|
776
771
|
}
|
|
777
772
|
validateLifecycleEnvironment(request.environment, agent);
|
|
778
773
|
const session = sessions?.sessions[effective.agentId];
|
|
779
|
-
|
|
780
|
-
|| activeRun.controlRequest?.state === "dispatching");
|
|
781
|
-
// An ensure call may reattach the already-admitted Run to its existing
|
|
774
|
+
// An ensure call may reattach the already-admitted Turn to its existing
|
|
782
775
|
// Conversation, but it may not manufacture a fresh Conversation without a
|
|
783
|
-
// pending managed Turn. Fresh replacement remains owned by
|
|
784
|
-
const mode =
|
|
776
|
+
// pending managed Turn. Fresh replacement remains owned by Turn dispatch.
|
|
777
|
+
const mode = activeTurn === null
|
|
785
778
|
? roleAgentSessionResumeMode(sessions, effective.agentId, effective)
|
|
786
|
-
:
|
|
779
|
+
: session?.nativeSessionId === undefined ? activeTurn.mode : "resume";
|
|
787
780
|
if (request.scope === "task" && mode === "resume"
|
|
788
781
|
&& (session?.nativeSessionId === undefined
|
|
789
782
|
|| session.nativeSessionId.trim().length === 0)) {
|
|
@@ -799,7 +792,7 @@ export function createRuntimeLifecycleDispatcher(store, schedulerStore, sessionH
|
|
|
799
792
|
effective,
|
|
800
793
|
workspace: effective.workspace.root,
|
|
801
794
|
...(managedWorkspace === undefined ? {} : { managedWorkspace }),
|
|
802
|
-
...(
|
|
795
|
+
...(activeTurn === null ? {} : { turnId: activeTurn.id }),
|
|
803
796
|
...(request.environment === undefined
|
|
804
797
|
? {}
|
|
805
798
|
: { environment: request.environment })
|
|
@@ -852,7 +845,7 @@ function resolveRuntimeDesiredEffective(store, request, role) {
|
|
|
852
845
|
});
|
|
853
846
|
}
|
|
854
847
|
function assertRuntimeLaunchRequestCurrent(store, request) {
|
|
855
|
-
let
|
|
848
|
+
let activeTurn = null;
|
|
856
849
|
if (request.owner.scope === "task") {
|
|
857
850
|
const task = store.getTask(request.owner.taskId);
|
|
858
851
|
if (task === null
|
|
@@ -860,10 +853,10 @@ function assertRuntimeLaunchRequestCurrent(store, request) {
|
|
|
860
853
|
|| task.executionGate.state !== "enabled") {
|
|
861
854
|
throw new Error(`Task is no longer active: ${request.owner.taskId}.`);
|
|
862
855
|
}
|
|
863
|
-
|
|
864
|
-
if (request.
|
|
865
|
-
&&
|
|
866
|
-
throw new Error(`Role
|
|
856
|
+
activeTurn = store.getActiveTurn(request.owner.taskId, request.owner.roleName);
|
|
857
|
+
if (request.turnId !== undefined
|
|
858
|
+
&& activeTurn?.id !== request.turnId) {
|
|
859
|
+
throw new Error(`Role Turn is no longer current: ${request.turnId}.`);
|
|
867
860
|
}
|
|
868
861
|
}
|
|
869
862
|
const role = request.owner.scope === "task"
|
|
@@ -875,8 +868,8 @@ function assertRuntimeLaunchRequestCurrent(store, request) {
|
|
|
875
868
|
const sessions = request.owner.scope === "task"
|
|
876
869
|
? store.getTaskRoleSessionSet(request.owner.taskId, request.owner.roleName)
|
|
877
870
|
: store.getGlobalRoleSessionSet(request.owner.roleName);
|
|
878
|
-
const expectedEffective = request.owner.scope === "task" && request.
|
|
879
|
-
?
|
|
871
|
+
const expectedEffective = request.owner.scope === "task" && request.turnId !== undefined
|
|
872
|
+
? activeTurn?.effective
|
|
880
873
|
: activeLiveRoleAgentSession(sessions)?.effective
|
|
881
874
|
?? currentDesiredEffective(store, request, role);
|
|
882
875
|
if (expectedEffective === undefined
|
|
@@ -887,7 +880,7 @@ function assertRuntimeLaunchRequestCurrent(store, request) {
|
|
|
887
880
|
throw new Error(`Role launch state changed: ${request.owner.roleName}.`);
|
|
888
881
|
}
|
|
889
882
|
if (request.owner.scope === "task" && request.managedWorkspace !== undefined) {
|
|
890
|
-
const expectedWorkspace =
|
|
883
|
+
const expectedWorkspace = activeTurn?.workspace
|
|
891
884
|
?? currentDesiredManagedWorkspace(store, request.owner.taskId, request.owner.roleName);
|
|
892
885
|
if (expectedWorkspace === undefined
|
|
893
886
|
|| !sameManagedWorkspaceIdentity(expectedWorkspace, request.managedWorkspace)) {
|
|
@@ -1129,7 +1122,7 @@ function providerTurnControlParams(params) {
|
|
|
1129
1122
|
return {
|
|
1130
1123
|
taskId: requiredParam(value.taskId),
|
|
1131
1124
|
roleName: requiredParam(value.roleName),
|
|
1132
|
-
|
|
1125
|
+
...(value.turnId === undefined ? {} : { turnId: requiredParam(value.turnId) }),
|
|
1133
1126
|
agentId: requiredParam(value.agentId),
|
|
1134
1127
|
launchId: requiredParam(value.launchId),
|
|
1135
1128
|
nativeSessionId: requiredParam(value.nativeSessionId),
|