@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
@@ -2,21 +2,21 @@ import { selectedSchedulerRoles, selectedActiveSchedulerTasks } from "./ports.js
2
2
  import { nextPendingBatch } from "../coordination/workMailbox.js";
3
3
  import { RUNTIME_DIAGNOSTIC_AFTER_MS, SEMANTIC_STALL_WINDOW_MS } from "../runtime/runtimeHealthPolicy.js";
4
4
  /**
5
- * Default window of no durable progress before a live-but-idle Run becomes a
6
- * traceable needs-attention signal. It is deliberately long: a healthy Run that
5
+ * Default window of no durable progress before a live-but-idle Turn becomes a
6
+ * traceable needs-attention signal. It is deliberately long: a healthy Turn that
7
7
  * is simply slow keeps its structured checkpoint fresh and never crosses it.
8
8
  */
9
9
  export const DEFAULT_STALL_WINDOW_MS = SEMANTIC_STALL_WINDOW_MS;
10
10
  /** Cheap workflow-stall candidate filter; the real threshold remains 30m. */
11
11
  export const DEFAULT_WORKFLOW_STALL_CANDIDATE_AGE_MS = RUNTIME_DIAGNOSTIC_AFTER_MS;
12
- export const RUN_PROGRESS_EVENT = "run.progress";
13
- export const RUN_STALLED_EVENT = "run.stalled";
14
- export const RUN_RECOVERED_EVENT = "run.recovered";
15
- export const RUN_DIAGNOSTIC_FINISHED_EVENT = "runtime.diagnostic-finished";
12
+ export const TURN_PROGRESS_EVENT = "turn.progress";
13
+ export const TURN_STALLED_EVENT = "turn.stalled";
14
+ export const TURN_RECOVERED_EVENT = "turn.recovered";
15
+ export const TURN_DIAGNOSTIC_FINISHED_EVENT = "runtime.diagnostic-finished";
16
16
  /** Structured, non-Message recovery evidence written by an explicit Leader. */
17
17
  /** Workflow-semantic events that count for the durable progress clock. */
18
18
  const ACTIVITY_EVENT_TYPES = new Set([
19
- RUN_PROGRESS_EVENT,
19
+ TURN_PROGRESS_EVENT,
20
20
  "message.sent",
21
21
  "input.answered",
22
22
  "input.auto-answered",
@@ -34,24 +34,23 @@ const ACTIVITY_EVENT_TYPES = new Set([
34
34
  * as exact-generation diagnostic evidence; it never changes the durable
35
35
  * progress clock, suppresses workflow attention, or authorizes recovery.
36
36
  */
37
- export function projectRoleRunHealth(input) {
37
+ export function projectRoleTurnHealth(input) {
38
38
  const windowMs = input.windowMs ?? DEFAULT_STALL_WINDOW_MS;
39
39
  const diagnosticAfterMs = input.diagnosticAfterMs
40
40
  ?? DEFAULT_WORKFLOW_STALL_CANDIDATE_AGE_MS;
41
41
  if (!Number.isFinite(windowMs) || windowMs <= 0) {
42
- throw new Error("Role run stall window must be a positive number of milliseconds.");
42
+ throw new Error("Role Turn stall window must be a positive number of milliseconds.");
43
43
  }
44
- const evaluation = evaluateRoleRunStall({
44
+ const evaluation = evaluateRoleTurnStall({
45
45
  progressAt: input.progressAt,
46
46
  now: input.now,
47
47
  windowMs
48
48
  });
49
49
  const idleMs = evaluation.idleMs;
50
- const candidateStart = input.deliveredAt ?? input.createdAt;
51
- const candidateAge = input.now.getTime() - Date.parse(candidateStart);
50
+ const candidateAge = input.now.getTime() - Date.parse(input.createdAt);
52
51
  const candidate = Number.isFinite(candidateAge) && candidateAge >= windowMs;
53
52
  const providerAcceptance = input.providerAcceptance
54
- ?? (input.deliveredAt === undefined ? "ambiguous" : "accepted");
53
+ ?? "ambiguous";
55
54
  const hostLiveness = input.hostLiveness;
56
55
  const session = input.nativeSession;
57
56
  const nativeSession = session === null || session === undefined
@@ -74,7 +73,7 @@ export function projectRoleRunHealth(input) {
74
73
  // no persisted nativeSessionId. With a present host, durable no-progress
75
74
  // evidence is still actionable in that case. Only an explicit stopped or
76
75
  // broken Session blocks the projection; identity mismatches are fenced by
77
- // reconcileStalledRoleRuns below before this projection is routed.
76
+ // reconcileStalledRoleTurns below before this projection is routed.
78
77
  const workflowStall = candidate
79
78
  && evaluation.stalled
80
79
  && hostLiveness === "present"
@@ -108,16 +107,16 @@ export function projectRoleRunHealth(input) {
108
107
  /**
109
108
  * Pure stall decision. Given the last durable progress timestamp and whatever
110
109
  * progress point the previous attention (if any) recorded, decide whether the
111
- * Run is stalled and whether this is new evidence worth surfacing again.
110
+ * Turn is stalled and whether this is new evidence worth surfacing again.
112
111
  */
113
- export function evaluateRoleRunStall(input) {
112
+ export function evaluateRoleTurnStall(input) {
114
113
  const windowMs = input.windowMs ?? DEFAULT_STALL_WINDOW_MS;
115
114
  if (!Number.isFinite(windowMs) || windowMs <= 0) {
116
- throw new Error("Role run stall window must be a positive number of milliseconds.");
115
+ throw new Error("Role Turn stall window must be a positive number of milliseconds.");
117
116
  }
118
117
  const progressMs = Date.parse(input.progressAt);
119
118
  if (!Number.isFinite(progressMs)) {
120
- throw new Error("Role run stall progress timestamp is invalid.");
119
+ throw new Error("Role Turn stall progress timestamp is invalid.");
121
120
  }
122
121
  const idleMs = input.now.getTime() - progressMs;
123
122
  const stalled = idleMs >= windowMs;
@@ -125,29 +124,29 @@ export function evaluateRoleRunStall(input) {
125
124
  return { stalled, isNewEpisode, idleMs, progressAt: input.progressAt };
126
125
  }
127
126
  /**
128
- * Latest durable progress timestamp for an active Run. Provider acceptance
129
- * (deliveredAt), explicit workflow checkpoints, and semantic domain activity
130
- * count as progress. Provider operations, tokens, CPU/memory and bookkeeping
131
- * timestamps are intentionally excluded.
127
+ * Latest durable progress timestamp for an active Turn. Turn creation,
128
+ * explicit workflow checkpoints, and semantic domain activity count as
129
+ * progress. Provider operations, tokens, CPU/memory and bookkeeping are
130
+ * intentionally excluded.
132
131
  */
133
132
  export function latestDurableProgressAt(input) {
134
133
  const candidates = [
135
- input.deliveredAt,
134
+ input.startedAt,
136
135
  input.latestCheckpointAt,
137
136
  input.latestActivityAt
138
137
  ].filter((value) => (typeof value === "string" && Number.isFinite(Date.parse(value))));
139
138
  if (candidates.length === 0) {
140
- throw new Error("No durable progress timestamp is available for the Run.");
139
+ throw new Error("No durable progress timestamp is available for the Turn.");
141
140
  }
142
141
  return candidates.reduce((latest, value) => (Date.parse(value) > Date.parse(latest) ? value : latest));
143
142
  }
144
- /** Most recent semantic timestamp carried by a Run progress event. */
145
- export function latestRunProgressAt(events, runId) {
143
+ /** Most recent semantic timestamp carried by a Turn progress event. */
144
+ export function latestTurnProgressAt(events, turnId) {
146
145
  let latest;
147
146
  for (const event of events) {
148
- if (event.type !== RUN_PROGRESS_EVENT)
147
+ if (event.type !== TURN_PROGRESS_EVENT)
149
148
  continue;
150
- if (event.payload.runId !== runId)
149
+ if (event.payload.turnId !== turnId)
151
150
  continue;
152
151
  const progressAt = typeof event.payload.progressAt === "string"
153
152
  && Number.isFinite(Date.parse(event.payload.progressAt))
@@ -164,89 +163,45 @@ export function latestRunProgressAt(events, runId) {
164
163
  }
165
164
  /**
166
165
  * Resolve the exact semantic progress fence shared by resource production and
167
- * stall consumption for one active Run. The richer adapter fold remains
168
- * authoritative when available; the retained Run progress/activity events are
169
- * the provider-neutral fallback. Resource evidence carries this value as an
170
- * exact fence, but never advances it.
166
+ * stall consumption for one active Turn. Resource evidence carries this value
167
+ * as an exact fence, but never advances it.
171
168
  */
172
- export function currentRoleRunProgressAt(store, taskId, roleName, run, events, progressFacts) {
173
- let richerProgress;
174
- try {
175
- richerProgress = store.getRunDurableProgress?.(taskId, roleName, run.id);
176
- }
177
- catch {
178
- // A related-record fold is advisory. The retained Run/Event fence remains
179
- // usable when that optional read is unavailable.
180
- richerProgress = undefined;
181
- }
182
- if (run.deliveredAt === undefined)
183
- return { progressAt: run.createdAt };
184
- // The richer fold is authoritative when present; skip the per-candidate
185
- // full-history scans that every stall candidate otherwise pays.
186
- if (richerProgress !== undefined && richerProgress !== null) {
169
+ export function currentRoleTurnProgressAt(store, taskId, roleName, run) {
170
+ const progress = store.getTurnDurableProgress(taskId, roleName, run.id);
171
+ if (progress !== null) {
187
172
  return {
188
- progressAt: richerProgress.progressAt,
189
- ...(richerProgress.evidence === undefined
173
+ progressAt: progress.progressAt,
174
+ ...(progress.evidence === undefined
190
175
  ? {}
191
- : { evidence: richerProgress.evidence })
176
+ : { evidence: progress.evidence })
192
177
  };
193
178
  }
194
- // A present fold port is authoritative even when this Run has no entry. Do
195
- // not evaluate a default history argument or fall back to listEvents in
196
- // that case: an absent entry is the folded empty fact.
197
- const folded = store.getRunProgressFacts !== undefined;
198
- const foldedFacts = folded
199
- ? progressFacts ?? store.getRunProgressFacts?.(taskId, run.id)
200
- : undefined;
201
- if (folded) {
202
- const fallbackProgressAt = latestDurableProgressAt({
203
- deliveredAt: run.deliveredAt,
204
- latestCheckpointAt: foldedFacts?.latestCheckpointAt,
205
- latestActivityAt: foldedFacts?.latestActivityAt
206
- });
207
- return { progressAt: fallbackProgressAt };
208
- }
209
- // Legacy ports retain the per-Run event-history fallback. Resolve the
210
- // history only in this branch so fold-backed callers perform zero scans.
211
- const history = events ?? store.listEvents?.(taskId) ?? [];
212
- const fallbackProgressAt = latestDurableProgressAt({
213
- deliveredAt: run.deliveredAt,
214
- latestCheckpointAt: latestRunProgressAt(history, run.id),
215
- latestActivityAt: latestRunActivityAt(history, run.id)
216
- });
217
- return {
218
- progressAt: fallbackProgressAt
219
- };
179
+ return { progressAt: run.createdAt };
220
180
  }
221
181
  /**
222
- * Computes the current semantic progress fence for an exact Run. The optional
223
- * related-record readers mirror the adapter's Work/Review/Integration fold;
224
- * narrow scheduler ports can omit them and still retain Run/Event evidence.
182
+ * Computes the current semantic progress fence for an exact Turn. The optional
183
+ * related-record readers add Work/Review/Integration evidence to the Turn and
184
+ * event facts used by on-demand projections.
225
185
  */
226
- export function latestRunDurableProgressAt(store, taskId, roleName, runId, precomputed) {
227
- const run = store.getAgentRun(taskId, runId);
186
+ export function latestTurnDurableProgressAt(store, taskId, roleName, turnId, precomputed) {
187
+ const run = store.getTurn(taskId, turnId);
228
188
  if (run === null || run.taskId !== taskId || run.roleName !== roleName)
229
189
  return null;
230
- // The adapter folds checkpoint/activity once per revision. When the fold
231
- // port exists, a missing per-Run entry is an authoritative empty fold: use
232
- // its (possibly undefined) values directly and never re-scan the whole
233
- // history per candidate. Legacy callers without the port omit precomputed
234
- // and keep the per-candidate full-history scans.
190
+ // Scheduler reconciliation supplies its once-per-revision fold; on-demand
191
+ // views omit it and compute the same facts from the Task event history.
235
192
  const folded = precomputed !== undefined;
236
193
  const events = folded ? undefined : store.listEvents(taskId);
237
194
  const latestCheckpointAt = folded
238
195
  ? precomputed.latestCheckpointAt
239
- : latestRunProgressAt(events, run.id);
196
+ : latestTurnProgressAt(events, run.id);
240
197
  const latestActivityAt = folded
241
198
  ? precomputed.latestActivityAt
242
- : latestRunActivityAt(events, run.id);
243
- const baseline = run.deliveredAt === undefined
244
- ? run.createdAt
245
- : latestDurableProgressAt({
246
- deliveredAt: run.deliveredAt,
247
- latestCheckpointAt,
248
- latestActivityAt
249
- });
199
+ : latestTurnActivityAt(events, run.id);
200
+ const baseline = latestDurableProgressAt({
201
+ startedAt: run.createdAt,
202
+ latestCheckpointAt,
203
+ latestActivityAt
204
+ });
250
205
  if (run.workItemId === undefined) {
251
206
  return { progressAt: baseline };
252
207
  }
@@ -262,8 +217,8 @@ export function latestRunDurableProgressAt(store, taskId, roleName, runId, preco
262
217
  .filter(({ changeSetIds: ids }) => ids.some((id) => changeSetIds.has(id)))
263
218
  ?? [];
264
219
  const inputProgress = store.listInputRequests?.(taskId)
265
- .filter((request) => (request.requester.runId === run.id
266
- || request.blockedRefs.some((ref) => ref.type === "run" && ref.id === run.id)))
220
+ .filter((request) => (request.requester.turnId === run.id
221
+ || request.blockedRefs.some((ref) => ref.type === "turn" && ref.id === run.id)))
267
222
  ?? [];
268
223
  const related = [
269
224
  workItem?.updatedAt,
@@ -276,10 +231,7 @@ export function latestRunDurableProgressAt(store, taskId, roleName, runId, preco
276
231
  const latestRelatedAt = related.reduce((latest, value) => latest === undefined || Date.parse(value) > Date.parse(latest)
277
232
  ? value
278
233
  : latest, undefined);
279
- // Before provider acceptance the Run clock remains anchored to creation,
280
- // while related records are retained as diagnostic evidence only.
281
- return run.deliveredAt !== undefined
282
- && latestRelatedAt !== undefined
234
+ return latestRelatedAt !== undefined
283
235
  && Date.parse(latestRelatedAt) > Date.parse(baseline)
284
236
  ? { progressAt: latestRelatedAt, evidence: "work-review-integration" }
285
237
  : {
@@ -287,11 +239,11 @@ export function latestRunDurableProgressAt(store, taskId, roleName, runId, preco
287
239
  ...(latestRelatedAt === undefined ? {} : { evidence: "work-review-integration" })
288
240
  };
289
241
  }
290
- /** Most recent createdAt of a Run-scoped event of one type, if any. */
291
- export function latestRunEventTime(events, type, runId) {
242
+ /** Most recent createdAt of a Turn-scoped event of one type, if any. */
243
+ export function latestTurnEventTime(events, type, turnId) {
292
244
  let latest;
293
245
  for (const event of events) {
294
- if (event.type !== type || event.payload.runId !== runId)
246
+ if (event.type !== type || event.payload.turnId !== turnId)
295
247
  continue;
296
248
  if (latest === undefined || Date.parse(event.createdAt) > Date.parse(latest)) {
297
249
  latest = event.createdAt;
@@ -299,16 +251,16 @@ export function latestRunEventTime(events, type, runId) {
299
251
  }
300
252
  return latest;
301
253
  }
302
- /** Most recent non-control event carrying a Run identity. */
303
- export function latestRunActivityAt(events, runId) {
254
+ /** Most recent non-control event carrying a Turn identity. */
255
+ export function latestTurnActivityAt(events, turnId) {
304
256
  let latest;
305
257
  for (const event of events) {
306
258
  if (!ACTIVITY_EVENT_TYPES.has(event.type)
307
- || event.payload.runId !== runId
308
- || event.type === RUN_STALLED_EVENT
309
- || event.type === RUN_RECOVERED_EVENT)
259
+ || event.payload.turnId !== turnId
260
+ || event.type === TURN_STALLED_EVENT
261
+ || event.type === TURN_RECOVERED_EVENT)
310
262
  continue;
311
- const activityAt = event.type === RUN_PROGRESS_EVENT
263
+ const activityAt = event.type === TURN_PROGRESS_EVENT
312
264
  && typeof event.payload.progressAt === "string"
313
265
  && Number.isFinite(Date.parse(event.payload.progressAt))
314
266
  ? event.payload.progressAt
@@ -319,11 +271,11 @@ export function latestRunActivityAt(events, runId) {
319
271
  }
320
272
  return latest;
321
273
  }
322
- /** The progress point recorded by the most recent stall attention for a Run. */
323
- export function latestStallProgressAt(events, runId) {
274
+ /** The progress point recorded by the most recent stall attention for a Turn. */
275
+ export function latestStallProgressAt(events, turnId) {
324
276
  let latest;
325
277
  for (const event of events) {
326
- if (event.type !== RUN_STALLED_EVENT || event.payload.runId !== runId)
278
+ if (event.type !== TURN_STALLED_EVENT || event.payload.turnId !== turnId)
327
279
  continue;
328
280
  if (latest === undefined || Date.parse(event.createdAt) > Date.parse(latest.createdAt)) {
329
281
  latest = event;
@@ -332,10 +284,10 @@ export function latestStallProgressAt(events, runId) {
332
284
  return latest?.payload.progressAt;
333
285
  }
334
286
  /** Latest stall episode identity used for source-idempotent attention. */
335
- export function latestStallEvidenceKey(events, runId) {
287
+ export function latestStallEvidenceKey(events, turnId) {
336
288
  let latest;
337
289
  for (const event of events) {
338
- if (event.type !== RUN_STALLED_EVENT || event.payload.runId !== runId)
290
+ if (event.type !== TURN_STALLED_EVENT || event.payload.turnId !== turnId)
339
291
  continue;
340
292
  if (latest === undefined || Date.parse(event.createdAt) > Date.parse(latest.createdAt)) {
341
293
  latest = event;
@@ -349,27 +301,27 @@ export function latestStallEvidenceKey(events, runId) {
349
301
  };
350
302
  }
351
303
  /**
352
- * Folds a Task's event history into per-Run progress facts in one O(events)
304
+ * Folds a Task's event history into per-Turn progress facts in one O(events)
353
305
  * pass. The adapter builds this once per durable revision and serves the
354
306
  * stall reconciliation from it, replacing the per-candidate full-history
355
307
  * clones and scans that turned one pass into an O(candidates x events) hot
356
- * read. The fold mirrors latestRunProgressAt/latestRunActivityAt/
308
+ * read. The fold mirrors latestTurnProgressAt/latestTurnActivityAt/
357
309
  * latestStallProgressAt/latestStallEvidenceKey exactly.
358
310
  */
359
- export function foldRunProgressFacts(events) {
360
- const byRun = new Map();
311
+ export function foldTurnProgressFacts(events) {
312
+ const byTurn = new Map();
361
313
  const latestStallCreatedAt = new Map();
362
314
  for (const event of events) {
363
- const runId = event.payload.runId;
364
- if (typeof runId !== "string")
315
+ const turnId = event.payload.turnId;
316
+ if (typeof turnId !== "string")
365
317
  continue;
366
- let facts = byRun.get(runId);
318
+ let facts = byTurn.get(turnId);
367
319
  if (facts === undefined) {
368
320
  facts = {};
369
- byRun.set(runId, facts);
321
+ byTurn.set(turnId, facts);
370
322
  }
371
323
  const type = event.type;
372
- const isProgress = type === RUN_PROGRESS_EVENT;
324
+ const isProgress = type === TURN_PROGRESS_EVENT;
373
325
  if (isProgress) {
374
326
  const validProgress = typeof event.payload.progressAt === "string"
375
327
  && Number.isFinite(Date.parse(event.payload.progressAt));
@@ -386,11 +338,11 @@ export function foldRunProgressFacts(events) {
386
338
  facts.latestActivityAt = progressAt;
387
339
  }
388
340
  }
389
- else if (type === RUN_STALLED_EVENT) {
390
- const previousCreatedAt = latestStallCreatedAt.get(runId);
341
+ else if (type === TURN_STALLED_EVENT) {
342
+ const previousCreatedAt = latestStallCreatedAt.get(turnId);
391
343
  if (previousCreatedAt === undefined
392
344
  || Date.parse(event.createdAt) > Date.parse(previousCreatedAt)) {
393
- latestStallCreatedAt.set(runId, event.createdAt);
345
+ latestStallCreatedAt.set(turnId, event.createdAt);
394
346
  facts.latestStall = typeof event.payload.progressAt === "string"
395
347
  ? {
396
348
  progressAt: event.payload.progressAt,
@@ -402,33 +354,33 @@ export function foldRunProgressFacts(events) {
402
354
  }
403
355
  }
404
356
  else if (ACTIVITY_EVENT_TYPES.has(type)
405
- && type !== RUN_STALLED_EVENT
406
- && type !== RUN_RECOVERED_EVENT) {
357
+ && type !== TURN_STALLED_EVENT
358
+ && type !== TURN_RECOVERED_EVENT) {
407
359
  if (facts.latestActivityAt === undefined
408
360
  || Date.parse(event.createdAt) > Date.parse(facts.latestActivityAt)) {
409
361
  facts.latestActivityAt = event.createdAt;
410
362
  }
411
363
  }
412
364
  }
413
- return byRun;
365
+ return byTurn;
414
366
  }
415
367
  /** Yield the event loop so already-written control requests stay responsive. */
416
368
  function yieldEventLoop() {
417
369
  return new Promise((resolve) => setImmediate(resolve));
418
370
  }
419
371
  /**
420
- * True while a Run remains in an unresolved stall episode: a stall was raised
372
+ * True while a Turn remains in an unresolved stall episode: a stall was raised
421
373
  * and no later checkpoint has advanced its durable progress since. This is the
422
374
  * projection the runtime-health view reads to surface needs-attention.
423
375
  */
424
- export function isRoleRunStalled(events, runId) {
376
+ export function isRoleTurnStalled(events, turnId) {
425
377
  let stalled;
426
378
  let recoveredAt;
427
379
  let progressAt;
428
380
  for (const event of events) {
429
- if (event.payload.runId !== runId)
381
+ if (event.payload.turnId !== turnId)
430
382
  continue;
431
- if (event.type === RUN_STALLED_EVENT) {
383
+ if (event.type === TURN_STALLED_EVENT) {
432
384
  if (event.payload.status === "diagnostic-only")
433
385
  continue;
434
386
  if (stalled === undefined
@@ -437,14 +389,14 @@ export function isRoleRunStalled(events, runId) {
437
389
  }
438
390
  continue;
439
391
  }
440
- if (event.type === RUN_RECOVERED_EVENT) {
392
+ if (event.type === TURN_RECOVERED_EVENT) {
441
393
  if (recoveredAt === undefined
442
394
  || Date.parse(event.createdAt) > Date.parse(recoveredAt)) {
443
395
  recoveredAt = event.createdAt;
444
396
  }
445
397
  continue;
446
398
  }
447
- if (event.type !== RUN_PROGRESS_EVENT)
399
+ if (event.type !== TURN_PROGRESS_EVENT)
448
400
  continue;
449
401
  const candidate = typeof event.payload.progressAt === "string"
450
402
  && Number.isFinite(Date.parse(event.payload.progressAt))
@@ -467,32 +419,26 @@ export function isRoleRunStalled(events, runId) {
467
419
  return progressAt === undefined || Date.parse(progressAt) <= Date.parse(stalledProgressAt);
468
420
  }
469
421
  /**
470
- * Low-frequency health pass for active Task Role Runs. An unaccepted Run is
422
+ * Low-frequency health pass for active Task Role Turns. An unaccepted Turn is
471
423
  * watched as delivery-stalled after the reasonable delivery window; an
472
- * accepted Run is displayed as checkpoint-overdue after fifteen minutes, but
424
+ * accepted Turn is displayed as checkpoint-overdue after fifteen minutes, but
473
425
  * this scheduler performs no runtime inspection until the thirty-minute
474
426
  * diagnostic window is due.
475
- * Leader Runs are only persisted when classification reaches truly-stalled —
427
+ * Leader Turns are only persisted when classification reaches truly-stalled —
476
428
  * healthy downstream work, open user input, and recent own progress remain
477
429
  * structured waiting/working facts. No branch sends terminal bytes, retries,
478
- * replaces a Session, or changes Run status.
430
+ * replaces a Session, or changes Turn status.
479
431
  */
480
- export async function reconcileStalledRoleRuns(store, delivery, now, selection, windowMs = DEFAULT_STALL_WINDOW_MS, liveStatuses, resourceEvidence, diagnosticAfterMs = DEFAULT_WORKFLOW_STALL_CANDIDATE_AGE_MS) {
432
+ export async function reconcileStalledRoleTurns(store, delivery, now, selection, windowMs = DEFAULT_STALL_WINDOW_MS, liveStatuses, resourceEvidence, diagnosticAfterMs = DEFAULT_WORKFLOW_STALL_CANDIDATE_AGE_MS) {
481
433
  // Dirty mailbox passes are intentionally not a second scheduler. Full
482
- // reconcile owns the all-active-Run scan; dirty passes may still route the
434
+ // reconcile owns the all-active-Turn scan; dirty passes may still route the
483
435
  // existing mailbox work without manufacturing another episode.
484
436
  if (selection !== undefined && !selection.full)
485
437
  return [];
486
- if ((store.getRunProgressFacts === undefined && store.listEvents === undefined)
487
- || store.recordRoleRunStall === undefined)
438
+ if (store.recordRoleTurnStall === undefined)
488
439
  return [];
489
- // When the fold port exists, a missing per-Run entry is an authoritative
490
- // empty fold: the stall reconciliation must not re-scan the whole history
491
- // per candidate. Legacy stores without the port keep the per-candidate
492
- // scans.
493
- const foldPortPresent = store.getRunProgressFacts !== undefined;
494
440
  const candidates = selectedActiveSchedulerTasks(store, selection).flatMap((task) => (selectedSchedulerRoles(store, task.id, selection).flatMap((role) => {
495
- const run = store.getActiveAgentRun(task.id, role.name);
441
+ const run = store.getActiveTurn(task.id, role.name);
496
442
  if (run === null || run.status !== "active")
497
443
  return [];
498
444
  return [{
@@ -511,23 +457,20 @@ export async function reconcileStalledRoleRuns(store, delivery, now, selection,
511
457
  eventsByTask.set(taskId, events);
512
458
  return events;
513
459
  };
514
- const cadenceCandidates = candidates.filter(({ task, run }) => isStallCandidate(run, now, windowMs, latestRunEventTime(diagnosticEvents(task.id), RUN_DIAGNOSTIC_FINISHED_EVENT, run.id)));
460
+ const cadenceCandidates = candidates.filter(({ task, run }) => isStallCandidate(run, now, windowMs, latestTurnEventTime(diagnosticEvents(task.id), TURN_DIAGNOSTIC_FINISHED_EVENT, run.id)));
515
461
  const stallCandidates = cadenceCandidates.filter(({ task, role, run }) => {
516
- const progressFacts = foldPortPresent
517
- ? store.getRunProgressFacts?.(task.id, run.id)
518
- : undefined;
519
- const progressAt = currentRoleRunProgressAt(store, task.id, role.name, run, foldPortPresent ? undefined : diagnosticEvents(task.id), progressFacts).progressAt;
520
- return isStallCandidate(run, now, windowMs, latestRunEventTime(diagnosticEvents(task.id), RUN_DIAGNOSTIC_FINISHED_EVENT, run.id), progressAt);
462
+ const progressAt = currentRoleTurnProgressAt(store, task.id, role.name, run).progressAt;
463
+ return isStallCandidate(run, now, windowMs, latestTurnEventTime(diagnosticEvents(task.id), TURN_DIAGNOSTIC_FINISHED_EVENT, run.id), progressAt);
521
464
  });
522
465
  if (stallCandidates.length === 0)
523
466
  return [];
524
467
  const diagnosticStartedAt = now.toISOString();
525
468
  const finishDiagnostics = (outcome) => {
526
469
  for (const { task, role, run } of stallCandidates) {
527
- store.recordRoleRunDiagnostic?.({
470
+ store.recordRoleTurnDiagnostic?.({
528
471
  taskId: task.id,
529
472
  roleName: role.name,
530
- runId: run.id,
473
+ turnId: run.id,
531
474
  startedAt: diagnosticStartedAt,
532
475
  outcome,
533
476
  now
@@ -536,7 +479,7 @@ export async function reconcileStalledRoleRuns(store, delivery, now, selection,
536
479
  };
537
480
  const stallCandidateKeys = new Set(stallCandidates.map(({ task, role }) => (`${task.id}\0${role.name}`)));
538
481
  const observed = new Map();
539
- // Recent active Runs are known healthy enough for Leader classification from
482
+ // Recent active Turns are known healthy enough for Leader classification from
540
483
  // their exact acceptance/creation boundary, but are deliberately not read
541
484
  // from tmux or Event history until the 30-minute diagnostic window.
542
485
  for (const candidate of candidates) {
@@ -546,7 +489,7 @@ export async function reconcileStalledRoleRuns(store, delivery, now, selection,
546
489
  observed.set(key, {
547
490
  candidate,
548
491
  live: liveStatuses?.get(key) ?? "present",
549
- progressAt: candidate.run.deliveredAt ?? candidate.run.createdAt,
492
+ progressAt: candidate.run.createdAt,
550
493
  idleMs: 0,
551
494
  stalled: false,
552
495
  resourceActivity: false,
@@ -562,32 +505,30 @@ export async function reconcileStalledRoleRuns(store, delivery, now, selection,
562
505
  statuses = await delivery.inspectRoles(stallCandidates.map(({ task, role, run, session }) => ({
563
506
  taskId: task.id,
564
507
  roleName: role.name,
565
- runId: run.id,
566
- progressAt: run.deliveredAt ?? run.createdAt,
508
+ turnId: run.id,
509
+ progressAt: run.createdAt,
567
510
  agentId: session?.agentId ?? run.effective.agentId,
568
511
  adapterId: session?.adapterId ?? run.effective.adapterId,
569
512
  ...(session?.launchId === undefined ? {} : { launchId: session.launchId }),
570
513
  ...(session?.nativeSessionId === undefined
571
514
  ? {}
572
515
  : { nativeSessionId: session.nativeSessionId })
573
- })), stallCandidates.flatMap(({ task, role, run, session }) => (run.deliveredAt === undefined
574
- ? []
575
- : [{
576
- taskId: task.id,
577
- roleName: role.name,
578
- runId: run.id,
579
- progressAt: currentRoleRunProgressAt(store, task.id, role.name, run).progressAt,
580
- agentId: session?.agentId ?? run.effective.agentId,
581
- adapterId: session?.adapterId ?? run.effective.adapterId,
582
- ...(session?.launchId === undefined ? {} : { launchId: session.launchId }),
583
- ...(session?.nativeSessionId === undefined
584
- ? {}
585
- : { nativeSessionId: session.nativeSessionId })
586
- }])));
516
+ })), stallCandidates.map(({ task, role, run, session }) => ({
517
+ taskId: task.id,
518
+ roleName: role.name,
519
+ turnId: run.id,
520
+ progressAt: currentRoleTurnProgressAt(store, task.id, role.name, run).progressAt,
521
+ agentId: session?.agentId ?? run.effective.agentId,
522
+ adapterId: session?.adapterId ?? run.effective.adapterId,
523
+ ...(session?.launchId === undefined ? {} : { launchId: session.launchId }),
524
+ ...(session?.nativeSessionId === undefined
525
+ ? {}
526
+ : { nativeSessionId: session.nativeSessionId })
527
+ })));
587
528
  }
588
529
  catch {
589
530
  // Health inspection is advisory. Unknown host state must leave the exact
590
- // Run/Session fence untouched. Closing this window schedules the next
531
+ // Turn/Session fence untouched. Closing this window schedules the next
591
532
  // bounded diagnostic thirty minutes later instead of hot-looping.
592
533
  finishDiagnostics("observation-error");
593
534
  return [];
@@ -634,7 +575,7 @@ export async function reconcileStalledRoleRuns(store, delivery, now, selection,
634
575
  : byRole.get(key);
635
576
  }
636
577
  catch {
637
- // Without a complete live snapshot, especially for downstream Runs,
578
+ // Without a complete live snapshot, especially for downstream Turns,
638
579
  // Leader classification would be unsafe. Treat the whole advisory pass
639
580
  // as unknown instead of escalating a false stall.
640
581
  finishDiagnostics("observation-error");
@@ -654,30 +595,16 @@ export async function reconcileStalledRoleRuns(store, delivery, now, selection,
654
595
  });
655
596
  continue;
656
597
  }
657
- // Fold-backed adapters expose an authoritative per-Run fact, so do not
658
- // load the Task history just to satisfy currentRoleRunProgressAt or its
659
- // fallback. Legacy ports retain one history read for these projections.
660
- const progressFacts = foldPortPresent
661
- ? store.getRunProgressFacts?.(candidate.task.id, candidate.run.id)
662
- : undefined;
663
- const events = foldPortPresent
664
- ? undefined
665
- : store.listEvents?.(candidate.task.id);
666
- // Before exact acceptance there is no execution progress clock. Keep the
667
- // delivery watch anchored to the Run creation/transport boundary even if
668
- // checkpoints, output, or related WorkItem/Review/Integration records are
669
- // newer; those facts are useful evidence but cannot prove provider
670
- // acceptance or reset delivery timeout. Once accepted, deliveredAt is the
671
- // semantic baseline and the durable fold may advance it.
672
- const progress = currentRoleRunProgressAt(store, candidate.task.id, candidate.role.name, candidate.run, events, progressFacts);
598
+ const progressFacts = store.getTurnProgressFacts(candidate.task.id, candidate.run.id);
599
+ // The Turn creation boundary starts the execution clock; durable workflow
600
+ // facts may advance it independently of Provider transport observations.
601
+ const progress = currentRoleTurnProgressAt(store, candidate.task.id, candidate.role.name, candidate.run);
673
602
  const progressAt = progress.progressAt;
674
- const evaluation = evaluateRoleRunStall({
603
+ const evaluation = evaluateRoleTurnStall({
675
604
  progressAt,
676
605
  now,
677
606
  windowMs,
678
- lastAttentionProgressAt: foldPortPresent
679
- ? progressFacts?.latestStall?.progressAt
680
- : latestStallProgressAt(events ?? [], candidate.run.id)
607
+ lastAttentionProgressAt: progressFacts?.latestStall?.progressAt
681
608
  });
682
609
  const runAgentId = candidate.run.effective.agentId;
683
610
  const runAdapterId = candidate.run.effective.adapterId;
@@ -691,7 +618,7 @@ export async function reconcileStalledRoleRuns(store, delivery, now, selection,
691
618
  : {
692
619
  taskId: candidate.task.id,
693
620
  roleName: candidate.role.name,
694
- runId: candidate.run.id,
621
+ turnId: candidate.run.id,
695
622
  agentId: runAgentId,
696
623
  adapterId: runAdapterId,
697
624
  ...(candidate.session.nativeSessionId === undefined
@@ -709,30 +636,23 @@ export async function reconcileStalledRoleRuns(store, delivery, now, selection,
709
636
  && resourceEvidenceMatchesCurrentRun(resourceSnapshot, expectedResourceIdentity, progressAt)
710
637
  && resourceEvidenceIsFresh(resourceSnapshot, now, windowMs, diagnosticAfterMs);
711
638
  const resource = resourceIsCurrent ? resourceSnapshot : undefined;
712
- const health = projectRoleRunHealth({
639
+ const health = projectRoleTurnHealth({
713
640
  progressAt,
714
641
  createdAt: candidate.run.createdAt,
715
- ...(candidate.run.deliveredAt === undefined
716
- ? {}
717
- : { deliveredAt: candidate.run.deliveredAt }),
718
642
  now,
719
643
  windowMs,
720
644
  diagnosticAfterMs,
721
645
  hostLiveness: live,
722
646
  nativeSession: candidate.session,
723
- providerAcceptance: candidate.run.deliveredAt === undefined
724
- ? "ambiguous"
725
- : "accepted",
647
+ providerAcceptance: candidate.session === null ? "ambiguous" : "accepted",
726
648
  resource,
727
649
  roleName: candidate.role.name
728
650
  });
729
- // Delivery-stalled Runs retain the existing delivery clock and immediate
730
- // provider-uncertainty path. Accepted execution Runs use the shared
651
+ // Delivery-stalled Turns retain the existing delivery clock and immediate
652
+ // Provider-uncertainty path. Accepted execution Turns use the shared
731
653
  // projection. Exact Session/host resource evidence remains diagnostic.
732
654
  const resourceActivity = health.resourceActivity;
733
- const stalled = candidate.run.deliveredAt === undefined
734
- ? evaluation.stalled && live === "present"
735
- : health.stalled && sessionMatchesRun && sessionUsable;
655
+ const stalled = health.stalled && sessionMatchesRun && sessionUsable;
736
656
  observed.set(key, {
737
657
  candidate,
738
658
  live,
@@ -753,24 +673,22 @@ export async function reconcileStalledRoleRuns(store, delivery, now, selection,
753
673
  if (current.live !== "present" || !current.stalled || !current.stallCandidate)
754
674
  continue;
755
675
  const { candidate, progressAt } = current;
756
- const previous = foldPortPresent
757
- ? store.getRunProgressFacts?.(candidate.task.id, candidate.run.id)?.latestStall
758
- : latestStallEvidenceKey(store.listEvents?.(candidate.task.id) ?? [], candidate.run.id);
676
+ const previous = store.getTurnProgressFacts(candidate.task.id, candidate.run.id)?.latestStall;
759
677
  if (previous !== undefined
760
678
  && Date.parse(progressAt) > Date.parse(previous.progressAt)) {
761
679
  // A new semantic progress point closes the previous episode first. It
762
680
  // may itself already be older than the window, in which case the same
763
- // pass records the next Run+progressAt episode below.
764
- store.recordRoleRunProgress?.({
681
+ // pass records the next Turn+progressAt episode below.
682
+ store.recordRoleTurnProgress?.({
765
683
  taskId: candidate.task.id,
766
684
  roleName: candidate.role.name,
767
- runId: candidate.run.id,
685
+ turnId: candidate.run.id,
768
686
  progressAt,
769
687
  ...(current.evidence === undefined ? {} : { evidence: current.evidence }),
770
688
  now
771
689
  });
772
690
  }
773
- const kind = candidate.run.deliveredAt === undefined
691
+ const kind = candidate.session === null
774
692
  ? "delivery-stalled"
775
693
  : "workflow-not-progressing";
776
694
  const classification = candidate.role.name === "leader"
@@ -789,15 +707,15 @@ export async function reconcileStalledRoleRuns(store, delivery, now, selection,
789
707
  : []),
790
708
  ...(current.evidence === undefined ? [] : [current.evidence])
791
709
  ].join(":");
792
- // The final contract is one episode per Run + semantic progress point.
710
+ // The final contract is one episode per Turn + semantic progress point.
793
711
  // New role/session/provider evidence is retained in the Leader's next
794
712
  // diagnostic context, not duplicated as another Task-level alert.
795
713
  if (previous?.progressAt === progressAt)
796
714
  continue;
797
- const persisted = store.recordRoleRunStall({
715
+ const persisted = store.recordRoleTurnStall({
798
716
  taskId: candidate.task.id,
799
717
  roleName: candidate.role.name,
800
- runId: candidate.run.id,
718
+ turnId: candidate.run.id,
801
719
  agentId: candidate.run.effective.agentId,
802
720
  adapterId: candidate.run.effective.adapterId,
803
721
  session: candidate.session,
@@ -812,7 +730,7 @@ export async function reconcileStalledRoleRuns(store, delivery, now, selection,
812
730
  raised.push({
813
731
  taskId: candidate.task.id,
814
732
  roleName: candidate.role.name,
815
- runId: candidate.run.id,
733
+ turnId: candidate.run.id,
816
734
  status: persisted,
817
735
  kind,
818
736
  classification,
@@ -821,21 +739,19 @@ export async function reconcileStalledRoleRuns(store, delivery, now, selection,
821
739
  }
822
740
  }
823
741
  // A related WorkItem/Review/Integration fold may advance progress without
824
- // carrying the Run id. Materialize it once so context/web can clear the
742
+ // carrying the Turn id. Materialize it once so context/web can clear the
825
743
  // attention projection and the event history records the recovery boundary.
826
744
  for (const current of observed.values()) {
827
745
  if (current.live !== "present" || current.stalled || !current.stallCandidate)
828
746
  continue;
829
747
  const { candidate, progressAt } = current;
830
- const previous = foldPortPresent
831
- ? store.getRunProgressFacts?.(candidate.task.id, candidate.run.id)?.latestStall
832
- : latestStallEvidenceKey(store.listEvents?.(candidate.task.id) ?? [], candidate.run.id);
748
+ const previous = store.getTurnProgressFacts(candidate.task.id, candidate.run.id)?.latestStall;
833
749
  if (previous !== undefined
834
750
  && Date.parse(progressAt) > Date.parse(previous.progressAt)) {
835
- store.recordRoleRunProgress?.({
751
+ store.recordRoleTurnProgress?.({
836
752
  taskId: candidate.task.id,
837
753
  roleName: candidate.role.name,
838
- runId: candidate.run.id,
754
+ turnId: candidate.run.id,
839
755
  progressAt,
840
756
  ...(current.evidence === undefined ? {} : { evidence: current.evidence }),
841
757
  now
@@ -846,8 +762,7 @@ export async function reconcileStalledRoleRuns(store, delivery, now, selection,
846
762
  return raised;
847
763
  }
848
764
  function isStallCandidate(run, now, windowMs, lastDiagnosticFinishedAt, latestProgressAt) {
849
- const startAt = run.deliveredAt ?? run.createdAt;
850
- const baseline = [startAt, lastDiagnosticFinishedAt, latestProgressAt]
765
+ const baseline = [run.createdAt, lastDiagnosticFinishedAt, latestProgressAt]
851
766
  .filter((value) => (value !== undefined && Number.isFinite(Date.parse(value))))
852
767
  .reduce((latest, value) => (Date.parse(value) > Date.parse(latest) ? value : latest));
853
768
  const ageMs = now.getTime() - Date.parse(baseline);
@@ -859,7 +774,7 @@ function classifyLeaderStall(store, taskId, observed, now, windowMs) {
859
774
  if (store.hasOpenInputRequest(taskId))
860
775
  return "waiting-user";
861
776
  const downstream = [...observed.values()].filter((entry) => (entry.candidate.task.id === taskId && entry.candidate.role.name !== "leader"));
862
- // A present downstream Run keeps recovery Leader-owned. If that Run is
777
+ // A present downstream Turn keeps recovery Leader-owned. If that Turn is
863
778
  // itself stalled, this pass has just routed its structured attention to the
864
779
  // Leader; escalating the Leader to the Operator in the same pass would skip
865
780
  // the intended recovery owner.
@@ -868,13 +783,13 @@ function classifyLeaderStall(store, taskId, observed, now, windowMs) {
868
783
  const mailbox = store.getWorkMailbox({ kind: "role", taskId, roleName: "leader" });
869
784
  const pending = mailbox === null || mailbox === undefined ? null : nextPendingBatch(mailbox);
870
785
  const processing = mailbox?.processing;
871
- const processingCurrent = processing?.executionRef?.type === "run"
786
+ const processingCurrent = processing?.executionRef?.type === "turn"
872
787
  && processing.executionRef.taskId === taskId
873
788
  && leader !== undefined
874
789
  && processing.executionRef.id === leader.candidate.run.id;
875
- const currentRunId = leader?.candidate.run.id;
876
- if (processingCurrent && currentRunId !== undefined && processing !== null) {
877
- const actionProgressAt = latestLeaderActionProgressAt(store, taskId, currentRunId, processing.startedAt, processing.batch, now);
790
+ const currentTurnId = leader?.candidate.run.id;
791
+ if (processingCurrent && currentTurnId !== undefined && processing !== null) {
792
+ const actionProgressAt = latestLeaderActionProgressAt(store, taskId, currentTurnId, processing.startedAt, processing.batch, now);
878
793
  const actionProgressMs = Date.parse(actionProgressAt);
879
794
  const actionStalled = Number.isFinite(actionProgressMs)
880
795
  && now.getTime() - actionProgressMs >= windowMs;
@@ -893,7 +808,7 @@ function classifyLeaderStall(store, taskId, observed, now, windowMs) {
893
808
  return "truly-stalled";
894
809
  }
895
810
  const LEADER_ACTION_PROGRESS_TYPES = new Set([
896
- RUN_PROGRESS_EVENT,
811
+ TURN_PROGRESS_EVENT,
897
812
  "input.answered",
898
813
  "input.auto-answered",
899
814
  "input.cancelled",
@@ -909,7 +824,7 @@ const LEADER_ACTION_PROGRESS_TYPES = new Set([
909
824
  "decision.superseded",
910
825
  "milestone.added"
911
826
  ]);
912
- function latestLeaderActionProgressAt(store, taskId, runId, startedAt, batch, now) {
827
+ function latestLeaderActionProgressAt(store, taskId, turnId, startedAt, batch, now) {
913
828
  const startedMs = Date.parse(startedAt);
914
829
  if (!Number.isFinite(startedMs))
915
830
  return "";
@@ -920,14 +835,14 @@ function latestLeaderActionProgressAt(store, taskId, runId, startedAt, batch, no
920
835
  continue;
921
836
  if (event.payload.taskId !== undefined && event.payload.taskId !== taskId)
922
837
  continue;
923
- if (!leaderActionEventMatches(event, taskId, runId, batch))
838
+ if (!leaderActionEventMatches(event, taskId, turnId, batch))
924
839
  continue;
925
840
  const createdMs = Date.parse(event.createdAt);
926
841
  if (!Number.isFinite(createdMs)
927
842
  || createdMs < startedMs
928
843
  || createdMs > now.getTime())
929
844
  continue;
930
- const value = event.type === RUN_PROGRESS_EVENT
845
+ const value = event.type === TURN_PROGRESS_EVENT
931
846
  && typeof event.payload.progressAt === "string"
932
847
  && Number.isFinite(Date.parse(event.payload.progressAt))
933
848
  ? event.payload.progressAt
@@ -943,12 +858,12 @@ function latestLeaderActionProgressAt(store, taskId, runId, startedAt, batch, no
943
858
  }
944
859
  return latest;
945
860
  }
946
- function leaderActionEventMatches(event, taskId, runId, batch) {
861
+ function leaderActionEventMatches(event, taskId, turnId, batch) {
947
862
  if (event.taskId !== taskId)
948
863
  return false;
949
- if (event.payload.leaderRunId === runId)
864
+ if (event.payload.leaderTurnId === turnId)
950
865
  return true;
951
- if (event.payload.runId === runId)
866
+ if (event.payload.turnId === turnId)
952
867
  return true;
953
868
  const refs = batch.refs ?? [];
954
869
  const refMatches = (type, payloadKey) => {
@@ -957,7 +872,7 @@ function leaderActionEventMatches(event, taskId, runId, batch) {
957
872
  && ref.id === id
958
873
  && (ref.taskId === undefined || ref.taskId === taskId)));
959
874
  };
960
- if (refMatches("run", "runId"))
875
+ if (refMatches("turn", "turnId"))
961
876
  return true;
962
877
  if (refMatches("work-item", "workItemId"))
963
878
  return true;
@@ -983,7 +898,7 @@ function leaderStallEvidence(store, taskId, observed, now, windowMs) {
983
898
  : "recent";
984
899
  const processing = store.getWorkMailbox({ kind: "role", taskId, roleName: "leader" })?.processing;
985
900
  const leader = [...observed.values()].find((entry) => (entry.candidate.task.id === taskId && entry.candidate.role.name === "leader"));
986
- const processingCurrent = processing?.executionRef?.type === "run"
901
+ const processingCurrent = processing?.executionRef?.type === "turn"
987
902
  && processing.executionRef.taskId === taskId
988
903
  && leader !== undefined
989
904
  && processing.executionRef.id === leader.candidate.run.id;
@@ -1014,10 +929,10 @@ function exactLiveStatuses(statuses, candidates) {
1014
929
  throw new Error("Tmux Role stall snapshot is incomplete.");
1015
930
  return result;
1016
931
  }
1017
- function resourceForRun(snapshot, taskId, roleName, runId) {
932
+ function resourceForRun(snapshot, taskId, roleName, turnId) {
1018
933
  if (snapshot === undefined)
1019
934
  return undefined;
1020
- return snapshot.get(`${taskId}\0${roleName}\0${runId}`);
935
+ return snapshot.get(`${taskId}\0${roleName}\0${turnId}`);
1021
936
  }
1022
937
  function resourceEvidenceMatchesCurrentRun(resource, expected, progressAt) {
1023
938
  if (resource === undefined || expected === undefined)
@@ -1027,7 +942,7 @@ function resourceEvidenceMatchesCurrentRun(resource, expected, progressAt) {
1027
942
  || resource.progressAt !== progressAt
1028
943
  || identity.taskId !== expected.taskId
1029
944
  || identity.roleName !== expected.roleName
1030
- || identity.runId !== expected.runId
945
+ || identity.turnId !== expected.turnId
1031
946
  || identity.agentId !== expected.agentId
1032
947
  || identity.adapterId !== expected.adapterId
1033
948
  || identity.nativeSessionId !== expected.nativeSessionId