@zq-silk/yui 0.15.7 → 0.15.8

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 (281) hide show
  1. package/ARCHITECTURE.md +192 -399
  2. package/README.md +127 -1149
  3. package/dist/agent/adapterCatalog.js +15 -2
  4. package/dist/agent/agent.js +23 -3
  5. package/dist/agent/argumentPolicy.js +7 -1
  6. package/dist/agent/connectionPlan.js +62 -0
  7. package/dist/agent/executionComponents.js +158 -0
  8. package/dist/agent/launchEnvironment.js +31 -3
  9. package/dist/agent/managedRuntimeEnvironment.js +3 -5
  10. package/dist/{turn/turn.js → agentRun/agentRun.js} +166 -109
  11. package/dist/{turn/turnIdentity.js → agentRun/runIdentity.js} +4 -4
  12. package/dist/brief/taskBrief.js +12 -0
  13. package/dist/cli/agentConfigurationPicker.js +13 -0
  14. package/dist/cli/commandCatalog.js +157 -70
  15. package/dist/cli/interactionCandidates.js +5 -5
  16. package/dist/cli/interactionPolicy.js +38 -8
  17. package/dist/cli/invocationRouter.js +1 -1
  18. package/dist/cli/managedDiagnostics.js +28 -0
  19. package/dist/cli/operatorWizard.js +1 -7
  20. package/dist/cli/roleOptionOrder.js +27 -0
  21. package/dist/cli/roleWizard.js +50 -14
  22. package/dist/cli/updateOrchestrator.js +1 -1
  23. package/dist/cli/updatePorts.js +3 -4
  24. package/dist/cli.js +178 -95
  25. package/dist/commands/agentCommands.js +72 -14
  26. package/dist/commands/capabilityCommands.js +9 -6
  27. package/dist/commands/configCommands.js +20 -20
  28. package/dist/commands/deliveryGuardPreflight.js +2 -2
  29. package/dist/commands/executionAuditCommands.js +24 -24
  30. package/dist/commands/globalRoleCommands.js +1 -1
  31. package/dist/commands/grantCommands.js +4 -4
  32. package/dist/commands/operatorCommands.js +1 -7
  33. package/dist/commands/projectCommands.js +4 -4
  34. package/dist/commands/resourcesCommands.js +2 -2
  35. package/dist/commands/roleConfiguration.js +25 -5
  36. package/dist/commands/roleRuntimeGuard.js +4 -5
  37. package/dist/commands/sessionCommands.js +3 -7
  38. package/dist/commands/taskActivationCommands.js +259 -0
  39. package/dist/commands/taskActor.js +28 -49
  40. package/dist/commands/taskCommands.js +1188 -710
  41. package/dist/commands/taskContextCommand.js +39 -583
  42. package/dist/commands/taskExecutionCommands.js +32 -32
  43. package/dist/commands/taskInputCommands.js +40 -104
  44. package/dist/commands/taskIntegrationCommands.js +3 -2
  45. package/dist/commands/taskIntegrationQueueCommands.js +1 -1
  46. package/dist/commands/taskNextActionCommand.js +8 -8
  47. package/dist/commands/taskOverviewCommand.js +33 -45
  48. package/dist/commands/taskRemoteDeliveryCommand.js +2 -2
  49. package/dist/commands/taskRoleRuntimeStatus.js +133 -102
  50. package/dist/commands/telemetryCommands.js +36 -38
  51. package/dist/config/configCatalog.js +4 -4
  52. package/dist/config/yuiConfig.js +8 -8
  53. package/dist/context/contextSnapshot.js +10 -10
  54. package/dist/context/dispatchContext.js +11 -11
  55. package/dist/context/roleSessionContext.js +6 -3
  56. package/dist/context/{turnContextPack.js → runContextPack.js} +146 -81
  57. package/dist/context/{turnInputContract.js → runInputContract.js} +73 -60
  58. package/dist/context/sessionBootstrapManifest.js +21 -2
  59. package/dist/context/sourceRunContext.js +30 -0
  60. package/dist/context/taskContext.js +458 -0
  61. package/dist/context/wakeNotification.js +27 -27
  62. package/dist/controller/agentRuntimeObserver.js +21 -24
  63. package/dist/controller/capabilityBridge.js +17 -6
  64. package/dist/controller/clientRuntime.js +65 -92
  65. package/dist/controller/controller.js +114 -188
  66. package/dist/controller/fileSchedulerStoreAdapter.js +787 -887
  67. package/dist/controller/jobControl.js +54 -85
  68. package/dist/controller/resourceInventory.js +8 -27
  69. package/dist/controller/resourceInventoryLinux.js +12 -13
  70. package/dist/controller/runtime.js +529 -479
  71. package/dist/controller/runtimeEventInbox.js +55 -25
  72. package/dist/controller/runtimeEventProcessor.js +22 -31
  73. package/dist/controller/{runtimeHookTurnFence.js → runtimeHookRunFence.js} +91 -115
  74. package/dist/controller/runtimeLaunchCoordinator.js +80 -426
  75. package/dist/controller/runtimeObservationHook.js +14 -18
  76. package/dist/controller/sessionNotify.js +16 -24
  77. package/dist/controller/sessionOwnerReconciliation.js +168 -50
  78. package/dist/controller/structuredProviderObservation.js +138 -99
  79. package/dist/coordination/workMailbox.js +3 -3
  80. package/dist/coordination/workMailboxQueue.js +36 -33
  81. package/dist/core/boundedRpc.js +8 -1
  82. package/dist/core/controllerClient.js +20 -1
  83. package/dist/core/controllerServer.js +4 -4
  84. package/dist/doctor/doctor.js +13 -2
  85. package/dist/domain/agentResultTransport.js +9 -9
  86. package/dist/execution/codexThreadNaming.js +2 -8
  87. package/dist/execution/executionHealth.js +51 -63
  88. package/dist/execution/reviewMainRun.js +137 -0
  89. package/dist/execution/workItemExecution.js +28 -29
  90. package/dist/execution/workItemExecutionProjection.js +99 -107
  91. package/dist/execution/workItemMainRun.js +141 -0
  92. package/dist/executor/agentAdapter.js +227 -20
  93. package/dist/executor/agentConfigurationCatalog.js +126 -4
  94. package/dist/executor/agentConfigurationProbe.js +162 -4
  95. package/dist/executor/agentExecutor.js +79 -78
  96. package/dist/executor/effectiveLaunch.js +105 -18
  97. package/dist/executor/executorRegistry.js +29 -44
  98. package/dist/executor/fileRoleLaunchPlanner.js +229 -154
  99. package/dist/executor/workspacePreflightClassification.js +16 -16
  100. package/dist/grant/capabilityGrant.js +6 -3
  101. package/dist/input/inputRequest.js +12 -10
  102. package/dist/integration/gitIntegrationService.js +4 -11
  103. package/dist/integration/integrationQueueService.js +4 -4
  104. package/dist/interaction/operatorPresentation.js +1 -1
  105. package/dist/kernel/builtinCapabilities.js +247 -12
  106. package/dist/kernel/capabilityRegistry.js +64 -18
  107. package/dist/kernel/instanceHost.js +12 -1
  108. package/dist/kernel/kernelPorts.js +2 -2
  109. package/dist/lifecycle/canonicalLifecycleEvent.js +44 -49
  110. package/dist/lifecycle/exactRunTerminalization.js +449 -0
  111. package/dist/message/message.js +62 -6
  112. package/dist/message/messageContinuation.js +204 -0
  113. package/dist/observability/executionAudit.js +70 -72
  114. package/dist/observability/faultClassification.js +2 -2
  115. package/dist/observability/orchestrationMetrics.js +8 -8
  116. package/dist/operator/operatorSessionHistory.js +1 -7
  117. package/dist/output/agentConfigurationPresentation.js +8 -3
  118. package/dist/output/agentRunConfigurationPresentation.js +128 -0
  119. package/dist/output/rolePresentation.js +54 -3
  120. package/dist/plugins/pluginChild.js +104 -0
  121. package/dist/plugins/pluginIntent.js +26 -0
  122. package/dist/plugins/pluginInterpreter.js +43 -0
  123. package/dist/plugins/pluginPackage.js +101 -0
  124. package/dist/plugins/pluginProcess.js +112 -0
  125. package/dist/plugins/pluginService.js +380 -0
  126. package/dist/profile/agentProfile.js +1 -1
  127. package/dist/repository/gitWorkspace.js +26 -4
  128. package/dist/repository/project.js +19 -4
  129. package/dist/repository/taskBaseFreshness.js +13 -13
  130. package/dist/repository/taskWorkspaceCoordinator.js +20 -27
  131. package/dist/repository/taskWorkspacePreparer.js +344 -83
  132. package/dist/resources/autoResourceGc.js +3 -3
  133. package/dist/resources/liveReferences.js +3 -3
  134. package/dist/resources/projectResource.js +123 -0
  135. package/dist/resources/projectResourceService.js +421 -0
  136. package/dist/resources/resourceDiscovery.js +6 -6
  137. package/dist/resources/resourceGc.js +1 -1
  138. package/dist/resources/resourceRegistrar.js +1 -1
  139. package/dist/resources/resourceTypes.js +1 -1
  140. package/dist/review/deltaRecheck.js +3 -3
  141. package/dist/review/reviewAcceptance.js +16 -16
  142. package/dist/review/reviewDecision.js +7 -7
  143. package/dist/review/reviewRound.js +21 -20
  144. package/dist/review/reviewerAvailability.js +2 -2
  145. package/dist/role/role.js +51 -7
  146. package/dist/role/taskRoleUpdate.js +30 -0
  147. package/dist/runtime/acpProtocol.js +425 -0
  148. package/dist/runtime/acpSession.js +731 -0
  149. package/dist/runtime/acpSessionConfiguration.js +260 -0
  150. package/dist/runtime/agentDriver.js +30 -11
  151. package/dist/runtime/agentEndpoint.js +278 -0
  152. package/dist/runtime/agentEndpointIdentity.js +86 -0
  153. package/dist/runtime/agentEndpointOwnership.js +239 -0
  154. package/dist/runtime/agentError.js +2 -10
  155. package/dist/runtime/agentHost.js +565 -314
  156. package/dist/runtime/agentRunConfiguration.js +258 -0
  157. package/dist/runtime/builtinAgentDrivers.js +134 -18
  158. package/dist/runtime/builtinAgentErrorMappers.js +55 -3
  159. package/dist/runtime/builtinTranscriptUsage.js +1 -1
  160. package/dist/runtime/claude-process-owner +0 -0
  161. package/dist/runtime/codexAppServerRuntime.js +38 -30
  162. package/dist/runtime/codexInteractiveHost.js +41 -6
  163. package/dist/runtime/continuationManager.js +2 -6
  164. package/dist/runtime/executionEnvironment.js +30 -0
  165. package/dist/runtime/firstProgressAdvisory.js +11 -11
  166. package/dist/runtime/index.js +4 -3
  167. package/dist/runtime/jsonLineChannel.js +109 -0
  168. package/dist/runtime/launchBroker.js +91 -16
  169. package/dist/runtime/launchDiagnostics.js +2 -2
  170. package/dist/runtime/lifecycleReservation.js +10 -18
  171. package/dist/runtime/managedCaller.js +61 -17
  172. package/dist/runtime/nativeSessionControl.js +102 -0
  173. package/dist/runtime/ports.js +6 -21
  174. package/dist/runtime/processExitObservation.js +8 -7
  175. package/dist/runtime/promptEnvelope.js +17 -6
  176. package/dist/runtime/providerContinuation.js +3 -9
  177. package/dist/runtime/providerContinuationReconciliationService.js +4 -13
  178. package/dist/runtime/providerControl.js +2 -7
  179. package/dist/runtime/providerRuntimeIdentity.js +110 -222
  180. package/dist/runtime/providerRuntimeReconciler.js +5 -9
  181. package/dist/runtime/runtimeBinding.js +0 -1
  182. package/dist/runtime/runtimeContinuationProjection.js +4 -7
  183. package/dist/runtime/runtimeDeadlines.js +9 -0
  184. package/dist/runtime/runtimeHealthPolicy.js +1 -1
  185. package/dist/runtime/runtimeObservation.js +29 -65
  186. package/dist/runtime/runtimeProjection.js +43 -51
  187. package/dist/runtime/runtimeSessionCandidate.js +1 -3
  188. package/dist/runtime/sessionLaunchRequest.js +3 -7
  189. package/dist/runtime/sessionOwnerIdentity.js +7 -54
  190. package/dist/runtime/sessionOwnerRegistry.js +22 -17
  191. package/dist/runtime/sessionReconciliation.js +4 -8
  192. package/dist/runtime/sessionTerminationGuard.js +70 -259
  193. package/dist/runtime/sessionTokenMetrics.js +5 -16
  194. package/dist/runtime/structuredProviderHost.js +237 -117
  195. package/dist/runtime/taskRuntimeIsolation.js +39 -122
  196. package/dist/runtime/tmuxAdapters.js +39 -86
  197. package/dist/scheduler/activeRoleRunDelivery.js +354 -0
  198. package/dist/scheduler/leaderWakeupProcessor.js +75 -266
  199. package/dist/scheduler/operatorInputNotificationProcessor.js +1 -1
  200. package/dist/scheduler/ports.js +80 -9
  201. package/dist/scheduler/{roleTurnLiveness.js → roleRunLiveness.js} +26 -30
  202. package/dist/scheduler/{roleTurnStall.js → roleRunStall.js} +128 -139
  203. package/dist/scheduler/taskExecutionProjection.js +120 -124
  204. package/dist/scheduler/taskObservabilityProjection.js +29 -29
  205. package/dist/scheduler/taskWake.js +11 -4
  206. package/dist/scheduler/wakeReason.js +9 -1
  207. package/dist/setup/setupCommand.js +1 -0
  208. package/dist/storage/migrations/agentRunContract.js +159 -0
  209. package/dist/storage/migrations/removeRuntimeGeneration.js +207 -0
  210. package/dist/storage/sqliteSchema.js +431 -5
  211. package/dist/storage/sqliteStore.js +375 -220
  212. package/dist/storage/storageVersions.js +1 -1
  213. package/dist/storage/storeRpc.js +10 -5
  214. package/dist/storage/taskStore.js +13 -11
  215. package/dist/storage/upgrade/upgradeOrchestrator.js +5 -7
  216. package/dist/surface/surfaceContributions.js +102 -0
  217. package/dist/task/completionReadiness.js +32 -6
  218. package/dist/task/deliveryGuard.js +16 -16
  219. package/dist/task/draftPlan.js +72 -12
  220. package/dist/task/nextAction.js +144 -128
  221. package/dist/task/remoteDelivery.js +6 -6
  222. package/dist/task/task.js +184 -18
  223. package/dist/task/taskActivation.js +301 -0
  224. package/dist/task/taskActivationService.js +392 -0
  225. package/dist/task/taskRecordReference.js +5 -4
  226. package/dist/task/taskRecordRetirement.js +1 -1
  227. package/dist/telemetry/sqliteTelemetryStore.js +55 -68
  228. package/dist/telemetry/telemetryConfig.js +14 -14
  229. package/dist/telemetry/telemetryWiring.js +2 -2
  230. package/dist/web/assets/assetManifest.js +2 -0
  231. package/dist/web/assets/client/app.js +120 -20
  232. package/dist/web/assets/client/components.js +87 -54
  233. package/dist/web/assets/client/i18n.js +83 -41
  234. package/dist/web/assets/client/markdown.js +1 -1
  235. package/dist/web/assets/client/taskSurface.js +353 -0
  236. package/dist/web/assets/client/view.js +49 -44
  237. package/dist/web/assets/shell.js +1 -1
  238. package/dist/web/assets/styles/cards.js +22 -4
  239. package/dist/web/controllerWeb.js +60 -0
  240. package/dist/web/webMutation.js +28 -0
  241. package/dist/web/webServer.js +118 -8
  242. package/dist/web/webSnapshot.js +81 -74
  243. package/dist/web/webTaskSurface.js +64 -0
  244. package/dist/workItem/dependencyGate.js +1 -1
  245. package/dist/workItem/workItem.js +80 -48
  246. package/dist/workspace/workItemChangeSetManager.js +16 -9
  247. package/docs/agent-result-consumption.md +94 -0
  248. package/docs/agent-runtime-drivers.md +91 -0
  249. package/docs/architecture/README.md +38 -0
  250. package/docs/architecture/capabilities-and-resources.md +79 -0
  251. package/docs/managed-turn-and-session-runtime.md +222 -0
  252. package/docs/observability/README.md +81 -0
  253. package/docs/plugin-sdk.md +290 -0
  254. package/docs/provider-runtime.md +163 -0
  255. package/docs/release-workflow.md +303 -0
  256. package/docs/roles-and-configuration.md +113 -0
  257. package/docs/sqlite-control-plane-design.md +76 -0
  258. package/docs/task-dag-semantics.md +57 -0
  259. package/docs/task-delivery.md +103 -0
  260. package/docs/task-local-identity.md +6 -6
  261. package/docs/testing/verification-levels.md +86 -0
  262. package/i18n/README.zh-CN.md +98 -739
  263. package/package.json +2 -2
  264. package/skills/yui-leader/SKILL.md +130 -103
  265. package/skills/yui-leader/references/integration.md +39 -0
  266. package/skills/yui-leader/references/replicated-execution.md +42 -0
  267. package/skills/yui-leader/references/task-plugins.md +33 -0
  268. package/skills/yui-operator/SKILL.md +30 -59
  269. package/skills/yui-reviewer/SKILL.md +35 -36
  270. package/skills/yui-runtime/SKILL.md +88 -24
  271. package/skills/yui-runtime/references/publication.md +22 -0
  272. package/skills/yui-runtime/references/recovery.md +64 -0
  273. package/skills/yui-worker/SKILL.md +37 -39
  274. package/dist/cli/roleOptionCatalog.js +0 -68
  275. package/dist/context/sourceTurnContext.js +0 -30
  276. package/dist/execution/reviewMainTurn.js +0 -161
  277. package/dist/execution/workItemMainTurn.js +0 -164
  278. package/dist/lifecycle/exactTurnTerminalization.js +0 -407
  279. package/dist/runtime/preallocatedNativeSession.js +0 -13
  280. package/dist/runtime/runtimeStopReceipt.js +0 -42
  281. package/dist/scheduler/activeRoleTurnDelivery.js +0 -315
@@ -8,9 +8,9 @@ export function buildTaskObservabilityProjection(input) {
8
8
  const dag = projectDag(input.workItems);
9
9
  const workItems = input.workItems.map((item) => {
10
10
  const groups = item.executionGroups;
11
- const itemCost = projectWorkItemCost(groups, input.turns, now);
12
- const itemContext = projectContext(groups, input.turns, input.contextSnapshots);
13
- const producerObservability = projectWorkItemProducerObservability(item, input.turns);
11
+ const itemCost = projectWorkItemCost(groups, input.runs, now);
12
+ const itemContext = projectContext(groups, input.runs, input.contextSnapshots);
13
+ const producerObservability = projectWorkItemProducerObservability(item, input.runs);
14
14
  return Object.freeze({
15
15
  workItemId: item.id,
16
16
  title: item.title,
@@ -21,8 +21,8 @@ export function buildTaskObservabilityProjection(input) {
21
21
  resultCount: producerObservability?.resultCount ?? null
22
22
  });
23
23
  });
24
- const cost = projectCost(input.executionGroups, input.turns, input.events, now);
25
- const context = projectContext(input.executionGroups, input.turns, input.contextSnapshots);
24
+ const cost = projectCost(input.executionGroups, input.runs, input.events, now);
25
+ const context = projectContext(input.executionGroups, input.runs, input.contextSnapshots);
26
26
  return Object.freeze({
27
27
  dag,
28
28
  workItems,
@@ -31,21 +31,21 @@ export function buildTaskObservabilityProjection(input) {
31
31
  sessionTokens: Object.freeze([...(input.sessionTokens ?? [])])
32
32
  });
33
33
  }
34
- function projectWorkItemProducerObservability(item, turns) {
34
+ function projectWorkItemProducerObservability(item, runs) {
35
35
  const successfulLanes = item.executionGroups.flatMap((group) => group.lanes.flatMap((lane) => (lane.disposition === "succeeded" ? [{ group, lane }] : [])));
36
36
  let resultCount = 0;
37
37
  for (const { group, lane } of successfulLanes) {
38
- const turn = lane.successfulTurnId === undefined
38
+ const run = lane.successfulRunId === undefined
39
39
  ? undefined
40
- : turns.find(({ id }) => id === lane.successfulTurnId);
41
- if (turn === undefined
42
- || turn.result === undefined
43
- || turn.status !== "completed"
44
- || turn.taskId !== item.taskId
45
- || turn.workItemId !== item.id
46
- || turn.executionGroupId !== group.id
47
- || turn.executionLaneId !== lane.id
48
- || turn.roleName !== lane.roleName)
40
+ : runs.find(({ id }) => id === lane.successfulRunId);
41
+ if (run === undefined
42
+ || run.result === undefined
43
+ || run.status !== "completed"
44
+ || run.taskId !== item.taskId
45
+ || run.workItemId !== item.id
46
+ || run.executionGroupId !== group.id
47
+ || run.executionLaneId !== lane.id
48
+ || run.roleName !== lane.roleName)
49
49
  return null;
50
50
  resultCount += 1;
51
51
  }
@@ -69,7 +69,7 @@ function projectDag(workItems) {
69
69
  const unresolved = item.dependsOn.filter((dependency) => {
70
70
  return !dependencySatisfied(dependency, byId);
71
71
  });
72
- const projectedStatus = item.status === "pending"
72
+ const projectedStatus = item.status === "open"
73
73
  ? unresolved.length === 0 ? "ready" : "blocked"
74
74
  : item.status;
75
75
  return Object.freeze({
@@ -110,13 +110,13 @@ function rootCauses(id, byId) {
110
110
  }
111
111
  const unresolved = item.dependsOn.filter((dependency) => !dependencySatisfied(dependency, byId));
112
112
  if (unresolved.length === 0) {
113
- if (item.status === "failed" || item.status === "awaiting_acceptance")
113
+ if (item.status === "open" && item.currentCandidateId !== undefined)
114
114
  result.push(item.id);
115
115
  return;
116
116
  }
117
117
  for (const dependency of unresolved) {
118
118
  const target = byId.get(dependency);
119
- if (target?.status === "failed" || target?.status === "awaiting_acceptance")
119
+ if (target?.status === "open" && target.currentCandidateId !== undefined)
120
120
  result.push(target.id);
121
121
  else
122
122
  visit(dependency);
@@ -126,7 +126,7 @@ function rootCauses(id, byId) {
126
126
  return [...new Set(result)];
127
127
  }
128
128
  function dependencySatisfied(id, byId) {
129
- return byId.get(id)?.status === "completed";
129
+ return byId.get(id)?.status === "accepted";
130
130
  }
131
131
  function dependencyEdgeStatus(id, byId) {
132
132
  const target = byId.get(id);
@@ -136,14 +136,14 @@ function dependencyEdgeStatus(id, byId) {
136
136
  return "satisfied";
137
137
  if (target.status === "retired")
138
138
  return "dead";
139
- if (target.status === "failed" || target.status === "awaiting_acceptance")
140
- return "failed-open";
139
+ if (target.status === "open" && target.currentCandidateId !== undefined)
140
+ return "active";
141
141
  return "active";
142
142
  }
143
- function projectCost(groups, turns, _events, now) {
143
+ function projectCost(groups, runs, _events, now) {
144
144
  const uniqueGroups = [...new Map(groups.map((group) => [group.id, group])).values()];
145
145
  const groupIds = new Set(uniqueGroups.map(({ id }) => id));
146
- const attempts = turns.filter(({ executionGroupId }) => (executionGroupId !== undefined && groupIds.has(executionGroupId)));
146
+ const attempts = runs.filter(({ executionGroupId }) => (executionGroupId !== undefined && groupIds.has(executionGroupId)));
147
147
  const laneCount = uniqueGroups.reduce((total, group) => total + group.lanes.length, 0);
148
148
  return Object.freeze({
149
149
  tokens: 0,
@@ -158,9 +158,9 @@ function projectCost(groups, turns, _events, now) {
158
158
  marginalValueStatus: "unavailable"
159
159
  });
160
160
  }
161
- function projectWorkItemCost(groups, turns, now) {
161
+ function projectWorkItemCost(groups, runs, now) {
162
162
  const groupIds = new Set(groups.map(({ id }) => id));
163
- const attempts = turns.filter(({ executionGroupId }) => (executionGroupId !== undefined && groupIds.has(executionGroupId)));
163
+ const attempts = runs.filter(({ executionGroupId }) => (executionGroupId !== undefined && groupIds.has(executionGroupId)));
164
164
  const wallClockSeconds = groups.reduce((total, group) => {
165
165
  const started = Date.parse(group.createdAt);
166
166
  const ended = group.lanes.some(({ disposition }) => disposition === "open")
@@ -184,15 +184,15 @@ function projectWorkItemCost(groups, turns, now) {
184
184
  marginalValueStatus: "unavailable"
185
185
  });
186
186
  }
187
- function projectContext(groups, turns, snapshots) {
187
+ function projectContext(groups, runs, snapshots) {
188
188
  const refs = new Map();
189
189
  for (const group of groups) {
190
190
  const ref = group.assignment.contextSnapshotRef;
191
191
  if (ref !== undefined)
192
192
  refs.set(ref.id, ref);
193
193
  }
194
- for (const turn of turns) {
195
- const ref = turn.inputs[0].input.contextSnapshotRef;
194
+ for (const run of runs) {
195
+ const ref = run.inputs[0].input.contextSnapshotRef;
196
196
  if (ref !== undefined)
197
197
  refs.set(ref.id, ref);
198
198
  }
@@ -1,5 +1,6 @@
1
1
  import { requireIdentity, requirePositiveInteger, requireText, requireTimestamp } from "../domain/validation.js";
2
2
  import { TASK_RECORD_ID_PREFIXES } from "../task/taskRecordReference.js";
3
+ import { copyRef } from "../coordination/workMailbox.js";
3
4
  export const CURRENT_TASK_WAKE_SCHEMA_VERSION = 1;
4
5
  export function createTaskWake(input) {
5
6
  const wake = {
@@ -8,16 +9,22 @@ export function createTaskWake(input) {
8
9
  taskId: requireIdentity(input.taskId, "Task id"),
9
10
  seq: wakeSequence(input.id),
10
11
  reasons: Object.freeze(input.reasons.map((reason) => requireText(reason, "Wake reason"))),
12
+ ...(input.refs === undefined ? {} : { refs: Object.freeze(input.refs.map(copyRef)) }),
11
13
  fromCursor: requireTimestamp(input.fromCursor, "Wake fromCursor"),
12
14
  toCursor: requireTimestamp(input.toCursor, "Wake toCursor"),
13
15
  status: "dispatched",
14
- turnId: requireIdentity(input.turnId, "Wake Turn id"),
16
+ ...(input.runId === undefined ? {} : { runId: requireIdentity(input.runId, "Wake AgentRun id") }),
15
17
  createdAt: input.now.toISOString()
16
18
  };
17
19
  validateTaskWake(wake);
18
20
  return wake;
19
21
  }
20
22
  export function validateTaskWake(wake) {
23
+ if (wake.refs !== undefined) {
24
+ if (!Array.isArray(wake.refs))
25
+ throw new Error("Wake references must be an array.");
26
+ wake.refs.forEach(copyRef);
27
+ }
21
28
  if (wake.schemaVersion !== CURRENT_TASK_WAKE_SCHEMA_VERSION) {
22
29
  throw new Error(`Task wake ${wake.id} must use schemaVersion ${CURRENT_TASK_WAKE_SCHEMA_VERSION}.`);
23
30
  }
@@ -55,12 +62,12 @@ export function latestTaskWake(wakes) {
55
62
  return latest;
56
63
  }
57
64
  /**
58
- * The delta lower bound for a Task's first wake: the last Leader Turn's
65
+ * The delta lower bound for a Task's first wake: the last Leader AgentRun's
59
66
  * creation time, preserving the pre-ledger watermark semantics. When no
60
- * Leader Turn exists, the Task's own creation time bounds the window.
67
+ * Leader AgentRun exists, the Task's own creation time bounds the window.
61
68
  */
62
69
  export function fallbackWakeCursor(input) {
63
- return input.leaderTurnCreatedAt ?? input.taskCreatedAt;
70
+ return input.leaderRunCreatedAt ?? input.taskCreatedAt;
64
71
  }
65
72
  function wakeSequence(id) {
66
73
  const prefix = `${TASK_RECORD_ID_PREFIXES.taskWake}-`;
@@ -8,7 +8,7 @@
8
8
  * queue — the wake layer owns the meaning of its tags.
9
9
  *
10
10
  * Tag wire format: `<kind>` or `<kind>:<ref>`. The ref is an exact record
11
- * identifier (InputRequest id, Turn id, ...) or, for `force-wake`, the
11
+ * identifier (InputRequest id, AgentRun id, ...) or, for `force-wake`, the
12
12
  * operator-supplied text. Unknown kinds parse losslessly so a newer producer
13
13
  * can never wedge an older reader.
14
14
  */
@@ -17,6 +17,7 @@ export const WAKE_REASON_KINDS = Object.freeze([
17
17
  "input-timeout",
18
18
  "role-turn-stalled",
19
19
  "agent-error",
20
+ "activation-failed",
20
21
  "agent-session-recovery",
21
22
  "review-failed",
22
23
  "task-reopened",
@@ -27,6 +28,13 @@ export const WAKE_REASON_KINDS = Object.freeze([
27
28
  "force-wake"
28
29
  ]);
29
30
  const IMMEDIATE_KINDS = new Set([
31
+ "operator-input",
32
+ "user-message",
33
+ "task-created",
34
+ "execution-started",
35
+ "session-replaced",
36
+ "force-wake",
37
+ "activation-failed",
30
38
  "task-reopened",
31
39
  "leader-turn-failed",
32
40
  "role-turn-failed"
@@ -60,6 +60,7 @@ export async function runSetupCommand(args, env, executor, io = {}) {
60
60
  `Leader configuration: ${roleStatus.leader}.`,
61
61
  `Tmux: ${tmuxBin}.`,
62
62
  "Setup did not configure Review, Worker, Profiles, or shell completion.",
63
+ "Provider login and native key/initialization confirmations remain with the Provider; setup did not call a model.",
63
64
  "Run `yui operator enter` to continue, or `yui config show` to inspect configuration."
64
65
  ].join("\n")}\n`;
65
66
  }
@@ -0,0 +1,159 @@
1
+ import { createContextSnapshot, contextContentDigest } from "../../context/contextSnapshot.js";
2
+ /** Storage 12 -> 13 only. Text, native identities and opaque record/receipt ids
3
+ * are never renamed. Ordinary runtime stores do not interpret old payloads.
4
+ * v13 also introduces optional Message recipient/continuation/handovers.
5
+ * Existing messages have no recipient and remain informational/Leader wakes;
6
+ * migration never guesses an owner or causes historical messages to execute.
7
+ */
8
+ export function migrateAgentRunContract(db) {
9
+ // T08 already stored real planning executions. Use that frozen evidence,
10
+ // never the Task's current status, to classify their shared Session snapshots.
11
+ const planningLaunches = new Set();
12
+ for (const row of db.prepare("SELECT task_id, payload FROM turns").all()) {
13
+ const run = JSON.parse(row.payload);
14
+ if (run.purpose === "planning") {
15
+ planningLaunches.add(`${row.task_id}:${contextContentDigest(run.effective)}`);
16
+ }
17
+ }
18
+ const snapshotDigests = new Map();
19
+ const renameKey = (key) => {
20
+ if (key === "turn")
21
+ return "run";
22
+ if (key === "turns")
23
+ return "runs";
24
+ if (key === "turnId")
25
+ return "runId";
26
+ if (key === "turnIds")
27
+ return "runIds";
28
+ if (/NativeTurn|ProviderTurn|CompletedTurn|nativeTurn|providerTurn/.test(key))
29
+ return key;
30
+ return key.replaceAll("Turn", "Run").replace(/^turn(?=[A-Z])/, "run")
31
+ .replaceAll("TURN_", "RUN_").replaceAll("_TURN", "_RUN");
32
+ };
33
+ const untouched = new Set(["advanced", "metadata", "environment", "raw", "output", "body",
34
+ "description", "summary", "directive", "config"]);
35
+ const migrate = (value, taskId, purpose) => {
36
+ if (Array.isArray(value))
37
+ return value.map((entry) => migrate(entry, taskId, purpose));
38
+ if (value === null || typeof value !== "object")
39
+ return value;
40
+ const input = value;
41
+ const ownerTaskId = typeof input.taskId === "string" ? input.taskId : taskId;
42
+ const executionPurpose = typeof input.purpose === "string" ? input.purpose : purpose;
43
+ const result = {};
44
+ for (const [key, child] of Object.entries(input)) {
45
+ let next = untouched.has(key) ? child : migrate(child, ownerTaskId, executionPurpose);
46
+ if ((key === "type" || key === "recordKind" || key === "store") && child === "turn")
47
+ next = "run";
48
+ if (key === "type" && typeof child === "string" && child.startsWith("turn.")
49
+ && typeof input.id === "string" && input.id.startsWith("event-") && input.createdAt !== undefined) {
50
+ next = child.replace(/^turn\./, "run.");
51
+ }
52
+ if (key === "store" && child === "source-turn")
53
+ next = "source-run";
54
+ // Runtime envelopes are structured evidence encoded inside Task Events,
55
+ // not free-form Provider output.
56
+ if (key === "observation" && typeof child === "string") {
57
+ const parsed = JSON.parse(child);
58
+ next = JSON.stringify(migrate(parsed, ownerTaskId));
59
+ }
60
+ result[renameKey(key)] = next;
61
+ }
62
+ if (typeof input.digest === "string" && typeof input.id === "string"
63
+ && input.id.startsWith("context-snapshot-") && typeof input.sequence === "number") {
64
+ result.digest = snapshotDigests.get(input.digest) ?? input.digest;
65
+ }
66
+ // A Run has its own authoritative purpose. Session copies are classified
67
+ // by the exact planning launch they retain; activation never upgrades them.
68
+ if (input.schemaVersion === 3 && typeof input.agentId === "string"
69
+ && typeof input.sourceDesiredRevision === "number" && input.workspace !== undefined
70
+ && input.context !== undefined && input.permission !== undefined) {
71
+ result.executionAuthority = executionPurpose === "planning"
72
+ || (ownerTaskId !== undefined && planningLaunches.has(`${ownerTaskId}:${contextContentDigest(input)}`))
73
+ ? "planning" : "delivery";
74
+ }
75
+ return result;
76
+ };
77
+ const snapshots = db.prepare("SELECT rowid, payload FROM context_snapshots")
78
+ .all();
79
+ const originals = new Map(snapshots.map((row) => {
80
+ const snapshot = JSON.parse(row.payload);
81
+ return [`${snapshot.taskId}/${snapshot.id}`, { ...row, snapshot }];
82
+ }));
83
+ const visiting = new Set();
84
+ const completed = new Set();
85
+ const migrateSnapshot = (key) => {
86
+ if (completed.has(key))
87
+ return;
88
+ const row = originals.get(key);
89
+ if (row === undefined)
90
+ return; // Preserve unresolved historical references as references.
91
+ if (visiting.has(key))
92
+ throw new Error("Context Snapshot references contain a cycle.");
93
+ visiting.add(key);
94
+ const original = row.snapshot;
95
+ // Dependency order, not wall-clock order: async callers can freeze with
96
+ // an earlier pass timestamp while referencing an already committed parent.
97
+ const visitReferences = (value) => {
98
+ if (value === null || typeof value !== "object")
99
+ return;
100
+ if (Array.isArray(value)) {
101
+ value.forEach(visitReferences);
102
+ return;
103
+ }
104
+ const ref = value;
105
+ if (typeof ref.id === "string" && ref.id.startsWith("context-snapshot-")
106
+ && typeof ref.taskId === "string" && typeof ref.digest === "string"
107
+ && typeof ref.sequence === "number")
108
+ migrateSnapshot(`${ref.taskId}/${ref.id}`);
109
+ for (const [field, child] of Object.entries(ref)) {
110
+ if (!untouched.has(field))
111
+ visitReferences(child);
112
+ }
113
+ };
114
+ visitReferences(original.parentRef);
115
+ visitReferences(original.resources);
116
+ const current = migrate(original);
117
+ const resources = current.resources.map((entry) => ({
118
+ ...entry, ref: { ...entry.ref, digest: contextContentDigest(entry.value) }
119
+ }));
120
+ const refs = current.refs.map((ref) => resources.find((entry) => entry.ref.layer === ref.layer && entry.ref.store === ref.store && entry.ref.refId === ref.refId)?.ref ?? ref);
121
+ const snapshot = createContextSnapshot({ ...current, resources, refs, frozenAt: new Date(current.frozenAt) });
122
+ snapshotDigests.set(original.digest, snapshot.digest);
123
+ db.prepare("UPDATE context_snapshots SET payload = ?, digest = ? WHERE rowid = ?")
124
+ .run(JSON.stringify(snapshot), snapshot.digest, row.rowid);
125
+ visiting.delete(key);
126
+ completed.add(key);
127
+ };
128
+ for (const key of originals.keys())
129
+ migrateSnapshot(key);
130
+ const tables = [
131
+ "config", "agent_profiles", "global_roles", "global_role_session_sets",
132
+ "task_records", "task_roles", "role_session_sets", "work_items", "work_item_candidates",
133
+ "turns", "active_turns", "review_rounds", "change_sets", "integration_attempts",
134
+ "messages", "input_requests", "decisions", "milestones", "events", "task_projections",
135
+ "durable_jobs", "integration_queue", "task_wakes", "telemetry", "telemetry_aggregate"
136
+ ];
137
+ const rewriteColumn = (table, column) => {
138
+ const columns = db.prepare(`PRAGMA table_info("${table}")`).all();
139
+ if (!columns.some((entry) => entry.name === column))
140
+ return;
141
+ const keys = columns.filter((entry) => entry.pk > 0).sort((a, b) => a.pk - b.pk).map((entry) => entry.name);
142
+ if (!keys.length)
143
+ throw new Error(`Migration requires the primary key of ${table}.`);
144
+ const quote = (value) => `"${value.replaceAll('"', '""')}"`;
145
+ const rows = db.prepare(`SELECT * FROM ${quote(table)} WHERE ${quote(column)} IS NOT NULL`)
146
+ .all();
147
+ const update = db.prepare(`UPDATE ${quote(table)} SET ${quote(column)} = ? WHERE ${keys.map((key) => `${quote(key)} = ?`).join(" AND ")}`);
148
+ for (const row of rows) {
149
+ update.run(JSON.stringify(migrate(JSON.parse(row[column]), typeof row.task_id === "string" ? row.task_id : undefined)), ...keys.map((key) => row[key]));
150
+ }
151
+ };
152
+ for (const table of tables) {
153
+ rewriteColumn(table, "payload");
154
+ }
155
+ for (const column of ["pending", "processing"]) {
156
+ rewriteColumn("mailboxes", column);
157
+ }
158
+ db.prepare("UPDATE id_sequences SET kind = 'run' WHERE kind = 'turn'").run();
159
+ }
@@ -0,0 +1,207 @@
1
+ import { createHash } from "node:crypto";
2
+ /** Storage 3 -> 4. Frozen migration; runtime code must not depend on this module. */
3
+ export const REMOVE_RUNTIME_GENERATION_SQL = `
4
+ -- data-transform: remove-runtime-generation-records-v1
5
+ DROP TRIGGER telemetry_ai;
6
+ DROP TRIGGER telemetry_au;
7
+ DROP INDEX idx_telemetry_turn;
8
+ ALTER TABLE telemetry RENAME TO telemetry_v3;
9
+ ALTER TABLE telemetry_aggregate RENAME TO telemetry_aggregate_v3;
10
+ CREATE TABLE telemetry (
11
+ task_id TEXT NOT NULL, role_name TEXT NOT NULL, turn_id TEXT NOT NULL,
12
+ progress_id TEXT NOT NULL, sequence INTEGER, payload TEXT NOT NULL,
13
+ received_at TEXT NOT NULL,
14
+ PRIMARY KEY (task_id, role_name, turn_id, progress_id)
15
+ ) WITHOUT ROWID;
16
+ CREATE INDEX idx_telemetry_turn ON telemetry(task_id, turn_id);
17
+ INSERT INTO telemetry
18
+ SELECT task_id, role_name, turn_id, progress_id, sequence, payload, received_at
19
+ FROM (
20
+ SELECT *, ROW_NUMBER() OVER (
21
+ PARTITION BY task_id, role_name, turn_id, progress_id
22
+ ORDER BY received_at DESC, COALESCE(sequence, -1) DESC, generation DESC
23
+ ) AS rank FROM telemetry_v3
24
+ ) WHERE rank = 1;
25
+ CREATE TABLE telemetry_aggregate (
26
+ task_id TEXT NOT NULL, role_name TEXT NOT NULL, turn_id TEXT NOT NULL,
27
+ first_at TEXT NOT NULL, last_at TEXT NOT NULL, count INTEGER NOT NULL,
28
+ max_sequence INTEGER, error_count INTEGER NOT NULL DEFAULT 0, updated_at TEXT NOT NULL,
29
+ PRIMARY KEY (task_id, role_name, turn_id)
30
+ ) WITHOUT ROWID;
31
+ INSERT INTO telemetry_aggregate
32
+ SELECT task_id, role_name, turn_id, MIN(first_at), MAX(last_at), SUM(count),
33
+ MAX(max_sequence), SUM(error_count), MAX(updated_at)
34
+ FROM telemetry_aggregate_v3 GROUP BY task_id, role_name, turn_id;
35
+ DROP TABLE telemetry_v3;
36
+ DROP TABLE telemetry_aggregate_v3;
37
+ CREATE TRIGGER telemetry_ai AFTER INSERT ON telemetry
38
+ BEGIN
39
+ INSERT INTO telemetry_aggregate
40
+ (task_id, role_name, turn_id, first_at, last_at, count, max_sequence, error_count, updated_at)
41
+ VALUES (NEW.task_id, NEW.role_name, NEW.turn_id, NEW.received_at, NEW.received_at,
42
+ 1, NEW.sequence,
43
+ CASE WHEN json_valid(NEW.payload)
44
+ AND (COALESCE(json_extract(NEW.payload, '$.error'), '') <> ''
45
+ OR COALESCE(json_extract(NEW.payload, '$.errorKind'), '') <> '')
46
+ THEN 1 ELSE 0 END, NEW.received_at)
47
+ ON CONFLICT(task_id, role_name, turn_id) DO UPDATE SET
48
+ first_at = MIN(telemetry_aggregate.first_at, excluded.first_at),
49
+ last_at = MAX(telemetry_aggregate.last_at, excluded.last_at),
50
+ count = telemetry_aggregate.count + 1,
51
+ max_sequence = CASE WHEN excluded.max_sequence IS NOT NULL
52
+ AND (telemetry_aggregate.max_sequence IS NULL OR excluded.max_sequence > telemetry_aggregate.max_sequence)
53
+ THEN excluded.max_sequence ELSE telemetry_aggregate.max_sequence END,
54
+ error_count = telemetry_aggregate.error_count + excluded.error_count,
55
+ updated_at = excluded.updated_at;
56
+ END;
57
+ CREATE TRIGGER telemetry_au AFTER UPDATE ON telemetry
58
+ BEGIN
59
+ UPDATE telemetry_aggregate SET
60
+ last_at = MAX(last_at, NEW.received_at),
61
+ max_sequence = CASE WHEN NEW.sequence IS NOT NULL
62
+ AND (max_sequence IS NULL OR NEW.sequence > max_sequence) THEN NEW.sequence ELSE max_sequence END,
63
+ updated_at = NEW.received_at
64
+ WHERE task_id = NEW.task_id AND role_name = NEW.role_name AND turn_id = NEW.turn_id;
65
+ END;
66
+
67
+ DROP INDEX idx_session_owners_task;
68
+ ALTER TABLE session_owners RENAME TO session_owners_v3;
69
+ CREATE TABLE session_owners (
70
+ process_key TEXT PRIMARY KEY, scope TEXT NOT NULL CHECK(scope IN ('task','global')),
71
+ task_id TEXT, role_name TEXT NOT NULL, agent_id TEXT NOT NULL, native_session_id TEXT,
72
+ provider_root_pid INTEGER, payload TEXT NOT NULL, recorded_at TEXT NOT NULL
73
+ );
74
+ INSERT INTO session_owners
75
+ SELECT process_key, scope, task_id, role_name, agent_id, native_session_id,
76
+ provider_root_pid, payload, recorded_at
77
+ FROM (
78
+ SELECT *,
79
+ CAST(provider_root_pid AS TEXT) || '-' || json_extract(payload, '$.providerRoot.startIdentity') AS process_key,
80
+ ROW_NUMBER() OVER (
81
+ PARTITION BY provider_root_pid, json_extract(payload, '$.providerRoot.startIdentity')
82
+ ORDER BY recorded_at DESC, launch_id DESC
83
+ ) AS rank
84
+ FROM session_owners_v3
85
+ ) WHERE rank = 1;
86
+ DROP TABLE session_owners_v3;
87
+ CREATE INDEX idx_session_owners_task ON session_owners(task_id, role_name);
88
+
89
+ DROP INDEX idx_runtime_session_cleanup_required;
90
+ ALTER TABLE runtime_session_candidates RENAME TO runtime_session_candidates_v3;
91
+ CREATE TABLE runtime_session_candidates (
92
+ scope TEXT NOT NULL CHECK(scope IN ('task','global')), task_id TEXT NOT NULL,
93
+ role_name TEXT NOT NULL, agent_id TEXT NOT NULL, adapter_id TEXT NOT NULL,
94
+ native_session_id TEXT NOT NULL, session_updated_at TEXT NOT NULL,
95
+ PRIMARY KEY(scope, task_id, role_name),
96
+ CHECK((scope = 'task' AND length(task_id) > 0) OR (scope = 'global' AND task_id = ''))
97
+ );
98
+ INSERT INTO runtime_session_candidates
99
+ SELECT scope, task_id, role_name, agent_id, adapter_id, native_session_id, session_updated_at
100
+ FROM runtime_session_candidates_v3;
101
+ DROP TABLE runtime_session_candidates_v3;
102
+
103
+ -- Retire only obsolete launch bookkeeping; preserve pending intent and cleanup requests.
104
+ UPDATE mailboxes SET processing = NULL
105
+ WHERE target_kind IN ('role-runtime','global-role-runtime')
106
+ AND json_extract(processing, '$.owner') = 'runtime-lifecycle'
107
+ AND json_extract(processing, '$.batch.reasons') = '["runtime-launch-reserved"]';
108
+ `;
109
+ export function removeRuntimeGenerationRecords(db) {
110
+ const object = (value) => (value !== null && typeof value === "object" && !Array.isArray(value));
111
+ const obsolete = new Set([
112
+ "runtimeGenerationId", "hostActivationId", "activationId", "continuationGeneration"
113
+ ]);
114
+ // Accepted Jobs retain exactly the Session authority they already had.
115
+ // Never refresh a revoked/stale binding to the currently active Session.
116
+ for (const row of db.prepare("SELECT task_id, job_id, payload FROM durable_jobs").all()) {
117
+ const job = JSON.parse(row.payload);
118
+ if (!object(job.operation) || typeof job.operation.actorId !== "string")
119
+ continue;
120
+ const prefix = `task:${row.task_id}/role:`;
121
+ if (!job.operation.actorId.startsWith(prefix))
122
+ continue;
123
+ const roleName = job.operation.actorId.slice(prefix.length);
124
+ const roleRow = db.prepare("SELECT payload FROM task_roles WHERE task_id = ? AND role_name = ?")
125
+ .get(row.task_id, roleName);
126
+ const sessionsRow = db.prepare("SELECT payload FROM role_session_sets WHERE task_id = ? AND role_name = ?")
127
+ .get(row.task_id, roleName);
128
+ if (roleRow === undefined || sessionsRow === undefined)
129
+ continue;
130
+ const role = JSON.parse(roleRow.payload);
131
+ const sessions = JSON.parse(sessionsRow.payload);
132
+ const session = sessions.sessions?.[sessions.activeAgentId];
133
+ if (session?.status !== "active" || role.activeAgentId !== sessions.activeAgentId)
134
+ continue;
135
+ const key = db.prepare("SELECT hash FROM job_caller_key_hashes WHERE task_id = ? AND role_name = ? AND agent_id = ?")
136
+ .get(row.task_id, roleName, session.agentId);
137
+ if (key === undefined)
138
+ continue;
139
+ const identity = [session.agentId, session.adapterId, session.nativeSessionId];
140
+ const prior = createHash("sha256").update(JSON.stringify([key.hash, ...identity])).digest("hex");
141
+ if (job.operation.authorityRef !== prior)
142
+ continue;
143
+ job.operation.authorityRef = createHash("sha256").update(JSON.stringify(identity)).digest("hex");
144
+ db.prepare("UPDATE durable_jobs SET payload = ? WHERE task_id = ? AND job_id = ?")
145
+ .run(JSON.stringify(job), row.task_id, row.job_id);
146
+ }
147
+ db.exec("DROP TABLE job_caller_key_hashes");
148
+ const clean = (value) => {
149
+ if (Array.isArray(value))
150
+ return value.map(clean);
151
+ if (!object(value))
152
+ return value;
153
+ const next = {};
154
+ for (const [key, child] of Object.entries(value)) {
155
+ if (obsolete.has(key))
156
+ continue;
157
+ if (key === "activations" && typeof value.providerNamespace === "string"
158
+ && Array.isArray(value.conversations))
159
+ continue;
160
+ if (key === "generation" && typeof value.continuationId === "string")
161
+ continue;
162
+ if (key === "generation" && value.kind === "yui-task-runtime-isolation")
163
+ continue;
164
+ next[key] = clean(child);
165
+ }
166
+ if (typeof next.providerNamespace === "string" && Array.isArray(next.conversations)
167
+ && object(next.authority) && next.authority.owner === "controller") {
168
+ next.authority.holderId = "controller";
169
+ }
170
+ if (next.authorityOwner === "controller" && typeof next.holderId === "string") {
171
+ next.holderId = "controller";
172
+ }
173
+ if (next.kind === "yui-task-runtime-isolation" && object(next.roots)) {
174
+ next.roots.runtime = next.roots.generation;
175
+ delete next.roots.generation;
176
+ }
177
+ return next;
178
+ };
179
+ // These are system-owned record families, not arbitrary user documents or Job outputs.
180
+ for (const table of ["role_session_sets", "global_role_session_sets", "turns", "session_owners", "events"]) {
181
+ const columns = db.prepare(`PRAGMA table_info("${table}")`).all();
182
+ const keys = columns.filter((column) => column.pk > 0).sort((a, b) => a.pk - b.pk).map((c) => c.name);
183
+ const rows = db.prepare(`SELECT * FROM "${table}"`).all();
184
+ const where = keys.map((key) => `"${key}" = ?`).join(" AND ");
185
+ const update = db.prepare(`UPDATE "${table}" SET payload = ?${table === "events" ? ", type = ?" : ""} WHERE ${where}`);
186
+ for (const row of rows) {
187
+ const raw = JSON.parse(row.payload);
188
+ const migrated = clean(raw);
189
+ if (table === "events" && object(migrated.payload)
190
+ && typeof migrated.payload.observation === "string"
191
+ && typeof migrated.type === "string" && migrated.type.startsWith("runtime.")) {
192
+ const observation = JSON.parse(migrated.payload.observation);
193
+ const current = clean(observation);
194
+ if (typeof current.kind === "string" && current.kind.startsWith("activation.")) {
195
+ // Keep the historical fact, but it is no longer a current lifecycle event.
196
+ migrated.type = "runtime.historical-attachment-observed";
197
+ }
198
+ if (typeof current.stopReceiptId === "string") {
199
+ current.stopRequested = true;
200
+ delete current.stopReceiptId;
201
+ }
202
+ migrated.payload.observation = JSON.stringify(current);
203
+ }
204
+ update.run(JSON.stringify(migrated), ...(table === "events" ? [migrated.type] : []), ...keys.map((key) => row[key]));
205
+ }
206
+ }
207
+ }