@zq-silk/yui 0.13.10 → 0.14.1

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.
Files changed (230) hide show
  1. package/ARCHITECTURE.md +69 -428
  2. package/README.md +113 -300
  3. package/dist/agent/argumentPolicy.js +2 -1
  4. package/dist/agent/managedRuntimeEnvironment.js +2 -2
  5. package/dist/cli/commandCatalog.js +39 -77
  6. package/dist/cli/interactionCandidates.js +4 -4
  7. package/dist/cli/interactionPolicy.js +7 -10
  8. package/dist/cli/invocationRouter.js +1 -1
  9. package/dist/cli/updateCommand.js +6 -27
  10. package/dist/cli/updateOrchestrator.js +63 -484
  11. package/dist/cli/updatePorts.js +15 -333
  12. package/dist/cli/upgradeCommand.js +8 -65
  13. package/dist/cli.js +57 -242
  14. package/dist/commands/configCommands.js +11 -62
  15. package/dist/commands/executionAuditCommands.js +23 -23
  16. package/dist/commands/globalRoleCommands.js +2 -7
  17. package/dist/commands/jobCommands.js +1 -4
  18. package/dist/commands/projectCommands.js +5 -5
  19. package/dist/commands/releaseCommands.js +3 -10
  20. package/dist/commands/resourcesCommands.js +2 -2
  21. package/dist/commands/sessionCommands.js +2 -2
  22. package/dist/commands/taskActor.js +31 -39
  23. package/dist/commands/taskChangeSetCommands.js +5 -12
  24. package/dist/commands/taskCommands.js +464 -1249
  25. package/dist/commands/taskContextCommand.js +35 -36
  26. package/dist/commands/taskExecutionCommands.js +33 -46
  27. package/dist/commands/taskInputCommands.js +20 -42
  28. package/dist/commands/taskNextActionCommand.js +9 -9
  29. package/dist/commands/taskOverviewCommand.js +56 -95
  30. package/dist/commands/taskRoleRuntimeStatus.js +77 -107
  31. package/dist/commands/taskUpstreamCommands.js +3 -3
  32. package/dist/commands/taskWorkspaceCommands.js +7 -79
  33. package/dist/commands/telemetryCommands.js +32 -124
  34. package/dist/commands/workflowCommands.js +3 -3
  35. package/dist/completion/fileCompletionManager.js +1 -1
  36. package/dist/config/configCatalog.js +4 -6
  37. package/dist/config/yuiConfig.js +6 -43
  38. package/dist/context/dispatchContext.js +14 -30
  39. package/dist/context/roleSessionContext.js +2 -2
  40. package/dist/context/sessionBootstrapManifest.js +20 -3
  41. package/dist/context/{runContextPack.js → turnContextPack.js} +58 -73
  42. package/dist/context/turnInputContract.js +204 -0
  43. package/dist/context/wakeNotification.js +2 -2
  44. package/dist/controller/agentRuntimeObserver.js +18 -18
  45. package/dist/controller/clientRuntime.js +13 -20
  46. package/dist/controller/controller.js +89 -116
  47. package/dist/controller/fileSchedulerStoreAdapter.js +511 -742
  48. package/dist/controller/jobControl.js +17 -17
  49. package/dist/controller/resourceInventory.js +6 -6
  50. package/dist/controller/resourceInventoryLinux.js +38 -59
  51. package/dist/controller/resourceInventoryRpc.js +3 -3
  52. package/dist/controller/resourceInventoryWorker.js +1 -1
  53. package/dist/controller/runtime.js +46 -53
  54. package/dist/controller/runtimeEventInbox.js +23 -31
  55. package/dist/controller/runtimeEventProcessor.js +15 -15
  56. package/dist/controller/{runtimeHookRunFence.js → runtimeHookTurnFence.js} +87 -81
  57. package/dist/controller/runtimeLaunchCoordinator.js +3 -3
  58. package/dist/controller/runtimeObservationHook.js +11 -8
  59. package/dist/controller/sessionNotify.js +11 -11
  60. package/dist/controller/structuredProviderObservation.js +89 -16
  61. package/dist/coordination/workMailbox.js +309 -579
  62. package/dist/coordination/workMailboxQueue.js +5 -6
  63. package/dist/core/controllerClient.js +1 -91
  64. package/dist/core/controllerServer.js +3 -6
  65. package/dist/doctor/doctor.js +51 -314
  66. package/dist/execution/candidateConvergence.js +3 -3
  67. package/dist/execution/executionGroup.js +32 -33
  68. package/dist/execution/executionHealth.js +36 -38
  69. package/dist/execution/resourceBroker.js +12 -12
  70. package/dist/executor/agentExecutor.js +30 -265
  71. package/dist/executor/effectiveLaunch.js +9 -19
  72. package/dist/executor/executorRegistry.js +43 -17
  73. package/dist/executor/fileRoleLaunchPlanner.js +91 -57
  74. package/dist/executor/launchPlan.js +3 -3
  75. package/dist/executor/workspacePreflightClassification.js +16 -16
  76. package/dist/grant/capabilityGrant.js +20 -25
  77. package/dist/input/inputRequest.js +12 -12
  78. package/dist/integration/changeSet.js +9 -13
  79. package/dist/integration/changeSetManifest.js +1 -1
  80. package/dist/integration/gitIntegrationService.js +2 -2
  81. package/dist/integration/integrationQueueService.js +5 -21
  82. package/dist/integration/overlapDiagnostics.js +2 -2
  83. package/dist/interaction/operatorPresentation.js +1 -1
  84. package/dist/job/jobRunner.js +1 -1
  85. package/dist/lifecycle/canonicalLifecycleEvent.js +25 -25
  86. package/dist/lifecycle/{exactRunTerminalization.js → exactTurnTerminalization.js} +116 -232
  87. package/dist/message/message.js +6 -6
  88. package/dist/observability/executionAudit.js +74 -86
  89. package/dist/observability/faultClassification.js +7 -24
  90. package/dist/observability/orchestrationMetrics.js +5 -5
  91. package/dist/observability/runtimeIdentity.js +34 -101
  92. package/dist/profile/agentProfile.js +1 -1
  93. package/dist/release/cliHomeReleaseFence.js +3 -3
  94. package/dist/release/releaseWorkflowEngine.js +6 -10
  95. package/dist/release/releaseWorkflowPorts.js +2 -2
  96. package/dist/release/workflowFileLock.js +1 -1
  97. package/dist/repository/checkoutSwap.js +1 -1
  98. package/dist/repository/gitWorkspace.js +0 -13
  99. package/dist/repository/project.js +1 -1
  100. package/dist/repository/projectMaintenanceLock.js +1 -2
  101. package/dist/repository/taskBaseFreshness.js +13 -13
  102. package/dist/repository/taskWorkspaceCoordinator.js +21 -25
  103. package/dist/repository/taskWorkspaceIdentity.js +8 -21
  104. package/dist/repository/taskWorkspacePreparer.js +63 -620
  105. package/dist/resources/autoResourceGc.js +3 -3
  106. package/dist/resources/liveReferences.js +1 -1
  107. package/dist/resources/resourceGc.js +1 -1
  108. package/dist/resources/sqliteResourceRegistry.js +1 -1
  109. package/dist/review/reviewDecision.js +7 -7
  110. package/dist/review/reviewFindingLedger.js +1 -1
  111. package/dist/review/reviewOutcomeClassifier.js +35 -50
  112. package/dist/review/reviewRound.js +14 -25
  113. package/dist/review/reviewerAvailability.js +4 -4
  114. package/dist/review/taskFinalReviewContractResolution.js +41 -0
  115. package/dist/role/role.js +6 -3
  116. package/dist/runtime/agentDriver.js +4 -0
  117. package/dist/runtime/agentHost.js +151 -36
  118. package/dist/runtime/builtinAgentDrivers.js +79 -2
  119. package/dist/runtime/codexAppServerRuntime.js +122 -0
  120. package/dist/runtime/continuationManager.js +3 -5
  121. package/dist/runtime/exactControlPlane.js +17 -42
  122. package/dist/runtime/firstProgressAdvisory.js +1 -1
  123. package/dist/runtime/index.js +2 -2
  124. package/dist/runtime/launchDiagnostics.js +2 -2
  125. package/dist/runtime/lifecycleReservation.js +4 -4
  126. package/dist/runtime/processExitObservation.js +1 -1
  127. package/dist/runtime/processExitOutbox.js +0 -32
  128. package/dist/runtime/promptEnvelope.js +9 -9
  129. package/dist/runtime/providerContinuation.js +2 -2
  130. package/dist/runtime/providerContinuationReconciliationService.js +2 -2
  131. package/dist/runtime/providerRuntimeIdentity.js +76 -24
  132. package/dist/runtime/recentTurnIds.js +38 -0
  133. package/dist/runtime/runtimeContinuationProjection.js +2 -2
  134. package/dist/runtime/runtimeHealthPolicy.js +1 -1
  135. package/dist/runtime/runtimeObservation.js +58 -21
  136. package/dist/runtime/runtimeProjection.js +19 -27
  137. package/dist/runtime/sessionLaunchRequest.js +2 -2
  138. package/dist/runtime/structuredProviderHost.js +177 -12
  139. package/dist/runtime/tmuxAdapters.js +46 -11
  140. package/dist/scheduler/activeRoleTurnDelivery.js +245 -0
  141. package/dist/scheduler/leaderWakeupProcessor.js +147 -445
  142. package/dist/scheduler/operatorEvent.js +4 -6
  143. package/dist/scheduler/resourceQueueProjection.js +71 -0
  144. package/dist/scheduler/{roleRunLiveness.js → roleTurnLiveness.js} +24 -28
  145. package/dist/scheduler/{roleRunStall.js → roleTurnStall.js} +162 -247
  146. package/dist/scheduler/taskExecutionProjection.js +99 -99
  147. package/dist/scheduler/taskObservabilityProjection.js +10 -10
  148. package/dist/scheduler/taskWake.js +4 -4
  149. package/dist/scheduler/wakeReason.js +6 -10
  150. package/dist/scheduler/wakeupQueue.js +0 -9
  151. package/dist/setup/setupCommand.js +2 -2
  152. package/dist/storage/currentTaskStore.js +46 -0
  153. package/dist/storage/persistenceWorker.js +1 -1
  154. package/dist/storage/sqliteSchema.js +81 -485
  155. package/dist/storage/sqliteStore.js +206 -365
  156. package/dist/storage/storageSchema.js +1 -15
  157. package/dist/storage/storageVersions.js +8 -9
  158. package/dist/storage/storeRpc.js +10 -34
  159. package/dist/storage/taskStore.js +63 -3415
  160. package/dist/storage/upgrade/recordVersions.js +55 -149
  161. package/dist/storage/upgrade/upgradeOrchestrator.js +89 -1752
  162. package/dist/task/completionReadiness.js +5 -5
  163. package/dist/task/deliveryGuard.js +10 -10
  164. package/dist/task/nextAction.js +74 -77
  165. package/dist/task/task.js +2 -4
  166. package/dist/task/taskRecordReference.js +3 -3
  167. package/dist/task/taskRecordRetirement.js +1 -1
  168. package/dist/telemetry/sqliteTelemetryStore.js +53 -53
  169. package/dist/telemetry/telemetryConfig.js +14 -14
  170. package/dist/telemetry/telemetryWiring.js +8 -14
  171. package/dist/turn/turn.js +356 -0
  172. package/dist/{run/runIdentity.js → turn/turnIdentity.js} +4 -4
  173. package/dist/verification/gateArtifactStore.js +2 -2
  174. package/dist/web/assets/client/app.js +1 -1
  175. package/dist/web/assets/client/components.js +29 -54
  176. package/dist/web/assets/client/i18n.js +17 -37
  177. package/dist/web/assets/client/view.js +37 -37
  178. package/dist/web/assets/shell.js +1 -1
  179. package/dist/web/assets/styles/cards.js +5 -5
  180. package/dist/web/assets/styles/widgets.js +1 -2
  181. package/dist/web/webSnapshot.js +65 -60
  182. package/dist/workItem/workItem.js +11 -11
  183. package/dist/workspace/workItemChangeSetManager.js +1 -1
  184. package/docs/task-local-identity.md +5 -5
  185. package/i18n/README.zh-CN.md +42 -55
  186. package/package.json +1 -1
  187. package/skills/yui-leader/SKILL.md +85 -83
  188. package/skills/yui-operator/SKILL.md +16 -16
  189. package/skills/yui-reviewer/SKILL.md +10 -10
  190. package/skills/yui-runtime/SKILL.md +22 -24
  191. package/skills/yui-worker/SKILL.md +16 -18
  192. package/dist/context/runContextContract.js +0 -162
  193. package/dist/executor/turnCompletion.js +0 -126
  194. package/dist/review/taskFinalReviewContractEvent.js +0 -1
  195. package/dist/review/taskFinalReviewContractRebind.js +0 -367
  196. package/dist/run/agentRun.js +0 -356
  197. package/dist/run/rejectedYieldAttempt.js +0 -221
  198. package/dist/run/runControlRequest.js +0 -50
  199. package/dist/run/yieldReceipt.js +0 -65
  200. package/dist/runtime/turnCompletion.js +0 -3
  201. package/dist/scheduler/actionability.js +0 -323
  202. package/dist/scheduler/activeRoleRunDelivery.js +0 -868
  203. package/dist/scheduler/activeTaskProgress.js +0 -126
  204. package/dist/storage/compatibleTaskStore.js +0 -248
  205. package/dist/storage/migration/baseline.js +0 -78
  206. package/dist/storage/migration/classifier.js +0 -51
  207. package/dist/storage/migration/compatibleCodec.js +0 -53
  208. package/dist/storage/migration/engine.js +0 -147
  209. package/dist/storage/migration/index.js +0 -33
  210. package/dist/storage/migration/planner.js +0 -154
  211. package/dist/storage/migration/productionRegistry.js +0 -3931
  212. package/dist/storage/migration/registry.js +0 -169
  213. package/dist/storage/migration/report.js +0 -54
  214. package/dist/storage/migration/types.js +0 -31
  215. package/dist/storage/upgrade/homeClassification.js +0 -353
  216. package/dist/storage/upgrade/homeMigrationTarget.js +0 -595
  217. package/dist/storage/upgrade/migrationReceipt.js +0 -67
  218. package/dist/storage/upgrade/offlineUpgradeInventory.js +0 -453
  219. package/dist/storage/upgrade/productionMigrationRegistry.js +0 -6
  220. package/dist/storage/upgrade/pseudoLayoutRepair.js +0 -241
  221. package/dist/storage/upgrade/recordVersionScan.js +0 -176
  222. package/dist/storage/upgrade/sqliteFileSet.js +0 -50
  223. package/dist/storage/upgrade/sqliteMigrationTarget.js +0 -375
  224. package/dist/storage/upgrade/sqliteRecordMigrationTarget.js +0 -305
  225. package/dist/storage/upgrade/sqliteStateMigration.js +0 -926
  226. package/dist/storage/upgrade/switchProgress.js +0 -80
  227. package/dist/storage/upgrade/upgradeReceipt.js +0 -161
  228. package/dist/storage/upgradeCoordination.js +0 -187
  229. package/dist/storage/upgradeFence.js +0 -366
  230. package/dist/telemetry/telemetryCompaction.js +0 -252
@@ -97,7 +97,7 @@ export class FileRoleLaunchPlanner {
97
97
  || role === null) {
98
98
  throw new Error(`Task runtime is not current: ${input.taskId}/${input.roleName}.`);
99
99
  }
100
- const run = this.store.getActiveAgentRun(input.taskId, input.roleName);
100
+ const run = this.store.getActiveTurn(input.taskId, input.roleName);
101
101
  const sessions = this.store.getTaskRoleSessionSet(input.taskId, input.roleName);
102
102
  const session = sessions?.sessions[role.activeAgentId];
103
103
  if (session === undefined || session.launchId === undefined) {
@@ -111,11 +111,11 @@ export class FileRoleLaunchPlanner {
111
111
  agentId: session.agentId,
112
112
  adapterId: session.adapterId,
113
113
  workspace: effective.workspace.root,
114
- ...(run === null ? {} : { runId: run.id }),
114
+ ...(run === null ? {} : { turnId: run.id }),
115
115
  launchId: session.launchId,
116
116
  nativeSessionId: session.nativeSessionId
117
117
  });
118
- if (descriptor.runId !== input.runId
118
+ if (descriptor.turnId !== input.turnId
119
119
  || descriptor.launchId !== input.launchId
120
120
  || descriptor.nativeSessionId !== input.nativeSessionId
121
121
  || descriptor.agentId !== input.agentId
@@ -140,22 +140,22 @@ export class FileRoleLaunchPlanner {
140
140
  const role = this.store.getRole(input.taskId, input.roleName);
141
141
  if (role === null)
142
142
  throw new Error(`Role not found: ${input.taskId}/${input.roleName}.`);
143
- const activeRun = this.store.getActiveAgentRun(task.id, role.name);
144
- if (input.runId !== undefined && activeRun?.id !== input.runId) {
145
- throw new Error(`Role Run is no longer current: ${input.runId}.`);
143
+ const activeTurn = this.store.getActiveTurn(task.id, role.name);
144
+ if (input.turnId !== undefined && activeTurn?.id !== input.turnId) {
145
+ throw new Error(`Role Turn is no longer current: ${input.turnId}.`);
146
146
  }
147
- const runWorkspace = activeRun?.workspace;
147
+ const runWorkspace = activeTurn?.workspace;
148
148
  const main = this.store.getTaskWorkspace(task.id);
149
149
  // Quick Win (EXE-04/EXE-08): classify workspace preflight failures so
150
150
  // split-brain state is never reported as a transient Provider failure.
151
- const preflight = classifyWorkspacePreflight(this.store, task, input.roleName, activeRun === null ? null : { id: activeRun.id, workspace: activeRun.workspace }, this.#inspectWorkspacePhysicalState);
151
+ const preflight = classifyWorkspacePreflight(this.store, task, input.roleName, activeTurn === null ? null : { id: activeTurn.id, workspace: activeTurn.workspace }, this.#inspectWorkspacePhysicalState);
152
152
  if (preflight !== null) {
153
153
  throw new Error(formatWorkspacePreflightError(preflight));
154
154
  }
155
155
  const assignedWorkItem = this.store.listWorkItems(task.id).find((item) => item.assignee === role.name
156
156
  && !["completed", "failed", "retired"].includes(item.status));
157
- // A Run snapshot is authoritative for the live launch. In particular,
158
- // a reviewer Run must launch from its ReviewRound-owned workspace rather
157
+ // A Turn snapshot is authoritative for the live launch. In particular,
158
+ // a Reviewer Turn must launch from its ReviewRound-owned workspace rather
159
159
  // than falling back to the WorkItem Develop workspace. Without an
160
160
  // active snapshot, resolve the normal Role/WorkItem assignment.
161
161
  const workspace = runWorkspace !== undefined
@@ -183,8 +183,8 @@ export class FileRoleLaunchPlanner {
183
183
  && isolatedWorkItem !== null
184
184
  && (isolatedWorkItem.assignee === undefined || isolatedWorkItem.assignee === role.name)
185
185
  && !["completed", "failed", "retired"].includes(isolatedWorkItem.status)
186
- && (activeRun === null
187
- || activeRun.workItemId === workspace.owner.workItemId)
186
+ && (activeTurn === null
187
+ || activeTurn.workItemId === workspace.owner.workItemId)
188
188
  && sameWorkspaceProjects(workspace, task.projectBindings.map(({ projectId }) => projectId))
189
189
  && sameWritableProjects(workspace, isolatedWorkItem.writeProjectIds);
190
190
  const runScoped = runWorkspace !== undefined
@@ -192,29 +192,29 @@ export class FileRoleLaunchPlanner {
192
192
  && sameWorkspaceProjects(runWorkspace, task.projectBindings.map(({ projectId }) => projectId))
193
193
  && (runWorkspace.owner.type === "task"
194
194
  || (runWorkspace.owner.type === "work-item"
195
- && runWorkspace.owner.workItemId === activeRun?.workItemId)
195
+ && runWorkspace.owner.workItemId === activeTurn?.workItemId)
196
196
  || (runWorkspace.owner.type === "review-round"
197
- && activeRun?.purpose === "review"
198
- && runWorkspace.owner.reviewRoundId === activeRun.reviewRoundId)
197
+ && activeTurn?.purpose === "review"
198
+ && runWorkspace.owner.reviewRoundId === activeTurn.reviewRoundId)
199
199
  || (runWorkspace.owner.type === "execution-lane"
200
- && runWorkspace.owner.executionGroupId === activeRun?.executionGroupId
201
- && runWorkspace.owner.executionLaneId === activeRun?.executionLaneId
202
- && ((runWorkspace.owner.purpose === "review" && activeRun?.purpose === "review")
203
- || (runWorkspace.owner.purpose === "execution" && activeRun?.purpose === "execution"))));
200
+ && runWorkspace.owner.executionGroupId === activeTurn?.executionGroupId
201
+ && runWorkspace.owner.executionLaneId === activeTurn?.executionLaneId
202
+ && ((runWorkspace.owner.purpose === "review" && activeTurn?.purpose === "review")
203
+ || (runWorkspace.owner.purpose === "execution" && activeTurn?.purpose === "execution"))));
204
204
  if (!runScoped && !sharedMain && !isolated) {
205
205
  throw new Error(`Role workspace is not ready: ${input.taskId}/${input.roleName}.`);
206
206
  }
207
207
  }
208
208
  const sessionSet = this.store.getTaskRoleSessionSet(task.id, role.name);
209
- const resolvedEffective = activeRun?.effective
209
+ const resolvedEffective = activeTurn?.effective
210
210
  ?? activeLiveRoleAgentSession(sessionSet)?.effective
211
211
  ?? resolveTaskRoleEffectiveLaunch(this.store, role);
212
212
  if (input.effective !== undefined
213
213
  && !isDeepStrictEqual(resolvedEffective, input.effective)) {
214
- throw new Error(`Role launch effective Run snapshot changed: ${input.taskId}/${input.roleName}.`);
214
+ throw new Error(`Role launch effective Turn snapshot changed: ${input.taskId}/${input.roleName}.`);
215
215
  }
216
- if (activeRun !== null && input.effective === undefined) {
217
- throw new Error(`Role launch is missing the effective Run snapshot: ${input.taskId}/${input.roleName}.`);
216
+ if (activeTurn !== null && input.effective === undefined) {
217
+ throw new Error(`Role launch is missing the effective Turn snapshot: ${input.taskId}/${input.roleName}.`);
218
218
  }
219
219
  const effective = input.effective ?? resolvedEffective;
220
220
  const existing = sessionSet?.sessions[effective.agentId];
@@ -230,7 +230,7 @@ export class FileRoleLaunchPlanner {
230
230
  throw new Error(`Task Role still has a live Session: ${task.id}/${role.name}.`);
231
231
  }
232
232
  return this.#compile(role, input, { scope: "task", taskId: task.id }, resolveTaskRoleSessionTitle(input.mode === "resume" ? existing?.title : undefined, task, role.name), input.mode === "resume" && compatibleExisting ? existing.nativeSessionId : undefined, runWorkspace, effective, {
233
- purpose: activeRun?.purpose ?? "execution"
233
+ purpose: activeTurn?.purpose ?? "execution"
234
234
  });
235
235
  }
236
236
  planGlobalRole(input) {
@@ -280,7 +280,7 @@ export class FileRoleLaunchPlanner {
280
280
  const adapter = resolveAgentAdapter(binding.adapterId);
281
281
  const effectiveWorkspace = effective.workspace.root;
282
282
  const agentWorkspace = nativeAgentWorkspace(effective.workspace);
283
- if (adapter.id === "codex" && (owner.scope !== "task" || input.runId === undefined)) {
283
+ if (adapter.id === "codex" && (owner.scope !== "task" || input.turnId === undefined)) {
284
284
  const codexConfig = inspectCodexLaunchConfig({
285
285
  environment: launchEnvironment,
286
286
  workspace: agentWorkspace,
@@ -325,21 +325,21 @@ export class FileRoleLaunchPlanner {
325
325
  sessionManifestDigest: bootstrap.manifest.digest,
326
326
  sessionCliPath: bootstrap.sessionCliPath
327
327
  };
328
- const managedRun = owner.scope === "task" && input.runId !== undefined
329
- ? this.store.getAgentRun(owner.taskId, input.runId)
328
+ const managedRun = owner.scope === "task" && input.turnId !== undefined
329
+ ? this.store.getTurn(owner.taskId, input.turnId)
330
330
  : null;
331
331
  const driver = builtinAgentDriverRegistry().require(builtinDriverIdForAdapter(configured.adapterId));
332
- if (owner.scope === "task" && input.runId !== undefined) {
332
+ if (owner.scope === "task" && input.turnId !== undefined) {
333
333
  const admission = managedRuntimeAdmission(driver.capabilities);
334
334
  if (!admission.admitted) {
335
- throw new Error(`Agent Driver ${driver.id} cannot host managed Runs; missing capabilities: `
335
+ throw new Error(`Agent Driver ${driver.id} cannot host managed Turns; missing capabilities: `
336
336
  + admission.missing.join(", "));
337
337
  }
338
338
  }
339
339
  const roleConfig = binding.config.adapterId === "claude"
340
340
  && owner.scope === "task"
341
- && input.runId !== undefined
342
- ? managedClaudeControlPlaneConfig(binding.config, owner.taskId, managedRun?.workItemId, input.runId)
341
+ && input.turnId !== undefined
342
+ ? managedClaudeControlPlaneConfig(binding.config, owner.taskId, managedRun?.workItemId, input.turnId)
343
343
  : binding.config;
344
344
  const effectiveConfig = withNativeProjectDirectories(roleConfig, nativeAdditionalDirectories(effective.workspace, agentWorkspace));
345
345
  const compileInput = {
@@ -356,18 +356,18 @@ export class FileRoleLaunchPlanner {
356
356
  }
357
357
  // A previous attempt may have persisted a preallocated/discovered ID
358
358
  // before its receipt was committed. Reuse that fixed session rather than
359
- // allocating a second native session for the same durable AgentRun.
359
+ // allocating a second native session for the same durable Turn.
360
360
  const resumeNativeSessionId = input.mode === "resume"
361
361
  ? requireText(input.nativeSessionId, "Native session id")
362
362
  : knownNativeSessionId;
363
363
  const launchMode = resumeNativeSessionId === undefined
364
364
  ? "new"
365
365
  : "resume";
366
- const managedControl = owner.scope === "task" && input.runId !== undefined;
366
+ const managedControl = owner.scope === "task" && input.turnId !== undefined;
367
367
  const managedProviderEnvironment = managedControl
368
368
  && configured.adapterId === "codex"
369
369
  ? {
370
- // Managed Codex Runs are non-interactive. Use the Codex execution
370
+ // Managed Codex Turns are non-interactive. Use the Codex execution
371
371
  // identity for provider requests while clientInfo still identifies Yui.
372
372
  CODEX_INTERNAL_ORIGINATOR_OVERRIDE: "codex_exec"
373
373
  }
@@ -409,17 +409,17 @@ export class FileRoleLaunchPlanner {
409
409
  }
410
410
  if (binding.adapterId === "codex") {
411
411
  // Global/interactive Codex sessions still use notify for presentation.
412
- // Managed Runs receive lifecycle facts through their ordinary App Server
412
+ // Managed Turns receive lifecycle facts through their ordinary App Server
413
413
  // subscription, avoiding a second Hook channel for the same Turn.
414
- if (owner.scope !== "task" || input.runId === undefined) {
414
+ if (owner.scope !== "task" || input.turnId === undefined) {
415
415
  args = addCodexSessionNotify(args, launchMode, this.#cliPath);
416
416
  }
417
- // Managed Codex Runs use disposable proxy clients against the shared
417
+ // Managed Codex Turns use disposable proxy clients against the shared
418
418
  // native App Server. Their Session Manifest points at the Yui Skills;
419
419
  // no Yui Hook config is installed.
420
- if (owner.scope === "task" && input.runId !== undefined) {
420
+ if (owner.scope === "task" && input.turnId !== undefined) {
421
421
  if (managedRun === null || managedRun.status !== "active") {
422
- throw new Error(`Managed Codex Run is no longer active: ${input.runId}.`);
422
+ throw new Error(`Managed Codex Turn is no longer active: ${input.turnId}.`);
423
423
  }
424
424
  }
425
425
  session = launchMode === "resume"
@@ -439,9 +439,9 @@ export class FileRoleLaunchPlanner {
439
439
  }
440
440
  const managedClaudeRun = binding.adapterId === "claude"
441
441
  && owner.scope === "task"
442
- && input.runId !== undefined;
442
+ && input.turnId !== undefined;
443
443
  if (managedClaudeRun && (managedRun === null || managedRun.status !== "active")) {
444
- throw new Error(`Managed Claude Run is no longer active: ${input.runId}.`);
444
+ throw new Error(`Managed Claude Turn is no longer active: ${input.turnId}.`);
445
445
  }
446
446
  const runtimeDescriptor = owner.scope === "task"
447
447
  ? createExactTaskRuntimeDescriptor({
@@ -451,7 +451,7 @@ export class FileRoleLaunchPlanner {
451
451
  agentId: configured.id,
452
452
  adapterId: configured.adapterId,
453
453
  workspace: effectiveWorkspace,
454
- ...(input.runId === undefined ? {} : { runId: input.runId }),
454
+ ...(input.turnId === undefined ? {} : { turnId: input.turnId }),
455
455
  ...(input.launchId === undefined ? {} : { launchId: input.launchId }),
456
456
  ...(session === null
457
457
  ? {}
@@ -474,15 +474,15 @@ export class FileRoleLaunchPlanner {
474
474
  }
475
475
  // Session lifecycle and Turn submission are separate atomic operations.
476
476
  // Planning only starts or restores the exact native Session; delivery
477
- // submits the Run input after that Session fact is durable.
477
+ // submits the Turn input after that Session fact is durable.
478
478
  if (managedControl && (managedRun === null || managedRun.status !== "active")) {
479
- throw new Error(`Managed Run is no longer active: ${input.runId}.`);
479
+ throw new Error(`Managed Turn is no longer active: ${input.turnId}.`);
480
480
  }
481
481
  const providerAuthority = managedControl
482
482
  ? this.#providerAuthorityForLaunch(owner.taskId, role.name, input.launchId, input.mode)
483
483
  : undefined;
484
484
  const providerOwnedTurn = managedControl && input.mode === "resume"
485
- ? this.#providerOwnedTurnForLaunch(owner.taskId, role.name, input.runId)
485
+ ? this.#providerOwnedTurnForLaunch(owner.taskId, role.name, input.turnId)
486
486
  : undefined;
487
487
  const providerNativeSessionId = binding.adapterId === "claude"
488
488
  ? preallocatedNativeSessionId
@@ -557,7 +557,7 @@ export class FileRoleLaunchPlanner {
557
557
  ...(input.launchId === undefined
558
558
  ? {}
559
559
  : { YUI_LAUNCH_ID: input.launchId }),
560
- ...(input.runId === undefined ? {} : { YUI_RUN_ID: input.runId }),
560
+ ...(input.turnId === undefined ? {} : { YUI_TURN_ID: input.turnId }),
561
561
  ...(session === null
562
562
  ? {}
563
563
  : { YUI_NATIVE_SESSION_ID: session.nativeSessionId })
@@ -567,7 +567,13 @@ export class FileRoleLaunchPlanner {
567
567
  const scopedLaunch = owner.scope === "task"
568
568
  ? this.#applyWorkspaceScope(owner.taskId, role, launch, workspaceOverride)
569
569
  : launch;
570
- const ordinaryConversationLaunch = withCodexThreadEnvironment(scopedLaunch);
570
+ const sharedDaemonLaunch = owner.scope === "global" && configured.adapterId === "codex"
571
+ ? {
572
+ ...scopedLaunch,
573
+ args: addCodexSharedDaemonRemote(scopedLaunch.args, launchMode, scopedLaunch.env)
574
+ }
575
+ : scopedLaunch;
576
+ const ordinaryConversationLaunch = withCodexThreadEnvironment(sharedDaemonLaunch);
571
577
  return {
572
578
  role: {
573
579
  name: role.name,
@@ -617,17 +623,18 @@ export class FileRoleLaunchPlanner {
617
623
  }
618
624
  throw new Error(`Provider writer authority is unknown: ${taskId}/${roleName}.`);
619
625
  }
620
- #providerOwnedTurnForLaunch(taskId, roleName, runId) {
626
+ #providerOwnedTurnForLaunch(taskId, roleName, turnId) {
621
627
  const binding = this.store.getTaskRoleSessionSet(taskId, roleName)?.providerBinding;
622
- if (binding === null || binding === undefined || binding.turn?.runId !== runId)
628
+ if (binding === null || binding === undefined
629
+ || binding.turn?.turnId !== turnId)
623
630
  return undefined;
624
631
  const turn = binding.turn;
625
- if (turn === null || !["accepted", "running"].includes(turn.status))
632
+ if (turn === null || turn.status !== "accepted")
626
633
  return undefined;
627
- if (turn.turnId === undefined) {
634
+ if (turn.nativeTurnId === undefined) {
628
635
  throw new Error(`Active Provider Turn has no native identity: ${taskId}/${roleName}.`);
629
636
  }
630
- return { attemptId: turn.attemptId, turnId: turn.turnId };
637
+ return { attemptId: turn.attemptId, turnId: turn.nativeTurnId };
631
638
  }
632
639
  #applyWorkspaceScope(taskId, role, launch, workspaceOverride) {
633
640
  const workspace = workspaceOverride
@@ -748,17 +755,16 @@ function ensureClaudeLifecyclePlugin(home, cliPath) {
748
755
  }, null, 2)}\n`);
749
756
  return root;
750
757
  }
751
- function managedClaudeControlPlaneConfig(config, taskId, workItemId, runId) {
758
+ function managedClaudeControlPlaneConfig(config, taskId, workItemId, turnId) {
752
759
  if (config.permission.strategy !== "configured")
753
760
  return config;
754
761
  const managed = [
755
- `Bash(yui task run context ${taskId}/${runId}:*)`,
762
+ `Bash(yui task turn context ${taskId}/${turnId}:*)`,
756
763
  `Bash(yui --json task context ${taskId})`,
757
764
  `Bash(yui --json task work list ${taskId})`,
758
765
  ...(workItemId === undefined
759
766
  ? []
760
- : [`Bash(yui --json task work show ${workItemId})`]),
761
- `Bash(yui task run yield ${runId} --summary-file -:*)`
767
+ : [`Bash(yui --json task work show ${workItemId})`])
762
768
  ];
763
769
  const existing = (config.permission.allowedTools ?? [])
764
770
  .filter((rule) => !isManagedYuiBashRule(rule));
@@ -830,10 +836,38 @@ function addCodexSessionNotify(args, mode, cliPath) {
830
836
  }
831
837
  return [...args.slice(0, -2), ...managed, ...args.slice(-2)];
832
838
  }
839
+ /**
840
+ * Global Codex keeps its native TUI, but the TUI is only a client attachment
841
+ * to the user's shared App Server. The daemon owns the Thread and its writer,
842
+ * so Desktop can open the same Yui-created Thread without a second rollout
843
+ * writer or a Yui-specific takeover.
844
+ */
845
+ export function addCodexSharedDaemonRemote(args, mode, environment = {}) {
846
+ const sessionEnvironment = Object.fromEntries(Object.entries(environment).filter(([name]) => name.startsWith("YUI_")));
847
+ const remote = [
848
+ "--remote",
849
+ "unix://",
850
+ ...(Object.keys(sessionEnvironment).length === 0
851
+ ? []
852
+ : ["--config", codexShellEnvironmentConfig(sessionEnvironment)])
853
+ ];
854
+ if (mode === "new")
855
+ return [...args, ...remote];
856
+ if (args.length < 2 || args.at(-2) !== "resume") {
857
+ throw new Error("Codex resume launch shape is invalid.");
858
+ }
859
+ return [...args.slice(0, -2), ...remote, ...args.slice(-2)];
860
+ }
861
+ function codexShellEnvironmentConfig(environment) {
862
+ const entries = Object.entries(environment)
863
+ .sort(([left], [right]) => left.localeCompare(right))
864
+ .map(([name, value]) => `${JSON.stringify(name)}=${JSON.stringify(value)}`);
865
+ return `shell_environment_policy.set={${entries.join(",")}}`;
866
+ }
833
867
  /**
834
868
  * Codex 0.145 discovers lifecycle hooks from its effective config. Keep Yui's
835
869
  * two handlers invocation-local: this avoids mutating CODEX_HOME or the Task
836
- * workspace, while the exact launch environment supplies the durable Run fence.
870
+ * workspace, while the exact launch environment supplies the durable Turn fence.
837
871
  */
838
872
  function codexLifecycleHooksConfig(cliPath) {
839
873
  const command = [
@@ -1,13 +1,13 @@
1
- export function withYuiRunEnvironment(launch, yuiHome, role, run, nativeSessionId, nativeSessionRoot) {
1
+ export function withYuiTurnEnvironment(launch, yuiHome, role, turn, nativeSessionId, nativeSessionRoot) {
2
2
  return {
3
3
  ...launch,
4
4
  env: {
5
5
  ...launch.env,
6
6
  YUI_HOME: yuiHome,
7
- YUI_TASK_ID: run.taskId,
7
+ YUI_TASK_ID: turn.taskId,
8
8
  YUI_ROLE: role.name,
9
9
  YUI_AGENT_ID: role.activeAgentId ?? role.agent ?? "",
10
- YUI_RUN_ID: run.id,
10
+ YUI_TURN_ID: turn.id,
11
11
  YUI_WORKSPACE: role.workspace,
12
12
  ...(nativeSessionRoot === undefined ? {} : { YUI_NATIVE_SESSION_ROOT: nativeSessionRoot }),
13
13
  ...(nativeSessionId === undefined ? {} : { YUI_NATIVE_SESSION_ID: nativeSessionId })
@@ -20,7 +20,7 @@ function ownerLabel(owner) {
20
20
  * This is a read-only check: it never mutates Git state, creates Sessions, or
21
21
  * writes recovery records.
22
22
  */
23
- export function classifyWorkspacePreflight(store, task, roleName, activeRun, inspectPhysical) {
23
+ export function classifyWorkspacePreflight(store, task, roleName, activeTurn, inspectPhysical) {
24
24
  const main = store.getTaskWorkspace(task.id);
25
25
  const bindings = task.projectBindings.map(({ projectId, directory }) => ({ projectId, directory }));
26
26
  // 1. Owner validation: the Task main workspace must be a durable,
@@ -41,26 +41,26 @@ export function classifyWorkspacePreflight(store, task, roleName, activeRun, ins
41
41
  actual
42
42
  };
43
43
  }
44
- // 2. Run snapshot validation: when the active Run carries a workspace
44
+ // 2. Turn snapshot validation: when the active Turn carries a workspace
45
45
  // snapshot its launch-stable identity must match the durable
46
46
  // ManagedWorkspace. Audit timestamps may change without changing the
47
- // owner, root, or Project entries authorized for the Run.
48
- if (activeRun?.workspace !== undefined) {
49
- const durableRunWorkspace = store.getManagedWorkspace(activeRun.workspace.owner);
47
+ // owner, root, or Project entries authorized for the Turn.
48
+ if (activeTurn?.workspace !== undefined) {
49
+ const durableRunWorkspace = store.getManagedWorkspace(activeTurn.workspace.owner);
50
50
  if (durableRunWorkspace === null
51
- || !sameManagedWorkspaceIdentity(durableRunWorkspace, activeRun.workspace)) {
51
+ || !sameManagedWorkspaceIdentity(durableRunWorkspace, activeTurn.workspace)) {
52
52
  const diff = [];
53
53
  if (durableRunWorkspace === null) {
54
- diff.push(`durable ManagedWorkspace for owner ${JSON.stringify(activeRun.workspace.owner)} is missing`);
54
+ diff.push(`durable ManagedWorkspace for owner ${JSON.stringify(activeTurn.workspace.owner)} is missing`);
55
55
  }
56
56
  else {
57
- if (durableRunWorkspace.root !== activeRun.workspace.root) {
58
- diff.push(`root: durable=${durableRunWorkspace.root} run=${activeRun.workspace.root}`);
57
+ if (durableRunWorkspace.root !== activeTurn.workspace.root) {
58
+ diff.push(`root: durable=${durableRunWorkspace.root} run=${activeTurn.workspace.root}`);
59
59
  }
60
- if (durableRunWorkspace.entries.length !== activeRun.workspace.entries.length) {
61
- diff.push(`entries: durable=${durableRunWorkspace.entries.length} run=${activeRun.workspace.entries.length}`);
60
+ if (durableRunWorkspace.entries.length !== activeTurn.workspace.entries.length) {
61
+ diff.push(`entries: durable=${durableRunWorkspace.entries.length} run=${activeTurn.workspace.entries.length}`);
62
62
  }
63
- for (const [index, entry] of activeRun.workspace.entries.entries()) {
63
+ for (const [index, entry] of activeTurn.workspace.entries.entries()) {
64
64
  const durableEntry = durableRunWorkspace.entries[index];
65
65
  if (durableEntry === undefined) {
66
66
  diff.push(`entry[${index}] ${entry.projectId}: missing from durable`);
@@ -76,10 +76,10 @@ export function classifyWorkspacePreflight(store, task, roleName, activeRun, ins
76
76
  }
77
77
  return {
78
78
  kind: "workspace-stale",
79
- reason: `Role Run workspace is not the durable owner: ${task.id}/${roleName}.`,
79
+ reason: `Role Turn workspace is not the durable owner: ${task.id}/${roleName}.`,
80
80
  taskId: task.id,
81
81
  roleName,
82
- runId: activeRun.id,
82
+ turnId: activeTurn.id,
83
83
  diff
84
84
  };
85
85
  }
@@ -89,7 +89,7 @@ export function classifyWorkspacePreflight(store, task, roleName, activeRun, ins
89
89
  // from that immutable boundary. A reset/repoint outside the lineage is
90
90
  // physical drift and must fail before Provider launch.
91
91
  if (inspectPhysical !== undefined) {
92
- const effective = activeRun?.workspace ?? main;
92
+ const effective = activeTurn?.workspace ?? main;
93
93
  for (const entry of effective.entries) {
94
94
  const physical = inspectPhysical(entry);
95
95
  if (!physical.recordedBaseIsAncestor) {
@@ -121,7 +121,7 @@ export function formatWorkspacePreflightError(classification) {
121
121
  + "` to inspect the binding, ManagedWorkspace, and physical HEAD.");
122
122
  break;
123
123
  case "workspace-stale":
124
- lines.push(` Run ${classification.runId} snapshot differs from the durable ManagedWorkspace:`);
124
+ lines.push(` Turn ${classification.turnId} snapshot differs from the durable ManagedWorkspace:`);
125
125
  for (const entry of classification.diff) {
126
126
  lines.push(` - ${entry}`);
127
127
  }
@@ -1,6 +1,6 @@
1
1
  import { requireIdentity, requireText, requireTimestamp, requirePositiveInteger } from "../domain/validation.js";
2
2
  import { validateTaskRecordReference } from "../task/taskRecordReference.js";
3
- export const CAPABILITY_GRANT_SCHEMA_VERSION = 1;
3
+ export const CAPABILITY_GRANT_SCHEMA_VERSION = 2;
4
4
  const IRREVERSIBILITY_RANK = {
5
5
  none: 0,
6
6
  reversible: 1,
@@ -19,6 +19,7 @@ export function createCapabilityGrant(id, taskId, input, now) {
19
19
  ...(input.expiresAt === undefined ? {} : { expiresAt: requireTimestamp(input.expiresAt, "Capability grant expiresAt") }),
20
20
  ...(input.maxUses === undefined ? {} : { maxUses: requirePositiveInteger(input.maxUses, "Capability grant maxUses") }),
21
21
  usesUsed: 0,
22
+ useReservations: Object.freeze([]),
22
23
  irreversibilityCeiling: normalizeCeiling(input.irreversibilityCeiling),
23
24
  createdAt: timestamp,
24
25
  updatedAt: timestamp
@@ -61,16 +62,14 @@ export function checkGrant(grant, request, now, options) {
61
62
  }
62
63
  return { allowed: true };
63
64
  }
64
- /** The reservation keys recorded on a grant, or an empty list for legacy grants. */
65
+ /** The reservation keys recorded on a grant. */
65
66
  export function grantUseReservations(grant) {
66
- return grant.useReservations ?? [];
67
+ return grant.useReservations;
67
68
  }
68
69
  /**
69
70
  * Record one use against the grant, failing closed once the limit is reached.
70
- * When `reservationKey` is supplied it is appended to the grant's durable
71
- * reservations so a later resume can recognize this exact attempt and avoid a
72
- * double charge. A use recorded without a key (legacy callers) leaves the
73
- * reservation list untouched.
71
+ * The reservation key is appended to the grant's durable reservations so a
72
+ * later resume recognizes this exact attempt and avoids a double charge.
74
73
  */
75
74
  export function recordGrantUse(grant, now, reservationKey) {
76
75
  validateCapabilityGrant(grant);
@@ -80,9 +79,7 @@ export function recordGrantUse(grant, now, reservationKey) {
80
79
  return validateCapabilityGrant({
81
80
  ...grant,
82
81
  usesUsed: grant.usesUsed + 1,
83
- ...(reservationKey === undefined
84
- ? {}
85
- : { useReservations: Object.freeze([...grantUseReservations(grant), reservationKey]) }),
82
+ useReservations: Object.freeze([...grantUseReservations(grant), reservationKey]),
86
83
  updatedAt: now.toISOString()
87
84
  });
88
85
  }
@@ -105,7 +102,7 @@ export function revokeGrant(grant, revokedBy, now) {
105
102
  }
106
103
  export function validateCapabilityGrant(grant) {
107
104
  if (grant.schemaVersion !== CAPABILITY_GRANT_SCHEMA_VERSION) {
108
- throw new Error("Capability grant must use schemaVersion 1.");
105
+ throw new Error("Capability grant must use schemaVersion 2.");
109
106
  }
110
107
  validateTaskRecordReference({ taskId: grant.taskId, localId: grant.id }, "capabilityGrant");
111
108
  requireIdentity(grant.taskId, "Task id");
@@ -125,22 +122,20 @@ export function validateCapabilityGrant(grant) {
125
122
  if (grant.maxUses !== undefined && grant.usesUsed > grant.maxUses) {
126
123
  throw new Error("Capability grant usesUsed cannot exceed maxUses.");
127
124
  }
128
- if (grant.useReservations !== undefined) {
129
- if (!Array.isArray(grant.useReservations)) {
130
- throw new Error("Capability grant useReservations must be an array.");
131
- }
132
- for (const key of grant.useReservations) {
133
- if (typeof key !== "string" || key.length === 0) {
134
- throw new Error("Capability grant useReservations must be non-empty strings.");
135
- }
136
- }
137
- if (new Set(grant.useReservations).size !== grant.useReservations.length) {
138
- throw new Error("Capability grant useReservations must be unique.");
139
- }
140
- if (grant.useReservations.length > grant.usesUsed) {
141
- throw new Error("Capability grant useReservations cannot outnumber usesUsed.");
125
+ if (!Array.isArray(grant.useReservations)) {
126
+ throw new Error("Capability grant useReservations must be an array.");
127
+ }
128
+ for (const key of grant.useReservations) {
129
+ if (typeof key !== "string" || key.length === 0) {
130
+ throw new Error("Capability grant useReservations must be non-empty strings.");
142
131
  }
143
132
  }
133
+ if (new Set(grant.useReservations).size !== grant.useReservations.length) {
134
+ throw new Error("Capability grant useReservations must be unique.");
135
+ }
136
+ if (grant.useReservations.length > grant.usesUsed) {
137
+ throw new Error("Capability grant useReservations cannot outnumber usesUsed.");
138
+ }
144
139
  normalizeCeiling(grant.irreversibilityCeiling);
145
140
  if ((grant.revokedAt === undefined) !== (grant.revokedBy === undefined)) {
146
141
  throw new Error("Capability grant revocation requires both revokedAt and revokedBy.");
@@ -14,7 +14,7 @@ export function createInputRequest(id, taskId, requester, input, now) {
14
14
  const timestamp = requireDate(now, "Input request creation time");
15
15
  const choices = normalizeChoices(input.choices);
16
16
  return validateInputRequest({
17
- schemaVersion: 2,
17
+ schemaVersion: 3,
18
18
  id: requireIdentity(id, "Input request id"),
19
19
  taskId: requireIdentity(taskId, "Input request Task id"),
20
20
  requester: normalizeRequester(requester),
@@ -72,12 +72,12 @@ export function validateInputRequest(value) {
72
72
  "schemaVersion", "id", "taskId", "requester", "question", "choices",
73
73
  "blockedRefs", "policy", "status", "createdAt", "updatedAt", ...terminalField
74
74
  ], "Input request");
75
- if (request.schemaVersion !== 2)
76
- throw new Error("Input request must use schemaVersion 2.");
75
+ if (request.schemaVersion !== 3)
76
+ throw new Error("Input request must use schemaVersion 3.");
77
77
  const choices = validateChoices(request.choices);
78
78
  const createdAt = requireTimestamp(request.createdAt, "Input request createdAt");
79
79
  const base = {
80
- schemaVersion: 2,
80
+ schemaVersion: 3,
81
81
  id: requireIdentity(request.id, "Input request id"),
82
82
  taskId: requireIdentity(request.taskId, "Input request Task id"),
83
83
  requester: normalizeRequester(request.requester),
@@ -91,10 +91,10 @@ export function validateInputRequest(value) {
91
91
  validateTaskRecordReference({ taskId: base.taskId, localId: base.id }, "inputRequest");
92
92
  validateTaskRecordReference({
93
93
  taskId: base.requester.taskId,
94
- localId: base.requester.runId
95
- }, "agentRun");
94
+ localId: base.requester.turnId
95
+ }, "turn");
96
96
  for (const reference of base.blockedRefs) {
97
- validateTaskRecordReference({ taskId: reference.taskId, localId: reference.id }, reference.type === "run" ? "agentRun" : "workItem");
97
+ validateTaskRecordReference({ taskId: reference.taskId, localId: reference.id }, reference.type === "turn" ? "turn" : "workItem");
98
98
  }
99
99
  if (base.requester.taskId !== base.taskId
100
100
  || base.blockedRefs.some(({ taskId }) => taskId !== base.taskId)) {
@@ -175,8 +175,8 @@ function normalizeBlockedRefs(value) {
175
175
  const references = value.map((reference) => {
176
176
  const item = record(reference, "Input blocked reference");
177
177
  exact(item, ["type", "taskId", "id"], "Input blocked reference");
178
- if (item.type !== "work-item" && item.type !== "run") {
179
- throw new Error("Input blocked reference type must be work-item or run.");
178
+ if (item.type !== "work-item" && item.type !== "turn") {
179
+ throw new Error("Input blocked reference type must be work-item or turn.");
180
180
  }
181
181
  return {
182
182
  type: item.type,
@@ -224,15 +224,15 @@ function validateBlockedRefs(value) {
224
224
  function normalizeRequester(value) {
225
225
  const requester = record(value, "Input requester");
226
226
  exact(requester, requester.nativeSessionId === undefined
227
- ? ["taskId", "roleName", "agentId", "runId"]
228
- : ["taskId", "roleName", "agentId", "runId", "nativeSessionId"], "Input requester");
227
+ ? ["taskId", "roleName", "agentId", "turnId"]
228
+ : ["taskId", "roleName", "agentId", "turnId", "nativeSessionId"], "Input requester");
229
229
  if (requester.roleName !== "leader")
230
230
  throw new Error("Input requester must be the Task Leader.");
231
231
  return {
232
232
  taskId: requireIdentity(requester.taskId, "Input requester Task id"),
233
233
  roleName: "leader",
234
234
  agentId: requireIdentity(requester.agentId, "Input requester Agent id"),
235
- runId: requireIdentity(requester.runId, "Input requester Run id"),
235
+ turnId: requireIdentity(requester.turnId, "Input requester Turn id"),
236
236
  ...(requester.nativeSessionId === undefined
237
237
  ? {}
238
238
  : { nativeSessionId: requireIdentity(requester.nativeSessionId, "Input requester native session id") })
@@ -3,18 +3,16 @@ import { validateTaskRecordReference } from "../task/taskRecordReference.js";
3
3
  import { validateChangeSetManifest } from "./changeSetManifest.js";
4
4
  export function createWorkItemChangeSet(input, now) {
5
5
  return validateChangeSet({
6
- schemaVersion: 3,
6
+ schemaVersion: 4,
7
7
  ...input,
8
8
  changedPaths: [...input.changedPaths],
9
- ...(input.manifest === undefined
10
- ? {}
11
- : { manifest: validateChangeSetManifest(input.manifest) }),
9
+ manifest: validateChangeSetManifest(input.manifest),
12
10
  createdAt: now.toISOString()
13
11
  });
14
12
  }
15
13
  export function validateChangeSet(changeSet) {
16
- if (changeSet.schemaVersion !== 3)
17
- throw new Error("ChangeSet must use schemaVersion 3.");
14
+ if (changeSet.schemaVersion !== 4)
15
+ throw new Error("ChangeSet must use schemaVersion 4.");
18
16
  validateTaskRecordReference({ taskId: changeSet.taskId, localId: changeSet.id }, "changeSet");
19
17
  validateTaskRecordReference({
20
18
  taskId: changeSet.taskId,
@@ -28,13 +26,11 @@ export function validateChangeSet(changeSet) {
28
26
  }
29
27
  requireText(changeSet.branch, "ChangeSet branch");
30
28
  normalizedUniqueText(changeSet.changedPaths, "ChangeSet path");
31
- if (changeSet.manifest !== undefined) {
32
- const manifest = validateChangeSetManifest(changeSet.manifest);
33
- const changed = new Set(changeSet.changedPaths);
34
- for (const deleted of manifest.deletedPaths) {
35
- if (!changed.has(deleted)) {
36
- throw new Error(`ChangeSet manifest deleted path is not a changed path: ${deleted}.`);
37
- }
29
+ const manifest = validateChangeSetManifest(changeSet.manifest);
30
+ const changed = new Set(changeSet.changedPaths);
31
+ for (const deleted of manifest.deletedPaths) {
32
+ if (!changed.has(deleted)) {
33
+ throw new Error(`ChangeSet manifest deleted path is not a changed path: ${deleted}.`);
38
34
  }
39
35
  }
40
36
  requireTimestamp(changeSet.createdAt, "ChangeSet createdAt");