@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.
Files changed (229) hide show
  1. package/ARCHITECTURE.md +69 -428
  2. package/README.md +108 -298
  3. package/dist/agent/managedRuntimeEnvironment.js +2 -2
  4. package/dist/cli/commandCatalog.js +39 -77
  5. package/dist/cli/interactionCandidates.js +4 -4
  6. package/dist/cli/interactionPolicy.js +7 -10
  7. package/dist/cli/invocationRouter.js +1 -1
  8. package/dist/cli/updateCommand.js +6 -27
  9. package/dist/cli/updateOrchestrator.js +63 -484
  10. package/dist/cli/updatePorts.js +15 -333
  11. package/dist/cli/upgradeCommand.js +8 -65
  12. package/dist/cli.js +57 -242
  13. package/dist/commands/configCommands.js +11 -62
  14. package/dist/commands/executionAuditCommands.js +23 -23
  15. package/dist/commands/globalRoleCommands.js +2 -7
  16. package/dist/commands/jobCommands.js +1 -4
  17. package/dist/commands/projectCommands.js +5 -5
  18. package/dist/commands/releaseCommands.js +3 -10
  19. package/dist/commands/resourcesCommands.js +2 -2
  20. package/dist/commands/sessionCommands.js +2 -2
  21. package/dist/commands/taskActor.js +31 -39
  22. package/dist/commands/taskChangeSetCommands.js +5 -12
  23. package/dist/commands/taskCommands.js +464 -1249
  24. package/dist/commands/taskContextCommand.js +35 -36
  25. package/dist/commands/taskExecutionCommands.js +33 -46
  26. package/dist/commands/taskInputCommands.js +20 -42
  27. package/dist/commands/taskNextActionCommand.js +9 -9
  28. package/dist/commands/taskOverviewCommand.js +56 -95
  29. package/dist/commands/taskRoleRuntimeStatus.js +77 -107
  30. package/dist/commands/taskUpstreamCommands.js +3 -3
  31. package/dist/commands/taskWorkspaceCommands.js +7 -79
  32. package/dist/commands/telemetryCommands.js +32 -124
  33. package/dist/commands/workflowCommands.js +3 -3
  34. package/dist/completion/fileCompletionManager.js +1 -1
  35. package/dist/config/configCatalog.js +4 -6
  36. package/dist/config/yuiConfig.js +6 -43
  37. package/dist/context/dispatchContext.js +14 -30
  38. package/dist/context/roleSessionContext.js +2 -2
  39. package/dist/context/sessionBootstrapManifest.js +2 -2
  40. package/dist/context/{runContextPack.js → turnContextPack.js} +58 -73
  41. package/dist/context/turnInputContract.js +204 -0
  42. package/dist/context/wakeNotification.js +2 -2
  43. package/dist/controller/agentRuntimeObserver.js +18 -18
  44. package/dist/controller/clientRuntime.js +13 -20
  45. package/dist/controller/controller.js +89 -116
  46. package/dist/controller/fileSchedulerStoreAdapter.js +511 -742
  47. package/dist/controller/jobControl.js +17 -17
  48. package/dist/controller/resourceInventory.js +6 -6
  49. package/dist/controller/resourceInventoryLinux.js +38 -59
  50. package/dist/controller/resourceInventoryRpc.js +3 -3
  51. package/dist/controller/resourceInventoryWorker.js +1 -1
  52. package/dist/controller/runtime.js +46 -53
  53. package/dist/controller/runtimeEventInbox.js +23 -31
  54. package/dist/controller/runtimeEventProcessor.js +15 -15
  55. package/dist/controller/{runtimeHookRunFence.js → runtimeHookTurnFence.js} +87 -81
  56. package/dist/controller/runtimeLaunchCoordinator.js +3 -3
  57. package/dist/controller/runtimeObservationHook.js +11 -8
  58. package/dist/controller/sessionNotify.js +11 -11
  59. package/dist/controller/structuredProviderObservation.js +89 -16
  60. package/dist/coordination/workMailbox.js +309 -579
  61. package/dist/coordination/workMailboxQueue.js +5 -6
  62. package/dist/core/controllerClient.js +1 -91
  63. package/dist/core/controllerServer.js +3 -6
  64. package/dist/doctor/doctor.js +51 -314
  65. package/dist/execution/candidateConvergence.js +3 -3
  66. package/dist/execution/executionGroup.js +32 -33
  67. package/dist/execution/executionHealth.js +36 -38
  68. package/dist/execution/resourceBroker.js +12 -12
  69. package/dist/executor/agentExecutor.js +30 -265
  70. package/dist/executor/effectiveLaunch.js +9 -19
  71. package/dist/executor/executorRegistry.js +43 -17
  72. package/dist/executor/fileRoleLaunchPlanner.js +56 -56
  73. package/dist/executor/launchPlan.js +3 -3
  74. package/dist/executor/workspacePreflightClassification.js +16 -16
  75. package/dist/grant/capabilityGrant.js +20 -25
  76. package/dist/input/inputRequest.js +12 -12
  77. package/dist/integration/changeSet.js +9 -13
  78. package/dist/integration/changeSetManifest.js +1 -1
  79. package/dist/integration/gitIntegrationService.js +2 -2
  80. package/dist/integration/integrationQueueService.js +5 -21
  81. package/dist/integration/overlapDiagnostics.js +2 -2
  82. package/dist/interaction/operatorPresentation.js +1 -1
  83. package/dist/job/jobRunner.js +1 -1
  84. package/dist/lifecycle/canonicalLifecycleEvent.js +25 -25
  85. package/dist/lifecycle/{exactRunTerminalization.js → exactTurnTerminalization.js} +116 -232
  86. package/dist/message/message.js +6 -6
  87. package/dist/observability/executionAudit.js +74 -86
  88. package/dist/observability/faultClassification.js +7 -24
  89. package/dist/observability/orchestrationMetrics.js +5 -5
  90. package/dist/observability/runtimeIdentity.js +34 -101
  91. package/dist/profile/agentProfile.js +1 -1
  92. package/dist/release/cliHomeReleaseFence.js +3 -3
  93. package/dist/release/releaseWorkflowEngine.js +6 -10
  94. package/dist/release/releaseWorkflowPorts.js +2 -2
  95. package/dist/release/workflowFileLock.js +1 -1
  96. package/dist/repository/checkoutSwap.js +1 -1
  97. package/dist/repository/gitWorkspace.js +0 -13
  98. package/dist/repository/project.js +1 -1
  99. package/dist/repository/projectMaintenanceLock.js +1 -2
  100. package/dist/repository/taskBaseFreshness.js +13 -13
  101. package/dist/repository/taskWorkspaceCoordinator.js +21 -25
  102. package/dist/repository/taskWorkspaceIdentity.js +8 -21
  103. package/dist/repository/taskWorkspacePreparer.js +63 -620
  104. package/dist/resources/autoResourceGc.js +3 -3
  105. package/dist/resources/liveReferences.js +1 -1
  106. package/dist/resources/resourceGc.js +1 -1
  107. package/dist/resources/sqliteResourceRegistry.js +1 -1
  108. package/dist/review/reviewDecision.js +7 -7
  109. package/dist/review/reviewFindingLedger.js +1 -1
  110. package/dist/review/reviewOutcomeClassifier.js +35 -50
  111. package/dist/review/reviewRound.js +14 -25
  112. package/dist/review/reviewerAvailability.js +4 -4
  113. package/dist/review/taskFinalReviewContractResolution.js +41 -0
  114. package/dist/role/role.js +6 -3
  115. package/dist/runtime/agentDriver.js +4 -0
  116. package/dist/runtime/agentHost.js +151 -36
  117. package/dist/runtime/builtinAgentDrivers.js +79 -2
  118. package/dist/runtime/codexAppServerRuntime.js +122 -0
  119. package/dist/runtime/continuationManager.js +3 -5
  120. package/dist/runtime/exactControlPlane.js +17 -42
  121. package/dist/runtime/firstProgressAdvisory.js +1 -1
  122. package/dist/runtime/index.js +2 -2
  123. package/dist/runtime/launchDiagnostics.js +2 -2
  124. package/dist/runtime/lifecycleReservation.js +4 -4
  125. package/dist/runtime/processExitObservation.js +1 -1
  126. package/dist/runtime/processExitOutbox.js +0 -32
  127. package/dist/runtime/promptEnvelope.js +9 -9
  128. package/dist/runtime/providerContinuation.js +2 -2
  129. package/dist/runtime/providerContinuationReconciliationService.js +2 -2
  130. package/dist/runtime/providerRuntimeIdentity.js +76 -24
  131. package/dist/runtime/recentTurnIds.js +38 -0
  132. package/dist/runtime/runtimeContinuationProjection.js +2 -2
  133. package/dist/runtime/runtimeHealthPolicy.js +1 -1
  134. package/dist/runtime/runtimeObservation.js +58 -21
  135. package/dist/runtime/runtimeProjection.js +19 -27
  136. package/dist/runtime/sessionLaunchRequest.js +2 -2
  137. package/dist/runtime/structuredProviderHost.js +177 -12
  138. package/dist/runtime/tmuxAdapters.js +46 -11
  139. package/dist/scheduler/activeRoleTurnDelivery.js +245 -0
  140. package/dist/scheduler/leaderWakeupProcessor.js +147 -445
  141. package/dist/scheduler/operatorEvent.js +4 -6
  142. package/dist/scheduler/resourceQueueProjection.js +71 -0
  143. package/dist/scheduler/{roleRunLiveness.js → roleTurnLiveness.js} +24 -28
  144. package/dist/scheduler/{roleRunStall.js → roleTurnStall.js} +162 -247
  145. package/dist/scheduler/taskExecutionProjection.js +99 -99
  146. package/dist/scheduler/taskObservabilityProjection.js +10 -10
  147. package/dist/scheduler/taskWake.js +4 -4
  148. package/dist/scheduler/wakeReason.js +6 -10
  149. package/dist/scheduler/wakeupQueue.js +0 -9
  150. package/dist/setup/setupCommand.js +2 -2
  151. package/dist/storage/currentTaskStore.js +46 -0
  152. package/dist/storage/persistenceWorker.js +1 -1
  153. package/dist/storage/sqliteSchema.js +81 -485
  154. package/dist/storage/sqliteStore.js +206 -365
  155. package/dist/storage/storageSchema.js +1 -15
  156. package/dist/storage/storageVersions.js +8 -9
  157. package/dist/storage/storeRpc.js +10 -34
  158. package/dist/storage/taskStore.js +63 -3415
  159. package/dist/storage/upgrade/recordVersions.js +55 -149
  160. package/dist/storage/upgrade/upgradeOrchestrator.js +89 -1752
  161. package/dist/task/completionReadiness.js +5 -5
  162. package/dist/task/deliveryGuard.js +10 -10
  163. package/dist/task/nextAction.js +74 -77
  164. package/dist/task/task.js +2 -4
  165. package/dist/task/taskRecordReference.js +3 -3
  166. package/dist/task/taskRecordRetirement.js +1 -1
  167. package/dist/telemetry/sqliteTelemetryStore.js +53 -53
  168. package/dist/telemetry/telemetryConfig.js +14 -14
  169. package/dist/telemetry/telemetryWiring.js +8 -14
  170. package/dist/turn/turn.js +356 -0
  171. package/dist/{run/runIdentity.js → turn/turnIdentity.js} +4 -4
  172. package/dist/verification/gateArtifactStore.js +2 -2
  173. package/dist/web/assets/client/app.js +1 -1
  174. package/dist/web/assets/client/components.js +29 -54
  175. package/dist/web/assets/client/i18n.js +17 -37
  176. package/dist/web/assets/client/view.js +37 -37
  177. package/dist/web/assets/shell.js +1 -1
  178. package/dist/web/assets/styles/cards.js +5 -5
  179. package/dist/web/assets/styles/widgets.js +1 -2
  180. package/dist/web/webSnapshot.js +65 -60
  181. package/dist/workItem/workItem.js +11 -11
  182. package/dist/workspace/workItemChangeSetManager.js +1 -1
  183. package/docs/task-local-identity.md +5 -5
  184. package/i18n/README.zh-CN.md +41 -54
  185. package/package.json +1 -1
  186. package/skills/yui-leader/SKILL.md +85 -83
  187. package/skills/yui-operator/SKILL.md +16 -16
  188. package/skills/yui-reviewer/SKILL.md +10 -10
  189. package/skills/yui-runtime/SKILL.md +17 -18
  190. package/skills/yui-worker/SKILL.md +16 -18
  191. package/dist/context/runContextContract.js +0 -162
  192. package/dist/executor/turnCompletion.js +0 -126
  193. package/dist/review/taskFinalReviewContractEvent.js +0 -1
  194. package/dist/review/taskFinalReviewContractRebind.js +0 -367
  195. package/dist/run/agentRun.js +0 -356
  196. package/dist/run/rejectedYieldAttempt.js +0 -221
  197. package/dist/run/runControlRequest.js +0 -50
  198. package/dist/run/yieldReceipt.js +0 -65
  199. package/dist/runtime/turnCompletion.js +0 -3
  200. package/dist/scheduler/actionability.js +0 -323
  201. package/dist/scheduler/activeRoleRunDelivery.js +0 -868
  202. package/dist/scheduler/activeTaskProgress.js +0 -126
  203. package/dist/storage/compatibleTaskStore.js +0 -248
  204. package/dist/storage/migration/baseline.js +0 -78
  205. package/dist/storage/migration/classifier.js +0 -51
  206. package/dist/storage/migration/compatibleCodec.js +0 -53
  207. package/dist/storage/migration/engine.js +0 -147
  208. package/dist/storage/migration/index.js +0 -33
  209. package/dist/storage/migration/planner.js +0 -154
  210. package/dist/storage/migration/productionRegistry.js +0 -3931
  211. package/dist/storage/migration/registry.js +0 -169
  212. package/dist/storage/migration/report.js +0 -54
  213. package/dist/storage/migration/types.js +0 -31
  214. package/dist/storage/upgrade/homeClassification.js +0 -353
  215. package/dist/storage/upgrade/homeMigrationTarget.js +0 -595
  216. package/dist/storage/upgrade/migrationReceipt.js +0 -67
  217. package/dist/storage/upgrade/offlineUpgradeInventory.js +0 -453
  218. package/dist/storage/upgrade/productionMigrationRegistry.js +0 -6
  219. package/dist/storage/upgrade/pseudoLayoutRepair.js +0 -241
  220. package/dist/storage/upgrade/recordVersionScan.js +0 -176
  221. package/dist/storage/upgrade/sqliteFileSet.js +0 -50
  222. package/dist/storage/upgrade/sqliteMigrationTarget.js +0 -375
  223. package/dist/storage/upgrade/sqliteRecordMigrationTarget.js +0 -305
  224. package/dist/storage/upgrade/sqliteStateMigration.js +0 -918
  225. package/dist/storage/upgrade/switchProgress.js +0 -80
  226. package/dist/storage/upgrade/upgradeReceipt.js +0 -161
  227. package/dist/storage/upgradeCoordination.js +0 -187
  228. package/dist/storage/upgradeFence.js +0 -366
  229. 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));