@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
@@ -1,10 +1,11 @@
1
1
  import { usageError } from "../errors/cliError.js";
2
- import { isRoleRunStalled, latestStallProgressAt } from "../scheduler/roleRunStall.js";
2
+ import { isRoleTurnStalled, latestStallProgressAt } from "../scheduler/roleTurnStall.js";
3
3
  import { formatTimestamp } from "../output/timePresentation.js";
4
4
  import { pendingWakeupProjection } from "../storage/taskStore.js";
5
5
  import { defaultTableWidth, renderTable } from "../output/table.js";
6
6
  import { projectTaskExecutionFromFacts } from "../scheduler/taskExecutionProjection.js";
7
7
  import { resolveRuntimeHealth } from "../config/yuiConfig.js";
8
+ import { projectNextAction } from "../task/nextAction.js";
8
9
  export function parseTaskListOptions(args) {
9
10
  const allowed = new Set(["--all", "--verbose"]);
10
11
  if (args.some((argument) => !allowed.has(argument))
@@ -61,7 +62,7 @@ function buildTaskOverviewEntry(task, store, now, runtimeHealthPolicy) {
61
62
  const workItems = store.listWorkItems(task.id);
62
63
  const inputRequests = store.listInputRequests(task.id);
63
64
  const openInputRequests = inputRequests.filter((request) => request.status === "open");
64
- const agentRuns = store.listAgentRuns(task.id);
65
+ const turns = store.listTurns(task.id);
65
66
  const events = store.listEvents(task.id);
66
67
  const leaderFailure = store.getLeaderFailure(task.id);
67
68
  const leaderMailbox = store.getWorkMailbox({ kind: "role", taskId: task.id, roleName: "leader" });
@@ -73,14 +74,13 @@ function buildTaskOverviewEntry(task, store, now, runtimeHealthPolicy) {
73
74
  const session = store.getRoleSession(task.id, role.name);
74
75
  return session === null ? [] : [{ roleName: role.name, ...session }];
75
76
  });
76
- const attention = collectAttention(task, agentRuns, events, leaderFailure);
77
+ const attention = collectAttention(task, turns, events, leaderFailure);
77
78
  const blockers = collectBlockers(workItems, openInputRequests, attention);
78
- const legacyNext = deriveNextAction(task, brief, workItems, openInputRequests, blockers, pendingWakeup);
79
79
  const leader = {
80
80
  role: "leader",
81
81
  roleStatus: leaderRole === null
82
82
  ? "missing"
83
- : agentRuns.some((run) => run.roleName === "leader" && run.status === "active")
83
+ : turns.some((run) => run.roleName === "leader" && run.status === "active")
84
84
  ? "running"
85
85
  : roleSessions.find((session) => session.roleName === "leader")?.status ?? "idle",
86
86
  summary: brief?.leaderSummary ?? null,
@@ -90,28 +90,29 @@ function buildTaskOverviewEntry(task, store, now, runtimeHealthPolicy) {
90
90
  summaryStatus: brief === null ? "missing" : "available"
91
91
  };
92
92
  const counts = countWorkItems(workItems);
93
- const runtimeRuns = agentRuns
93
+ const runtimeTurns = turns
94
94
  .filter((run) => run.status === "active")
95
95
  .map((run) => ({
96
96
  id: run.id,
97
97
  roleName: run.roleName,
98
98
  status: run.status,
99
- delivery: run.deliveredAt === undefined ? "pending" : "delivered",
100
- deliveredAt: run.deliveredAt ?? null,
99
+ runtime: roleSessions.some((session) => (session.roleName === run.roleName
100
+ && session.agentId === run.effective.agentId
101
+ && session.status !== "ended")) ? "session-active" : "starting",
101
102
  createdAt: run.createdAt,
102
103
  updatedAt: run.updatedAt
103
104
  }));
104
105
  const runtime = {
105
- activeRuns: runtimeRuns,
106
- activeRunCount: runtimeRuns.length,
107
- pendingDeliveryCount: runtimeRuns.filter((run) => run.delivery === "pending").length
106
+ activeTurns: runtimeTurns,
107
+ activeTurnCount: runtimeTurns.length,
108
+ pendingDeliveryCount: runtimeTurns.filter((run) => run.runtime === "starting").length
108
109
  };
109
110
  // Fold the execution projection from the facts already read above instead of
110
111
  // reading the store a second time for the same unchanged revision.
111
112
  const execution = projectTaskExecutionFromFacts({
112
113
  task,
113
114
  roles,
114
- runs: agentRuns,
115
+ turns,
115
116
  workItems,
116
117
  inputRequests,
117
118
  reviewRounds,
@@ -127,21 +128,15 @@ function buildTaskOverviewEntry(task, store, now, runtimeHealthPolicy) {
127
128
  now,
128
129
  runtimeHealthPolicy
129
130
  });
130
- const next = execution.status === "stopped"
131
- ? {
132
- action: execution.action,
133
- owner: execution.owner,
134
- kind: "execution",
135
- summary: execution.summary
136
- }
137
- : execution.action === "recover-execution"
138
- ? {
139
- action: execution.action,
140
- owner: execution.owner,
141
- kind: "execution",
142
- summary: execution.summary
143
- }
144
- : legacyNext;
131
+ const nextActionFacts = store.readNextActionFacts(task.id);
132
+ if (nextActionFacts === null) {
133
+ throw new Error(`Task disappeared while building its overview: ${task.id}.`);
134
+ }
135
+ const next = overviewNextAction(task, workItems, execution, projectNextAction({
136
+ ...nextActionFacts,
137
+ currentTaskReviewCandidate: null,
138
+ executionGroups: execution.executionGroups
139
+ }));
145
140
  return {
146
141
  ...task,
147
142
  brief,
@@ -163,7 +158,7 @@ function buildTaskOverviewEntry(task, store, now, runtimeHealthPolicy) {
163
158
  execution
164
159
  };
165
160
  }
166
- function collectAttention(task, runs, events, failure) {
161
+ function collectAttention(task, turns, events, failure) {
167
162
  const items = [];
168
163
  if (failure !== null) {
169
164
  items.push({
@@ -175,21 +170,21 @@ function collectAttention(task, runs, events, failure) {
175
170
  updatedAt: failure.lastFailedAt
176
171
  });
177
172
  }
178
- for (const run of runs) {
179
- if (run.roleName !== "leader"
180
- || run.status !== "active"
181
- || !isRoleRunStalled(events, run.id))
173
+ for (const turn of turns) {
174
+ if (turn.roleName !== "leader"
175
+ || turn.status !== "active"
176
+ || !isRoleTurnStalled(events, turn.id))
182
177
  continue;
183
- const progressAt = latestStallProgressAt(events, run.id) ?? run.updatedAt;
178
+ const progressAt = latestStallProgressAt(events, turn.id) ?? turn.updatedAt;
184
179
  items.push({
185
180
  kind: "leader-stalled",
186
- id: `leader-stall:${run.id}:${progressAt}`,
181
+ id: `leader-stall:${turn.id}:${progressAt}`,
187
182
  status: "needs-attention",
188
183
  owner: "operator",
189
- summary: `Run ${run.id} for ${run.roleName} has no durable progress after ${progressAt}.`,
184
+ summary: `Turn ${turn.id} for ${turn.roleName} has no durable progress after ${progressAt}.`,
190
185
  updatedAt: progressAt,
191
- runId: run.id,
192
- roleName: run.roleName
186
+ turnId: turn.id,
187
+ roleName: turn.roleName
193
188
  });
194
189
  }
195
190
  return items.sort((left, right) => (Date.parse(left.updatedAt) - Date.parse(right.updatedAt)
@@ -266,64 +261,30 @@ function collectBlockers(workItems, openInputRequests, attention) {
266
261
  }
267
262
  return blockers;
268
263
  }
269
- function deriveNextAction(task, brief, workItems, openInputRequests, blockers, pendingWakeup) {
270
- const input = openInputRequests[0];
271
- if (input !== undefined) {
272
- return {
273
- action: "answer-input",
274
- owner: "user",
275
- kind: "input",
276
- id: input.id,
277
- summary: input.question
278
- };
279
- }
280
- const attention = blockers.find((blocker) => blocker.kind === "attention");
281
- if (attention !== undefined) {
282
- return {
283
- action: attention.action,
284
- owner: attention.owner,
285
- kind: "attention",
286
- id: attention.id,
287
- summary: attention.summary
288
- };
289
- }
290
- const workBlocker = blockers.find((blocker) => blocker.kind === "work");
291
- if (workBlocker !== undefined) {
292
- return {
293
- action: workBlocker.action,
294
- owner: workBlocker.owner,
295
- kind: "work",
296
- id: workBlocker.id,
297
- summary: workBlocker.summary
298
- };
299
- }
300
- if (pendingWakeup !== null) {
301
- return {
302
- action: "process-leader-wakeup",
303
- owner: "leader",
304
- kind: "wakeup",
305
- summary: pendingWakeup.reasons.join(", ")
306
- };
307
- }
308
- const pending = workItems.find((item) => item.status === "pending");
309
- if (pending !== undefined) {
310
- return {
311
- action: "start-work-item",
312
- owner: pending.assignee ?? "leader",
313
- kind: "work",
314
- id: pending.id,
315
- summary: pending.title
316
- };
317
- }
318
- if (task.status === "active" && brief === null) {
319
- return {
320
- action: "update-leader-summary",
321
- owner: "leader",
322
- kind: "summary",
323
- summary: "No persisted Task Brief is available."
324
- };
325
- }
326
- return null;
264
+ function overviewNextAction(task, workItems, execution, action) {
265
+ if (task.status !== "active" && task.status !== "draft")
266
+ return null;
267
+ const primary = action.refs[0];
268
+ const workItem = action.refs
269
+ .filter(({ kind }) => kind === "work-item")
270
+ .map(({ id }) => workItems.find((item) => item.id === id))
271
+ .find((item) => item !== undefined);
272
+ const kind = primary?.kind === "input-request"
273
+ ? "input"
274
+ : primary?.kind === "work-item"
275
+ ? "work"
276
+ : action.kind.includes("attention") || action.kind.includes("inconsistency")
277
+ ? "attention"
278
+ : "execution";
279
+ return {
280
+ action: action.kind,
281
+ owner: action.kind === "resolve-input"
282
+ ? "user"
283
+ : workItem?.assignee ?? execution.owner,
284
+ kind,
285
+ ...(primary === undefined ? {} : { id: primary.id }),
286
+ summary: action.reason
287
+ };
327
288
  }
328
289
  function countWorkItems(items) {
329
290
  const counts = {
@@ -1,12 +1,12 @@
1
1
  import { isDeepStrictEqual } from "node:util";
2
- import { agentRunDeliveryReceiptId } from "../run/agentRun.js";
3
2
  import { hasRuntimeCleanupObligation, runtimeLifecycleTarget } from "../runtime/lifecycleReservation.js";
4
- import { isRoleRunStalled, latestStallProgressAt } from "../scheduler/roleRunStall.js";
3
+ import { isRoleTurnStalled, latestStallProgressAt } from "../scheduler/roleTurnStall.js";
5
4
  import { createRuntimeObservation, runtimeObservationFromTaskEvent } from "../runtime/runtimeObservation.js";
6
- import { classifyRuntimeHealth, projectRuntimeMailbox, projectRuntimeObservation, projectRuntimeTaskEvents, runtimeDisplayStatus } from "../runtime/runtimeProjection.js";
7
- import { latestRunDurableProgressAt } from "../scheduler/roleRunStall.js";
5
+ import { classifyRuntimeHealth, projectRuntimeObservation, projectRuntimeTaskEvents, runtimeDisplayStatus } from "../runtime/runtimeProjection.js";
6
+ import { latestTurnDurableProgressAt } from "../scheduler/roleTurnStall.js";
8
7
  import { resolveRuntimeHealth } from "../config/yuiConfig.js";
9
8
  import { builtinDriverIdForAdapter } from "../runtime/builtinAgentDrivers.js";
9
+ import { formatTurnReceiptId } from "../task/taskRecordReference.js";
10
10
  import { operationalTaskRecords } from "../task/taskRecordRetirement.js";
11
11
  import { projectSessionTokenMetrics, resolveSessionTokenIdentity } from "../runtime/sessionTokenMetrics.js";
12
12
  export function inspectTaskRoleRuntimeStatuses(taskId, roles, store, panes, now = new Date()) {
@@ -21,12 +21,12 @@ export function inspectTaskRoleRuntimeStatuses(taskId, roles, store, panes, now
21
21
  return roles.map((role) => inspectTaskRoleRuntimeStatus(taskId, role, store, panesByRole.get(role.name), role.name === "leader" ? taskOpenInputRequestCount : 0, now));
22
22
  }
23
23
  export function renderTaskRoleRuntimeStatus(status) {
24
- const activeRun = status.activeRun === null
24
+ const activeTurn = status.activeTurn === null
25
25
  ? "-"
26
- : `${status.activeRun.id} (${activeRunDeliveryLabel(status.activeRun)})`;
27
- const lastRun = status.activeRun !== null || status.lastRun === null
26
+ : `${status.activeTurn.id} (${activeTurnDeliveryLabel(status.activeTurn)})`;
27
+ const lastTurn = status.activeTurn !== null || status.lastTurn === null
28
28
  ? undefined
29
- : `${status.lastRun.id} (${status.lastRun.status}${status.lastRun.endedAt === undefined ? "" : ` at ${status.lastRun.endedAt}`})`;
29
+ : `${status.lastTurn.id} (${status.lastTurn.status}${status.lastTurn.result === undefined ? "" : ` at ${status.lastTurn.result.completedAt}`})`;
30
30
  const activeWork = status.activeWork === null
31
31
  ? "-"
32
32
  : `${status.activeWork.id} (${status.activeWork.status}) ${status.activeWork.title}`;
@@ -84,15 +84,15 @@ export function renderTaskRoleRuntimeStatus(status) {
84
84
  ` Desired drift ${status.effectiveLaunch === null
85
85
  ? "-"
86
86
  : status.launchDrift ? "pending next launch" : "none"}`,
87
- ` Run/session ${status.runSessionDrift ? "snapshot mismatch" : "snapshot consistent"}`,
87
+ ` Turn/session ${status.turnSessionDrift ? "snapshot mismatch" : "snapshot consistent"}`,
88
88
  ` Active work ${activeWork}`,
89
- ` Active run ${activeRun}`,
90
- ...(lastRun === undefined ? [] : [` Last run ${lastRun}`]),
91
- ` Run attention ${status.stall.active
89
+ ` Active run ${activeTurn}`,
90
+ ...(lastTurn === undefined ? [] : [` Last turn ${lastTurn}`]),
91
+ ` Turn attention ${status.stall.active
92
92
  ? `needs-attention (${status.stall.kind ?? "workflow-not-progressing"}; no workflow progress since ${status.stall.progressAt ?? "unknown"})`
93
93
  : "none"}`,
94
94
  ` Native session ${nativeSession}`,
95
- ` Agent runtime ${runtime}`,
95
+ ` AgentRuntime ${runtime}`,
96
96
  ` Session tokens ${sessionTokens}`,
97
97
  ` Runtime cleanup ${status.runtimeCleanupPending ? "pending" : "none"}`,
98
98
  ` tmux pane ${tmux}`,
@@ -103,15 +103,15 @@ export function renderTaskRoleRuntimeStatus(status) {
103
103
  export function taskRoleActiveWorkLabel(status) {
104
104
  if (status.activeWork !== null)
105
105
  return `${status.activeWork.id}: ${status.activeWork.title}`;
106
- return status.activeRun === null ? "-" : status.activeRun.id;
106
+ return status.activeTurn === null ? "-" : status.activeTurn.id;
107
107
  }
108
- /** Issue 09: compact last-Run outcome label for the Role list table. */
109
- export function taskRoleLastRunLabel(status) {
110
- if (status.activeRun !== null)
111
- return `${status.activeRun.id} ${status.activeRun.status}`;
112
- if (status.lastRun === null)
108
+ /** Issue 09: compact last-Turn outcome label for the Role list table. */
109
+ export function taskRoleLastTurnLabel(status) {
110
+ if (status.activeTurn !== null)
111
+ return `${status.activeTurn.id} ${status.activeTurn.status}`;
112
+ if (status.lastTurn === null)
113
113
  return "-";
114
- return `${status.lastRun.id} ${status.lastRun.status}`;
114
+ return `${status.lastTurn.id} ${status.lastTurn.status}`;
115
115
  }
116
116
  export function taskRoleNativeSessionLabel(status) {
117
117
  if (status.runtimeCleanupPending && status.nativeSession === null)
@@ -140,26 +140,26 @@ export function taskRoleTmuxLabel(status) {
140
140
  : `running (${status.tmux.currentCommand})`;
141
141
  }
142
142
  function inspectTaskRoleRuntimeStatus(taskId, role, store, pane, openInputRequestCount, now) {
143
- const activeRun = store.getActiveAgentRun(taskId, role.name);
144
- // Issue 09: the last Run outcome is a separate axis from the Session
145
- // lifecycle. A Session that stops after its Run yielded must not retroactively
146
- // turn that Run into a failure; the status display keeps both visible.
147
- const lastRun = operationalTaskRecords(store.listAgentRuns(taskId), store.listEvents(taskId), "agent-run")
143
+ const activeTurn = store.getActiveTurn(taskId, role.name);
144
+ // The last Turn outcome is a separate axis from the Session lifecycle. A
145
+ // Session that stops after its Turn completed must not retroactively turn
146
+ // that Turn into a failure; the status display keeps both visible.
147
+ const lastTurn = operationalTaskRecords(store.listTurns(taskId), store.listEvents(taskId), "turn")
148
148
  .filter((candidate) => candidate.roleName === role.name)
149
149
  .sort((left, right) => Date.parse(right.updatedAt) - Date.parse(left.updatedAt))[0]
150
150
  ?? null;
151
- const activeWork = activeRun?.workItemId === undefined
151
+ const activeWork = activeTurn?.workItemId === undefined
152
152
  ? null
153
- : store.getWorkItem(taskId, activeRun.workItemId);
153
+ : store.getWorkItem(taskId, activeTurn.workItemId);
154
154
  const sessions = store.getTaskRoleSessionSet(taskId, role.name);
155
- const effectiveAgentId = activeRun?.effective.agentId ?? sessions?.activeAgentId;
155
+ const effectiveAgentId = activeTurn?.effective.agentId ?? sessions?.activeAgentId;
156
156
  const nativeSession = effectiveAgentId === undefined
157
157
  ? null
158
158
  : sessions?.sessions[effectiveAgentId] ?? null;
159
- const effectiveLaunch = activeRun?.effective ?? nativeSession?.effective ?? null;
160
- const runSessionDrift = activeRun !== null
159
+ const effectiveLaunch = activeTurn?.effective ?? nativeSession?.effective ?? null;
160
+ const turnSessionDrift = activeTurn !== null
161
161
  && nativeSession !== null
162
- && !isDeepStrictEqual(activeRun.effective, nativeSession.effective);
162
+ && !isDeepStrictEqual(activeTurn.effective, nativeSession.effective);
163
163
  const recovery = inspectTaskRoleSessionRecovery(taskId, role.name, store);
164
164
  const tmux = pane === undefined
165
165
  ? { state: "missing" }
@@ -170,13 +170,13 @@ function inspectTaskRoleRuntimeStatus(taskId, role, store, pane, openInputReques
170
170
  ...(pane.pid === undefined ? {} : { pid: pane.pid }),
171
171
  currentCommand: pane.currentCommand
172
172
  };
173
- // The active Run snapshot is authoritative for the live Role session. It
173
+ // The active Turn snapshot is authoritative for the live Role session. It
174
174
  // may point at a ReviewRound-owned workspace, which is intentionally
175
175
  // distinct from the WorkItem Develop workspace.
176
- const managedWorkspace = activeRun?.workspace
177
- ?? (activeRun?.workItemId === undefined
176
+ const managedWorkspace = activeTurn?.workspace
177
+ ?? (activeTurn?.workItemId === undefined
178
178
  ? store.getTaskWorkspace(taskId)
179
- : store.getWorkItemWorkspace(taskId, activeRun.workItemId));
179
+ : store.getWorkItemWorkspace(taskId, activeTurn.workItemId));
180
180
  const workspace = managedWorkspace === null
181
181
  ? { managed: false, path: role.workspace }
182
182
  : { ...managedWorkspace, managed: true };
@@ -184,16 +184,16 @@ function inspectTaskRoleRuntimeStatus(taskId, role, store, pane, openInputReques
184
184
  const sessionTokens = projectSessionTokenMetrics(events, resolveSessionTokenIdentity(nativeSession === null
185
185
  ? null
186
186
  : { taskId, roleName: role.name, ...nativeSession }));
187
- const runtime = projectTaskRoleRuntime(activeRun, nativeSession, tmux, events, store.getWorkMailbox({ kind: "role", taskId, roleName: role.name }), store, taskId, role.name, now);
188
- const stalled = activeRun !== null && isRoleRunStalled(events, activeRun.id);
189
- const stallProgressAt = activeRun === null
187
+ const runtime = projectTaskRoleRuntime(activeTurn, nativeSession, tmux, events, store.getWorkMailbox({ kind: "role", taskId, roleName: role.name }), store, taskId, role.name, now);
188
+ const stalled = activeTurn !== null && isRoleTurnStalled(events, activeTurn.id);
189
+ const stallProgressAt = activeTurn === null
190
190
  ? undefined
191
- : latestStallProgressAt(events, activeRun.id);
192
- const stallKind = activeRun === null
191
+ : latestStallProgressAt(events, activeTurn.id);
192
+ const stallKind = activeTurn === null
193
193
  ? undefined
194
- : latestStallKind(events, activeRun.id);
195
- const health = calculateHealth(role, activeRun, lastRun, nativeSession, recovery.runtimeCleanupPending, tmux, openInputRequestCount, stalled, runtime);
196
- const stall = activeRun === null
194
+ : latestStallKind(events, activeTurn.id);
195
+ const health = calculateHealth(role, activeTurn, lastTurn, nativeSession, recovery.runtimeCleanupPending, tmux, openInputRequestCount, stalled, runtime);
196
+ const stall = activeTurn === null
197
197
  ? { active: false }
198
198
  : {
199
199
  active: stalled,
@@ -209,12 +209,12 @@ function inspectTaskRoleRuntimeStatus(taskId, role, store, pane, openInputReques
209
209
  effectiveLaunch,
210
210
  launchDrift: effectiveLaunch !== null
211
211
  && effectiveLaunch.sourceDesiredRevision !== role.launchRevision,
212
- runSessionDrift,
212
+ turnSessionDrift,
213
213
  ...health,
214
214
  openInputRequestCount,
215
215
  role,
216
- activeRun,
217
- lastRun,
216
+ activeTurn,
217
+ lastTurn,
218
218
  activeWork,
219
219
  nativeSession,
220
220
  tmux,
@@ -236,17 +236,17 @@ function sessionCumulativeTokenLabel(metrics) {
236
236
  ];
237
237
  return `${total.totalTokens} (${breakdown.join(", ")})`;
238
238
  }
239
- function latestStallKind(events, runId) {
239
+ function latestStallKind(events, turnId) {
240
240
  const event = [...events]
241
- .filter((candidate) => candidate.type === "run.stalled"
242
- && candidate.payload.runId === runId
241
+ .filter((candidate) => candidate.type === "turn.stalled"
242
+ && candidate.payload.turnId === turnId
243
243
  && candidate.payload.status !== "diagnostic-only")
244
244
  .sort((left, right) => Date.parse(right.createdAt) - Date.parse(left.createdAt))[0];
245
245
  return event?.payload.kind === "delivery-stalled" || event?.payload.kind === "workflow-not-progressing"
246
246
  ? event.payload.kind
247
247
  : undefined;
248
248
  }
249
- function calculateHealth(role, activeRun, lastRun, nativeSession, runtimeCleanupPending, tmux, openInputRequestCount, stalled, runtime) {
249
+ function calculateHealth(role, activeTurn, lastTurn, nativeSession, runtimeCleanupPending, tmux, openInputRequestCount, stalled, runtime) {
250
250
  if (runtimeCleanupPending && nativeSession === null) {
251
251
  return {
252
252
  health: "needs-attention",
@@ -254,26 +254,17 @@ function calculateHealth(role, activeRun, lastRun, nativeSession, runtimeCleanup
254
254
  };
255
255
  }
256
256
  if (nativeSession?.status === "ended" && nativeSession.endReason === "failed") {
257
- // Issue 09: a broken Session only fails a live Run. When the last Run
258
- // already yielded, the Session death is a lifecycle event, not a Run
259
- // failure — surface it as attention with the persisted Run outcome.
260
- if (activeRun !== null) {
257
+ // A broken Session only fails a live Turn. When the last Turn already
258
+ // completed, the Session death is a lifecycle event, not a Turn failure —
259
+ // surface it as attention with the persisted Turn outcome.
260
+ if (activeTurn !== null) {
261
261
  return { health: "failed", healthReason: "the active native session is broken" };
262
262
  }
263
263
  return {
264
264
  health: "needs-attention",
265
- healthReason: lastRun === null
265
+ healthReason: lastTurn === null
266
266
  ? "the native session is broken"
267
- : `the native session is broken; last run ${lastRun.id} ${lastRun.status}`
268
- };
269
- }
270
- const awaitingProviderAcceptance = activeRun?.pushedAt !== undefined
271
- && activeRun.deliveredAt === undefined;
272
- if (awaitingProviderAcceptance
273
- && tmux.state !== "running") {
274
- return {
275
- health: "needs-attention",
276
- healthReason: "the pushed active Run is awaiting provider acceptance and has no live tmux pane"
267
+ : `the native session is broken; last Turn ${lastTurn.id} ${lastTurn.status}`
277
268
  };
278
269
  }
279
270
  if (tmux.state === "exited") {
@@ -282,23 +273,17 @@ function calculateHealth(role, activeRun, lastRun, nativeSession, runtimeCleanup
282
273
  healthReason: "the tmux pane exited; Provider Conversation/continuation state is unobservable"
283
274
  };
284
275
  }
285
- if (activeRun !== null) {
286
- if (activeRun.deliveredAt !== undefined && tmux.state !== "running") {
287
- return { health: "needs-attention", healthReason: "the delivered active Run has no live tmux pane" };
276
+ if (activeTurn !== null) {
277
+ if (tmux.state !== "running") {
278
+ return { health: "needs-attention", healthReason: "the active Turn has no live tmux pane" };
288
279
  }
289
280
  if (stalled) {
290
281
  return {
291
282
  health: "needs-attention",
292
- healthReason: "the live active Run has no durable progress in the configured stall window"
283
+ healthReason: "the live active Turn has no durable progress in the configured stall window"
293
284
  };
294
285
  }
295
- if (activeRun.deliveredAt !== undefined) {
296
- if (runtime === null) {
297
- return {
298
- health: "needs-attention",
299
- healthReason: "the delivered Run has no authoritative Agent Driver state"
300
- };
301
- }
286
+ if (runtime !== null) {
302
287
  switch (runtime.healthLayer) {
303
288
  case "broken":
304
289
  return { health: "failed", healthReason: runtime.healthReason };
@@ -328,6 +313,10 @@ function calculateHealth(role, activeRun, lastRun, nativeSession, runtimeCleanup
328
313
  return { health: "running", healthReason: runtime.healthReason };
329
314
  }
330
315
  }
316
+ return {
317
+ health: "starting",
318
+ healthReason: "the active Turn is awaiting Provider runtime observations"
319
+ };
331
320
  }
332
321
  if (nativeSession?.status === "active" && tmux.state !== "running") {
333
322
  return { health: "needs-attention", healthReason: "the native session is running without a live tmux pane" };
@@ -341,25 +330,11 @@ function calculateHealth(role, activeRun, lastRun, nativeSession, runtimeCleanup
341
330
  healthReason: `${openInputRequestCount} open InputRequest${openInputRequestCount === 1 ? "" : "s"} require user input`
342
331
  };
343
332
  }
344
- if (activeRun !== null) {
345
- if (activeRun.deliveredAt !== undefined)
346
- return {
347
- health: "needs-attention",
348
- healthReason: "the delivered Run has no authoritative Agent Driver state"
349
- };
350
- if (activeRun.pushedAt !== undefined) {
351
- return {
352
- health: "awaiting-provider-acceptance",
353
- healthReason: "the pushed active Run is awaiting provider acceptance"
354
- };
355
- }
356
- return { health: "starting", healthReason: "the active Run is awaiting tmux delivery" };
357
- }
358
333
  return tmux.state === "running"
359
334
  ? { health: "ready", healthReason: "the native Agent pane is ready without active work" }
360
335
  : { health: "idle", healthReason: "there is no active work or live tmux pane" };
361
336
  }
362
- function projectTaskRoleRuntime(run, session, tmux, events, mailbox, store, taskId, roleName, now) {
337
+ function projectTaskRoleRuntime(run, session, tmux, events, _mailbox, store, taskId, roleName, now) {
363
338
  if (run === null || session?.launchId === undefined)
364
339
  return null;
365
340
  let driverId;
@@ -372,7 +347,7 @@ function projectTaskRoleRuntime(run, session, tmux, events, mailbox, store, task
372
347
  const fence = {
373
348
  taskId: run.taskId,
374
349
  roleName: run.roleName,
375
- runId: run.id,
350
+ turnId: run.id,
376
351
  agentId: run.effective.agentId,
377
352
  driverId,
378
353
  launchId: session.launchId,
@@ -381,17 +356,18 @@ function projectTaskRoleRuntime(run, session, tmux, events, mailbox, store, task
381
356
  nativeTurnId: runtimeNativeTurnId(events, {
382
357
  taskId: run.taskId,
383
358
  roleName: run.roleName,
384
- runId: run.id,
359
+ turnId: run.id,
385
360
  agentId: run.effective.agentId,
386
361
  driverId,
387
362
  launchId: session.launchId,
388
363
  nativeSessionId: session.nativeSessionId,
389
- receiptId: agentRunDeliveryReceiptId(run)
364
+ receiptId: store.getTaskRoleSessionSet(taskId, roleName)?.providerBinding?.turn?.attemptId
365
+ ?? formatTurnReceiptId(run.taskId, run.id)
390
366
  }) ?? run.id,
391
- receiptId: agentRunDeliveryReceiptId(run)
367
+ receiptId: store.getTaskRoleSessionSet(taskId, roleName)?.providerBinding?.turn?.attemptId
368
+ ?? formatTurnReceiptId(run.taskId, run.id)
392
369
  };
393
370
  let projection = projectRuntimeTaskEvents(fence, run.createdAt, events);
394
- projection = projectRuntimeMailbox(projection, mailbox);
395
371
  projection = projectRuntimeObservation(projection, createRuntimeObservation({
396
372
  schemaVersion: 2,
397
373
  eventId: `runtime-host-${run.id}`,
@@ -404,10 +380,8 @@ function projectTaskRoleRuntime(run, session, tmux, events, mailbox, store, task
404
380
  }));
405
381
  // The semantic progress fence is the same durable fold the scheduler stall
406
382
  // pass consumes, so CLI/Web/scheduler share one progress clock.
407
- const semanticProgress = run.deliveredAt === undefined
408
- ? { progressAt: run.createdAt }
409
- : latestRunDurableProgressAt(store, taskId, roleName, run.id)
410
- ?? { progressAt: run.deliveredAt };
383
+ const semanticProgress = latestTurnDurableProgressAt(store, taskId, roleName, run.id)
384
+ ?? { progressAt: run.createdAt };
411
385
  const classification = classifyRuntimeHealth({
412
386
  projection,
413
387
  semanticProgressAt: semanticProgress.progressAt,
@@ -444,7 +418,7 @@ function runtimeNativeTurnId(events, expected) {
444
418
  .filter((observation) => observation !== null
445
419
  && observation.fence.taskId === expected.taskId
446
420
  && observation.fence.roleName === expected.roleName
447
- && observation.fence.runId === expected.runId
421
+ && observation.fence.turnId === expected.turnId
448
422
  && observation.fence.agentId === expected.agentId
449
423
  && observation.fence.driverId === expected.driverId
450
424
  && observation.fence.launchId === expected.launchId
@@ -459,10 +433,6 @@ function runtimeNativeTurnId(events, expected) {
459
433
  ?.fence.nativeTurnId
460
434
  ?? observations.at(-1)?.fence.nativeTurnId;
461
435
  }
462
- function activeRunDeliveryLabel(run) {
463
- if (run.deliveredAt !== undefined)
464
- return "delivered";
465
- if (run.pushedAt !== undefined)
466
- return "pushed (awaiting provider acceptance)";
467
- return "queued";
436
+ function activeTurnDeliveryLabel(run) {
437
+ return run.status === "active" ? "active" : run.status;
468
438
  }
@@ -42,11 +42,11 @@ async function integrateUpstream(args, store, options) {
42
42
  throw usageError(`Task must be active to integrate upstream: ${taskId}/${task.status}.`);
43
43
  }
44
44
  // RFC Phase 4: integrating upstream rewrites the Task worktree HEAD. Refuse
45
- // while the Leader has an active Run so the merge never lands under a
45
+ // while the Leader has an active Turn so the merge never lands under a
46
46
  // running Session.
47
- const activeLeaderRun = store.getActiveAgentRun(taskId, "leader");
47
+ const activeLeaderRun = store.getActiveTurn(taskId, "leader");
48
48
  if (activeLeaderRun !== null) {
49
- throw usageError(`Task has an active Leader Run (${activeLeaderRun.id}); stop it before integrating upstream: ${taskId}.`);
49
+ throw usageError(`Task has an active Leader Turn (${activeLeaderRun.id}); stop it before integrating upstream: ${taskId}.`);
50
50
  }
51
51
  const workspace = store.getTaskWorkspace(taskId);
52
52
  if (workspace === null) {