@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
@@ -0,0 +1,356 @@
1
+ import { validateEffectiveLaunchSnapshot } from "../executor/effectiveLaunch.js";
2
+ import { validateTaskRecordReference } from "../task/taskRecordReference.js";
3
+ import { validateManagedWorkspace } from "../worktree/managedWorkspace.js";
4
+ import { createTurnInput, createTurnInputEnvelope, validateTurnInput } from "../context/turnInputContract.js";
5
+ export function createTurn(id, taskId, roleName, mode, input, now, context) {
6
+ if (mode !== "new" && mode !== "resume") {
7
+ throw new Error(`Turn dispatch mode is invalid: ${mode}.`);
8
+ }
9
+ const timestamp = now.toISOString();
10
+ const normalizedInput = validateTurnInput(input);
11
+ return {
12
+ schemaVersion: 1,
13
+ id: requireSafeIdentity(id, "Turn id"),
14
+ taskId: requireSafeIdentity(taskId, "Task id"),
15
+ roleName: requireSafeIdentity(roleName, "Role name"),
16
+ mode,
17
+ inputs: [turnInputRecord(normalizedInput, 1, timestamp)],
18
+ purpose: context.purpose ?? "execution",
19
+ ...(context.workItemId === undefined
20
+ ? {}
21
+ : { workItemId: requireSafeIdentity(context.workItemId, "Work item id") }),
22
+ ...(context.reviewRoundId === undefined
23
+ ? {}
24
+ : { reviewRoundId: requireSafeIdentity(context.reviewRoundId, "ReviewRound id") }),
25
+ ...(context.executionGroupId === undefined
26
+ ? {}
27
+ : { executionGroupId: requireSafeIdentity(context.executionGroupId, "ExecutionGroup id") }),
28
+ ...(context.executionLaneId === undefined
29
+ ? {}
30
+ : { executionLaneId: requireSafeIdentity(context.executionLaneId, "ExecutionLane id") }),
31
+ ...(context.workspace === undefined
32
+ ? {}
33
+ : { workspace: validateManagedWorkspace(context.workspace) }),
34
+ effective: validateEffectiveLaunchSnapshot(context.effective),
35
+ status: "active",
36
+ createdAt: timestamp,
37
+ updatedAt: timestamp
38
+ };
39
+ }
40
+ export function isActiveTurn(run) {
41
+ return run.status === "active";
42
+ }
43
+ /** Binds a freshly created, not-yet-persisted Turn to its frozen Context. */
44
+ export function withTurnContextSnapshot(run, snapshot, deltaRefIds = []) {
45
+ const initial = run.inputs[0];
46
+ if (run.status !== "active" || initial.input.contextSnapshotRef !== undefined) {
47
+ throw new Error(`Cannot replace the Turn Context Snapshot: ${run.id}.`);
48
+ }
49
+ const input = createTurnInput({
50
+ ...initial.input,
51
+ contextSnapshotRef: snapshot,
52
+ deltaRefIds
53
+ });
54
+ return validateTurn(Object.freeze({
55
+ ...run,
56
+ inputs: [turnInputRecord(input, 1, initial.submittedAt), ...run.inputs.slice(1)]
57
+ }));
58
+ }
59
+ export function appendTurnInput(run, input, now) {
60
+ validateTurn(run);
61
+ if (run.status !== "active")
62
+ throw new Error(`Cannot append input to terminal Turn: ${run.id}.`);
63
+ const timestamp = now.toISOString();
64
+ if (Date.parse(timestamp) < Date.parse(run.updatedAt)) {
65
+ throw new Error("Turn input timestamp moved backwards.");
66
+ }
67
+ return validateTurn({
68
+ ...run,
69
+ inputs: [...run.inputs, turnInputRecord(input, run.inputs.length + 1, timestamp)],
70
+ updatedAt: timestamp
71
+ });
72
+ }
73
+ /** Derives Provider-visible identity from the Turn, the sole semantic owner. */
74
+ export function turnInputEnvelope(run, sequence = 1) {
75
+ validateTurn(run);
76
+ const record = run.inputs[sequence - 1];
77
+ if (record === undefined)
78
+ throw new Error(`Turn input does not exist: ${run.id}/${sequence}.`);
79
+ return createTurnInputEnvelope(turnEnvelopeContext(run), record.input);
80
+ }
81
+ export function validateTurn(run) {
82
+ rejectUnknownFields(run, [
83
+ "schemaVersion",
84
+ "id",
85
+ "taskId",
86
+ "roleName",
87
+ "mode",
88
+ "inputs",
89
+ "purpose",
90
+ "workItemId",
91
+ "reviewRoundId",
92
+ "executionGroupId",
93
+ "executionLaneId",
94
+ "workspace",
95
+ "effective",
96
+ "status",
97
+ "result",
98
+ "createdAt",
99
+ "updatedAt"
100
+ ], "Turn");
101
+ if (run.schemaVersion !== 1)
102
+ throw new Error("Turn must use schemaVersion 1.");
103
+ validateTaskRecordReference({ taskId: run.taskId, localId: run.id }, "turn");
104
+ requireSafeIdentity(run.roleName, "Role name");
105
+ if (run.mode !== "new" && run.mode !== "resume") {
106
+ throw new Error(`Turn dispatch mode is invalid: ${String(run.mode)}.`);
107
+ }
108
+ if (!Array.isArray(run.inputs) || run.inputs.length === 0) {
109
+ throw new Error("Turn requires at least one input.");
110
+ }
111
+ for (const [index, record] of run.inputs.entries()) {
112
+ if (record.sequence !== index + 1)
113
+ throw new Error("Turn input sequence is invalid.");
114
+ requireTimestamp(record.submittedAt, "Turn input submittedAt");
115
+ validateTurnInput(record.input);
116
+ if (index > 0 && Date.parse(record.submittedAt) < Date.parse(run.inputs[index - 1].submittedAt)) {
117
+ throw new Error("Turn input timestamps moved backwards.");
118
+ }
119
+ }
120
+ if (!["execution", "review"].includes(run.purpose)) {
121
+ throw new Error(`Turn purpose is invalid: ${String(run.purpose)}.`);
122
+ }
123
+ if (run.workItemId !== undefined) {
124
+ validateTaskRecordReference({ taskId: run.taskId, localId: run.workItemId }, "workItem");
125
+ }
126
+ if (run.reviewRoundId !== undefined) {
127
+ validateTaskRecordReference({ taskId: run.taskId, localId: run.reviewRoundId }, "reviewRound");
128
+ }
129
+ if ((run.executionGroupId === undefined) !== (run.executionLaneId === undefined)) {
130
+ throw new Error("Turn execution lineage is incomplete.");
131
+ }
132
+ if (run.executionGroupId !== undefined) {
133
+ requireSafeIdentity(run.executionGroupId, "ExecutionGroup id");
134
+ requireSafeIdentity(run.executionLaneId, "ExecutionLane id");
135
+ }
136
+ if (run.workspace !== undefined) {
137
+ validateManagedWorkspace(run.workspace);
138
+ if (run.workspace.owner.taskId !== run.taskId) {
139
+ throw new Error("Turn workspace belongs to another Task.");
140
+ }
141
+ if (run.workspace.owner.type === "work-item"
142
+ && run.workspace.owner.workItemId !== run.workItemId) {
143
+ throw new Error("Turn workspace belongs to another Work Item.");
144
+ }
145
+ if (run.workspace.owner.type === "work-item" && run.workItemId === undefined) {
146
+ throw new Error("A WorkItem workspace requires a WorkItem Turn reference.");
147
+ }
148
+ if (run.workspace.owner.type === "review-round" && run.purpose !== "review") {
149
+ throw new Error("A ReviewRound workspace requires a review Turn.");
150
+ }
151
+ if (run.workspace.owner.type === "integration-attempt") {
152
+ throw new Error("An IntegrationAttempt workspace cannot be used by a Turn.");
153
+ }
154
+ if (run.workspace.owner.type === "execution-lane") {
155
+ if (run.workspace.owner.executionGroupId !== run.executionGroupId
156
+ || run.workspace.owner.executionLaneId !== run.executionLaneId) {
157
+ throw new Error("Turn Execution Lane workspace lineage does not match the Turn.");
158
+ }
159
+ if (run.workspace.owner.purpose === "execution"
160
+ && run.workspace.owner.workItemId !== run.workItemId) {
161
+ throw new Error("Turn Execution Lane workspace WorkItem does not match the Turn.");
162
+ }
163
+ if (run.workspace.owner.purpose === "review"
164
+ && run.workspace.owner.reviewRoundId !== run.reviewRoundId) {
165
+ throw new Error("Turn review Lane workspace ReviewRound does not match the Turn.");
166
+ }
167
+ }
168
+ if (run.workspace.owner.type === "review-round"
169
+ && run.workspace.owner.reviewRoundId !== run.reviewRoundId) {
170
+ throw new Error(`Turn ReviewRound workspace owner does not match ${run.reviewRoundId ?? "none"}.`);
171
+ }
172
+ }
173
+ if (run.purpose === "review") {
174
+ if (run.reviewRoundId === undefined) {
175
+ throw new Error("A review Turn requires a ReviewRound reference.");
176
+ }
177
+ if (run.workspace === undefined
178
+ || !((run.workspace.owner.type === "review-round"
179
+ && run.workspace.owner.reviewRoundId === run.reviewRoundId)
180
+ || (run.workspace.owner.type === "execution-lane"
181
+ && run.workspace.owner.purpose === "review"
182
+ && run.workspace.owner.reviewRoundId === run.reviewRoundId))) {
183
+ throw new Error(`A review Turn requires its exact ReviewRound workspace owner: ${run.reviewRoundId}.`);
184
+ }
185
+ if (run.workspace.entries.length === 0
186
+ || run.workspace.entries.some(({ access }) => access !== "write")) {
187
+ throw new Error("A review Turn requires only isolated writable workspace entries.");
188
+ }
189
+ }
190
+ else {
191
+ if (run.reviewRoundId !== undefined) {
192
+ throw new Error("An execution Turn cannot reference a ReviewRound.");
193
+ }
194
+ if (run.workspace?.owner.type === "review-round") {
195
+ throw new Error("An execution Turn cannot use a ReviewRound-owned workspace.");
196
+ }
197
+ if (run.workspace?.owner.type === "execution-lane" && run.workspace.owner.purpose !== "execution") {
198
+ throw new Error("An execution Turn cannot use a review Lane workspace.");
199
+ }
200
+ }
201
+ validateEffectiveLaunchSnapshot(run.effective);
202
+ if (run.workspace !== undefined
203
+ && (run.effective.workspace.root !== run.workspace.root
204
+ || JSON.stringify(run.effective.workspace.entries) !== JSON.stringify(run.workspace.entries))) {
205
+ throw new Error("Turn effective workspace does not match its managed workspace.");
206
+ }
207
+ if (run.purpose === "review") {
208
+ if (run.effective.reviewRoundId !== run.reviewRoundId) {
209
+ throw new Error("Review Turn effective provenance does not match its ReviewRound.");
210
+ }
211
+ if (!run.workspace.entries.some(({ baseCommit }) => baseCommit === run.effective.reviewBaseCommit)) {
212
+ throw new Error("Review Turn effective base does not match its workspace.");
213
+ }
214
+ }
215
+ else if (run.effective.reviewRoundId !== undefined) {
216
+ throw new Error("Execution Turn cannot carry Review effective provenance.");
217
+ }
218
+ for (const record of run.inputs) {
219
+ createTurnInputEnvelope(turnEnvelopeContext(run), record.input);
220
+ }
221
+ if (!["active", "completed", "failed"].includes(run.status)) {
222
+ throw new Error(`Turn status is invalid: ${String(run.status)}.`);
223
+ }
224
+ requireTimestamp(run.createdAt, "Turn createdAt");
225
+ requireTimestamp(run.updatedAt, "Turn updatedAt");
226
+ if (run.status === "active") {
227
+ if (run.result !== undefined)
228
+ throw new Error("An active Turn cannot have a result.");
229
+ }
230
+ else {
231
+ const result = validateTurnResult(run.result);
232
+ if (run.status === "failed") {
233
+ if (!isTurnFailureReason(result.failureReason)) {
234
+ throw new Error(`Failed Turn reason is invalid: ${String(result.failureReason)}.`);
235
+ }
236
+ }
237
+ else if (result.failureReason !== undefined) {
238
+ throw new Error("A completed Turn cannot carry a failure reason.");
239
+ }
240
+ }
241
+ return run;
242
+ }
243
+ export function completeTurn(run, output, now, provider) {
244
+ return finishTurn(run, "completed", output, now, undefined, provider);
245
+ }
246
+ export function failTurn(run, reason, output, now, provider) {
247
+ return finishTurn(run, "failed", output, now, reason, provider);
248
+ }
249
+ function finishTurn(run, status, output, now, failureReason, provider) {
250
+ if (run.status !== "active") {
251
+ throw new Error(`Turn is already terminal: ${run.id}.`);
252
+ }
253
+ const timestamp = now.toISOString();
254
+ const terminal = {
255
+ ...run,
256
+ status,
257
+ result: {
258
+ schemaVersion: 1,
259
+ output: requireResultText(output, "Turn result output"),
260
+ completedAt: timestamp,
261
+ ...(provider === undefined ? {} : { provider: validateTurnProviderResult(provider) }),
262
+ ...(failureReason === undefined ? {} : { failureReason })
263
+ },
264
+ updatedAt: timestamp,
265
+ };
266
+ return validateTurn(terminal);
267
+ }
268
+ function validateTurnResult(result) {
269
+ if (result === undefined || result.schemaVersion !== 1) {
270
+ throw new Error("A terminal Turn requires TurnResult schemaVersion 1.");
271
+ }
272
+ requireResultText(result.output, "Turn result output");
273
+ requireTimestamp(result.completedAt, "Turn result completedAt");
274
+ if (result.provider !== undefined)
275
+ validateTurnProviderResult(result.provider);
276
+ return result;
277
+ }
278
+ function validateTurnProviderResult(provider) {
279
+ requireText(provider.providerNamespace, "Provider namespace");
280
+ requireText(provider.accountScope, "Provider account scope");
281
+ requireText(provider.conversationId, "Provider Conversation id");
282
+ requireText(provider.activationId, "Provider Activation id");
283
+ requireText(provider.nativeTurnId, "Provider native Turn id");
284
+ if (!["completed", "failed", "cancelled"].includes(provider.status)) {
285
+ throw new Error(`Provider Turn result status is invalid: ${String(provider.status)}.`);
286
+ }
287
+ return provider;
288
+ }
289
+ function turnInputRecord(input, sequence, submittedAt) {
290
+ const normalized = validateTurnInput(input);
291
+ return Object.freeze({
292
+ sequence,
293
+ submittedAt,
294
+ input: normalized
295
+ });
296
+ }
297
+ function turnEnvelopeContext(run) {
298
+ return {
299
+ turnId: run.id,
300
+ roleName: run.roleName,
301
+ purpose: run.purpose,
302
+ subject: {
303
+ taskId: run.taskId,
304
+ ...(run.workItemId === undefined ? {} : { workItemId: run.workItemId }),
305
+ ...(run.reviewRoundId === undefined ? {} : { reviewRoundId: run.reviewRoundId }),
306
+ ...(run.executionGroupId === undefined ? {} : {
307
+ executionGroupId: run.executionGroupId,
308
+ executionLaneId: run.executionLaneId
309
+ })
310
+ }
311
+ };
312
+ }
313
+ function requireSafeIdentity(value, label) {
314
+ const normalized = requireText(value, label);
315
+ if (["__proto__", "prototype", "constructor", ".", ".."].includes(normalized)
316
+ || /[\/\\\0]/.test(normalized)) {
317
+ throw new Error(`${label} is invalid.`);
318
+ }
319
+ return normalized;
320
+ }
321
+ function requireText(value, label) {
322
+ if (typeof value !== "string" || value.includes("\0"))
323
+ throw new Error(`${label} is invalid.`);
324
+ const normalized = value.trim();
325
+ if (normalized.length === 0)
326
+ throw new Error(`${label} is required.`);
327
+ return normalized;
328
+ }
329
+ /** Result transport preserves the provider's complete text, including outer whitespace. */
330
+ function requireResultText(value, label) {
331
+ if (typeof value !== "string" || value.includes("\0"))
332
+ throw new Error(`${label} is invalid.`);
333
+ if (value.trim().length === 0)
334
+ throw new Error(`${label} is required.`);
335
+ return value;
336
+ }
337
+ function requireTimestamp(value, label) {
338
+ if (typeof value !== "string" || !Number.isFinite(Date.parse(value))) {
339
+ throw new Error(`${label} must be an ISO timestamp.`);
340
+ }
341
+ }
342
+ function isTurnFailureReason(value) {
343
+ return [
344
+ "startup-failed",
345
+ "runtime-failed",
346
+ "delivery-unknown",
347
+ "missing-result",
348
+ "cancelled"
349
+ ].includes(String(value));
350
+ }
351
+ function rejectUnknownFields(value, fields, label) {
352
+ const allowed = new Set(fields);
353
+ const unknown = Object.keys(value).find((field) => !allowed.has(field));
354
+ if (unknown !== undefined)
355
+ throw new Error(`${label} has unknown field: ${unknown}.`);
356
+ }
@@ -1,22 +1,22 @@
1
1
  import { MAX_SESSION_TITLE_LENGTH } from "../runtime/sessionTitle.js";
2
2
  /**
3
3
  * Prefixes managed launch input with its session title. The bootstrap body
4
- * exposes the exact current Run identity to the Agent, while structured
4
+ * exposes the exact current Turn identity to the Agent, while structured
5
5
  * delivery metadata and control hooks retain the delivery fence.
6
6
  */
7
7
  export function prefixYuiTitleInput(input, title) {
8
8
  const text = input.replace(/\r/g, "").trim();
9
9
  if (text.length === 0)
10
10
  throw new Error("Managed input body is required.");
11
- return `${requireRunTitle(title)}\n\n${text}`;
11
+ return `${requireTurnTitle(title)}\n\n${text}`;
12
12
  }
13
- function requireRunTitle(value) {
13
+ function requireTurnTitle(value) {
14
14
  const title = value.trim();
15
15
  if (title.length === 0
16
16
  || title.length > MAX_SESSION_TITLE_LENGTH
17
17
  || /[\r\n\0]/u.test(title)
18
18
  || !title.startsWith("Yui ")) {
19
- throw new Error("Run title is invalid.");
19
+ throw new Error("Turn title is invalid.");
20
20
  }
21
21
  return title;
22
22
  }
@@ -4,8 +4,8 @@ import { gateArtifactKey, isReusableGateArtifact, validateGateArtifact, verifyGa
4
4
  /**
5
5
  * Issue 08: the GateArtifact store adapter.
6
6
  *
7
- * Every function delegates to a {@link GateArtifactStorePort} (satisfied by
8
- * both SqliteTaskStore and FileTaskStore). The artifact record and its step
7
+ * Every function delegates to a {@link GateArtifactStorePort}. The artifact
8
+ * record and its step
9
9
  * logs are persisted atomically by the store implementation; this module
10
10
  * owns the domain logic (key computation, log hashing, reuse lookup,
11
11
  * retention) but no filesystem paths.
@@ -87,7 +87,7 @@ function urlTaskId() {
87
87
  return readQuery().get("task") || null;
88
88
  }
89
89
 
90
- // The visible detail section is part of the URL (?task=X&section=runs) so a
90
+ // The visible detail section is part of the URL (?task=X&section=exec) so a
91
91
  // refresh or a shared link lands on the same section. Writes always replace —
92
92
  // section moves are not history entries.
93
93
  function setSectionParam(targetId) {
@@ -114,9 +114,9 @@ export function executionBand(projection, t, locale) {
114
114
  head.append(pill(t, "exec.status", projection.status));
115
115
  head.append(node("span", "exec-band-owner",
116
116
  t("exec.owner." + projection.owner) + " · " + t("exec.action." + projection.action)));
117
- if (projection.activeRuns && projection.activeRuns.length > 0) {
117
+ if (projection.activeTurns && projection.activeTurns.length > 0) {
118
118
  head.append(node("span", "exec-band-executors",
119
- projection.activeRuns.length + " " + t("exec.executors")));
119
+ projection.activeTurns.length + " " + t("exec.executors")));
120
120
  }
121
121
  if (projection.monitoring === "stopped") {
122
122
  head.append(node("span", "exec-band-stopped", t("exec.monitoring.stopped")));
@@ -296,7 +296,7 @@ export function candidateList(candidates, t, locale) {
296
296
  row.append(node("span", "candidate-summary", candidate.summary));
297
297
  const source = candidate.source.type === "direct"
298
298
  ? t("candidate.source.direct")
299
- : t("candidate.source.run") + " " + candidate.source.runId;
299
+ : t("candidate.source.turn") + " " + candidate.source.turnId;
300
300
  row.append(node("span", "candidate-source", source));
301
301
  row.append(node("time", "", formatDateTime(candidate.createdAt, locale)));
302
302
  list.append(row);
@@ -478,7 +478,7 @@ export function inputCard(input, _options, t, locale, actions) {
478
478
  }
479
479
  if (input.requester) {
480
480
  top.append(node("span", "input-requester",
481
- t("detail.requester") + " · " + input.requester.roleName + " / " + input.requester.runId));
481
+ t("detail.requester") + " · " + input.requester.roleName + " / " + input.requester.turnId));
482
482
  }
483
483
  card.append(top);
484
484
  card.append(answerActions(input, actions, t));
@@ -611,71 +611,46 @@ export function workItemCard(item, titles, t, locale, actions, taskId) {
611
611
  return card;
612
612
  }
613
613
 
614
- export function runCard(run, t, locale) {
614
+ export function turnCard(turn, t, locale) {
615
615
  const card = node("article", "execute-card");
616
- card.dataset.status = run.status;
616
+ card.dataset.status = turn.status;
617
617
 
618
618
  const idRow = node("div", "execute-id");
619
- idRow.append(statusDot(run.status));
620
- idRow.append(node("span", "role", run.roleName));
621
- idRow.append(node("span", "", run.id));
622
- if (run.workItemId) idRow.append(node("span", "", t("detail.workItem") + " · " + run.workItemId));
623
- if (run.purpose) idRow.append(chip(t("run.purpose." + run.purpose)));
624
- idRow.append(node("time", "", formatDateTime(run.endedAt || run.updatedAt, locale)));
619
+ idRow.append(statusDot(turn.status));
620
+ idRow.append(node("span", "role", turn.roleName));
621
+ idRow.append(node("span", "", turn.id));
622
+ if (turn.workItemId) idRow.append(node("span", "", t("detail.workItem") + " · " + turn.workItemId));
623
+ if (turn.purpose) idRow.append(chip(t("turn.purpose." + turn.purpose)));
624
+ idRow.append(node("time", "", formatDateTime(turn.result?.completedAt || turn.updatedAt, locale)));
625
625
  card.append(idRow);
626
626
 
627
- card.append(richText(t("detail.instruction"), run.assignment?.directive || run.assignment?.action || "-", t, { className: "execute-io", threshold: 320 }));
628
- if (run.summary) {
629
- card.append(richText(t("detail.outcome"), run.summary, t, { className: "execute-io outcome", threshold: 320 }));
627
+ const visibleInput = turn.inputs && turn.inputs.length ? turn.inputs[0].input : null;
628
+ card.append(richText(t("detail.instruction"), visibleInput?.directive || visibleInput?.action || "-", t, { className: "execute-io", threshold: 320 }));
629
+ if (turn.result?.output) {
630
+ card.append(richText(t("detail.outcome"), turn.result.output, t, { className: "execute-io outcome", threshold: 320 }));
630
631
  }
631
632
 
632
633
  const foot = node("div", "execute-foot");
633
634
  const tags = node("div", "execute-tags");
634
- tags.append(chip(t("mode." + run.mode)));
635
- if (run.executionGroupId) {
636
- tags.append(chip(t("detail.lineage") + " · " + run.executionGroupId
637
- + (run.executionLaneId ? "/" + run.executionLaneId : "")));
638
- }
639
- const deliveryKey = run.deliveredAt
640
- ? "delivery.delivered"
641
- : run.pushedAt
642
- ? "delivery.pushed"
643
- : "delivery.pending";
644
- tags.append(chip(t(deliveryKey)));
645
- const badge = run.effective ? agentBadge(run.effective) : (run.agentId ? agentBadge(run) : null);
635
+ tags.append(chip(t("mode." + turn.mode)));
636
+ if (turn.executionGroupId) {
637
+ tags.append(chip(t("detail.lineage") + " · " + turn.executionGroupId
638
+ + (turn.executionLaneId ? "/" + turn.executionLaneId : "")));
639
+ }
640
+ const badge = turn.effective ? agentBadge(turn.effective) : (turn.agentId ? agentBadge(turn) : null);
646
641
  if (badge) tags.append(badge);
647
642
  foot.append(tags);
648
- foot.append(pill(t, "run", run.status));
643
+ foot.append(pill(t, "turn", turn.status));
649
644
  card.append(foot);
650
645
 
651
- if (run.effective) {
646
+ if (turn.effective) {
652
647
  const eff = node("div", "record-meta");
653
- eff.append(node("span", "", t("detail.effective") + " · r" + run.effective.sourceDesiredRevision));
654
- eff.append(node("span", "", t("detail.profileIntent") + " · " + run.effective.profileAccess));
655
- eff.append(node("span", "", t("detail.permission") + " · " + run.effective.permission.strategy));
648
+ eff.append(node("span", "", t("detail.effective") + " · r" + turn.effective.sourceDesiredRevision));
649
+ eff.append(node("span", "", t("detail.profileIntent") + " · " + turn.effective.profileAccess));
650
+ eff.append(node("span", "", t("detail.permission") + " · " + turn.effective.permission.strategy));
656
651
  card.append(eff);
657
652
  }
658
653
 
659
- // Leader Run disposition: the machine-derived outcome of a terminal Leader
660
- // Run, plus any structured wait reference.
661
- if (run.disposition) {
662
- const disposition = node("div", "record-meta");
663
- disposition.append(node("span", "", t("run.disposition") + " · " + t("run.disposition." + run.disposition)));
664
- if (run.waitReason) {
665
- disposition.append(node("span", "", t("run.waitReason") + " · " + run.waitReason.kind
666
- + (run.waitReason.ref ? " (" + run.waitReason.ref + ")" : "")));
667
- }
668
- card.append(disposition);
669
- }
670
-
671
- // Idempotent yield receipt on a yielded Run.
672
- if (run.yieldReceipt) {
673
- const receipt = node("div", "record-meta");
674
- receipt.append(node("span", "mono", t("run.yieldReceipt") + " · " + run.yieldReceipt.receiptId));
675
- receipt.append(node("time", "", formatDateTime(run.yieldReceipt.committedAt, locale)));
676
- card.append(receipt);
677
- }
678
-
679
654
  return card;
680
655
  }
681
656
 
@@ -728,7 +703,7 @@ export function roleCard(role, task, t, locale, actions) {
728
703
  headRight.append(pill(t, "role", role.status));
729
704
  const open = node("button", "record-open", "");
730
705
  open.type = "button";
731
- open.append(node("span", "", t("actions.openRun")), node("span", "arrow", "→"));
706
+ open.append(node("span", "", t("actions.openSession")), node("span", "arrow", "→"));
732
707
  open.addEventListener("click", function () {
733
708
  if (actions.openTerminal) actions.openTerminal({ scope: "task", taskId: task.id, roleName: role.name });
734
709
  });
@@ -861,7 +836,7 @@ export function messageCard(message, t, locale) {
861
836
  const meta = node("div", "record-meta");
862
837
  meta.append(node("span", "mono", message.id));
863
838
  meta.append(node("time", "", formatDateTime(message.createdAt, locale)));
864
- if (message.runId) meta.append(node("span", "mono", message.runId + (message.workItemId ? " · " + message.workItemId : "")));
839
+ if (message.turnId) meta.append(node("span", "mono", message.turnId + (message.workItemId ? " · " + message.workItemId : "")));
865
840
  card.append(meta);
866
841
 
867
842
  card.append(richText(null, message.body, t));